Make kmscon listen to a FIFO in /tmp that clients can write to to request a keymap change. Patch by Mathieu Othacehe <m.othacehe@gmail.com>. Modified by Florian Pelz <pelzflorian@pelzflorian.de> and Ludovic Courtès <ludo@gnu.org>. diff --git a/src/pty.c b/src/pty.c index 1443f4a..f64cb5b 100644 --- a/src/pty.c +++ b/src/pty.c @@ -46,6 +46,8 @@ #define KMSCON_NREAD 16384 +#define INPUT_KEYMAP_UPDATE_FILE "/tmp/kmscon-%d-keymap-update" + struct kmscon_pty { unsigned long ref; struct ev_eloop *eloop; @@ -241,9 +243,22 @@ static bool pty_is_open(struct kmscon_pty *pty) return pty->fd >= 0; } +static int kmscon_keymap_update(pid_t pid) +{ + char *file; + int ret; + + ret = asprintf(&file, INPUT_KEYMAP_UPDATE_FILE, pid); + if (ret < 0) + return ret; + + return setenv("KEYMAP_UPDATE", file, 1); +} + static void __attribute__((noreturn)) exec_child(const char *term, const char *colorterm, char **argv, - const char *seat, const char *vtnr, bool env_reset) + const char *seat, const char *vtnr, bool env_reset, + pid_t kmscon_pid) { char **env; char **def_argv; @@ -277,6 +292,8 @@ exec_child(const char *term, const char *colorterm, char **argv, if (vtnr) setenv("XDG_VTNR", vtnr, 1); + kmscon_keymap_update(kmscon_pid); + execve(argv[0], argv, environ); log_err("failed to exec child %s: %m", argv[0]); @@ -383,12 +400,14 @@ static int pty_spawn(struct kmscon_pty *pty, int master, unsigned short width, unsigned short height) { pid_t pid; + pid_t kmscon_pid; struct winsize ws; memset(&ws, 0, sizeof(ws)); ws.ws_col = width; ws.ws_row = height; + kmscon_pid = getpid(); pid = fork(); switch (pid) { case -1: @@ -397,7 +416,7 @@ static int pty_spawn(struct kmscon_pty *pty, int master, case 0: setup_child(master, &ws); exec_child(pty->term, pty->colorterm, pty->argv, pty->seat, - pty->vtnr, pty->env_reset); + pty->vtnr, pty->env_reset, kmscon_pid); exit(EXIT_FAILURE); default: log_debug("forking child %d", pid); diff --git a/src/uterm_input.c b/src/uterm_input.c index 6fcbc4b..990a09d 100644 --- a/src/uterm_input.c +++ b/src/uterm_input.c @@ -178,6 +178,10 @@ static void input_new_dev(struct uterm_input *input, if (ret) goto err_rcodepoints; + /* Add the FIFO fd only to the first input poll loop. */ + if (shl_dlist_empty(&input->devices)) + uxkb_dev_keymap_update(dev); + if (input->awake > 0) { ret = input_wake_up_dev(dev); if (ret) diff --git a/src/uterm_input_internal.h b/src/uterm_input_internal.h index 04e6cc9..ec44459 100644 --- a/src/uterm_input_internal.h +++ b/src/uterm_input_internal.h @@ -39,6 +39,8 @@ #include "shl_misc.h" #include "uterm_input.h" +#define INPUT_KEYMAP_UPDATE_FILE "/tmp/kmscon-%d-keymap-update" + enum uterm_input_device_capability { UTERM_DEVICE_HAS_KEYS = (1 << 0), UTERM_DEVICE_HAS_LEDS = (1 << 1), @@ -62,6 +64,8 @@ struct uterm_input_dev { bool repeating; struct ev_timer *repeat_timer; + struct ev_fd *fd_update; + int rupdate_fd; }; struct uterm_input { @@ -95,6 +99,7 @@ void uxkb_desc_destroy(struct uterm_input *input); int uxkb_dev_init(struct uterm_input_dev *dev); void uxkb_dev_destroy(struct uterm_input_dev *dev); +int uxkb_dev_keymap_update(struct uterm_input_dev *dev); int uxkb_dev_process(struct uterm_input_dev *dev, uint16_t key_state, uint16_t code); diff --git a/src/uterm_input_uxkb.c b/src/uterm_input_uxkb.c index 925c755..8fe08f8 100644 --- a/src/uterm_input_uxkb.c +++ b/src/uterm_input_uxkb.c @@ -31,6 +31,9 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <xkbcommon/xkbcommon.h> #include "shl_hook.h" #include "shl_llog.h" @@ -178,6 +181,106 @@ static void timer_event(struct ev_timer *timer, uint64_t num, void *data) shl_hook_call(dev->input->hook, dev->input, &dev->repeat_event); } +static void uxkb_keymap_update_handler(struct ev_fd *fd, int mask, void *data) +{ + struct uterm_input_dev *dev = data; + char in; + char keymap[4][255]; + int pos = 0; + int curr_keymap = 0; + int ret; + char *model, *layout, *variant, *options; + + if (!(mask & EV_READABLE)) + return; + + memset(keymap, 0, sizeof(keymap)); + + model = keymap[0]; + layout = keymap[1]; + variant = keymap[2]; + options = keymap[3]; + + do { + ret = read(dev->rupdate_fd, &in, sizeof(in)); + if (ret <= 0) + break; + + keymap[curr_keymap][pos++] = in; + + if (in == '\0') { + curr_keymap++; + pos = 0; + } + } while (1); + + llog_info(dev->input, "HANDLER CALLED %s|%s|%s\n", + model, layout, variant); + + struct uterm_input *input = dev->input; + struct shl_dlist *iter; + + /* Apply the new layout to all the inputs. */ + shl_dlist_for_each(iter, &input->devices) { + struct uterm_input_dev *dev; + dev = shl_dlist_entry(iter, + struct uterm_input_dev, + list); + uxkb_desc_init(dev->input, model, layout, variant, options, NULL); + dev->state = xkb_state_new(dev->input->keymap); + if (!dev->state) { + llog_error(dev->input, "cannot create XKB state"); + return; + } + } + + /* The client will now close the FIFO. Close it too, and re-create a + * FIFO so other clients can eventually connect. */ + ev_eloop_rm_fd(fd); + close(dev->rupdate_fd); + dev->rupdate_fd = -1; + uxkb_dev_keymap_update(dev); + +} + +int uxkb_dev_keymap_update(struct uterm_input_dev *dev) +{ + int ret; + char *file; + int pid = getpid(); + + ret = asprintf(&file, INPUT_KEYMAP_UPDATE_FILE, pid); + if (ret < 0) + return ret; + + (void) unlink(file); + ret = mkfifo(file, S_IRWXU); + if (ret < 0) { + llog_warn(dev->input, "could not open fifo"); + return -EFAULT; + } + dev->rupdate_fd = open(file, O_RDONLY | O_NONBLOCK); + if (dev->rupdate_fd < 0) { + llog_warn(dev->input, "cannot open file %s (%d): %m", + file, errno); + return -EFAULT; + } + + setenv("KEYMAP_UPDATE", file, 1); + + ret = ev_eloop_new_fd(dev->input->eloop, &dev->fd_update, + dev->rupdate_fd, EV_READABLE, + uxkb_keymap_update_handler, dev); + if (ret) { + llog_error(dev->input, "could not init keymap update"); + close(dev->rupdate_fd); + dev->rupdate_fd = -1; + return ret; + } + + return 0; +} + int uxkb_dev_init(struct uterm_input_dev *dev) { int ret;