drm working

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
This commit is contained in:
Caleb Connolly 2023-11-21 02:49:38 +00:00
parent 01af13950c
commit 3a71d0437b
No known key found for this signature in database
GPG key ID: 7930459FB9303217
9 changed files with 665 additions and 63 deletions

37
include/framebuffer.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef _FRAMEBUFFER_H
#define _FRAMEBUFFER_H
#include <stdint.h>
#include <libdrm/drm.h>
#include <libdrm/drm_mode.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
struct modeset_buf {
uint32_t width;
uint32_t height;
uint32_t pitch;
uint32_t size;
uint32_t handle;
uint8_t *map;
uint32_t fb;
};
struct drm_framebuffer {
unsigned int front_buf;
struct modeset_buf bufs[2];
drmModeModeInfo mode;
uint32_t conn;
uint32_t mm_width;
uint32_t mm_height;
uint32_t crtc;
drmModeCrtc *saved_crtc;
};
extern struct drm_framebuffer *drm;
int drm_framebuffer_init(int *handle, const char *card);
void drm_framebuffer_close(int handle);
#endif

View file

@ -85,6 +85,8 @@
*/
int tfb_acquire_fb(u32 flags, const char *fb_device, const char *tty_device);
int tfb_acquire_drm(uint32_t flags, const char *device);
/**
* Release the framebuffer device
*
@ -97,23 +99,6 @@ int tfb_acquire_fb(u32 flags, const char *fb_device, const char *tty_device);
*/
void tfb_release_fb(void);
/**
* Limit the drawing to a window at (x, y) having size (w, h)
*
* In case the application does not want to use the whole screen, it can call
* this function to get the coordinate system shifted by (+x, +y) and everything
* outside of it just cut off. Using windows smaller than then screen could
* improve application's performance.
*
* @param[in] x X coordinate of the window, in pixels
* @param[in] y Y coordinate of the window, in pixels
* @param[in] w Width of the window, in pixels
* @param[in] h Height of the window, in pixels
*
* @return #TFB_SUCCESS in case of success or #TFB_ERR_INVALID_WINDOW.
*/
int tfb_set_window(u32 x, u32 y, u32 w, u32 h);
/**
* Limit the drawing to a window having size (w, h) at the center of the screen
*
@ -529,6 +514,7 @@ extern u8 __fb_g_pos;
extern u8 __fb_b_pos;
extern uint32_t tfb_red;
extern uint32_t tfb_green;
extern uint32_t tfb_blue;
extern uint32_t tfb_white;
extern uint32_t tfb_gray;

View file

@ -2,7 +2,8 @@ project('pbsplash', 'c')
cc = meson.get_compiler('c')
deps = [
cc.find_library('m', required : false)
cc.find_library('m', required : false),
dependency('libdrm', required : true),
]
inc = [

View file

@ -1,8 +1,8 @@
#include "pbsplash.h"
#include <math.h>
#include <stdio.h>
#include <tfblib/tfb_colors.h>
#include <tfblib/tfblib.h>
#include "tfblib.h"
struct col color = { .r = 255, .g = 255, .b = 255, .a = 255 };
@ -28,6 +28,14 @@ static void circles_wave(int frame, int w, int y_off, long dpi)
amplitude * 2 + rad * 2 + 6, tfb_black);
tfb_fill_circle(x, y, rad, t_col);
}
// tfb_draw_line(0, 0, 100, 500, tfb_red);
// tfb_draw_line(100, 0, 100, 500, tfb_green);
// tfb_draw_line(200, 0, 100, 500, tfb_blue);
// tfb_fill_rect(400, 300, 200, 500, tfb_red);
// tfb_fill_rect(600, 300, 200, 500, tfb_green);
// tfb_fill_rect(800, 300, 200, 500, tfb_blue);
}
void animate_frame(int frame, int w, int y_off, long dpi)

View file

@ -8,6 +8,7 @@
#include <string.h>
#include "pbsplash.h"
#include "framebuffer.h"
#include "tfblib.h"
extern inline uint32_t tfb_make_color(uint8_t red, uint8_t green, uint8_t blue);
@ -49,11 +50,6 @@ uint8_t __fb_r_pos;
uint8_t __fb_g_pos;
uint8_t __fb_b_pos;
int tfb_set_center_window_size(uint32_t w, uint32_t h)
{
return tfb_set_window(__fb_screen_w / 2 - w / 2, __fb_screen_h / 2 - h / 2, w, h);
}
void tfb_clear_screen(uint32_t color)
{
if (__fb_pitch == (uint32_t)4 * __fb_screen_w) {
@ -141,10 +137,22 @@ void tfb_fill_rect(int x, int y, int w, int h, uint32_t color)
if (w < 0 || h < 0)
return;
/*
for (uint32_t cy = y; cy < yend; cy++) {
//memset(dest, color, w);
for (uint32_t cx = x; cx < x + w; cx++)
tfb_draw_pixel(cx, cy, color);
}
*/
w = MIN(w, MAX(0, (int)__fb_win_end_x - x));
yend = MIN(y + h, __fb_win_end_y);
dest = __fb_buffer + y * __fb_pitch + (x << 2);
dest = __fb_buffer + y * __fb_pitch + (x * 4);
/* drm alignment weirdness */
//if (drm)
w *= 4;
for (uint32_t cy = y; cy < yend; cy++, dest += __fb_pitch)
memset(dest, color, w);

488
src/drm.c Normal file
View file

@ -0,0 +1,488 @@
/*
* modeset - DRM Double-Buffered Modesetting Example
*
* Written 2012 by David Rheinsberg <david.rheinsberg@gmail.com>
* Dedicated to the Public Domain.
*/
/*
* DRM Double-Buffered Modesetting Howto
* This example extends the modeset.c howto and introduces double-buffering.
* When drawing a new frame into a framebuffer, we should always draw into an
* unused buffer and not into the front buffer. If we draw into the front
* buffer, we might have drawn half the frame when the display-controller starts
* scanning out the next frame. Hence, we see flickering on the screen.
* The technique to avoid this is called double-buffering. We have two
* framebuffers, the front buffer which is currently used for scanout and a
* back-buffer that is used for drawing operations. When a frame is done, we
* simply swap both buffers.
* Swapping does not mean copying data, instead, only the pointers to the
* buffers are swapped.
*
* Please read modeset.c before reading this file as most of the functions stay
* the same. Only the differences are highlighted here.
* Also note that triple-buffering or any other number of buffers can be easily
* implemented by following the scheme here. However, in this example we limit
* the number of buffers to 2 so it is easier to follow.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "framebuffer.h"
struct modeset_buf;
struct drm_framebuffer;
static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn,
struct drm_framebuffer *dev);
static int modeset_create_fb(int fd, struct modeset_buf *buf);
static void modeset_destroy_fb(int fd, struct modeset_buf *buf);
static int modeset_setup_dev(int fd, drmModeRes *res, drmModeConnector *conn,
struct drm_framebuffer *dev);
static int drm_init(int fd);
/*
* modeset_open() stays the same as before.
*/
static int drm_open(int *handle, const char *node)
{
int fd, ret;
uint64_t has_dumb;
fd = open(node, O_RDWR | O_CLOEXEC);
if (fd < 0) {
ret = -errno;
fprintf(stderr, "cannot open '%s': %m\n", node);
return ret;
}
if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 ||
!has_dumb) {
fprintf(stderr, "drm device '%s' does not support dumb buffers\n",
node);
close(fd);
return -EOPNOTSUPP;
}
ret = drm_init(fd);
if (ret) {
close(fd);
return ret;
}
*handle = fd;
return 0;
}
/*
* Previously, we used the modeset_dev objects to hold buffer informations, too.
* Technically, we could have split them but avoided this to make the
* example simpler.
* However, in this example we need 2 buffers. One back buffer and one front
* buffer. So we introduce a new structure modeset_buf which contains everything
* related to a single buffer. Each device now gets an array of two of these
* buffers.
* Each buffer consists of width, height, stride, size, handle, map and fb-id.
* They have the same meaning as before.
*
* Each device also gets a new integer field: front_buf. This field contains the
* index of the buffer that is currently used as front buffer / scanout buffer.
* In our example it can be 0 or 1. We flip it by using XOR:
* dev->front_buf ^= dev->front_buf
*
* Everything else stays the same.
*/
struct drm_framebuffer *drm = NULL;
/*
* modeset_prepare() stays the same.
*/
static int drm_init(int fd)
{
drmModeRes *res;
drmModeConnector *conn;
unsigned int i;
struct drm_framebuffer *dev;
struct modeset_buf *buf;
int ret;
/* retrieve resources */
res = drmModeGetResources(fd);
if (!res) {
fprintf(stderr, "cannot retrieve DRM resources (%d): %m\n",
errno);
return -errno;
}
/* iterate all connectors */
for (i = 0; i < res->count_connectors; ++i) {
/* get information for each connector */
conn = drmModeGetConnector(fd, res->connectors[i]);
if (!conn) {
fprintf(stderr, "cannot retrieve DRM connector %u:%u (%d): %m\n",
i, res->connectors[i], errno);
continue;
}
/* create a device structure */
dev = malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
dev->conn = conn->connector_id;
dev->mm_width = conn->mmWidth;
dev->mm_height = conn->mmHeight;
/* call helper function to prepare this connector */
ret = modeset_setup_dev(fd, res, conn, dev);
if (ret) {
if (ret != -ENOENT) {
errno = -ret;
fprintf(stderr, "cannot setup device for connector %u:%u (%d): %m\n",
i, res->connectors[i], errno);
}
free(dev);
drmModeFreeConnector(conn);
continue;
}
/* free connector data and link device into global list */
drmModeFreeConnector(conn);
drm = dev;
break;
}
/* free resources again */
drmModeFreeResources(res);
/* perform actual modesetting on each found connector+CRTC */
dev->saved_crtc = drmModeGetCrtc(fd, dev->crtc);
buf = &dev->bufs[dev->front_buf];
ret = drmModeSetCrtc(fd, dev->crtc, buf->fb, 0, 0,
&dev->conn, 1, &dev->mode);
if (ret)
fprintf(stderr, "cannot set CRTC for connector %u (%d): %m\n",
dev->conn, errno);
return 0;
}
/*
* modeset_setup_dev() sets up all resources for a single device. It mostly
* stays the same, but one thing changes: We allocate two framebuffers instead
* of one. That is, we call modeset_create_fb() twice.
* We also copy the width/height information into both framebuffers so
* modeset_create_fb() can use them without requiring a pointer to modeset_dev.
*/
static int modeset_setup_dev(int fd, drmModeRes *res, drmModeConnector *conn,
struct drm_framebuffer *dev)
{
int ret;
/* check if a monitor is connected */
if (conn->connection != DRM_MODE_CONNECTED) {
fprintf(stderr, "ignoring unused connector %u\n",
conn->connector_id);
return -ENOENT;
}
/* check if there is at least one valid mode */
if (conn->count_modes == 0) {
fprintf(stderr, "no valid mode for connector %u\n",
conn->connector_id);
return -EFAULT;
}
/* copy the mode information into our device structure and into both
* buffers */
memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode));
dev->bufs[0].width = conn->modes[0].hdisplay;
dev->bufs[0].height = conn->modes[0].vdisplay;
dev->bufs[1].width = conn->modes[0].hdisplay;
dev->bufs[1].height = conn->modes[0].vdisplay;
fprintf(stderr, "mode for connector %u is %ux%u\n",
conn->connector_id, dev->bufs[0].width, dev->bufs[0].height);
/* find a crtc for this connector */
ret = modeset_find_crtc(fd, res, conn, dev);
if (ret) {
fprintf(stderr, "no valid crtc for connector %u\n",
conn->connector_id);
return ret;
}
/* create framebuffer #1 for this CRTC */
ret = modeset_create_fb(fd, &dev->bufs[0]);
if (ret) {
fprintf(stderr, "cannot create framebuffer for connector %u\n",
conn->connector_id);
return ret;
}
/* create framebuffer #2 for this CRTC */
ret = modeset_create_fb(fd, &dev->bufs[1]);
if (ret) {
fprintf(stderr, "cannot create framebuffer for connector %u\n",
conn->connector_id);
modeset_destroy_fb(fd, &dev->bufs[0]);
return ret;
}
return 0;
}
/*
* modeset_find_crtc() stays the same.
*/
static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn,
struct drm_framebuffer *dev)
{
drmModeEncoder *enc;
unsigned int i, j;
int32_t crtc;
/* first try the currently conected encoder+crtc */
if (conn->encoder_id)
enc = drmModeGetEncoder(fd, conn->encoder_id);
else
enc = NULL;
if (enc) {
if (enc->crtc_id) {
crtc = enc->crtc_id;
if (crtc >= 0) {
drmModeFreeEncoder(enc);
dev->crtc = crtc;
return 0;
}
}
drmModeFreeEncoder(enc);
}
/* If the connector is not currently bound to an encoder or if the
* encoder+crtc is already used by another connector (actually unlikely
* but lets be safe), iterate all other available encoders to find a
* matching CRTC. */
for (i = 0; i < conn->count_encoders; ++i) {
enc = drmModeGetEncoder(fd, conn->encoders[i]);
if (!enc) {
fprintf(stderr, "cannot retrieve encoder %u:%u (%d): %m\n",
i, conn->encoders[i], errno);
continue;
}
/* iterate all global CRTCs */
for (j = 0; j < res->count_crtcs; ++j) {
/* check whether this CRTC works with the encoder */
if (!(enc->possible_crtcs & (1 << j)))
continue;
/* check that no other device already uses this CRTC */
crtc = res->crtcs[j];
/* we have found a CRTC, so save it and return */
if (crtc >= 0) {
drmModeFreeEncoder(enc);
dev->crtc = crtc;
return 0;
}
}
drmModeFreeEncoder(enc);
}
fprintf(stderr, "cannot find suitable CRTC for connector %u\n",
conn->connector_id);
return -ENOENT;
}
/*
* modeset_create_fb() is mostly the same as before. Buf instead of writing the
* fields of a modeset_dev, we now require a buffer pointer passed as @buf.
* Please note that buf->width and buf->height are initialized by
* modeset_setup_dev() so we can use them here.
*/
static int modeset_create_fb(int fd, struct modeset_buf *buf)
{
struct drm_mode_create_dumb creq;
struct drm_mode_destroy_dumb dreq;
struct drm_mode_map_dumb mreq;
int ret;
/* create dumb buffer */
memset(&creq, 0, sizeof(creq));
creq.width = buf->width;
creq.height = buf->height;
creq.bpp = 32;
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
if (ret < 0) {
fprintf(stderr, "cannot create dumb buffer (%d): %m\n",
errno);
return -errno;
}
buf->pitch = creq.pitch;
buf->size = creq.size;
buf->handle = creq.handle;
/* create framebuffer object for the dumb-buffer */
ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->pitch,
buf->handle, &buf->fb);
if (ret) {
fprintf(stderr, "cannot create framebuffer (%d): %m\n",
errno);
ret = -errno;
goto err_destroy;
}
/* prepare buffer for memory mapping */
memset(&mreq, 0, sizeof(mreq));
mreq.handle = buf->handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret) {
fprintf(stderr, "cannot map dumb buffer (%d): %m\n",
errno);
ret = -errno;
goto err_fb;
}
/* perform actual memory mapping */
buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, mreq.offset);
if (buf->map == MAP_FAILED) {
fprintf(stderr, "cannot mmap dumb buffer (%d): %m\n",
errno);
ret = -errno;
goto err_fb;
}
/* clear the framebuffer to 0 */
memset(buf->map, 0, buf->size);
return 0;
err_fb:
drmModeRmFB(fd, buf->fb);
err_destroy:
memset(&dreq, 0, sizeof(dreq));
dreq.handle = buf->handle;
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
return ret;
}
/*
* modeset_destroy_fb() is a new function. It does exactly the reverse of
* modeset_create_fb() and destroys a single framebuffer. The modeset.c example
* used to do this directly in modeset_cleanup().
* We simply unmap the buffer, remove the drm-FB and destroy the memory buffer.
*/
static void modeset_destroy_fb(int fd, struct modeset_buf *buf)
{
struct drm_mode_destroy_dumb dreq;
/* unmap buffer */
munmap(buf->map, buf->size);
/* delete framebuffer */
drmModeRmFB(fd, buf->fb);
/* delete dumb buffer */
memset(&dreq, 0, sizeof(dreq));
dreq.handle = buf->handle;
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
}
int drm_framebuffer_init(int *handle, const char *card)
{
int ret;
card = card ?: "/dev/dri/card0";
printf("using card '%s'\n", card);
/* open the DRM device */
ret = drm_open(handle, card);
if (ret) {
errno = -ret;
fprintf(stderr, "drm_open failed with error %d: %m\n", errno);
return ret;
}
printf("DRM mode %dx%d @ %dHz\n", drm->bufs[0].width, drm->bufs[0].height,
drm->mode.vrefresh);
/* draw some colors for 5seconds */
// modeset_draw(*handle);
// /* cleanup everything */
// drm_framebuffer_close(*handle);
return ret;
}
/*
* modeset_cleanup() stays the same as before. But it now calls
* modeset_destroy_fb() instead of accessing the framebuffers directly.
*/
void drm_framebuffer_close(int handle)
{
/* restore saved CRTC configuration */
drmModeSetCrtc(handle,
drm->saved_crtc->crtc_id,
drm->saved_crtc->buffer_id,
drm->saved_crtc->x,
drm->saved_crtc->y,
&drm->conn,
1,
&drm->saved_crtc->mode);
drmModeFreeCrtc(drm->saved_crtc);
/* destroy framebuffers */
modeset_destroy_fb(handle, &drm->bufs[1]);
modeset_destroy_fb(handle, &drm->bufs[0]);
/* free allocated memory */
free(drm);
}
/*
* This was a very short extension to the basic modesetting example that shows
* how double-buffering is implemented. Double-buffering is the de-facto
* standard in any graphics application so any other example will be based on
* this. It is important to understand the ideas behind it as the code is pretty
* easy and short compared to modeset.c.
*
* Double-buffering doesn't solve all problems. Vsync'ed page-flips solve most
* of the problems that still occur, but has problems on it's own (see
* modeset-vsync.c for a discussion).
*
* If you want more code, I can recommend reading the source-code of:
* - plymouth (which uses dumb-buffers like this example; very easy to understand)
* - kmscon (which uses libuterm to do this)
* - wayland (very sophisticated DRM renderer; hard to understand fully as it
* uses more complicated techniques like DRM planes)
* - xserver (very hard to understand as it is split across many files/projects)
*
* Any feedback is welcome. Feel free to use this code freely for your own
* documentation or projects.
*
* - Hosted on http://github.com/dvdhrm/docs
* - Written by David Rheinsberg <david.rheinsberg@gmail.com>
*/

View file

@ -15,6 +15,7 @@
#include <termios.h>
#include <unistd.h>
#include "framebuffer.h"
#include "pbsplash.h"
#include "tfblib.h"
@ -23,15 +24,14 @@
struct fb_var_screeninfo __fbi;
#define ROTATE_SWAP_XY (__fbi.rotate == 1 || __fbi.rotate == 3)
int __tfb_ttyfd = -1;
static int fbfd = -1;
static int drmfd = -1;
static void tfb_init_colors(void);
int tfb_set_window(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
static int tfb_set_window(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t xoffset, uint32_t yoffset)
{
if (x + w > (uint32_t)__fb_screen_w) {
fprintf(stderr, "tfb_set_window: window exceeds screen width\n");
@ -41,8 +41,8 @@ int tfb_set_window(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
fprintf(stderr, "tfb_set_window: window exceeds screen height\n");
}
__fb_off_x = __fbi.xoffset + x;
__fb_off_y = __fbi.yoffset + y;
__fb_off_x = xoffset + x;
__fb_off_y = yoffset + y;
__fb_win_w = w;
__fb_win_h = h;
__fb_win_end_x = __fb_off_x + __fb_win_w;
@ -51,6 +51,42 @@ int tfb_set_window(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
return 0;
}
int tfb_acquire_drm(uint32_t flags, const char *device)
{
int ret;
ret = drm_framebuffer_init(&drmfd, device);
if (ret) {
fprintf(stderr, "Failed to get framebuffer\n");
return ret;
}
__fb_real_buffer = drm->bufs[0].map;
__fb_buffer = drm->bufs[1].map;
__fb_pitch = drm->bufs[0].pitch;
__fb_size = drm->bufs[0].size;
__fb_pitch_div4 = __fb_pitch >> 2;
__fb_screen_w = drm->bufs[0].width;
__fb_screen_h = drm->bufs[0].height;
__fb_r_pos = 16;
__fb_r_mask_size = 8;
__fb_r_mask = 0xff << __fb_r_pos;
__fb_g_pos = 8;
__fb_g_mask_size = 8;
__fb_g_mask = 0xff << __fb_g_pos;
__fb_b_pos = 0;
__fb_b_mask_size = 8;
__fb_b_mask = 0xff << __fb_b_pos;
tfb_set_window(0, 0, __fb_screen_w, __fb_screen_h, 0, 0);
tfb_init_colors();
return 0;
}
int tfb_acquire_fb(uint32_t flags, const char *fb_device, const char *tty_device)
{
static struct fb_fix_screeninfo fb_fixinfo;
@ -134,8 +170,8 @@ int tfb_acquire_fb(uint32_t flags, const char *fb_device, const char *tty_device
__fb_buffer = __fb_real_buffer;
}
__fb_screen_w = ROTATE_SWAP_XY ? __fbi.yres : __fbi.xres;
__fb_screen_h = ROTATE_SWAP_XY ? __fbi.xres : __fbi.yres;
__fb_screen_w = __fbi.xres;
__fb_screen_h = __fbi.yres;
__fb_r_pos = __fbi.red.offset;
__fb_r_mask_size = __fbi.red.length;
@ -149,7 +185,7 @@ int tfb_acquire_fb(uint32_t flags, const char *fb_device, const char *tty_device
__fb_b_mask_size = __fbi.blue.length;
__fb_b_mask = ((1 << __fb_b_mask_size) - 1) << __fb_b_pos;
tfb_set_window(0, 0, __fb_screen_w, __fb_screen_h);
tfb_set_window(0, 0, __fb_screen_w, __fb_screen_h, __fbi.xoffset, __fbi.yoffset);
tfb_init_colors();
return 0;
@ -157,6 +193,10 @@ int tfb_acquire_fb(uint32_t flags, const char *fb_device, const char *tty_device
void tfb_release_fb(void)
{
if (drmfd >= 0) {
drm_framebuffer_close(drmfd);
return;
}
if (__fb_real_buffer)
munmap(__fb_real_buffer, __fb_size);
@ -219,6 +259,23 @@ void tfb_flush_window(void)
int tfb_flush_fb(void)
{
int ret;
struct modeset_buf *buf;
if (drmfd >= 0) {
//printf("%s buffer %d\n", __func__, drm->front_buf);
buf = &drm->bufs[drm->front_buf ^ 1];
ret = drmModeSetCrtc(drmfd, drm->crtc, buf->fb, 0, 0,
&drm->conn, 1, &drm->mode);
if (ret)
fprintf(stderr, "cannot flip CRTC for connector %u (%d): %m\n",
drm->conn, errno);
else
drm->front_buf ^= 1;
/* Swap the tfblib copies of the pointers */
__fb_buffer = buf->map; //drm->bufs[drm->front_buf ^ 1].map;
return 0;
}
__fbi.activate |= FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &__fbi) < 0) {
perror("Couldn't flush framebuffer");
@ -230,11 +287,17 @@ int tfb_flush_fb(void)
uint32_t tfb_screen_width_mm(void)
{
return ROTATE_SWAP_XY ? __fbi.height : __fbi.width;
if (drmfd >= 0)
return drm->mm_width;
return __fbi.width;
}
uint32_t tfb_screen_height_mm(void)
{
return ROTATE_SWAP_XY ? __fbi.width : __fbi.height;
if (drmfd >= 0)
return drm->mm_height;
return __fbi.height;
}
/*
@ -246,6 +309,7 @@ uint32_t tfb_screen_height_mm(void)
*/
uint32_t tfb_red;
uint32_t tfb_green;
uint32_t tfb_blue;
uint32_t tfb_white;
uint32_t tfb_gray;
@ -254,6 +318,7 @@ uint32_t tfb_black;
static void tfb_init_colors(void)
{
tfb_red = tfb_make_color(255, 0, 0);
tfb_green = tfb_make_color(0, 255, 0);
tfb_blue = tfb_make_color(0, 0, 255);
tfb_white = tfb_make_color(255, 255, 255);
tfb_gray = tfb_make_color(128, 128, 128);

View file

@ -4,6 +4,7 @@ src = [
'timespec.c',
'pbsplash.c',
'fb.c',
'drm.c',
'drawing.c',
]

View file

@ -6,8 +6,6 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <tfblib/tfb_colors.h>
#include <tfblib/tfblib.h>
#include <time.h>
#include <unistd.h>
@ -17,6 +15,7 @@
#include "nanosvg.h"
#include "nanosvgrast.h"
#include "pbsplash.h"
#include "tfblib.h"
#include "timespec.h"
#define MSG_MAX_LEN 4096
@ -30,7 +29,7 @@
volatile sig_atomic_t terminate = 0;
bool debug = false;
bool debug = true;
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
static int screenWidth, screenHeight;
@ -78,9 +77,10 @@ static void draw_svg(NSVGimage *image, int x, int y, int w, int h)
LOG("draw_svg: (%d, %d), %dx%d, %f\n", x, y, w, h, sz);
NSVGrasterizer *rast = nsvgCreateRasterizer();
unsigned char *img = zalloc(w * h * 4);
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
nsvgRasterize(rast, image, 0, 0, sz, img, w, h, w * 4);
blit_buf(img, x, y, w, h, false);
blit_buf(img, x, y, w, h, bg, false);
free(img);
nsvgDeleteRasterizer(rast);
@ -94,10 +94,11 @@ static void draw_text(const NSVGimage *font, const char *text, int x, int y, int
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
unsigned char *img = zalloc(width * height * 4);
NSVGrasterizer *rast = nsvgCreateRasterizer();
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height, width * 4, text);
blit_buf(img, x, y, width, height, true);
blit_buf(img, x, y, width, height, bg, true);
free(img);
free(shapes);
@ -198,7 +199,7 @@ static void calculate_dpi_info(struct dpi_info *dpi_info)
int h_mm = tfb_screen_height_mm();
if ((w_mm < 1 || h_mm < 1) && !dpi_info->dpi) {
fprintf(stderr, "ERROR!!!: Invalid screen size: %dx%d\n", w_mm, h_mm);
fprintf(stderr, "ERROR!!!: Invalid screen size: %dmmx%dmm\n", w_mm, h_mm);
// Assume a dpi of 300
// This should be readable everywhere
@ -467,22 +468,28 @@ int main(int argc, char **argv)
}
}
{
FILE *fp = fopen("/sys/devices/virtual/tty/tty0/active", "r");
int len = strlen(active_tty);
char *ptr = active_tty + len;
if (fp != NULL) {
fgets(ptr, TTY_PATH_LEN - len, fp);
*(ptr + strlen(ptr) - 1) = '\0';
fclose(fp);
}
}
// {
// FILE *fp = fopen("/sys/devices/virtual/tty/tty0/active", "r");
// int len = strlen(active_tty);
// char *ptr = active_tty + len;
// if (fp != NULL) {
// fgets(ptr, TTY_PATH_LEN - len, fp);
// *(ptr + strlen(ptr) - 1) = '\0';
// fclose(fp);
// }
// }
LOG("active tty: '%s'\n", active_tty);
// LOG("active tty: '%s'\n", active_tty);
if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0", active_tty)) !=
TFB_SUCCESS) {
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
// if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0", active_tty)) !=
// TFB_SUCCESS) {
// fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
// rc = 1;
// return rc;
// }
if ((rc = tfb_acquire_drm(0, "/dev/dri/card0")) != 0) {
fprintf(stderr, "tfb_acquire_drm() failed with error code: %d\n", rc);
rc = 1;
return rc;
}
@ -521,15 +528,16 @@ int main(int argc, char **argv)
show_messages(&msgs, &dpi_info);
no_messages:
/* This is necessary to copy the parts we draw once (like the logo) to the front buffer */
tfb_flush_window();
tfb_flush_fb();
int tick = 0;
int tty = open(active_tty, O_RDWR);
if (!tty) {
fprintf(stderr, "Failed to open tty %s (%d)\n", active_tty, errno);
goto out;
}
// int tty = open(active_tty, O_RDWR);
// if (!tty) {
// fprintf(stderr, "Failed to open tty %s (%d)\n", active_tty, errno);
// goto out;
// }
struct timespec epoch, start, end, diff;
int target_fps = 60;
@ -546,7 +554,7 @@ no_messages:
tfb_flush_fb();
clock_gettime(CLOCK_REALTIME, &end);
diff = timespec_sub(end, start);
// printf("%05d: %09ld\n", tick, diff.tv_nsec);
//printf("%05d: %09ld\n", tick, diff.tv_nsec);
if (diff.tv_nsec < 1000000000 / target_fps) {
struct timespec sleep_time = {
.tv_sec = 0,
@ -559,7 +567,7 @@ no_messages:
out:
// Before we exit print the logo so it will persist
if (image_info.image) {
ioctl(tty, KDSETMODE, KD_TEXT);
//ioctl(tty, KDSETMODE, KD_TEXT);
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width,
image_info.height);
}