summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/core/freebsd
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/core/freebsd')
-rw-r--r--contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h167
-rw-r--r--contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_freebsd.c613
2 files changed, 780 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h b/contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h
new file mode 100644
index 0000000..fc6e5e8
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h
@@ -0,0 +1,167 @@
1#include <sys/kbio.h>
2
3/* *INDENT-OFF* */ // clang-format off
4/*
5 * Automatically generated from /usr/share/vt/keymaps/us.acc.kbd.
6 * DO NOT EDIT!
7 */
8static keymap_t keymap_default_us_acc = { 0x6d, {
9/* alt
10 * scan cntrl alt alt cntrl
11 * code base shift cntrl shift alt shift cntrl shift spcl flgs
12 * ---------------------------------------------------------------------------
13 */
14/*00*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
15/*01*/{{ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, DBG, }, 0x03,0x00 },
16/*02*/{{ '1', '!', NOP, NOP, '1', '!', NOP, NOP, }, 0x33,0x00 },
17/*03*/{{ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, }, 0x00,0x00 },
18/*04*/{{ '3', '#', NOP, NOP, '3', '#', NOP, NOP, }, 0x33,0x00 },
19/*05*/{{ '4', '$', NOP, NOP, '4', '$', NOP, NOP, }, 0x33,0x00 },
20/*06*/{{ '5', '%', NOP, NOP, '5', '%', NOP, NOP, }, 0x33,0x00 },
21/*07*/{{ '6', '^', 0x1E, 0x1E, '6', DCIR, 0x1E, 0x1E, }, 0x04,0x00 },
22/*08*/{{ '7', '&', NOP, NOP, '7', '&', NOP, NOP, }, 0x33,0x00 },
23/*09*/{{ '8', '*', NOP, NOP, '8', DRIN, NOP, NOP, }, 0x37,0x00 },
24/*0a*/{{ '9', '(', NOP, NOP, '9', '(', NOP, NOP, }, 0x33,0x00 },
25/*0b*/{{ '0', ')', NOP, NOP, '0', ')', NOP, NOP, }, 0x33,0x00 },
26/*0c*/{{ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, }, 0x00,0x00 },
27/*0d*/{{ '=', '+', NOP, NOP, '=', '+', NOP, NOP, }, 0x33,0x00 },
28/*0e*/{{ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, }, 0x00,0x00 },
29/*0f*/{{ 0x09, BTAB, NEXT, NEXT, 0x09, BTAB, NOP, NOP, }, 0x77,0x00 },
30/*10*/{{ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, }, 0x00,0x01 },
31/*11*/{{ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, }, 0x00,0x01 },
32/*12*/{{ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, }, 0x00,0x01 },
33/*13*/{{ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, }, 0x00,0x01 },
34/*14*/{{ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, }, 0x00,0x01 },
35/*15*/{{ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, }, 0x00,0x01 },
36/*16*/{{ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, }, 0x00,0x01 },
37/*17*/{{ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, }, 0x00,0x01 },
38/*18*/{{ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, }, 0x00,0x01 },
39/*19*/{{ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, }, 0x00,0x01 },
40/*1a*/{{ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, }, 0x00,0x00 },
41/*1b*/{{ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, }, 0x00,0x00 },
42/*1c*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
43/*1d*/{{ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, }, 0xFF,0x00 },
44/*1e*/{{ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, }, 0x00,0x01 },
45/*1f*/{{ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, }, 0x00,0x01 },
46/*20*/{{ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, }, 0x00,0x01 },
47/*21*/{{ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, }, 0x00,0x01 },
48/*22*/{{ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, }, 0x00,0x01 },
49/*23*/{{ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, }, 0x00,0x01 },
50/*24*/{{ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, }, 0x00,0x01 },
51/*25*/{{ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, }, 0x00,0x01 },
52/*26*/{{ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, }, 0x00,0x01 },
53/*27*/{{ ';', ':', NOP, NOP, ';', ':', NOP, NOP, }, 0x33,0x00 },
54/*28*/{{ '\'', '"', NOP, NOP, DACU, DUML, NOP, NOP, }, 0x3F,0x00 },
55/*29*/{{ '`', '~', NOP, NOP, DGRA, DTIL, NOP, NOP, }, 0x3F,0x00 },
56/*2a*/{{ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, }, 0xFF,0x00 },
57/*2b*/{{ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, }, 0x00,0x00 },
58/*2c*/{{ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, }, 0x00,0x01 },
59/*2d*/{{ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, }, 0x00,0x01 },
60/*2e*/{{ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, }, 0x00,0x01 },
61/*2f*/{{ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, }, 0x00,0x01 },
62/*30*/{{ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, }, 0x00,0x01 },
63/*31*/{{ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, }, 0x00,0x01 },
64/*32*/{{ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, }, 0x00,0x01 },
65/*33*/{{ ',', '<', NOP, NOP, DCED, '<', NOP, NOP, }, 0x3B,0x00 },
66/*34*/{{ '.', '>', NOP, NOP, '.', '>', NOP, NOP, }, 0x33,0x00 },
67/*35*/{{ '/', '?', NOP, NOP, '/', '?', NOP, NOP, }, 0x33,0x00 },
68/*36*/{{ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, }, 0xFF,0x00 },
69/*37*/{{ '*', '*', '*', '*', '*', '*', '*', '*', }, 0x00,0x00 },
70/*38*/{{ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, }, 0xFF,0x00 },
71/*39*/{{ ' ', ' ', 0x00, 0x00, ' ', ' ', SUSP, SUSP, }, 0x03,0x00 },
72/*3a*/{{ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, }, 0xFF,0x00 },
73/*3b*/{{ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11),}, 0xFF,0x00 },
74/*3c*/{{ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12),}, 0xFF,0x00 },
75/*3d*/{{ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13),}, 0xFF,0x00 },
76/*3e*/{{ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14),}, 0xFF,0x00 },
77/*3f*/{{ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15),}, 0xFF,0x00 },
78/*40*/{{ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16),}, 0xFF,0x00 },
79/*41*/{{ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7),}, 0xFF,0x00 },
80/*42*/{{ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8),}, 0xFF,0x00 },
81/*43*/{{ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9),}, 0xFF,0x00 },
82/*44*/{{ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10),}, 0xFF,0x00 },
83/*45*/{{ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, }, 0xFF,0x00 },
84/*46*/{{ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, }, 0xFF,0x00 },
85/*47*/{{ F(49), '7', '7', '7', '7', '7', '7', '7', }, 0x80,0x02 },
86/*48*/{{ F(50), '8', '8', '8', '8', '8', '8', '8', }, 0x80,0x02 },
87/*49*/{{ F(51), '9', '9', '9', '9', '9', '9', '9', }, 0x80,0x02 },
88/*4a*/{{ F(52), '-', '-', '-', '-', '-', '-', '-', }, 0x80,0x02 },
89/*4b*/{{ F(53), '4', '4', '4', '4', '4', '4', '4', }, 0x80,0x02 },
90/*4c*/{{ F(54), '5', '5', '5', '5', '5', '5', '5', }, 0x80,0x02 },
91/*4d*/{{ F(55), '6', '6', '6', '6', '6', '6', '6', }, 0x80,0x02 },
92/*4e*/{{ F(56), '+', '+', '+', '+', '+', '+', '+', }, 0x80,0x02 },
93/*4f*/{{ F(57), '1', '1', '1', '1', '1', '1', '1', }, 0x80,0x02 },
94/*50*/{{ F(58), '2', '2', '2', '2', '2', '2', '2', }, 0x80,0x02 },
95/*51*/{{ F(59), '3', '3', '3', '3', '3', '3', '3', }, 0x80,0x02 },
96/*52*/{{ F(60), '0', '0', '0', '0', '0', '0', '0', }, 0x80,0x02 },
97/*53*/{{ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, }, 0x03,0x02 },
98/*54*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
99/*55*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
100/*56*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
101/*57*/{{ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11),}, 0xFF,0x00 },
102/*58*/{{ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12),}, 0xFF,0x00 },
103/*59*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
104/*5a*/{{ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, }, 0xFF,0x00 },
105/*5b*/{{ '/', '/', '/', '/', '/', '/', '/', '/', }, 0x00,0x02 },
106/*5c*/{{ NEXT, NEXT, NOP, NOP, DBG, DBG, DBG, DBG, }, 0xFF,0x00 },
107/*5d*/{{ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, }, 0xFF,0x00 },
108/*5e*/{{ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49),}, 0xFF,0x00 },
109/*5f*/{{ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50),}, 0xFF,0x00 },
110/*60*/{{ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51),}, 0xFF,0x00 },
111/*61*/{{ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53),}, 0xFF,0x00 },
112/*62*/{{ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55),}, 0xFF,0x00 },
113/*63*/{{ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57),}, 0xFF,0x00 },
114/*64*/{{ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58),}, 0xFF,0x00 },
115/*65*/{{ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59),}, 0xFF,0x00 },
116/*66*/{{ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60),}, 0xFF,0x00 },
117/*67*/{{ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61),}, 0xFF,0x00 },
118/*68*/{{ SPSC, SPSC, SUSP, SUSP, NOP, NOP, SUSP, SUSP, }, 0xFF,0x00 },
119/*69*/{{ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62),}, 0xFF,0x00 },
120/*6a*/{{ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63),}, 0xFF,0x00 },
121/*6b*/{{ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64),}, 0xFF,0x00 },
122/*6c*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
123} };
124
125static accentmap_t accentmap_default_us_acc = { 11, {
126 // dgra=0
127 { '`', { { 'a',0xe0 }, { 'A',0xc0 }, { 'e',0xe8 }, { 'E',0xc8 },
128 { 'i',0xec }, { 'I',0xcc }, { 'o',0xf2 }, { 'O',0xd2 },
129 { 'u',0xf9 }, { 'U',0xd9 }, }, },
130 // dacu=1
131 { 0xb4, { { 'a',0xe1 }, { 'A',0xc1 }, { 'e',0xe9 }, { 'E',0xc9 },
132 { 'i',0xed }, { 'I',0xcd }, { 'o',0xf3 }, { 'O',0xd3 },
133 { 'u',0xfa }, { 'U',0xda }, { 'y',0xfd }, { 'Y',0xdd }, }, },
134 // dcir=2
135 { '^', { { 'a',0xe2 }, { 'A',0xc2 }, { 'e',0xea }, { 'E',0xca },
136 { 'i',0xee }, { 'I',0xce }, { 'o',0xf4 }, { 'O',0xd4 },
137 { 'u',0xfb }, { 'U',0xdb }, }, },
138 // dtil=3
139 { '~', { { 'a',0xe3 }, { 'A',0xc3 }, { 'n',0xf1 }, { 'N',0xd1 },
140 { 'o',0xf5 }, { 'O',0xd5 }, }, },
141 // dmac=4
142 { 0x00 },
143 // dbre=5
144 { 0x00 },
145 // ddot=6
146 { 0x00 },
147 // duml=7
148 { 0xa8, { { 'a',0xe4 }, { 'A',0xc4 }, { 'e',0xeb }, { 'E',0xcb },
149 { 'i',0xef }, { 'I',0xcf }, { 'o',0xf6 }, { 'O',0xd6 },
150 { 'u',0xfc }, { 'U',0xdc }, { 'y',0xff }, }, },
151 // dsla=8
152 { 0x00 },
153 // drin=9
154 { 0xb0, { { 'a',0xe5 }, { 'A',0xc5 }, }, },
155 // dced=10
156 { 0xb8, { { 'c',0xe7 }, { 'C',0xc7 }, }, },
157 // dapo=11
158 { 0x00 },
159 // ddac=12
160 { 0x00 },
161 // dogo=13
162 { 0x00 },
163 // dcar=14
164 { 0x00 },
165} };
166
167/* *INDENT-ON* */ // clang-format on
diff --git a/contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_freebsd.c b/contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_freebsd.c
new file mode 100644
index 0000000..16a2171
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/core/freebsd/SDL_evdev_kbd_freebsd.c
@@ -0,0 +1,613 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22#include "SDL_internal.h"
23
24#include "../linux/SDL_evdev_kbd.h"
25
26#ifdef SDL_INPUT_FBSDKBIO
27
28// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD
29
30#include <unistd.h>
31#include <fcntl.h>
32#include <sys/ioctl.h>
33#include <sys/kbio.h>
34#include <sys/consio.h>
35
36#include <signal.h>
37
38#include "../../events/SDL_events_c.h"
39#include "SDL_evdev_kbd_default_keyaccmap.h"
40
41typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
42
43/*
44 * Keyboard State
45 */
46
47struct SDL_EVDEV_keyboard_state
48{
49 int console_fd;
50 int keyboard_fd;
51 unsigned long old_kbd_mode;
52 unsigned short **key_maps;
53 keymap_t *key_map;
54 keyboard_info_t *kbInfo;
55 unsigned char shift_down[4]; // shift state counters..
56 bool dead_key_next;
57 int npadch; // -1 or number assembled on pad
58 accentmap_t *accents;
59 unsigned int diacr;
60 bool rep; // flag telling character repeat
61 unsigned char lockstate;
62 unsigned char ledflagstate;
63 char shift_state;
64 char text[128];
65 unsigned int text_len;
66};
67
68static bool SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
69{
70 return ioctl(kbd->keyboard_fd, GIO_KEYMAP, kbd->key_map) >= 0;
71}
72
73static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
74static int kbd_cleanup_sigactions_installed = 0;
75static int kbd_cleanup_atexit_installed = 0;
76
77static struct sigaction old_sigaction[NSIG];
78
79static int fatal_signals[] = {
80 // Handlers for SIGTERM and SIGINT are installed in SDL_InitQuit.
81 SIGHUP, SIGQUIT, SIGILL, SIGABRT,
82 SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
83 SIGSYS
84};
85
86static void kbd_cleanup(void)
87{
88 struct mouse_info mData;
89 SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
90 if (!kbd) {
91 return;
92 }
93 kbd_cleanup_state = NULL;
94 SDL_zero(mData);
95 mData.operation = MOUSE_SHOW;
96 ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
97 if (kbd->keyboard_fd != kbd->console_fd) {
98 close(kbd->keyboard_fd);
99 }
100 ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
101 ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
102}
103
104void SDL_EVDEV_kbd_reraise_signal(int sig)
105{
106 raise(sig);
107}
108
109siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
110void *SDL_EVDEV_kdb_cleanup_ucontext = NULL;
111
112static void kbd_cleanup_signal_action(int signum, siginfo_t *info, void *ucontext)
113{
114 struct sigaction *old_action_p = &(old_sigaction[signum]);
115 sigset_t sigset;
116
117 // Restore original signal handler before going any further.
118 sigaction(signum, old_action_p, NULL);
119
120 // Unmask current signal.
121 sigemptyset(&sigset);
122 sigaddset(&sigset, signum);
123 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
124
125 // Save original signal info and context for archeologists.
126 SDL_EVDEV_kdb_cleanup_siginfo = info;
127 SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
128
129 // Restore keyboard.
130 kbd_cleanup();
131
132 // Reraise signal.
133 SDL_EVDEV_kbd_reraise_signal(signum);
134}
135
136static void kbd_unregister_emerg_cleanup(void)
137{
138 int tabidx;
139
140 kbd_cleanup_state = NULL;
141
142 if (!kbd_cleanup_sigactions_installed) {
143 return;
144 }
145 kbd_cleanup_sigactions_installed = 0;
146
147 for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
148 struct sigaction *old_action_p;
149 struct sigaction cur_action;
150 int signum = fatal_signals[tabidx];
151 old_action_p = &(old_sigaction[signum]);
152
153 // Examine current signal action
154 if (sigaction(signum, NULL, &cur_action)) {
155 continue;
156 }
157
158 // Check if action installed and not modified
159 if (!(cur_action.sa_flags & SA_SIGINFO) || cur_action.sa_sigaction != &kbd_cleanup_signal_action) {
160 continue;
161 }
162
163 // Restore original action
164 sigaction(signum, old_action_p, NULL);
165 }
166}
167
168static void kbd_cleanup_atexit(void)
169{
170 // Restore keyboard.
171 kbd_cleanup();
172
173 // Try to restore signal handlers in case shared library is being unloaded
174 kbd_unregister_emerg_cleanup();
175}
176
177static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
178{
179 int tabidx;
180
181 if (kbd_cleanup_state) {
182 return;
183 }
184 kbd_cleanup_state = kbd;
185
186 if (!kbd_cleanup_atexit_installed) {
187 /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
188 * functions that are called when the shared library is unloaded.
189 * -- man atexit(3)
190 */
191 atexit(kbd_cleanup_atexit);
192 kbd_cleanup_atexit_installed = 1;
193 }
194
195 if (kbd_cleanup_sigactions_installed) {
196 return;
197 }
198 kbd_cleanup_sigactions_installed = 1;
199
200 for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
201 struct sigaction *old_action_p;
202 struct sigaction new_action;
203 int signum = fatal_signals[tabidx];
204 old_action_p = &(old_sigaction[signum]);
205 if (sigaction(signum, NULL, old_action_p)) {
206 continue;
207 }
208
209 /* Skip SIGHUP and SIGPIPE if handler is already installed
210 * - assume the handler will do the cleanup
211 */
212 if ((signum == SIGHUP || signum == SIGPIPE) && (old_action_p->sa_handler != SIG_DFL || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL)) {
213 continue;
214 }
215
216 new_action = *old_action_p;
217 new_action.sa_flags |= SA_SIGINFO;
218 new_action.sa_sigaction = &kbd_cleanup_signal_action;
219 sigaction(signum, &new_action, NULL);
220 }
221}
222
223SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
224{
225 SDL_EVDEV_keyboard_state *kbd;
226 struct mouse_info mData;
227 char flag_state;
228 char *devicePath;
229
230 SDL_zero(mData);
231 mData.operation = MOUSE_HIDE;
232 kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state));
233 if (!kbd) {
234 return NULL;
235 }
236
237 kbd->npadch = -1;
238
239 // This might fail if we're not connected to a tty (e.g. on the Steam Link)
240 kbd->keyboard_fd = kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
241
242 kbd->shift_state = 0;
243
244 kbd->accents = SDL_calloc(1, sizeof(accentmap_t));
245 kbd->key_map = SDL_calloc(1, sizeof(keymap_t));
246 kbd->kbInfo = SDL_calloc(1, sizeof(keyboard_info_t));
247
248 ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
249 ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
250
251 if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
252 kbd->ledflagstate = flag_state;
253 }
254
255 if (ioctl(kbd->console_fd, GIO_DEADKEYMAP, kbd->accents) < 0) {
256 SDL_free(kbd->accents);
257 kbd->accents = &accentmap_default_us_acc;
258 }
259
260 if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
261 // Set the keyboard in XLATE mode and load the keymaps
262 ioctl(kbd->console_fd, KDSKBMODE, (unsigned long)(K_XLATE));
263 if (!SDL_EVDEV_kbd_load_keymaps(kbd)) {
264 SDL_free(kbd->key_map);
265 kbd->key_map = &keymap_default_us_acc;
266 }
267
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 }
280
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;
290 }
291
292 return kbd;
293}
294
295void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
296{
297 struct mouse_info mData;
298
299 if (!kbd) {
300 return;
301 }
302 SDL_zero(mData);
303 mData.operation = MOUSE_SHOW;
304 ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
305
306 kbd_unregister_emerg_cleanup();
307
308 if (kbd->keyboard_fd >= 0) {
309 // Restore the original keyboard mode
310 ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
311
312 close(kbd->keyboard_fd);
313 if (kbd->console_fd != kbd->keyboard_fd && kbd->console_fd >= 0) {
314 // Give back keyboard.
315 ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
316 }
317 kbd->console_fd = kbd->keyboard_fd = -1;
318 }
319
320 SDL_free(kbd);
321}
322
323void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
324{
325}
326
327void 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)
328{
329}
330
331void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
332{
333}
334
335/*
336 * Helper Functions.
337 */
338static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
339{
340 // c is already part of a UTF-8 sequence and safe to add as a character
341 if (kbd->text_len < (sizeof(kbd->text) - 1)) {
342 kbd->text[kbd->text_len++] = (char)c;
343 }
344}
345
346static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
347{
348 if (c < 0x80)
349 /* 0******* */
350 put_queue(kbd, c);
351 else if (c < 0x800) {
352 /* 110***** 10****** */
353 put_queue(kbd, 0xc0 | (c >> 6));
354 put_queue(kbd, 0x80 | (c & 0x3f));
355 } else if (c < 0x10000) {
356 if (c >= 0xD800 && c < 0xE000) {
357 return;
358 }
359 if (c == 0xFFFF) {
360 return;
361 }
362 /* 1110**** 10****** 10****** */
363 put_queue(kbd, 0xe0 | (c >> 12));
364 put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
365 put_queue(kbd, 0x80 | (c & 0x3f));
366 } else if (c < 0x110000) {
367 /* 11110*** 10****** 10****** 10****** */
368 put_queue(kbd, 0xf0 | (c >> 18));
369 put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
370 put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
371 put_queue(kbd, 0x80 | (c & 0x3f));
372 }
373}
374
375/*
376 * We have a combining character DIACR here, followed by the character CH.
377 * If the combination occurs in the table, return the corresponding value.
378 * Otherwise, if CH is a space or equals DIACR, return DIACR.
379 * Otherwise, conclude that DIACR was not combining after all,
380 * queue it and return CH.
381 */
382static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
383{
384 unsigned int d = kbd->diacr;
385 unsigned int i, j;
386
387 kbd->diacr = 0;
388
389 for (i = 0; i < kbd->accents->n_accs; i++) {
390 if (kbd->accents->acc[i].accchar == d) {
391 for (j = 0; j < NUM_ACCENTCHARS; ++j) {
392 if (kbd->accents->acc[i].map[j][0] == 0) { // end of table
393 break;
394 }
395 if (kbd->accents->acc[i].map[j][0] == ch) {
396 return kbd->accents->acc[i].map[j][1];
397 }
398 }
399 }
400 }
401
402 if (ch == ' ' || ch == d) {
403 put_utf8(kbd, d);
404 return 0;
405 }
406 put_utf8(kbd, d);
407
408 return ch;
409}
410
411static bool vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
412{
413 return (kbd->ledflagstate & flag) != 0;
414}
415
416static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
417{
418 kbd->ledflagstate ^= flag;
419 ioctl(kbd->keyboard_fd, KDSKBSTATE, (unsigned long)(kbd->ledflagstate));
420}
421
422/*
423 * Special function handlers
424 */
425
426static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
427{
428 if (up_flag) {
429 return; // no action, if this is a key release
430 }
431
432 if (kbd->diacr) {
433 value = handle_diacr(kbd, value);
434 }
435
436 if (kbd->dead_key_next) {
437 kbd->dead_key_next = false;
438 kbd->diacr = value;
439 return;
440 }
441 put_utf8(kbd, value);
442}
443
444static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
445{
446 if (up_flag)
447 return;
448
449 kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
450}
451
452static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
453{
454 int old_state = kbd->shift_state;
455
456 if (kbd->rep)
457 return;
458
459 if (up_flag) {
460 /*
461 * handle the case that two shift or control
462 * keys are depressed simultaneously
463 */
464 if (kbd->shift_down[value]) {
465 kbd->shift_down[value]--;
466 }
467 } else
468 kbd->shift_down[value]++;
469
470 if (kbd->shift_down[value])
471 kbd->shift_state |= (1 << value);
472 else
473 kbd->shift_state &= ~(1 << value);
474
475 // kludge
476 if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
477 put_utf8(kbd, kbd->npadch);
478 kbd->npadch = -1;
479 }
480}
481
482void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
483{
484 keymap_t key_map;
485 struct keyent_t keysym;
486 unsigned int final_key_state;
487 unsigned int map_from_key_sym;
488
489 if (!kbd) {
490 return;
491 }
492
493 key_map = *kbd->key_map;
494
495 kbd->rep = (down == 2);
496
497 if (keycode < NUM_KEYS) {
498 if (keycode >= 89 && keycode <= 95) {
499 // These constitute unprintable language-related keys, so ignore them.
500 return;
501 }
502 if (keycode > 95) {
503 keycode -= 7;
504 }
505 if (vc_kbd_led(kbd, ALKED) || (kbd->shift_state & 0x8)) {
506 keycode += ALTGR_OFFSET;
507 }
508 keysym = key_map.key[keycode];
509 } else {
510 return;
511 }
512
513 final_key_state = kbd->shift_state & 0x7;
514 if ((keysym.flgs & FLAG_LOCK_C) && vc_kbd_led(kbd, LED_CAP)) {
515 final_key_state ^= 0x1;
516 }
517 if ((keysym.flgs & FLAG_LOCK_N) && vc_kbd_led(kbd, LED_NUM)) {
518 final_key_state ^= 0x1;
519 }
520
521 map_from_key_sym = keysym.map[final_key_state];
522 if ((keysym.spcl & (0x80 >> final_key_state)) || (map_from_key_sym & SPCLKEY)) {
523 // Special function.
524 if (map_from_key_sym == 0)
525 return; // Nothing to do.
526 if (map_from_key_sym & SPCLKEY) {
527 map_from_key_sym &= ~SPCLKEY;
528 }
529 if (map_from_key_sym >= F_ACC && map_from_key_sym <= L_ACC) {
530 // Accent function.
531 unsigned int accent_index = map_from_key_sym - F_ACC;
532 if (kbd->accents->acc[accent_index].accchar != 0) {
533 k_deadunicode(kbd, kbd->accents->acc[accent_index].accchar, !down);
534 }
535 } else {
536 switch (map_from_key_sym) {
537 case ASH: // alt/meta shift
538 k_shift(kbd, 3, down == 0);
539 break;
540 case LSHA: // left shift + alt lock
541 case RSHA: // right shift + alt lock
542 if (down == 0) {
543 chg_vc_kbd_led(kbd, ALKED);
544 }
545 SDL_FALLTHROUGH;
546 case LSH: // left shift
547 case RSH: // right shift
548 k_shift(kbd, 0, down == 0);
549 break;
550 case LCTRA: // left ctrl + alt lock
551 case RCTRA: // right ctrl + alt lock
552 if (down == 0) {
553 chg_vc_kbd_led(kbd, ALKED);
554 }
555 SDL_FALLTHROUGH;
556 case LCTR: // left ctrl
557 case RCTR: // right ctrl
558 k_shift(kbd, 1, down == 0);
559 break;
560 case LALTA: // left alt + alt lock
561 case RALTA: // right alt + alt lock
562 if (down == 0) {
563 chg_vc_kbd_led(kbd, ALKED);
564 }
565 SDL_FALLTHROUGH;
566 case LALT: // left alt
567 case RALT: // right alt
568 k_shift(kbd, 2, down == 0);
569 break;
570 case ALK: // alt lock
571 if (down == 1) {
572 chg_vc_kbd_led(kbd, ALKED);
573 }
574 break;
575 case CLK: // caps lock
576 if (down == 1) {
577 chg_vc_kbd_led(kbd, CLKED);
578 }
579 break;
580 case NLK: // num lock
581 if (down == 1) {
582 chg_vc_kbd_led(kbd, NLKED);
583 }
584 break;
585 case SLK: // scroll lock
586 if (down == 1) {
587 chg_vc_kbd_led(kbd, SLKED);
588 }
589 break;
590 default:
591 return;
592 }
593 }
594 } else {
595 if (map_from_key_sym == '\n' || map_from_key_sym == '\r') {
596 if (kbd->diacr) {
597 kbd->diacr = 0;
598 return;
599 }
600 }
601 if (map_from_key_sym >= ' ' && map_from_key_sym != 127) {
602 k_self(kbd, map_from_key_sym, !down);
603 }
604 }
605
606 if (kbd->text_len > 0) {
607 kbd->text[kbd->text_len] = '\0';
608 SDL_SendKeyboardText(kbd->text);
609 kbd->text_len = 0;
610 }
611}
612
613#endif // SDL_INPUT_FBSDKBIO