From 360d44d67e7be46108bec982ff2e79b89f04a9a3 Mon Sep 17 00:00:00 2001
From: Mathieu Othacehe <m.othacehe@gmail.com>
Date: Thu, 15 Nov 2018 14:34:40 +0900
Subject: [PATCH] add runtime keymap switch support.

---
 src/pty.c                  | 23 ++++++++++-
 src/uterm_input.c          |  2 +
 src/uterm_input_internal.h |  5 +++
 src/uterm_input_uxkb.c     | 83 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 111 insertions(+), 2 deletions(-)

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,8 @@ static void input_new_dev(struct uterm_input *input,
 	if (ret)
 		goto err_rcodepoints;
 
+	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..4760972 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,86 @@ 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[3][255];
+	int pos = 0;
+	int curr_keymap = 0;
+	int ret;
+	char *model, *layout, *variant;
+
+	if (!(mask & EV_READABLE))
+		return;
+
+	memset(keymap, 0, sizeof(keymap));
+
+	model = keymap[0];
+	layout = keymap[1];
+	variant = keymap[2];
+
+	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);
+	uxkb_desc_init(dev->input, model, layout, variant, NULL, NULL);
+
+	dev->state = xkb_state_new(dev->input->keymap);
+	if (!dev->state) {
+		llog_error(dev->input, "cannot create XKB state");
+		return;
+	}
+}
+
+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;
+
+	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;
-- 
2.17.1

