Skip to content

Commit 34dfd79

Browse files
committed
Support Linux framebuffer as new backend
1 parent c007403 commit 34dfd79

File tree

4 files changed

+184
-2
lines changed

4 files changed

+184
-2
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Generated
22
*.o
33
*.o.d
4-
demo-sdl
5-
.demo-sdl
4+
demo-*
5+
.demo-*
66
libtwin.a
77
.libtwin.a
88
libbackend.a

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ libtwin.a_cflags-y += $(shell sdl2-config --cflags)
9393
TARGET_LIBS += $(shell sdl2-config --libs)
9494
endif
9595

96+
ifeq ($(CONFIG_BACKEND_FBDEV), y)
97+
BACKEND = fbdev
98+
libtwin.a_files-y += backend/fbdev.c
99+
endif
100+
96101
# Standalone application
97102

98103
ifeq ($(CONFIG_DEMO_APPLICATIONS), y)

backend/.sdl.c.swo

16 KB
Binary file not shown.

backend/fbdev.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Twin - A Tiny Window System
3+
* Copyright (c) 2024 National Cheng Kung University, Taiwan
4+
* All rights reserved.
5+
*/
6+
7+
#include <fcntl.h>
8+
#include <linux/fb.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <sys/ioctl.h>
12+
#include <sys/mman.h>
13+
#include <twin.h>
14+
#include <unistd.h>
15+
16+
#include "twin_backend.h"
17+
18+
typedef struct {
19+
int fd;
20+
int depth;
21+
int image_y;
22+
int *pixels;
23+
int *fbdev_raw;
24+
twin_screen_t *screen;
25+
} twin_fbdev_t;
26+
27+
#define FBDEV_ENVVAR "FRAMEBUFFER"
28+
#define FBDEV_DEFAULT "/dev/fb0"
29+
#define SCREEN(x) ((twin_context_t *) x)->screen
30+
#define PRIV(x) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
31+
32+
static void _twin_fbdev_put_span(twin_coord_t left,
33+
twin_coord_t top,
34+
twin_coord_t right,
35+
twin_argb32_t *pixels,
36+
void *closure)
37+
{
38+
twin_screen_t *screen = SCREEN(closure);
39+
twin_fbdev_t *tx = PRIV(closure);
40+
41+
for (twin_coord_t ix = left, iy = top; ix < right; ix++) {
42+
twin_argb32_t pixel = *pixels++;
43+
tx->pixels[iy * screen->width + ix] = pixel;
44+
}
45+
46+
/* Update the pixels to the framebuffer */
47+
memcpy(tx->fbdev_raw, tx->pixels,
48+
sizeof(uint32_t) * screen->width * screen->height);
49+
}
50+
51+
static void twin_fbdev_get_screen_size(twin_fbdev_t *tx,
52+
int *width,
53+
int *height)
54+
{
55+
struct fb_var_screeninfo info;
56+
ioctl(tx->fd, FBIOGET_VSCREENINFO, &info);
57+
*width = info.xres;
58+
*height = info.yres;
59+
}
60+
61+
static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
62+
{
63+
int width, height;
64+
twin_fbdev_get_screen_size(tx, &width, &height);
65+
twin_screen_damage(tx->screen, 0, 0, width, height);
66+
}
67+
68+
static bool twin_fbdev_read_events(int file maybe_unused,
69+
twin_file_op_t ops maybe_unused,
70+
void *closure)
71+
{
72+
/* TODO: Implement events handling for mouse, keyboard, etc. */
73+
return true;
74+
}
75+
76+
static bool twin_fbdev_work(void *closure)
77+
{
78+
twin_screen_t *screen = SCREEN(closure);
79+
80+
if (twin_screen_damaged(screen))
81+
twin_screen_update(screen);
82+
return true;
83+
}
84+
85+
twin_context_t *twin_fbdev_init(int width, int height)
86+
{
87+
char *fbdev_path = getenv(FBDEV_ENVVAR);
88+
if (!fbdev_path) {
89+
printf("Environment variable $FRAMEBUFFER not set, use %s by default\n",
90+
FBDEV_DEFAULT);
91+
fbdev_path = FBDEV_DEFAULT;
92+
}
93+
94+
twin_context_t *ctx = calloc(1, sizeof(twin_context_t));
95+
if (!ctx)
96+
return NULL;
97+
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
98+
if (!ctx->priv)
99+
return NULL;
100+
101+
twin_fbdev_t *tx = ctx->priv;
102+
103+
/* Open the framebuffer device */
104+
tx->fd = open(fbdev_path, O_RDWR);
105+
if (tx->fd == -1) {
106+
printf("error : failed opening %s\n", fbdev_path);
107+
goto bail;
108+
}
109+
110+
/* Read framebuffer information */
111+
struct fb_var_screeninfo info;
112+
if (ioctl(tx->fd, FBIOGET_VSCREENINFO, &info) == -1) {
113+
printf("error : failed getting framebuffer information\n");
114+
goto bail_fd;
115+
}
116+
width = info.xres;
117+
height = info.yres;
118+
119+
/* Create memory mapping for accessing the framebuffer */
120+
tx->fbdev_raw = mmap(NULL, width * height * info.bits_per_pixel / 8,
121+
PROT_READ | PROT_WRITE, MAP_SHARED, tx->fd, 0);
122+
if (tx->fbdev_raw == MAP_FAILED) {
123+
printf("error : failed calling mmap()\n");
124+
goto bail_fd;
125+
}
126+
127+
/* Create buffer space for TWIN */
128+
tx->pixels = malloc(width * height * sizeof(uint32_t));
129+
if (!tx->pixels) {
130+
printf("error : failed calling malloc()\n");
131+
goto bail_fd;
132+
}
133+
memset(tx->pixels, 255, width * height * sizeof(uint32_t));
134+
135+
/* Create TWIN screen */
136+
ctx->screen =
137+
twin_screen_create(width, height, NULL, _twin_fbdev_put_span, ctx);
138+
139+
/* Setup file handler and work function for framebuffer backend */
140+
twin_set_file(twin_fbdev_read_events, 0, TWIN_READ, ctx);
141+
twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, ctx);
142+
143+
return ctx;
144+
145+
bail_fd:
146+
close(tx->fd);
147+
bail:
148+
free(ctx->priv);
149+
free(ctx);
150+
return NULL;
151+
}
152+
153+
static void twin_fbdev_configure(twin_context_t *ctx)
154+
{
155+
int width, height;
156+
twin_fbdev_t *tx = ctx->priv;
157+
twin_fbdev_get_screen_size(tx, &width, &height);
158+
twin_screen_resize(ctx->screen, width, height);
159+
}
160+
161+
static void twin_fbdev_exit(twin_context_t *ctx)
162+
{
163+
if (!ctx)
164+
return;
165+
close(PRIV(ctx)->fd);
166+
free(PRIV(ctx)->pixels);
167+
free(ctx->priv);
168+
free(ctx);
169+
}
170+
171+
/* Register the Linux framebuffer backend */
172+
173+
const twin_backend_t g_twin_backend = {
174+
.init = twin_fbdev_init,
175+
.configure = twin_fbdev_configure,
176+
.exit = twin_fbdev_exit,
177+
};

0 commit comments

Comments
 (0)