]> git.sur5r.net Git - i3/i3/commitdiff
Bugfix: add keymap fall back (_XKB_RULES_NAMES, then defaults)
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 11 Oct 2015 18:23:07 +0000 (20:23 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Wed, 28 Oct 2015 20:54:55 +0000 (21:54 +0100)
fixes #1983

src/bindings.c
src/main.c

index 32aac05af3d605e5c9c0f6fa4b7442e6db726e23..b96c401cd105f0e10a00ab914225185be07d2128 100644 (file)
@@ -626,6 +626,77 @@ CommandResult *run_binding(Binding *bind, Con *con) {
     return result;
 }
 
+static int fill_rmlvo_from_root(struct xkb_rule_names *xkb_names) {
+    xcb_intern_atom_reply_t *atom_reply;
+    size_t content_max_words = 256;
+
+    xcb_window_t root = root_screen->root;
+
+    atom_reply = xcb_intern_atom_reply(
+        conn, xcb_intern_atom(conn, 0, strlen("_XKB_RULES_NAMES"), "_XKB_RULES_NAMES"), NULL);
+    if (atom_reply == NULL)
+        return -1;
+
+    xcb_get_property_cookie_t prop_cookie;
+    xcb_get_property_reply_t *prop_reply;
+    prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
+                                             XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
+    prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
+    if (prop_reply == NULL) {
+        free(atom_reply);
+        return -1;
+    }
+    if (xcb_get_property_value_length(prop_reply) > 0 && prop_reply->bytes_after > 0) {
+        /* We received an incomplete value. Ask again but with a properly
+         * adjusted size. */
+        content_max_words += ceil(prop_reply->bytes_after / 4.0);
+        /* Repeat the request, with adjusted size */
+        free(prop_reply);
+        prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
+                                                 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
+        prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
+        if (prop_reply == NULL) {
+            free(atom_reply);
+            return -1;
+        }
+    }
+    if (xcb_get_property_value_length(prop_reply) == 0) {
+        free(atom_reply);
+        free(prop_reply);
+        return -1;
+    }
+
+    const char *walk = (const char *)xcb_get_property_value(prop_reply);
+    int remaining = xcb_get_property_value_length(prop_reply);
+    for (int i = 0; i < 5 && remaining > 0; i++) {
+        const int len = strnlen(walk, remaining);
+        remaining -= len;
+        switch (i) {
+            case 0:
+                asprintf((char **)&(xkb_names->rules), "%.*s", len, walk);
+                break;
+            case 1:
+                asprintf((char **)&(xkb_names->model), "%.*s", len, walk);
+                break;
+            case 2:
+                asprintf((char **)&(xkb_names->layout), "%.*s", len, walk);
+                break;
+            case 3:
+                asprintf((char **)&(xkb_names->variant), "%.*s", len, walk);
+                break;
+            case 4:
+                asprintf((char **)&(xkb_names->options), "%.*s", len, walk);
+                break;
+        }
+        DLOG("component %d of _XKB_RULES_NAMES is \"%.*s\"\n", i, len, walk);
+        walk += (len + 1);
+    }
+
+    free(atom_reply);
+    free(prop_reply);
+    return 0;
+}
+
 /*
  * Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
  *
@@ -638,12 +709,40 @@ bool load_keymap(void) {
         }
     }
 
-    struct xkb_keymap *new_keymap;
-    const int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
-    DLOG("device_id = %d\n", device_id);
-    if ((new_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) {
-        ELOG("xkb_x11_keymap_new_from_device failed\n");
-        return false;
+    struct xkb_keymap *new_keymap = NULL;
+    int32_t device_id;
+    if (xkb_supported && (device_id = xkb_x11_get_core_keyboard_device_id(conn)) > -1) {
+        if ((new_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) {
+            ELOG("xkb_x11_keymap_new_from_device failed\n");
+            return false;
+        }
+    } else {
+        /* Likely there is no XKB support on this server, possibly because it
+         * is a VNC server. */
+        LOG("No XKB / core keyboard device? Assembling keymap from local RMLVO.\n");
+        struct xkb_rule_names names = {
+            .rules = NULL,
+            .model = NULL,
+            .layout = NULL,
+            .variant = NULL,
+            .options = NULL};
+        if (fill_rmlvo_from_root(&names) == -1) {
+            ELOG("Could not get _XKB_RULES_NAMES atom from root window, falling back to defaults.\n");
+            if ((new_keymap = xkb_keymap_new_from_names(xkb_context, &names, 0)) == NULL) {
+                ELOG("xkb_keymap_new_from_names(NULL) failed\n");
+                return false;
+            }
+        }
+        new_keymap = xkb_keymap_new_from_names(xkb_context, &names, 0);
+        free((char *)names.rules);
+        free((char *)names.model);
+        free((char *)names.layout);
+        free((char *)names.variant);
+        free((char *)names.options);
+        if (new_keymap == NULL) {
+            ELOG("xkb_keymap_new_from_names(RMLVO) failed\n");
+            return false;
+        }
     }
     xkb_keymap_unref(xkb_keymap);
     xkb_keymap = new_keymap;
index 0dc259363163e7bb89998387acb27827cd73e572..563fb00c6a31085d78abb68403200c3de553f0eb 100644 (file)
@@ -87,6 +87,7 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment
 
 /* We hope that those are supported and set them to true */
 bool xcursor_supported = true;
+bool xkb_supported = true;
 
 /*
  * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
@@ -543,6 +544,7 @@ int main(int argc, char *argv[]) {
 
     const xcb_query_extension_reply_t *extreply;
     extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
+    xkb_supported = extreply->present;
     if (!extreply->present) {
         DLOG("xkb is not present on this server\n");
     } else {