Skip to content

Commit f398d11

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

File tree

3 files changed

+196
-2
lines changed

3 files changed

+196
-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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ 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+
libtwin.a_cflags-y += $(shell sdl2-config --cflags)
100+
TARGET_LIBS += $(shell sdl2-config --libs)
101+
endif
102+
96103
# Standalone application
97104

98105
ifeq ($(CONFIG_DEMO_APPLICATIONS), y)

backend/fbdev.c

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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_coord_t width, height;
26+
} twin_fbdev_t;
27+
28+
#define FBDEV_ENVVAR "FRAMEBUFFER"
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_begin(twin_coord_t left,
33+
twin_coord_t top,
34+
twin_coord_t right,
35+
twin_coord_t bottom,
36+
void *closure)
37+
{
38+
twin_fbdev_t *tx = PRIV(closure);
39+
tx->width = right - left;
40+
tx->height = bottom - top;
41+
tx->image_y = top;
42+
}
43+
44+
static void _twin_fbdev_put_span(twin_coord_t left,
45+
twin_coord_t top,
46+
twin_coord_t right,
47+
twin_argb32_t *pixels,
48+
void *closure)
49+
{
50+
twin_screen_t *screen = SCREEN(closure);
51+
twin_fbdev_t *tx = PRIV(closure);
52+
53+
for (twin_coord_t ix = left, iy = top; ix < right; ix++) {
54+
twin_argb32_t pixel = *pixels++;
55+
tx->pixels[iy * screen->width + ix] = pixel;
56+
}
57+
if ((top + 1 - tx->image_y) == tx->height) {
58+
/* Update the pixels to the framebuffer */
59+
memcpy(tx->fbdev_raw, tx->pixels,
60+
sizeof(uint32_t) * screen->width * screen->height);
61+
}
62+
}
63+
64+
static void twin_fbdev_get_screen_size(twin_fbdev_t *tx, int *width, int *height)
65+
{
66+
struct fb_var_screeninfo info;
67+
ioctl(tx->fd, FBIOGET_VSCREENINFO, &info);
68+
*width = info.xres;
69+
*height = info.yres;
70+
}
71+
72+
static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
73+
{
74+
int width, height;
75+
twin_fbdev_get_screen_size(tx, &width, &height);
76+
twin_screen_damage(tx->screen, 0, 0, width, height);
77+
}
78+
79+
static bool twin_fbdev_read_events(int file maybe_unused,
80+
twin_file_op_t ops maybe_unused,
81+
void *closure)
82+
{
83+
/* TODO: Implement events handling for mouse, keyboard, etc. */
84+
return true;
85+
}
86+
87+
static bool twin_fbdev_work(void *closure)
88+
{
89+
twin_screen_t *screen = SCREEN(closure);
90+
91+
if (twin_screen_damaged(screen))
92+
twin_screen_update(screen);
93+
return true;
94+
}
95+
96+
twin_context_t *twin_fbdev_init(int width, int height)
97+
{
98+
char *fbdev_path = getenv(FBDEV_ENVVAR);
99+
if(!fbdev_path) {
100+
printf("error : environment variable $FRAMEBUFFER not set\n");
101+
return NULL;
102+
}
103+
104+
twin_context_t *ctx = calloc(1, sizeof(twin_context_t));
105+
if (!ctx)
106+
return NULL;
107+
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
108+
if (!ctx->priv)
109+
return NULL;
110+
111+
twin_fbdev_t *tx = ctx->priv;
112+
113+
/* Open the framebuffer device */
114+
tx->fd = open(fbdev_path, O_RDWR);
115+
if (tx->fd == -1) {
116+
printf("error : failed opening %s\n", fbdev_path);
117+
goto bail;
118+
}
119+
120+
/* Read framebuffer information */
121+
struct fb_var_screeninfo info;
122+
if (ioctl(tx->fd, FBIOGET_VSCREENINFO, &info) == -1) {
123+
printf("error : failed getting framebuffer information\n");
124+
goto bail_fd;
125+
}
126+
width = info.xres;
127+
height = info.yres;
128+
129+
/* Create memory mapping for accessing the framebuffer */
130+
tx->fbdev_raw = mmap(NULL, width * height * info.bits_per_pixel / 8,
131+
PROT_READ | PROT_WRITE, MAP_SHARED, tx->fd, 0);
132+
if (tx->fbdev_raw == MAP_FAILED) {
133+
printf("error : failed calling mmap()\n");
134+
goto bail_fd;
135+
}
136+
137+
/* Create buffer space for TWIN */
138+
tx->pixels = malloc(width * height * sizeof(uint32_t));
139+
if (!tx->pixels) {
140+
printf("error : failed calling malloc()\n");
141+
goto bail_fd;
142+
}
143+
memset(tx->pixels, 255, width * height * sizeof(uint32_t));
144+
145+
/* Create TWIN screen */
146+
ctx->screen = twin_screen_create(width, height, _twin_fbdev_put_begin,
147+
_twin_fbdev_put_span, ctx);
148+
149+
/* Setup file handler and work function for framebuffer backend */
150+
twin_set_file(twin_fbdev_read_events, 0, TWIN_READ, ctx);
151+
twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, ctx);
152+
153+
return ctx;
154+
155+
bail_fd:
156+
close(tx->fd);
157+
bail:
158+
free(ctx->priv);
159+
free(ctx);
160+
return NULL;
161+
}
162+
163+
static void twin_fbdev_configure(twin_context_t *ctx)
164+
{
165+
int width, height;
166+
twin_fbdev_t *tx = ctx->priv;
167+
twin_fbdev_get_screen_size(tx, &width, &height);
168+
twin_screen_resize(ctx->screen, width, height);
169+
}
170+
171+
static void twin_fbdev_exit(twin_context_t *ctx)
172+
{
173+
if (!ctx)
174+
return;
175+
close(PRIV(ctx)->fd);
176+
free(PRIV(ctx)->pixels);
177+
free(ctx->priv);
178+
free(ctx);
179+
}
180+
181+
/* Register the Linux framebuffer backend */
182+
183+
const twin_backend_t g_twin_backend = {
184+
.init = twin_fbdev_init,
185+
.configure = twin_fbdev_configure,
186+
.exit = twin_fbdev_exit,
187+
};

0 commit comments

Comments
 (0)