Skip to content

Support VT switching for fbdev backend #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 53 additions & 18 deletions backend/fbdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ typedef struct {

/* Linux virtual terminal (VT) */
int vt_fd;
bool vt_active;
struct vt_mode old_vtm;

/* Linux framebuffer */
int fb_fd;
Expand Down Expand Up @@ -86,22 +88,6 @@ static void twin_fbdev_get_screen_size(twin_fbdev_t *tx,
*height = info.yres;
}

static void twin_fbdev_damage(twin_screen_t *screen, twin_fbdev_t *tx)
{
int width, height;
twin_fbdev_get_screen_size(tx, &width, &height);
twin_screen_damage(tx->screen, 0, 0, width, height);
}

static bool twin_fbdev_work(void *closure)
{
twin_screen_t *screen = SCREEN(closure);

if (twin_screen_damaged(screen))
twin_screen_update(screen);
return true;
}

static inline bool twin_fbdev_is_rgb565(twin_fbdev_t *tx)
{
return tx->fb_var.red.offset == 11 && tx->fb_var.red.length == 5 &&
Expand Down Expand Up @@ -192,6 +178,50 @@ static bool twin_fbdev_apply_config(twin_fbdev_t *tx)
return true;
}

static void twin_fbdev_damage(twin_fbdev_t *tx)
{
int width, height;
twin_fbdev_get_screen_size(tx, &width, &height);
twin_screen_damage(tx->screen, 0, 0, width, height);
}

static bool twin_fbdev_update_damage(void *closure)
{
twin_fbdev_t *tx = PRIV(closure);
twin_screen_t *screen = SCREEN(closure);

if (!tx->vt_active && twin_screen_damaged(screen))
twin_screen_update(screen);

return true;
}

static bool twin_fbdev_work(void *closure)
{
twin_fbdev_t *tx = PRIV(closure);
twin_screen_t *screen = SCREEN(closure);

if (tx->vt_active && (tx->fb_base != MAP_FAILED)) {
/* Unmap the fbdev */
munmap(tx->fb_base, tx->fb_len);
tx->fb_base = MAP_FAILED;
}

if (!tx->vt_active && (tx->fb_base == MAP_FAILED)) {
/* Restore the fbdev settings */
if (!twin_fbdev_apply_config(tx))
log_error("Failed to apply configurations to the fbdev");

/* Mark entire screen for refresh */
twin_screen_damage(screen, 0, 0, screen->width, screen->height);
}

if (!tx->vt_active && twin_screen_damaged(screen))
twin_screen_update(screen);

return true;
}

twin_context_t *twin_fbdev_init(int width, int height)
{
char *fbdev_path = getenv(FBDEV_NAME);
Expand All @@ -218,10 +248,13 @@ twin_context_t *twin_fbdev_init(int width, int height)
}

/* Set up virtual terminal environment */
if (!twin_vt_setup(&tx->vt_fd)) {
if (!twin_vt_setup(&tx->vt_fd, &tx->old_vtm, &tx->vt_active)) {
goto bail_fb_fd;
}

/* Set up signal handlers for switching TTYs */
twin_vt_setup_signal_handler();

/* Apply configurations to the framebuffer device */
if (!twin_fbdev_apply_config(tx)) {
log_error("Failed to apply configurations to the framebuffer device");
Expand Down Expand Up @@ -254,6 +287,9 @@ twin_context_t *twin_fbdev_init(int width, int height)
/* Setup file handler and work functions */
twin_set_work(twin_fbdev_work, TWIN_WORK_REDISPLAY, ctx);

/* Register a callback function to handle damaged rendering */
twin_screen_register_damaged(ctx->screen, twin_fbdev_update_damage, ctx);

return ctx;

bail_screen:
Expand Down Expand Up @@ -292,7 +328,6 @@ static void twin_fbdev_exit(twin_context_t *ctx)
}

/* Register the Linux framebuffer backend */

const twin_backend_t g_twin_backend = {
.init = twin_fbdev_init,
.configure = twin_fbdev_configure,
Expand Down
78 changes: 76 additions & 2 deletions backend/linux_vt.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@
#include <fcntl.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "twin_private.h"

#define VT_DEV_TTY_MAX 11
#define SIG_SWITCH_FROM SIGUSR1
#define SIG_SWITCH_TO SIGUSR2

static volatile sig_atomic_t *vt_fd;

static volatile sig_atomic_t *is_vt_actived;

static inline int twin_vt_open(int vt_num)
{
int fd;
Expand All @@ -35,7 +43,42 @@ static inline int twin_vt_mode(int fd, int mode)
return ioctl(fd, KDSETMODE, mode);
}

static inline bool twin_vt_setup(int *fd_ptr)
static void twin_vt_process_switch_signal(int signum)
{
if (signum == SIG_SWITCH_FROM) {
/* Notify kernel to release current virtual terminal */
ioctl(*vt_fd, VT_RELDISP, 1);

/* Set the virtual terminal display in text mode */
ioctl(*vt_fd, KDSETMODE, KD_TEXT);

*is_vt_actived = true;
} else if (signum == SIG_SWITCH_TO) {
/* Switch complete */
ioctl(*vt_fd, VT_RELDISP, VT_ACKACQ);

/* Restore the virtual terminal display in graphics mode */
ioctl(*vt_fd, KDSETMODE, KD_GRAPHICS);

*is_vt_actived = false;
}
}

static inline void twin_vt_setup_signal_handler()
{
struct sigaction tty_sig = {.sa_handler = twin_vt_process_switch_signal};
sigfillset(&tty_sig.sa_mask);

/* Set the signal handler for leaving the current TTY */
sigaction(SIG_SWITCH_FROM, &tty_sig, NULL);

/* Set the signal handler for returning to the previous TTY */
sigaction(SIG_SWITCH_TO, &tty_sig, NULL);
}

static inline bool twin_vt_setup(int *fd_ptr,
struct vt_mode *old_vtm,
bool *vt_active)
{
/* Open VT0 to inquire information */
if ((*fd_ptr = twin_vt_open(0)) < -1) {
Expand All @@ -57,12 +100,43 @@ static inline bool twin_vt_setup(int *fd_ptr)
return false;
}

/* Set VT to graphics mode to inhibit command-line text */
/* Attempt to activate the specified virtual terminal by its number */
if (ioctl(*fd_ptr, VT_ACTIVATE, vt_num) < 0) {
log_error("Failed to activate VT %d", vt_num);
return false;
}

/* Wait until the specified virtual terminal becomes the active VT */
if (ioctl(*fd_ptr, VT_WAITACTIVE, vt_num) < 0) {
log_error("Failed to activate VT %d", vt_num);
return false;
}

/* Save the virtual terminal mode */
if (ioctl(*fd_ptr, VT_GETMODE, &old_vtm) < 0) {
log_error("Failed to get VT mode");
return false;
}

/* Set the virtual terminal mode */
struct vt_mode vtm = {
.mode = VT_PROCESS, .relsig = SIGUSR1, .acqsig = SIGUSR2};

if (ioctl(*fd_ptr, VT_SETMODE, &vtm) < 0) {
log_error("Failed to set VT mode");
return false;
}

/* Set the virtual terminal display mode to graphics mode */
if (twin_vt_mode(*fd_ptr, KD_GRAPHICS) < 0) {
log_error("Failed to set KD_GRAPHICS mode");
twin_vt_mode(*fd_ptr, KD_TEXT);
return false;
}

vt_fd = fd_ptr;
is_vt_actived = vt_active;

return true;
}
#endif