Skip to content

Commit 1223562

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.
1 parent 501453e commit 1223562

File tree

2 files changed

+115
-9
lines changed

2 files changed

+115
-9
lines changed

backend/fbdev.c

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
#define FBDEV_DEFAULT "/dev/fb0"
2222
#define SCREEN(x) ((twin_context_t *) x)->screen
2323
#define PRIV(x) ((twin_fbdev_t *) ((twin_context_t *) x)->priv)
24+
#define SIG_SWITCH_FROM SIGUSR1
25+
#define SIG_SWITCH_TO SIGUSR2
26+
27+
static twin_context_t *global_ctx;
2428

2529
typedef struct {
2630
twin_screen_t *screen;
@@ -30,6 +34,8 @@ typedef struct {
3034

3135
/* Linux virtual terminal (VT) */
3236
int vt_fd;
37+
bool vt_active;
38+
struct vt_mode old_vtm;
3339

3440
/* Linux framebuffer */
3541
int fb_fd;
@@ -38,8 +44,8 @@ typedef struct {
3844
uint16_t cmap[3][256];
3945
uint8_t *fb_base;
4046
size_t fb_len;
41-
} twin_fbdev_t;
4247

48+
} twin_fbdev_t;
4349
/* color conversion */
4450
#define ARGB32_TO_RGB565_PERLINE(dest, pixels, width) \
4551
do { \
@@ -93,12 +99,25 @@ static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
9399
twin_screen_damage(tx->screen, 0, 0, width, height);
94100
}
95101

102+
static bool twin_fbdev_damaged(void *closure)
103+
{
104+
twin_fbdev_t *tx = PRIV(closure);
105+
twin_screen_t *screen = SCREEN(closure);
106+
107+
if (!tx->vt_active && twin_screen_damaged(screen))
108+
twin_screen_update(screen);
109+
110+
return true;
111+
}
112+
96113
static bool twin_fbdev_work(void *closure)
97114
{
115+
twin_fbdev_t *tx = PRIV(closure);
98116
twin_screen_t *screen = SCREEN(closure);
99117

100-
if (twin_screen_damaged(screen))
118+
if (!tx->vt_active && twin_screen_damaged(screen))
101119
twin_screen_update(screen);
120+
102121
return true;
103122
}
104123

@@ -192,6 +211,53 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
192211
return true;
193212
}
194213

214+
static void handle_tty_signal(int signum)
215+
{
216+
twin_fbdev_t *tx = PRIV(global_ctx);
217+
twin_screen_t *screen = SCREEN(global_ctx);
218+
219+
if (signum == SIG_SWITCH_FROM) {
220+
/* Notify kernel to release current virtual terminal */
221+
ioctl(tx->vt_fd, VT_RELDISP, 1);
222+
223+
/* Set the virtual terminal display in text mode */
224+
ioctl(tx->vt_fd, KDSETMODE, KD_TEXT);
225+
226+
tx->vt_active = true;
227+
228+
if (tx->fb_base != MAP_FAILED)
229+
munmap(tx->fb_base, tx->fb_len);
230+
tx->fb_base = MAP_FAILED;
231+
} else if (signum == SIG_SWITCH_TO) {
232+
/* Switch complete */
233+
ioctl(tx->vt_fd, VT_RELDISP, VT_ACKACQ);
234+
235+
/* Restore the virtual terminal display in graphics mode */
236+
ioctl(tx->vt_fd, KDSETMODE, KD_GRAPHICS);
237+
238+
/* Restore fbdev settings */
239+
if (twin_fbdev_apply_config(tx)) {
240+
tx->vt_active = 0;
241+
242+
/* Mark entire screen for refresh */
243+
twin_screen_damage(screen, 0, 0, screen->width, screen->height);
244+
}
245+
}
246+
}
247+
248+
static void twin_vt_setup_signal_handler(int *fd_ptr)
249+
{
250+
struct sigaction tty_sig;
251+
tty_sig.sa_handler = handle_tty_signal;
252+
sigfillset(&tty_sig.sa_mask);
253+
254+
/* Set the signal handler for leaving the current TTY */
255+
sigaction(SIG_SWITCH_FROM, &tty_sig, NULL);
256+
257+
/* Set the signal handler for returning to the previous TTY */
258+
sigaction(SIG_SWITCH_TO, &tty_sig, NULL);
259+
}
260+
195261
twin_context_t *twin_fbdev_init(int width, int height)
196262
{
197263
char *fbdev_path = getenv(FBDEV_NAME);
@@ -204,6 +270,8 @@ twin_context_t *twin_fbdev_init(int width, int height)
204270
twin_context_t *ctx = calloc(1, sizeof(twin_context_t));
205271
if (!ctx)
206272
return NULL;
273+
274+
global_ctx = ctx;
207275
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
208276
if (!ctx->priv)
209277
return NULL;
@@ -218,10 +286,13 @@ twin_context_t *twin_fbdev_init(int width, int height)
218286
}
219287

220288
/* Set up virtual terminal environment */
221-
if (!twin_vt_setup(&tx->vt_fd)) {
289+
if (!twin_vt_setup(&tx->vt_fd, &tx->old_vtm)) {
222290
goto bail_fb_fd;
223291
}
224292

293+
/* Set up signal handlers for switching TTYs */
294+
twin_vt_setup_signal_handler(&tx->vt_fd);
295+
225296
/* Apply configurations to the framebuffer device */
226297
if (!twin_fbdev_apply_config(tx)) {
227298
log_error("Failed to apply configurations to the framebuffer device");
@@ -254,6 +325,9 @@ twin_context_t *twin_fbdev_init(int width, int height)
254325
/* Setup file handler and work functions */
255326
twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, ctx);
256327

328+
/* Register a callback function to handle damaged rendering */
329+
twin_screen_register_damaged(ctx->screen, twin_fbdev_damaged, ctx);
330+
257331
return ctx;
258332

259333
bail_screen:
@@ -282,7 +356,7 @@ static void twin_fbdev_exit(twin_context_t *ctx)
282356
return;
283357

284358
twin_fbdev_t *tx = PRIV(ctx);
285-
twin_vt_mode(tx->vt_fd, KD_TEXT);
359+
twin_vt_display_mode(tx->vt_fd, KD_TEXT);
286360
munmap(tx->fb_base, tx->fb_len);
287361
twin_linux_input_destroy(tx->input);
288362
close(tx->vt_fd);
@@ -292,7 +366,6 @@ static void twin_fbdev_exit(twin_context_t *ctx)
292366
}
293367

294368
/* Register the Linux framebuffer backend */
295-
296369
const twin_backend_t g_twin_backend = {
297370
.init = twin_fbdev_init,
298371
.configure = twin_fbdev_configure,

backend/linux_vt.h

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
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"
@@ -30,13 +31,15 @@ static inline int twin_vt_open(int vt_num)
3031
return fd;
3132
}
3233

33-
static inline int twin_vt_mode(int fd, int mode)
34+
static inline int twin_vt_display_mode(int fd, int mode)
3435
{
3536
return ioctl(fd, KDSETMODE, mode);
3637
}
3738

38-
static inline bool twin_vt_setup(int *fd_ptr)
39+
static inline bool twin_vt_setup(int *fd_ptr, struct vt_mode *old_vtm)
3940
{
41+
struct vt_mode vtm;
42+
4043
/* Open VT0 to inquire information */
4144
if ((*fd_ptr = twin_vt_open(0)) < -1) {
4245
log_error("Failed to open VT0");
@@ -57,12 +60,42 @@ static inline bool twin_vt_setup(int *fd_ptr)
5760
return false;
5861
}
5962

60-
/* Set VT to graphics mode to inhibit command-line text */
61-
if (twin_vt_mode(*fd_ptr, KD_GRAPHICS) < 0) {
63+
/* Attempt to activate the specified virtual terminal by its number */
64+
if (ioctl(*fd_ptr, VT_ACTIVATE, vt_num) < 0) {
65+
log_error("Failed to activate VT %d", vt_num);
66+
return false;
67+
}
68+
69+
/* Wait until the specified virtual terminal becomes the active VT */
70+
if (ioctl(*fd_ptr, VT_WAITACTIVE, vt_num) < 0) {
71+
log_error("Failed to activate VT %d", vt_num);
72+
return false;
73+
}
74+
75+
/* Save the virtual terminal mode */
76+
if (ioctl(*fd_ptr, VT_GETMODE, &old_vtm) < 0) {
77+
log_error("Failed to get VT mode");
78+
return 0;
79+
}
80+
81+
/* Set the virtual terminal mode */
82+
vtm.mode = VT_PROCESS;
83+
vtm.relsig = SIGUSR1;
84+
vtm.acqsig = SIGUSR2;
85+
86+
if (ioctl(*fd_ptr, VT_SETMODE, &vtm) < 0) {
87+
log_error("Failed to set VT mode");
88+
return false;
89+
}
90+
91+
/* Set the virtual terminal display mode to graphics mode */
92+
if (twin_vt_display_mode(*fd_ptr, KD_GRAPHICS) < 0) {
6293
log_error("Failed to set KD_GRAPHICS mode");
94+
twin_vt_display_mode(*fd_ptr, KD_TEXT);
6395
return false;
6496
}
6597

6698
return true;
6799
}
100+
68101
#endif

0 commit comments

Comments
 (0)