Skip to content

Commit 6a919d1

Browse files
committed
Support Linux framebuffer and input system
1 parent ceb748f commit 6a919d1

File tree

5 files changed

+430
-0
lines changed

5 files changed

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

0 commit comments

Comments
 (0)