Skip to content

Commit 3fd7578

Browse files
committed
Support VT switching for fbdev backend
The fbdev backend supports framebuffer rendering on a specifiend VT. To enhancce its compatibility in environments with multiple TTYs, the following improvements ensure that each framebuffer device operates independently on its own VT without affecting others. - Implement signal handlers to manage virtual terminal switching. - Pause screen rendering when switching to another VT. - Resume screen rendering when switching back to the original VT. The improvement has been validated on Ubuntu 24.04 running in a virtual machine and on a Raspberry Pi 3B.
1 parent 501453e commit 3fd7578

File tree

2 files changed

+131
-20
lines changed

2 files changed

+131
-20
lines changed

backend/fbdev.c

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ typedef struct {
3030

3131
/* Linux virtual terminal (VT) */
3232
int vt_fd;
33+
bool vt_active;
34+
struct vt_mode old_vtm;
3335

3436
/* Linux framebuffer */
3537
int fb_fd;
@@ -38,6 +40,7 @@ typedef struct {
3840
uint16_t cmap[3][256];
3941
uint8_t *fb_base;
4042
size_t fb_len;
43+
4144
} twin_fbdev_t;
4245

4346
/* color conversion */
@@ -86,22 +89,6 @@ static void twin_fbdev_get_screen_size(twin_fbdev_t *tx,
8689
*height = info.yres;
8790
}
8891

89-
static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
90-
{
91-
int width, height;
92-
twin_fbdev_get_screen_size(tx, &width, &height);
93-
twin_screen_damage(tx->screen, 0, 0, width, height);
94-
}
95-
96-
static bool twin_fbdev_work(void *closure)
97-
{
98-
twin_screen_t *screen = SCREEN(closure);
99-
100-
if (twin_screen_damaged(screen))
101-
twin_screen_update(screen);
102-
return true;
103-
}
104-
10592
static inline bool twin_fbdev_is_rgb565(twin_fbdev_t *tx)
10693
{
10794
return tx->fb_var.red.offset == 11 && tx->fb_var.red.length == 5 &&
@@ -192,6 +179,50 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
192179
return true;
193180
}
194181

182+
static void twin_fbdev_damage(twin_fbdev_t *tx)
183+
{
184+
int width, height;
185+
twin_fbdev_get_screen_size(tx, &width, &height);
186+
twin_screen_damage(tx->screen, 0, 0, width, height);
187+
}
188+
189+
static bool twin_fbdev_damaged(void *closure)
190+
{
191+
twin_fbdev_t *tx = PRIV(closure);
192+
twin_screen_t *screen = SCREEN(closure);
193+
194+
if (!tx->vt_active && twin_screen_damaged(screen))
195+
twin_screen_update(screen);
196+
197+
return true;
198+
}
199+
200+
static bool twin_fbdev_work(void *closure)
201+
{
202+
twin_fbdev_t *tx = PRIV(closure);
203+
twin_screen_t *screen = SCREEN(closure);
204+
205+
if (tx->vt_active && (tx->fb_base != MAP_FAILED)) {
206+
/* Unmap the fbdev */
207+
munmap(tx->fb_base, tx->fb_len);
208+
tx->fb_base = MAP_FAILED;
209+
}
210+
211+
if (!tx->vt_active && (tx->fb_base == MAP_FAILED)) {
212+
/* Restore the fbdev settings */
213+
if (!twin_fbdev_apply_config(tx))
214+
log_error("Failed to apply configurations to the fbdev");
215+
216+
/* Mark entire screen for refresh */
217+
twin_screen_damage(screen, 0, 0, screen->width, screen->height);
218+
}
219+
220+
if (!tx->vt_active && twin_screen_damaged(screen))
221+
twin_screen_update(screen);
222+
223+
return true;
224+
}
225+
195226
twin_context_t *twin_fbdev_init(int width, int height)
196227
{
197228
char *fbdev_path = getenv(FBDEV_NAME);
@@ -204,6 +235,7 @@ twin_context_t *twin_fbdev_init(int width, int height)
204235
twin_context_t *ctx = calloc(1, sizeof(twin_context_t));
205236
if (!ctx)
206237
return NULL;
238+
207239
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
208240
if (!ctx->priv)
209241
return NULL;
@@ -218,10 +250,13 @@ twin_context_t *twin_fbdev_init(int width, int height)
218250
}
219251

220252
/* Set up virtual terminal environment */
221-
if (!twin_vt_setup(&tx->vt_fd)) {
253+
if (!twin_vt_setup(&tx->vt_fd, &tx->old_vtm, &tx->vt_active)) {
222254
goto bail_fb_fd;
223255
}
224256

257+
/* Set up signal handlers for switching TTYs */
258+
twin_vt_setup_signal_handler();
259+
225260
/* Apply configurations to the framebuffer device */
226261
if (!twin_fbdev_apply_config(tx)) {
227262
log_error("Failed to apply configurations to the framebuffer device");
@@ -254,6 +289,9 @@ twin_context_t *twin_fbdev_init(int width, int height)
254289
/* Setup file handler and work functions */
255290
twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, ctx);
256291

292+
/* Register a callback function to handle damaged rendering */
293+
twin_screen_register_damaged(ctx->screen, twin_fbdev_damaged, ctx);
294+
257295
return ctx;
258296

259297
bail_screen:
@@ -292,7 +330,6 @@ static void twin_fbdev_exit(twin_context_t *ctx)
292330
}
293331

294332
/* Register the Linux framebuffer backend */
295-
296333
const twin_backend_t g_twin_backend = {
297334
.init = twin_fbdev_init,
298335
.configure = twin_fbdev_configure,

backend/linux_vt.h

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,19 @@
1010
#include <fcntl.h>
1111
#include <linux/kd.h>
1212
#include <linux/vt.h>
13+
#include <signal.h>
1314
#include <stdlib.h>
1415
#include <sys/ioctl.h>
1516
#include "twin_private.h"
1617

1718
#define VT_DEV_TTY_MAX 11
19+
#define SIG_SWITCH_FROM SIGUSR1
20+
#define SIG_SWITCH_TO SIGUSR2
21+
22+
static int *vt_fd;
23+
24+
static bool *is_vt_active;
25+
1826
static inline int twin_vt_open(int vt_num)
1927
{
2028
int fd;
@@ -35,7 +43,42 @@ static inline int twin_vt_mode(int fd, int mode)
3543
return ioctl(fd, KDSETMODE, mode);
3644
}
3745

38-
static inline bool twin_vt_setup(int *fd_ptr)
46+
static inline void twin_vt_process_switch_signal(int signum)
47+
{
48+
if (signum == SIG_SWITCH_FROM) {
49+
/* Notify kernel to release current virtual terminal */
50+
ioctl(*vt_fd, VT_RELDISP, 1);
51+
52+
/* Set the virtual terminal display in text mode */
53+
ioctl(*vt_fd, KDSETMODE, KD_TEXT);
54+
55+
*is_vt_active = true;
56+
} else if (signum == SIG_SWITCH_TO) {
57+
/* Switch complete */
58+
ioctl(*vt_fd, VT_RELDISP, VT_ACKACQ);
59+
60+
/* Restore the virtual terminal display in graphics mode */
61+
ioctl(*vt_fd, KDSETMODE, KD_GRAPHICS);
62+
63+
*is_vt_active = false;
64+
}
65+
}
66+
67+
static inline void twin_vt_setup_signal_handler()
68+
{
69+
struct sigaction tty_sig = {.sa_handler = twin_vt_process_switch_signal};
70+
sigfillset(&tty_sig.sa_mask);
71+
72+
/* Set the signal handler for leaving the current TTY */
73+
sigaction(SIG_SWITCH_FROM, &tty_sig, NULL);
74+
75+
/* Set the signal handler for returning to the previous TTY */
76+
sigaction(SIG_SWITCH_TO, &tty_sig, NULL);
77+
}
78+
79+
static inline bool twin_vt_setup(int *fd_ptr,
80+
struct vt_mode *old_vtm,
81+
bool *vt_active)
3982
{
4083
/* Open VT0 to inquire information */
4184
if ((*fd_ptr = twin_vt_open(0)) < -1) {
@@ -57,12 +100,43 @@ static inline bool twin_vt_setup(int *fd_ptr)
57100
return false;
58101
}
59102

60-
/* Set VT to graphics mode to inhibit command-line text */
103+
/* Attempt to activate the specified virtual terminal by its number */
104+
if (ioctl(*fd_ptr, VT_ACTIVATE, vt_num) < 0) {
105+
log_error("Failed to activate VT %d", vt_num);
106+
return false;
107+
}
108+
109+
/* Wait until the specified virtual terminal becomes the active VT */
110+
if (ioctl(*fd_ptr, VT_WAITACTIVE, vt_num) < 0) {
111+
log_error("Failed to activate VT %d", vt_num);
112+
return false;
113+
}
114+
115+
/* Save the virtual terminal mode */
116+
if (ioctl(*fd_ptr, VT_GETMODE, &old_vtm) < 0) {
117+
log_error("Failed to get VT mode");
118+
return false;
119+
}
120+
121+
/* Set the virtual terminal mode */
122+
struct vt_mode vtm = {
123+
.mode = VT_PROCESS, .relsig = SIGUSR1, .acqsig = SIGUSR2};
124+
125+
if (ioctl(*fd_ptr, VT_SETMODE, &vtm) < 0) {
126+
log_error("Failed to set VT mode");
127+
return false;
128+
}
129+
130+
/* Set the virtual terminal display mode to graphics mode */
61131
if (twin_vt_mode(*fd_ptr, KD_GRAPHICS) < 0) {
62132
log_error("Failed to set KD_GRAPHICS mode");
133+
twin_vt_mode(*fd_ptr, KD_TEXT);
63134
return false;
64135
}
65136

137+
vt_fd = fd_ptr;
138+
is_vt_active = vt_active;
139+
66140
return true;
67141
}
68142
#endif

0 commit comments

Comments
 (0)