Skip to content

Commit 3b80fcd

Browse files
authored
KMSDRM/EVDEV: add VT switching support for FreeBSD (#14346)
Signed-off-by: Quentin Thébault <quentin.thebault@defenso.fr>
1 parent b3612f6 commit 3b80fcd

File tree

3 files changed

+215
-35
lines changed

3 files changed

+215
-35
lines changed

src/core/freebsd/SDL_evdev_kbd_freebsd.c

Lines changed: 202 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,26 @@
2727

2828
// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD
2929

30+
#include <errno.h>
3031
#include <unistd.h>
3132
#include <fcntl.h>
3233
#include <sys/ioctl.h>
3334
#include <sys/kbio.h>
3435
#include <sys/consio.h>
36+
#include <termios.h>
3537

3638
#include <signal.h>
3739

3840
#include "../../events/SDL_events_c.h"
3941
#include "SDL_evdev_kbd_default_keyaccmap.h"
4042

43+
#ifndef K_OFF
44+
#define K_OFF 0x04
45+
#endif
46+
4147
typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
4248

49+
4350
/*
4451
* Keyboard State
4552
*/
@@ -48,6 +55,7 @@ struct SDL_EVDEV_keyboard_state
4855
{
4956
int console_fd;
5057
int keyboard_fd;
58+
bool muted;
5159
unsigned long old_kbd_mode;
5260
unsigned short **key_maps;
5361
keymap_t *key_map;
@@ -63,6 +71,10 @@ struct SDL_EVDEV_keyboard_state
6371
char shift_state;
6472
char text[128];
6573
unsigned int text_len;
74+
void (*vt_release_callback)(void *);
75+
void *vt_release_callback_data;
76+
void (*vt_acquire_callback)(void *);
77+
void *vt_acquire_callback_data;
6678
};
6779

6880
static bool SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
@@ -83,22 +95,28 @@ static int fatal_signals[] = {
8395
SIGSYS
8496
};
8597

86-
static void kbd_cleanup(void)
98+
static void vt_update_mouse(SDL_EVDEV_keyboard_state *kbd, int operation)
8799
{
88100
struct mouse_info mData;
101+
102+
SDL_zero(mData);
103+
mData.operation = operation;
104+
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
105+
}
106+
107+
static void kbd_cleanup(void)
108+
{
89109
SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
90110
if (!kbd) {
91111
return;
92112
}
93113
kbd_cleanup_state = NULL;
94-
SDL_zero(mData);
95-
mData.operation = MOUSE_SHOW;
96114
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
97115
if (kbd->keyboard_fd != kbd->console_fd) {
98116
close(kbd->keyboard_fd);
99117
}
100118
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
101-
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
119+
vt_update_mouse(kbd, true);
102120
}
103121

104122
void SDL_EVDEV_kbd_reraise_signal(int sig)
@@ -220,15 +238,108 @@ static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
220238
}
221239
}
222240

241+
enum {
242+
VT_SIGNAL_NONE,
243+
VT_SIGNAL_RELEASE,
244+
VT_SIGNAL_ACQUIRE,
245+
};
246+
static int vt_release_signal;
247+
static int vt_acquire_signal;
248+
static SDL_AtomicInt vt_signal_pending;
249+
SDL_AtomicInt vt_current;
250+
251+
typedef void (*signal_handler)(int signum);
252+
253+
254+
static void kbd_vt_release_signal_action(int signum)
255+
{
256+
SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE);
257+
SDL_SetAtomicInt(&vt_current, VT_THEIRS);
258+
}
259+
260+
static void kbd_vt_acquire_signal_action(int signum)
261+
{
262+
SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE);
263+
SDL_SetAtomicInt(&vt_current, VT_OURS);
264+
}
265+
266+
static bool setup_vt_signal(int signum, signal_handler handler)
267+
{
268+
struct sigaction *old_action_p;
269+
struct sigaction new_action;
270+
old_action_p = &(old_sigaction[signum]);
271+
SDL_zero(new_action);
272+
new_action.sa_handler = handler;
273+
new_action.sa_flags = SA_RESTART;
274+
if (sigaction(signum, &new_action, old_action_p) < 0) {
275+
return false;
276+
}
277+
if (old_action_p->sa_handler != SIG_DFL) {
278+
// This signal is already in use
279+
if (signum == SIGUSR1 || signum == SIGUSR2) {
280+
/*
281+
* For the moment we have no other options in FreeBSD because
282+
* the vt(4) will not accept signal numbers over 32.
283+
*/
284+
return true;
285+
}
286+
sigaction(signum, old_action_p, NULL);
287+
return false;
288+
}
289+
return true;
290+
}
291+
292+
static void kbd_vt_quit(int console_fd)
293+
{
294+
struct vt_mode mode;
295+
296+
if (vt_release_signal) {
297+
sigaction(vt_release_signal, &old_sigaction[vt_release_signal], NULL);
298+
vt_release_signal = 0;
299+
}
300+
if (vt_acquire_signal) {
301+
sigaction(vt_acquire_signal, &old_sigaction[vt_acquire_signal], NULL);
302+
vt_acquire_signal = 0;
303+
}
304+
305+
SDL_zero(mode);
306+
mode.mode = VT_AUTO;
307+
ioctl(console_fd, VT_SETMODE, &mode);
308+
}
309+
310+
static bool kbd_vt_init(int console_fd)
311+
{
312+
struct vt_mode mode;
313+
314+
if (setup_vt_signal(SIGUSR1, kbd_vt_release_signal_action)) {
315+
vt_release_signal = SIGUSR1;
316+
}
317+
if (setup_vt_signal(SIGUSR2, kbd_vt_acquire_signal_action)) {
318+
vt_acquire_signal = SIGUSR2;
319+
}
320+
if (!vt_release_signal || !vt_acquire_signal ) {
321+
kbd_vt_quit(console_fd);
322+
return false;
323+
}
324+
325+
SDL_zero(mode);
326+
mode.mode = VT_PROCESS;
327+
mode.relsig = vt_release_signal;
328+
mode.acqsig = vt_acquire_signal;
329+
mode.frsig = SIGIO;
330+
if (ioctl(console_fd, VT_SETMODE, &mode) < 0) {
331+
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Failed VT_SETMODE ioctl: %s", strerror(errno));
332+
kbd_vt_quit(console_fd);
333+
return false;
334+
}
335+
return true;
336+
}
337+
223338
SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
224339
{
225340
SDL_EVDEV_keyboard_state *kbd;
226-
struct mouse_info mData;
227341
char flag_state;
228-
char *devicePath;
229342

230-
SDL_zero(mData);
231-
mData.operation = MOUSE_HIDE;
232343
kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state));
233344
if (!kbd) {
234345
return NULL;
@@ -246,7 +357,6 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
246357
kbd->kbInfo = SDL_calloc(1, sizeof(keyboard_info_t));
247358

248359
ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
249-
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
250360

251361
if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
252362
kbd->ledflagstate = flag_state;
@@ -265,43 +375,31 @@ SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
265375
kbd->key_map = &keymap_default_us_acc;
266376
}
267377

268-
if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) {
269-
/* Take keyboard from console and open the actual keyboard device.
270-
* Ensures that the keystrokes do not leak through to the console.
271-
*/
272-
ioctl(kbd->console_fd, CONS_RELKBD, 1ul);
273-
SDL_asprintf(&devicePath, "/dev/kbd%d", kbd->kbInfo->kb_index);
274-
kbd->keyboard_fd = open(devicePath, O_WRONLY | O_CLOEXEC);
275-
if (kbd->keyboard_fd == -1) {
276-
// Give keyboard back.
277-
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
278-
kbd->keyboard_fd = kbd->console_fd;
279-
}
378+
if (!kbd_vt_init(kbd->console_fd)) {
379+
SDL_LogInfo(SDL_LOG_CATEGORY_INPUT, "kbd_vt_init failed");
380+
}
280381

281-
/* Make sure to restore keyboard if application fails to call
282-
* SDL_Quit before exit or fatal signal is raised.
283-
*/
284-
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
285-
kbd_register_emerg_cleanup(kbd);
286-
}
287-
SDL_free(devicePath);
288-
} else
289-
kbd->keyboard_fd = kbd->console_fd;
382+
kbd->keyboard_fd = kbd->console_fd;
383+
384+
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
385+
kbd_register_emerg_cleanup(kbd);
386+
}
290387
}
291388

389+
vt_update_mouse(kbd, MOUSE_HIDE);
390+
292391
return kbd;
293392
}
294393

295394
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
296395
{
297-
struct mouse_info mData;
298-
299396
if (!kbd) {
300397
return;
301398
}
302-
SDL_zero(mData);
303-
mData.operation = MOUSE_SHOW;
304-
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
399+
400+
kbd_vt_quit(kbd->console_fd);
401+
402+
vt_update_mouse(kbd, MOUSE_SHOW);
305403

306404
kbd_unregister_emerg_cleanup();
307405

@@ -322,14 +420,83 @@ void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
322420

323421
void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
324422
{
423+
struct termios tios;
424+
425+
SDL_zero(tios);
426+
427+
if (!state) {
428+
return;
429+
}
430+
431+
if (muted == state->muted) {
432+
return;
433+
}
434+
435+
if (tcgetattr(state->console_fd, &tios) == -1) {
436+
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Could not get terminal mode: %s", strerror(errno));
437+
return;
438+
}
439+
440+
if (muted) {
441+
if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) {
442+
ioctl(state->console_fd, KDSKBMODE, K_OFF);
443+
cfmakeraw(&tios);
444+
445+
if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
446+
kbd_register_emerg_cleanup(state);
447+
}
448+
}
449+
} else {
450+
kbd_unregister_emerg_cleanup();
451+
452+
cfmakesane(&tios);
453+
ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
454+
}
455+
456+
if (tcsetattr(state->console_fd, TCSAFLUSH, &tios) == -1) {
457+
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Could not set terminal mode to %s: %s", muted ? "raw" : "sane", strerror(errno));
458+
return;
459+
}
460+
461+
state->muted = muted;
325462
}
326463

327464
void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void *), void *release_callback_data, void (*acquire_callback)(void *), void *acquire_callback_data)
328465
{
466+
if (state == NULL) {
467+
return;
468+
}
469+
470+
state->vt_release_callback = release_callback;
471+
state->vt_release_callback_data = release_callback_data;
472+
state->vt_acquire_callback = acquire_callback;
473+
state->vt_acquire_callback_data = acquire_callback_data;
329474
}
330475

331476
void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
332477
{
478+
if (!state) {
479+
return;
480+
}
481+
482+
int signal_pending = SDL_GetAtomicInt(&vt_signal_pending);
483+
484+
if (signal_pending != VT_SIGNAL_NONE) {
485+
if (signal_pending == VT_SIGNAL_RELEASE) {
486+
if (state->vt_release_callback) {
487+
vt_update_mouse(state, MOUSE_SHOW);
488+
state->vt_release_callback(state->vt_release_callback_data);
489+
}
490+
ioctl(state->console_fd, VT_RELDISP, 1);
491+
} else {
492+
if (state->vt_acquire_callback) {
493+
state->vt_acquire_callback(state->vt_acquire_callback_data);
494+
vt_update_mouse(state, MOUSE_HIDE);
495+
}
496+
ioctl(state->console_fd, VT_RELDISP, VT_ACKACQ);
497+
}
498+
SDL_CompareAndSwapAtomicInt(&vt_signal_pending, signal_pending, VT_SIGNAL_NONE);
499+
}
333500
}
334501

335502
/*

src/core/linux/SDL_evdev.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,11 @@ void SDL_EVDEV_Poll(void)
329329

330330
for (item = _this->first; item; item = item->next) {
331331
while ((len = read(item->fd, events, sizeof(events))) > 0) {
332+
#ifdef SDL_INPUT_FBSDKBIO
333+
if (SDL_GetAtomicInt(&vt_current) == VT_THEIRS) {
334+
continue;
335+
}
336+
#endif
332337
len /= sizeof(events[0]);
333338
for (i = 0; i < len; ++i) {
334339
struct input_event *event = &events[i];

src/core/linux/SDL_evdev_kbd.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
#ifndef SDL_evdev_kbd_h_
2323
#define SDL_evdev_kbd_h_
2424

25+
#ifdef SDL_INPUT_FBSDKBIO
26+
enum {
27+
VT_OURS,
28+
VT_THEIRS,
29+
};
30+
extern SDL_AtomicInt vt_current;
31+
#endif
32+
2533
struct SDL_EVDEV_keyboard_state;
2634
typedef struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state;
2735

0 commit comments

Comments
 (0)