Skip to content

Commit 9a897f6

Browse files
committed
Support Linux framebuffer and input system
1 parent ceb748f commit 9a897f6

File tree

5 files changed

+489
-0
lines changed

5 files changed

+489
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ libtwin.a_cflags-y += $(shell sdl2-config --cflags)
104104
TARGET_LIBS += $(shell sdl2-config --libs)
105105
endif
106106

107+
ifeq ($(CONFIG_BACKEND_FBDEV), y)
108+
BACKEND = fbdev
109+
libtwin.a_files-y += backend/fbdev.c
110+
libtwin.a_files-y += backend/linux_input.c
111+
endif
112+
107113
# Standalone application
108114

109115
ifeq ($(CONFIG_DEMO_APPLICATIONS), y)

backend/fbdev.c

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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 <linux/kd.h>
10+
#include <linux/vt.h>
11+
#include <signal.h>
12+
#include <stdio.h>
13+
#include <stdlib.h>
14+
#include <sys/ioctl.h>
15+
#include <sys/mman.h>
16+
#include <twin.h>
17+
#include <unistd.h>
18+
19+
#include "linux_input.h"
20+
#include "twin_backend.h"
21+
#include "twin_private.h"
22+
23+
#define FBDEV_NAME "FRAMEBUFFER"
24+
#define FBDEV_DEFAULT "/dev/fb0"
25+
#define SCREEN(x) ((twin_context_t *) x)->screen
26+
#define PRIV(x) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
27+
28+
typedef struct {
29+
twin_screen_t *screen;
30+
31+
/* Linux input system */
32+
void *input;
33+
34+
/* Linux virtual terminal (VT) */
35+
int vt_fd;
36+
int vt_num;
37+
bool vt_active;
38+
39+
/* Linux framebuffer */
40+
int fb_fd;
41+
struct fb_var_screeninfo fb_var;
42+
struct fb_fix_screeninfo fb_fix;
43+
uint16_t cmap[3][256];
44+
uint8_t *fb_base;
45+
size_t fb_len;
46+
} twin_fbdev_t;
47+
48+
static void _twin_fbdev_put_span(twin_coord_t left,
49+
twin_coord_t top,
50+
twin_coord_t right,
51+
twin_argb32_t *pixels,
52+
void *closure)
53+
{
54+
twin_screen_t *screen = SCREEN(closure);
55+
twin_fbdev_t *tx = PRIV(closure);
56+
57+
if (tx->fb_base == MAP_FAILED)
58+
return;
59+
60+
twin_coord_t width = right - left;
61+
off_t off = top * screen->width + left;
62+
uint32_t *src = pixels;
63+
uint32_t *dest =
64+
(uint32_t *) ((uintptr_t) tx->fb_base + (off * sizeof(uint32_t)));
65+
memcpy(dest, src, width * sizeof(uint32_t));
66+
}
67+
68+
static void twin_fbdev_get_screen_size(twin_fbdev_t *tx,
69+
int *width,
70+
int *height)
71+
{
72+
struct fb_var_screeninfo info;
73+
ioctl(tx->fb_fd, FBIOGET_VSCREENINFO, &info);
74+
*width = info.xres;
75+
*height = info.yres;
76+
}
77+
78+
static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
79+
{
80+
int width, height;
81+
twin_fbdev_get_screen_size(tx, &width, &height);
82+
twin_screen_damage(tx->screen, 0, 0, width, height);
83+
}
84+
85+
static bool twin_fbdev_work(void *closure)
86+
{
87+
twin_screen_t *screen = SCREEN(closure);
88+
89+
if (twin_screen_damaged(screen))
90+
twin_screen_update(screen);
91+
return true;
92+
}
93+
94+
static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
95+
{
96+
/* Read changable information of the framebuffer */
97+
if (ioctl(tx->fb_fd, FBIOGET_VSCREENINFO, &tx->fb_var) == -1) {
98+
log_error("Failed to get framebuffer information");
99+
return false;
100+
}
101+
102+
/* Set the virtual screen size to be the same as the physical screen */
103+
tx->fb_var.xres_virtual = tx->fb_var.xres;
104+
tx->fb_var.yres_virtual = tx->fb_var.yres;
105+
tx->fb_var.bits_per_pixel = 32;
106+
if (ioctl(tx->fb_fd, FBIOPUT_VSCREENINFO, &tx->fb_var) < 0) {
107+
log_error("Failed to set framebuffer mode");
108+
return false;
109+
}
110+
111+
/* Read changable information of the framebuffer again */
112+
if (ioctl(tx->fb_fd, FBIOGET_VSCREENINFO, &tx->fb_var) < 0) {
113+
log_error("Failed to get framebuffer information");
114+
return false;
115+
}
116+
117+
/* Check bits per pixel */
118+
if (tx->fb_var.bits_per_pixel != 32) {
119+
log_error("Failed to set framebuffer bpp to 32");
120+
return false;
121+
}
122+
123+
/* Read unchangable information of the framebuffer */
124+
ioctl(tx->fb_fd, FBIOGET_FSCREENINFO, &tx->fb_fix);
125+
126+
/* Align the framebuffer memory address with the page size */
127+
off_t pgsize = getpagesize();
128+
off_t start = (off_t) tx->fb_fix.smem_start & (pgsize - 1);
129+
130+
/* Round up the framebuffer memory size to match the page size */
131+
tx->fb_len = start + (size_t) tx->fb_fix.smem_len + (pgsize - 1);
132+
tx->fb_len &= ~(pgsize - 1);
133+
134+
/* Map framebuffer device to the virtual memory */
135+
tx->fb_base = mmap(NULL, tx->fb_len, PROT_READ | PROT_WRITE, MAP_SHARED,
136+
tx->fb_fd, 0);
137+
if (tx->fb_base == MAP_FAILED) {
138+
log_error("Failed to mmap framebuffer");
139+
return false;
140+
}
141+
142+
return true;
143+
}
144+
145+
static int twin_vt_open(int vt_num)
146+
{
147+
int fd;
148+
149+
char vt_dev[30] = {0};
150+
snprintf(vt_dev, 30, "/dev/tty%d", vt_num);
151+
152+
fd = open(vt_dev, O_RDWR);
153+
if (fd < 0) {
154+
log_error("Failed to open %s", vt_dev);
155+
}
156+
157+
return fd;
158+
}
159+
160+
static bool twin_vt_setup(twin_fbdev_t *tx)
161+
{
162+
/* Open VT0 to inquire information */
163+
if ((tx->vt_fd = twin_vt_open(0)) < -1) {
164+
return false;
165+
}
166+
167+
/* Inquire for current VT number */
168+
struct vt_stat vt;
169+
if (ioctl(tx->vt_fd, VT_GETSTATE, &vt) == -1) {
170+
log_error("Failed to get VT number");
171+
return false;
172+
}
173+
tx->vt_num = vt.v_active;
174+
175+
/* Open the VT */
176+
if ((tx->vt_fd = twin_vt_open(tx->vt_num)) < -1) {
177+
return false;
178+
}
179+
180+
/* Set VT to graphics mode to inhibit command-line text */
181+
if (ioctl(tx->vt_fd, KDSETMODE, KD_GRAPHICS) < 0) {
182+
log_error("Failed to set KD_GRAPHICS mode");
183+
return false;
184+
}
185+
186+
return true;
187+
}
188+
189+
twin_context_t *twin_fbdev_init(int width, int height)
190+
{
191+
char *fbdev_path = getenv(FBDEV_NAME);
192+
if (!fbdev_path) {
193+
log_info("Environment variable $FRAMEBUFFER not set, use %s by default",
194+
FBDEV_DEFAULT);
195+
fbdev_path = FBDEV_DEFAULT;
196+
}
197+
198+
twin_context_t *ctx = calloc(1, sizeof(twin_context_t));
199+
if (!ctx)
200+
return NULL;
201+
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
202+
if (!ctx->priv)
203+
return NULL;
204+
205+
twin_fbdev_t *tx = ctx->priv;
206+
207+
/* Open the framebuffer device */
208+
tx->fb_fd = open(fbdev_path, O_RDWR);
209+
if (tx->fb_fd == -1) {
210+
log_error("Failed to open %s", fbdev_path);
211+
goto bail;
212+
}
213+
214+
/* Set up virtual terminal environment */
215+
if (!twin_vt_setup(tx)) {
216+
goto bail_fb_fd;
217+
}
218+
219+
/* Apply configurations to the framebuffer device */
220+
if (!twin_fbdev_apply_config(tx)) {
221+
log_error("Failed to apply configurations to the framebuffer device");
222+
goto bail_vt_fd;
223+
}
224+
225+
/* Create TWIN screen */
226+
ctx->screen =
227+
twin_screen_create(width, height, NULL, _twin_fbdev_put_span, ctx);
228+
229+
/* Create Linux input system object */
230+
tx->input = twin_linux_input_create(ctx->screen);
231+
if (!tx->input) {
232+
log_error("Failed to create Linux input system object");
233+
goto bail_vt_fd;
234+
}
235+
236+
/* Setup file handler and work functions */
237+
twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, ctx);
238+
239+
return ctx;
240+
241+
bail_vt_fd:
242+
close(tx->vt_fd);
243+
bail_fb_fd:
244+
close(tx->fb_fd);
245+
bail:
246+
free(ctx->priv);
247+
free(ctx);
248+
return NULL;
249+
}
250+
251+
static void twin_fbdev_configure(twin_context_t *ctx)
252+
{
253+
int width, height;
254+
twin_fbdev_t *tx = ctx->priv;
255+
twin_fbdev_get_screen_size(tx, &width, &height);
256+
twin_screen_resize(ctx->screen, width, height);
257+
}
258+
259+
static void twin_fbdev_exit(twin_context_t *ctx)
260+
{
261+
if (!ctx)
262+
return;
263+
264+
twin_fbdev_t *tx = PRIV(ctx);
265+
ioctl(tx->vt_fd, KDSETMODE, KD_TEXT);
266+
munmap(tx->fb_base, tx->fb_len);
267+
twin_linux_input_destroy(tx->input);
268+
close(tx->vt_fd);
269+
close(tx->fb_fd);
270+
free(ctx->priv);
271+
free(ctx);
272+
}
273+
274+
/* Register the Linux framebuffer backend */
275+
276+
const twin_backend_t g_twin_backend = {
277+
.init = twin_fbdev_init,
278+
.configure = twin_fbdev_configure,
279+
.exit = twin_fbdev_exit,
280+
};

0 commit comments

Comments
 (0)