Skip to content

Commit 262e164

Browse files
committed
Support Linux framebuffer and input system
1 parent 9d1f437 commit 262e164

File tree

5 files changed

+330
-2
lines changed

5 files changed

+330
-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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ libtwin.a_cflags-y += $(shell sdl2-config --cflags)
9696
TARGET_LIBS += $(shell sdl2-config --libs)
9797
endif
9898

99+
ifeq ($(CONFIG_BACKEND_FBDEV), y)
100+
BACKEND = fbdev
101+
libtwin.a_files-y += backend/fbdev.c
102+
libtwin.a_files-y += backend/linux_input.c
103+
endif
104+
99105
# Standalone application
100106

101107
ifeq ($(CONFIG_DEMO_APPLICATIONS), y)

backend/fbdev.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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 "linux_input.h"
17+
#include "twin_backend.h"
18+
19+
typedef struct {
20+
int fd;
21+
int depth;
22+
int image_y;
23+
int *pixels;
24+
int *fbdev_raw;
25+
twin_screen_t *screen;
26+
twin_linux_input_t *input;
27+
} twin_fbdev_t;
28+
29+
#define FBDEV_NAME "FRAMEBUFFER"
30+
#define FBDEV_DEFAULT "/dev/fb0"
31+
#define SCREEN(x) ((twin_context_t *) x)->screen
32+
#define PRIV(x) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
33+
34+
static void _twin_fbdev_put_span(twin_coord_t left,
35+
twin_coord_t top,
36+
twin_coord_t right,
37+
twin_argb32_t *pixels,
38+
void *closure)
39+
{
40+
twin_screen_t *screen = SCREEN(closure);
41+
twin_fbdev_t *tx = PRIV(closure);
42+
43+
for (twin_coord_t ix = left, iy = top; ix < right; ix++) {
44+
twin_argb32_t pixel = *pixels++;
45+
tx->pixels[iy * screen->width + ix] = pixel;
46+
}
47+
48+
/* Update the pixels to the framebuffer */
49+
memcpy(tx->fbdev_raw, tx->pixels,
50+
sizeof(uint32_t) * screen->width * screen->height);
51+
}
52+
53+
static void twin_fbdev_get_screen_size(twin_fbdev_t *tx,
54+
int *width,
55+
int *height)
56+
{
57+
struct fb_var_screeninfo info;
58+
ioctl(tx->fd, FBIOGET_VSCREENINFO, &info);
59+
*width = info.xres;
60+
*height = info.yres;
61+
}
62+
63+
static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
64+
{
65+
int width, height;
66+
twin_fbdev_get_screen_size(tx, &width, &height);
67+
twin_screen_damage(tx->screen, 0, 0, width, height);
68+
}
69+
70+
static bool twin_fbdev_work(void *closure)
71+
{
72+
twin_screen_t *screen = SCREEN(closure);
73+
74+
if (twin_screen_damaged(screen))
75+
twin_screen_update(screen);
76+
return true;
77+
}
78+
79+
twin_context_t *twin_fbdev_init(int width, int height)
80+
{
81+
char *fbdev_path = getenv(FBDEV_NAME);
82+
if (!fbdev_path) {
83+
printf("Environment variable $FRAMEBUFFER not set, use %s by default\n",
84+
FBDEV_DEFAULT);
85+
fbdev_path = FBDEV_DEFAULT;
86+
}
87+
88+
twin_context_t *ctx = calloc(1, sizeof(twin_context_t));
89+
if (!ctx)
90+
return NULL;
91+
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
92+
if (!ctx->priv)
93+
return NULL;
94+
95+
twin_fbdev_t *tx = ctx->priv;
96+
97+
/* Open the framebuffer device */
98+
tx->fd = open(fbdev_path, O_RDWR);
99+
if (tx->fd == -1) {
100+
printf("error : failed opening %s\n", fbdev_path);
101+
goto bail;
102+
}
103+
104+
/* Read framebuffer information */
105+
struct fb_var_screeninfo info;
106+
if (ioctl(tx->fd, FBIOGET_VSCREENINFO, &info) == -1) {
107+
printf("error : failed getting framebuffer information\n");
108+
goto bail_fd;
109+
}
110+
width = info.xres;
111+
height = info.yres;
112+
113+
/* Create memory mapping for accessing the framebuffer */
114+
tx->fbdev_raw = mmap(NULL, width * height * info.bits_per_pixel / 8,
115+
PROT_READ | PROT_WRITE, MAP_SHARED, tx->fd, 0);
116+
if (tx->fbdev_raw == MAP_FAILED) {
117+
printf("error : failed calling mmap()\n");
118+
goto bail_fd;
119+
}
120+
121+
/* Create buffer space for TWIN */
122+
tx->pixels = malloc(width * height * sizeof(uint32_t));
123+
if (!tx->pixels) {
124+
printf("error : failed calling malloc()\n");
125+
goto bail_fd;
126+
}
127+
memset(tx->pixels, 255, width * height * sizeof(uint32_t));
128+
129+
/* Create TWIN screen */
130+
ctx->screen =
131+
twin_screen_create(width, height, NULL, _twin_fbdev_put_span, ctx);
132+
133+
/* Create Linux input system object */
134+
tx->input = twin_linux_input_create(ctx->screen);
135+
if (!tx->input) {
136+
printf("error : failed creating linux input system object.\n");
137+
goto bail_fd;
138+
}
139+
140+
/* Setup file handler and work functions */
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+
twin_linux_input_destroy(PRIV(ctx)->input);
166+
close(PRIV(ctx)->fd);
167+
free(PRIV(ctx)->pixels);
168+
free(ctx->priv);
169+
free(ctx);
170+
}
171+
172+
/* Register the Linux framebuffer backend */
173+
174+
const twin_backend_t g_twin_backend = {
175+
.init = twin_fbdev_init,
176+
.configure = twin_fbdev_configure,
177+
.exit = twin_fbdev_exit,
178+
};

backend/linux_input.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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 <stdio.h>
9+
#include <stdlib.h>
10+
#include <string.h>
11+
#include <twin.h>
12+
#include <unistd.h>
13+
14+
#include "linux_input.h"
15+
16+
#define MOUSE_EVENT_LEFT_BTN 0x1
17+
#define MOUSE_EVENT_RIGHT_BTN 0x2
18+
#define MOUSE_EVENT_DATA_SIZE 3
19+
20+
#define MOUSE_DEV_NAME "MOUSE"
21+
#define MOUSE_DEV_DEFAULT "/dev/input/mice"
22+
23+
static void _mouse_check_bounds(twin_linux_input_t *tm)
24+
{
25+
if (tm->x < 0)
26+
tm->x = 0;
27+
if (tm->x > tm->screen->width)
28+
tm->x = tm->screen->width;
29+
if (tm->y < 0)
30+
tm->y = 0;
31+
if (tm->y > tm->screen->height)
32+
tm->y = tm->screen->height;
33+
}
34+
35+
static bool twin_linux_input_events(int file, twin_file_op_t ops, void *closure)
36+
{
37+
twin_linux_input_t *tm = closure;
38+
char evts[34]; /* XXX: How big should we allocate for the event buffer? */
39+
int n = tm->res_cnt;
40+
twin_event_t tev;
41+
42+
/* Prepare input data for unpacking */
43+
if (n)
44+
memcpy(evts, tm->residual, n);
45+
n += read(file, evts + n, 32); /* XXX: Same as above */
46+
47+
char *ep;
48+
for (ep = evts; n >= MOUSE_EVENT_DATA_SIZE;
49+
n -= MOUSE_EVENT_DATA_SIZE, ep += MOUSE_EVENT_DATA_SIZE) {
50+
/* Add incremental change to the cursor position */
51+
int dx = +ep[1];
52+
int dy = -ep[2];
53+
if (dx || dy) {
54+
tm->x += dx, tm->y += dy;
55+
_mouse_check_bounds(tm);
56+
tev.kind = TwinEventMotion;
57+
tev.u.pointer.screen_x = tm->x;
58+
tev.u.pointer.screen_y = tm->y;
59+
tev.u.pointer.button = tm->btns;
60+
twin_screen_dispatch(tm->screen, &tev);
61+
}
62+
63+
/* Handle left mouse button */
64+
int btn = ep[0] & MOUSE_EVENT_LEFT_BTN;
65+
if (btn != tm->btns) {
66+
tm->btns = btn;
67+
tev.kind = btn ? TwinEventButtonDown : TwinEventButtonUp;
68+
tev.u.pointer.screen_x = tm->x;
69+
tev.u.pointer.screen_y = tm->y;
70+
tev.u.pointer.button = tm->btns;
71+
twin_screen_dispatch(tm->screen, &tev);
72+
}
73+
}
74+
75+
/* Preserve left unpacked data */
76+
tm->res_cnt = n;
77+
if (n)
78+
memcpy(tm->residual, ep, n);
79+
80+
return true;
81+
}
82+
83+
twin_linux_input_t *twin_linux_input_create(twin_screen_t *screen)
84+
{
85+
/* Read mouse device file path from environment variable */
86+
char *mouse_dev_path = getenv(MOUSE_DEV_NAME);
87+
if (!mouse_dev_path) {
88+
printf("Environment variable $MOUSE not set, use %s by default\n",
89+
MOUSE_DEV_DEFAULT);
90+
mouse_dev_path = MOUSE_DEV_DEFAULT;
91+
}
92+
93+
/* Create object for handling Linux input system */
94+
twin_linux_input_t *tm = calloc(1, sizeof(twin_linux_input_t));
95+
if (!tm)
96+
return NULL;
97+
98+
tm->screen = screen;
99+
100+
/* Centering the cursor position */
101+
tm->x = screen->width / 2;
102+
tm->y = screen->height / 2;
103+
104+
/* Open the mouse device file */
105+
tm->fd = open(mouse_dev_path, O_RDONLY);
106+
if (tm->fd < 0) {
107+
free(tm);
108+
return NULL;
109+
}
110+
111+
/* Set file handler for reading input device file */
112+
twin_set_file(twin_linux_input_events, tm->fd, TWIN_READ, tm);
113+
114+
return tm;
115+
}
116+
117+
void twin_linux_input_destroy(twin_linux_input_t *tm)
118+
{
119+
close(tm->fd);
120+
free(tm);
121+
}

backend/linux_input.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Twin - A Tiny Window System
3+
* Copyright (c) 2024 National Cheng Kung University, Taiwan
4+
* All rights reserved.
5+
*/
6+
7+
#ifndef _LINUX_INPUT_H__
8+
#define _LINUX_INPUT_H__
9+
10+
typedef struct {
11+
twin_screen_t *screen;
12+
int fd;
13+
char residual[2];
14+
int res_cnt;
15+
int btns;
16+
int x, y;
17+
} twin_linux_input_t;
18+
19+
twin_linux_input_t *twin_linux_input_create(twin_screen_t *screen);
20+
21+
void twin_linux_input_destroy(twin_linux_input_t *tm);
22+
23+
#endif

0 commit comments

Comments
 (0)