From eaa2a69bd44179b024051f3d1a09143e17ad5df8 Mon Sep 17 00:00:00 2001
From: Harry Jeffery <harry@exec64.co.uk>
Date: Sun, 18 Aug 2019 00:46:57 +0100
Subject: x11_window: Use XCB to load keymap

---
 src/dummy_window.c |  2 +-
 src/imv.c          |  2 +-
 src/window.h       |  2 +-
 src/wl_window.c    |  2 +-
 src/x11_window.c   | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---
 5 files changed, 53 insertions(+), 7 deletions(-)

(limited to 'src')

diff --git a/src/dummy_window.c b/src/dummy_window.c
index 937262b..e6ce54d 100644
--- a/src/dummy_window.c
+++ b/src/dummy_window.c
@@ -95,7 +95,7 @@ void imv_window_pump_events(struct imv_window *window, imv_event_handler handler
   (void)data;
 }
 
-const char *imv_window_keymap(struct imv_window *window)
+const char *imv_window_get_keymap(struct imv_window *window)
 {
   (void)window;
   return NULL;
diff --git a/src/imv.c b/src/imv.c
index 991de70..fd2bf1f 100644
--- a/src/imv.c
+++ b/src/imv.c
@@ -1090,7 +1090,7 @@ static bool setup_window(struct imv *imv)
     imv->keyboard = imv_keyboard_create();
     assert(imv->keyboard);
 
-    const char *keymap = imv_window_keymap(imv->window);
+    const char *keymap = imv_window_get_keymap(imv->window);
     if (keymap) {
       imv_keyboard_set_keymap(imv->keyboard, keymap);
     }
diff --git a/src/window.h b/src/window.h
index 7db1125..a496655 100644
--- a/src/window.h
+++ b/src/window.h
@@ -73,6 +73,6 @@ typedef void (*imv_event_handler)(void *data, const struct imv_event *e);
 
 void imv_window_pump_events(struct imv_window *window, imv_event_handler handler, void *data);
 
-const char *imv_window_keymap(struct imv_window *window);
+const char *imv_window_get_keymap(struct imv_window *window);
 
 #endif
diff --git a/src/wl_window.c b/src/wl_window.c
index e0fa251..ace66f0 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -827,7 +827,7 @@ void imv_window_pump_events(struct imv_window *window, imv_event_handler handler
   }
 }
 
-const char *imv_window_keymap(struct imv_window *window)
+const char *imv_window_get_keymap(struct imv_window *window)
 {
   return window->keymap;
 }
diff --git a/src/x11_window.c b/src/x11_window.c
index 1b7f724..315f600 100644
--- a/src/x11_window.c
+++ b/src/x11_window.c
@@ -13,6 +13,11 @@
 #include <X11/Xatom.h>
 #include <X11/Xlib.h>
 
+#include <xcb/xcb.h>
+#include <xkbcommon/xkbcommon-x11.h>
+
+#include "log.h"
+
 struct imv_window {
   Display    *x_display;
   Window     x_window;
@@ -32,6 +37,7 @@ struct imv_window {
   } pointer;
 
   int pipe_fds[2];
+  char *keymap;
 };
 
 static void set_nonblocking(int fd)
@@ -43,6 +49,44 @@ static void set_nonblocking(int fd)
   assert(rc != -1);
 }
 
+static void setup_keymap(struct imv_window *window)
+{
+  xcb_connection_t *conn = xcb_connect(NULL, NULL);
+  if (xcb_connection_has_error(conn)) {
+    imv_log(IMV_ERROR, "x11_window: Failed to load keymap. Could not connect via xcb.");
+    return;
+  }
+
+  if (!xkb_x11_setup_xkb_extension(conn,
+        XKB_X11_MIN_MAJOR_XKB_VERSION,
+        XKB_X11_MIN_MINOR_XKB_VERSION,
+        0, NULL, NULL, NULL, NULL)) {
+    xcb_disconnect(conn);
+    imv_log(IMV_ERROR, "x11_window: Failed to load keymap. xkb extension not supported by server.");
+    return;
+  }
+
+  int32_t device = xkb_x11_get_core_keyboard_device_id(conn);
+
+  struct xkb_context *context = xkb_context_new(0);
+  if (!context) {
+    xcb_disconnect(conn);
+    imv_log(IMV_ERROR, "x11_window: Failed to load keymap. Failed to initialise xkb context.");
+    return;
+  }
+
+  struct xkb_keymap *keymap =
+    xkb_x11_keymap_new_from_device(context, conn, device, 0);
+  if (keymap) {
+    window->keymap = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT);
+  } else {
+    imv_log(IMV_ERROR, "x11_window: Failed to load keymap. xkb_x11_keymap_new_from_device returned NULL.");
+  }
+  xkb_context_unref(context);
+
+  xcb_disconnect(conn);
+}
+
 struct imv_window *imv_window_create(int w, int h, const char *title)
 {
   /* Ensure event writes will always be atomic */
@@ -93,6 +137,8 @@ struct imv_window *imv_window_create(int w, int h, const char *title)
   assert(window->x_glc);
   glXMakeCurrent(window->x_display, window->x_window, window->x_glc);
 
+  setup_keymap(window);
+
   return window;
 }
 
@@ -102,6 +148,7 @@ void imv_window_free(struct imv_window *window)
   close(window->pipe_fds[1]);
   glXDestroyContext(window->x_display, window->x_glc);
   XCloseDisplay(window->x_display);
+  free(window->keymap);
   free(window);
 }
 
@@ -336,8 +383,7 @@ void imv_window_pump_events(struct imv_window *window, imv_event_handler handler
   }
 }
 
-const char *imv_window_keymap(struct imv_window *window)
+const char *imv_window_get_keymap(struct imv_window *window)
 {
-  (void)window;
-  return NULL;
+  return window->keymap;
 }
-- 
cgit v1.2.3