Bugfix: add keymap fall back (_XKB_RULES_NAMES, then defaults)
fixes #1983
This commit is contained in:
parent
6d385e65aa
commit
62bb7af5c3
105
src/bindings.c
105
src/bindings.c
|
@ -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,13 +709,41 @@ 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);
|
||||
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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue