aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--TODO2
-rw-r--r--doc/protocols112
-rw-r--r--doc/scancodes2
-rw-r--r--src/mds-kkbd.c195
-rw-r--r--src/mds-kkbd.h16
5 files changed, 288 insertions, 39 deletions
diff --git a/TODO b/TODO
index e59d74c..ca20837 100644
--- a/TODO
+++ b/TODO
@@ -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