diff options
Diffstat (limited to '')
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | doc/protocols | 112 | ||||
-rw-r--r-- | doc/scancodes | 2 | ||||
-rw-r--r-- | src/mds-kkbd.c | 195 | ||||
-rw-r--r-- | src/mds-kkbd.h | 16 |
5 files changed, 288 insertions, 39 deletions
@@ -49,6 +49,8 @@ Extra servers: workspace A simple, reference implementation of a, workspaces tray A simple, reference implementation of a, status icon tray cool-old-crt Old CRT emulator, like cool-old-term but display-wide + kbd2rat Control the rat cursor with the keyboard + multikey Bind keys to send sequences of keys Missing commands: diff --git a/doc/protocols b/doc/protocols index a4147cd..b0649aa 100644 --- a/doc/protocols +++ b/doc/protocols @@ -362,3 +362,115 @@ Compulsivity: required if supporting `Command: add-tray-icon` --------------------------------------------------------------------- +Command: key-sent + Announce a keyboard input event + +Required header: Keyboard + Any string that uniquely identifies the keyboard + Purpose: Enable multi-keyboard aware programs and give at + least on keyboard per seat in a multi-seat environment + Note: mds-kkbd uses `kernel` to indicate that it uses the kernel + and thus lumps together all keyboards. + +Required header: Released + `yes` if the key was released + `no` otherwise, that is, held down or pressed + Note: pause/break is automatically released directly after it + has been pressed. This is feature built into keyboards + and servers should not try to circumvent this + +Required header: Keycode + An unsigned 14-bit integer identifying the key, may be remapped + +Optional header: Scancode + Either an unsigned 7-bit integer or a single blank space + separated trio of unsigned 7-bit integers, identifying the key. + This is the scancode sent from the keyboard and optionally + unified by the keyboard driver, however with the typed/released + bit zeroed out. This may not be remapped. + +Optional header: Modifiers + Single blank space separated list of active modifiers: + shift) Shift (level 2) + ctrl) Control + alt) Alternative/Option + altgr) Alternative Graphic (level 3) + lvl*) * may be any 2ⁿ + 1 integer greater than 4 + super) Super + hyper) Hyper + caps) Caps (usually a lock key) + num) Num (usually a lock key) + scrl) Scroll (usually a lock key) + top) Top (historical) + front) Front (historical) + greek) Greek (historical) + compose) Compose (rare, it is usally a dead key) + Any key that has been locked should be prefix with `+`, + if the key has been locked by nullified with non-lock + modifier it should be prefixed with a `-`. + If no modifier is active or has been nullified, `none` + should be used. + +Optional header: Key + A textual representation of the key that has been typed or + released, as mapped by the keyboard layout. + esc) Escape + f*) F* where * is any integer + sysrq) System Request/Print Screen + scrl) Scroll (lock) + break) Break/Pause + backspace) Backspace + tab) Tab + return) Return/Enter + space) Blank Space + menu) Application Menu + ins) Insert + home) Home + pgup) Page Up + del) Delete + end) End + pgdown) Page Down + up) Up Arrow + left) Left Arrow + down) Down Arrow + right) Right Arrow + shift) Shift (level 2) + ctrl) Control + alt) Alternative/Option + altgr) Alternative Graphic (level 3) + lvl*) * may be any 2ⁿ + 1 integer greater than 4 + super) Super + hyper) Hyper + caps) Caps (usually a lock key) + num) Num (usually a lock key) + scrl) Scroll (usually a lock key) + top) Top (historical) + front) Front (historical) + greek) Greek (historical) + compose) Compose (usally a dead key) + letter *) * may be any UTF-8 encoded letter + Keys that lock/unlock a modifer should be suffixed with a + blank space and a `lock`. If the key is a dead key (even + the compose key) should use `dead` instead. A position, + either `left`, `right` or `keypad`, followed by a blank + space, should prefix any key that occurs on multiple + position on the keyboard. Keys without any meaning should + be identified as `unknown`. Modifiers and dead keys should + not affect the value. + +Optional header: Characters + UTF-8 encoded string that has been written + +Purpose: Enable the user to use a keyboard, physical or on-screen +Purpose: Enable programs to send keys as part of a script or + a reply of a recorded session + +Compulsivity: highly-recommended, a computer is as good as useless + without a keyboard + +Reference implementation: kkbd +Reference implementation: kbd +Reference implementation: keytrans + +--------------------------------------------------------------------- + diff --git a/doc/scancodes b/doc/scancodes index 3c29a8c..f978415 100644 --- a/doc/scancodes +++ b/doc/scancodes @@ -90,7 +90,7 @@ 97 :: right control 98 :: #divide 99 :: sysrq -100 :: alternative graph +100 :: alternative graphic 102 :: home 103 :: up 104 :: page up diff --git a/src/mds-kkbd.c b/src/mds-kkbd.c index 51a8953..afa9cac 100644 --- a/src/mds-kkbd.c +++ b/src/mds-kkbd.c @@ -111,6 +111,25 @@ static struct termios saved_stty; */ static int saved_kbd_mode; +/** + * Keycode remapping table + */ +static int* restrict mapping = NULL; + +/** + * The size of `mapping` + */ +static size_t mapping_size = 0; + +/** + * Scancode buffer + */ +static int scancode_buf[3] = { 0, 0, 0 }; + +/** + * The number of elements stored in `scancode_buf` + */ +static int scancode_ptr = 0; @@ -211,7 +230,8 @@ void fork_cleanup(int status) */ size_t marshal_server_size(void) { - size_t rc = 5 * sizeof(int) + sizeof(int32_t) + sizeof(struct termios); + size_t rc = 9 * sizeof(int) + sizeof(int32_t) + sizeof(struct termios); + rc += sizeof(size_t) + mapping_size * sizeof(int); rc += mds_message_marshal_size(&received); return rc; } @@ -232,9 +252,20 @@ int marshal_server(char* state_buf) buf_set_next(state_buf, int, saved_leds); buf_set_next(state_buf, struct termios, saved_stty); buf_set_next(state_buf, int, saved_kbd_mode); + buf_set_next(state_buf, int, scancode_ptr); + buf_set_next(state_buf, int, scancode_buf[0]); + buf_set_next(state_buf, int, scancode_buf[1]); + buf_set_next(state_buf, int, scancode_buf[2]); + buf_set_next(state_buf, size_t, mapping_size); + if (mapping_size > 0) + { + memcpy(state_buf, mapping, mapping_size * sizeof(int)); + state_buf += mapping_size * sizeof(int) / sizeof(char); + } mds_message_marshal(&received, state_buf); mds_message_destroy(&received); + free(mapping); return 0; } @@ -259,12 +290,24 @@ int unmarshal_server(char* state_buf) buf_get_next(state_buf, int, saved_leds); buf_get_next(state_buf, struct termios, saved_stty); buf_get_next(state_buf, int, saved_kbd_mode); + buf_get_next(state_buf, int, scancode_ptr); + buf_get_next(state_buf, int, scancode_buf[0]); + buf_get_next(state_buf, int, scancode_buf[1]); + buf_get_next(state_buf, int, scancode_buf[2]); + buf_get_next(state_buf, size_t, mapping_size); + if (mapping_size > 0) + { + fail_if (xmalloc(mapping, mapping_size, int)); + memcpy(mapping, state_buf, mapping_size * sizeof(int)); + state_buf += mapping_size * sizeof(int) / sizeof(char); + } fail_if (mds_message_unmarshal(&received, state_buf)); return 0; pfail: xperror(*argv); mds_message_destroy(&received); + free(mapping); abort(); /* We must abort on failure to not risk the keyboard getting stuck and freeze up the computer until someone ssh:es into it and kill the server. */ @@ -292,44 +335,11 @@ int __attribute__((const)) reexec_failure_recover(void) int master_loop(void) { int rc = 1; - int c, keycode, released; - int scancode[3]; - - while ((c = getchar()) != 1) /* Exit with ESCAPE */ - { - redo: - keycode = c & 0x7F; - released = !!(c & 0x80); - scancode[0] = keycode; - - if (keycode == 0) - { - scancode[1] = getchar(); - if ((scancode[1] & 0x80) == 0) - { - c = scancode[1]; - goto redo; - } - scancode[2] = getchar(); - if ((scancode[2] & 0x80) == 0) - { - printf("scancode: %i\n", scancode[1]); - printf("keycode: %i\n", scancode[1]); - printf("released: no\n"); - c = scancode[2]; - goto redo; - } - keycode = (scancode[1] & 0x7F) << 7; - keycode |= (scancode[2] & 0x7F); - printf("scancode: %i %i %i\n", - scancode[0], scancode[1], scancode[2]); - } - else - printf("scancode: %i\n", scancode[0]); - printf("keycode: %i\n", keycode); - printf("released: %s\n", released ? "yes" : "no"); - } + while (!reexecing && !terminating) + if (fetch_keys() < 0) + if (errno != EINTR) + goto pfail; /* while (!reexecing && !terminating) { @@ -368,6 +378,7 @@ int master_loop(void) if (!rc && reexecing) return 0; mds_message_destroy(&received); + free(mapping); return rc; } @@ -505,5 +516,113 @@ void close_input(void) } +/** + * Broadcast a keyboard input event + * + * @param scancode The scancode + * @param trio Whether the scancode has three integers rather than one + * @return Zero on success, -1 on error + */ +int send_key(int* restrict scancode, int trio) +{ + int keycode, released = (scancode[0] & 0x80) == 0x80; + scancode[0] &= 0x7F; + if (trio) + { + keycode = (scancode[1] &= 0x7F) << 7; + keycode |= (scancode[2] &= 0x7F); + } + else + keycode = scancode[0]; + + if ((size_t)keycode < mapping_size) + keycode = mapping[keycode]; + + printf("Command: key-sent\n"); + if (trio) + printf("Scancode: %i %i %i\n", scancode[0], scancode[1], scancode[2]); + else + printf("Scancode: %i\n", scancode[0]); + printf("Keycode: %i\n", keycode); + printf("Released: %s\n", released ? "yes" : "no"); + printf("Keyboard: kernel\n\n"); + + return 0; +} + + +/** + * Fetch and broadcast keys until interrupted + * + * @return Zero on success, -1 on error + */ +int fetch_keys(void) +{ +#ifdef DEBUG + int consecutive_escapes = 0; +#endif + int c; + ssize_t r; + + for (;;) + { + r = read(STDIN_FILENO, &c, sizeof(int)); + if (r <= 0) + { + if (r == 0) + { + raise(SIGTERM); + errno = 0; + } + break; + } + +#ifdef DEBUG + if ((c & 0x7F) == 1) /* Exit with ESCAPE, ESCAPE, ESCAPE */ + { + if (++consecutive_escapes >= 6) + { + raise(SIGTERM); + break; + } + } + else + consecutive_escapes = 0; +#endif + + redo: + scancode_buf[scancode_ptr] = c; + if (scancode_ptr == 0) + { + if ((c & 0x7F) == 0) + scancode_ptr++; + else + send_key(scancode_buf, 0); + } + else if (scancode_ptr == 1) + { + if ((c & 0x80) == 0) + { + scancode_ptr = 0; + goto redo; + } + scancode_ptr++; + } + else + { + scancode_ptr = 0; + if ((c & 0x80) == 0) + { + send_key(scancode_buf + 1, 0); + goto redo; + } + send_key(scancode_buf, 1); + } + } + + return errno == 0 ? 0 : -1; +} + + /* TODO delay and repetition */ diff --git a/src/mds-kkbd.h b/src/mds-kkbd.h index fc22f2b..3fa7eca 100644 --- a/src/mds-kkbd.h +++ b/src/mds-kkbd.h @@ -70,6 +70,22 @@ int open_input(void); */ void close_input(void); +/** + * Broadcast a keyboard input event + * + * @param scancode The scancode + * @param trio Whether the scancode has three integers rather than one + * @return Zero on success, -1 on error + */ +int send_key(int* restrict scancode, int trio); + +/** + * Fetch and broadcast keys until interrupted + * + * @return Zero on success, -1 on error + */ +int fetch_keys(void); + #endif |