]> git.sur5r.net Git - i3/i3/commitdiff
Switch to xcb-xkb and libxkbcommon
authorMichael Stapelberg <michael@stapelberg.de>
Thu, 2 Jan 2014 07:40:03 +0000 (08:40 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 21 Jun 2014 17:10:37 +0000 (19:10 +0200)
This removes our last dependency on Xlib! :)

(Okay, an Xlib dependency still comes in through other libraries that we
 link against, but it’s not us. Our code is simpler by this change and
 uses one less connection to X11.)

13 files changed:
common.mk
debian/control
i3-config-wizard/i3-config-wizard.mk
i3-config-wizard/main.c
i3bar/i3bar.mk
i3bar/src/xcb.c
include/handlers.h
include/i3.h
src/bindings.c
src/config.c
src/handlers.c
src/i3.mk
src/main.c

index b086bc8535ff5f349bebcfb3d0036a69f608aeb5..b9e15a286fc27d335e4ae8c12a36ec8188dbd972 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -92,6 +92,7 @@ else
 XCB_CFLAGS  += $(call cflags_for_lib, xcb-util)
 XCB_LIBS    += $(call ldflags_for_lib, xcb-util)
 endif
+XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb)
 
 # XCB keyboard stuff
 XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
@@ -105,9 +106,10 @@ XCB_WM_LIBS   := $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
 XCB_WM_LIBS   += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
 XCB_WM_LIBS   += $(call ldflags_for_lib, xcb-randr,xcb-randr)
 
-# Xlib
-X11_CFLAGS := $(call cflags_for_lib, x11)
-X11_LIBS   := $(call ldflags_for_lib, x11,X11)
+XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
+XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
+XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11)
+XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11)
 
 # Xcursor
 XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)
index b36d5db46ba970c094fd15e6bb2a40753bf7e5b8..9f6518e09e16737a7fce97b9c30c72bef060f78a 100644 (file)
@@ -10,6 +10,9 @@ Build-Depends: debhelper (>= 7.0.50~),
                libxcb-randr0-dev,
                libxcb-icccm4-dev,
                libxcb-cursor-dev,
+               libxcb-xkb-dev,
+               libxkbcommon-dev,
+               libxkbcommon-x11-dev,
                asciidoc (>= 8.4.4),
                xmlto,
                docbook-xml,
index e759b4bd8c9fae4c17e85a0367febdbd4b7e3566..1dab64526165e73c1642172c459ac4e33f9e8d3a 100644 (file)
@@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-config-wizard
 
 i3_config_wizard_SOURCES           := $(wildcard i3-config-wizard/*.c)
 i3_config_wizard_HEADERS           := $(wildcard i3-config-wizard/*.h)
-i3_config_wizard_CFLAGS             = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS)
-i3_config_wizard_LIBS               = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS)
+i3_config_wizard_CFLAGS             = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS)
+i3_config_wizard_LIBS               = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS)
 
 i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o)
 
index a76c211e42504a83edf35c3a98186ec56576c9b5..c09d459d8d67cf0495e64955bec2c0d3e11ddafd 100644 (file)
@@ -43,6 +43,9 @@
 #include <xcb/xcb_event.h>
 #include <xcb/xcb_keysyms.h>
 
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-x11.h>
+
 #include <X11/Xlib.h>
 #include <X11/keysym.h>
 #include <X11/XKBlib.h>
@@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap;
 static xcb_gcontext_t pixmap_gc;
 static xcb_key_symbols_t *symbols;
 xcb_window_t root;
-Display *dpy;
+static struct xkb_keymap *xkb_keymap;
+static uint8_t xkb_base_event;
+static uint8_t xkb_base_error;
 
 static void finish();
 
@@ -250,12 +255,24 @@ static char *next_state(const cmdp_token *token) {
              * This reduces a lot of confusion for users who switch keyboard
              * layouts from qwerty to qwertz or other slight variations of
              * qwerty (yes, that happens quite often). */
-            KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
-            if (!keysym_used_on_other_key(sym, keycode))
+            const xkb_keysym_t *syms;
+            int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
+            if (num == 0)
+                errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
+            if (!keysym_used_on_other_key(syms[0], keycode))
                 level = 0;
         }
-        KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, level);
-        char *str = XKeysymToString(sym);
+
+        const xkb_keysym_t *syms;
+        int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
+        if (num == 0)
+            errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
+        if (num > 1)
+            printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
+
+        char str[4096];
+        if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
+            errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
         const char *release = get_string("release");
         char *res;
         char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
@@ -642,8 +659,14 @@ static void handle_button_press(xcb_button_press_event_t *event) {
 static void finish() {
     printf("creating \"%s\"...\n", config_path);
 
-    if (!(dpy = XOpenDisplay(NULL)))
-        errx(1, "Could not connect to X11");
+    struct xkb_context *xkb_context;
+
+    if ((xkb_context = xkb_context_new(0)) == NULL)
+        errx(1, "could not create xkbcommon context");
+
+    int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
+    if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
+        errx(1, "xkb_x11_keymap_new_from_device failed");
 
     FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
     if (kc_config == NULL)
@@ -797,6 +820,16 @@ int main(int argc, char *argv[]) {
         xcb_connection_has_error(conn))
         errx(1, "Cannot open display\n");
 
+    if (xkb_x11_setup_xkb_extension(conn,
+                                    XKB_X11_MIN_MAJOR_XKB_VERSION,
+                                    XKB_X11_MIN_MINOR_XKB_VERSION,
+                                    0,
+                                    NULL,
+                                    NULL,
+                                    &xkb_base_event,
+                                    &xkb_base_error) != 1)
+        errx(EXIT_FAILURE, "Could not setup XKB extension.");
+
     if (socket_path == NULL)
         socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);
 
index 067802502f28ddc31aca70b1a39a6f21545e1440..53227a8e10fbb29d0468389bc1bb1a65c2fefa99 100644 (file)
@@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
 
 i3bar_SOURCES := $(wildcard i3bar/src/*.c)
 i3bar_HEADERS := $(wildcard i3bar/include/*.h)
-i3bar_CFLAGS   = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
-i3bar_LIBS     = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
+i3bar_CFLAGS   = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
+i3bar_LIBS     = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
 
 i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
 
index 763942af661f04f6320449989d43835f1e84fbb7..37a1334792eddb69ad5ec9e5074fca17303f88a4 100644 (file)
@@ -8,6 +8,7 @@
  *
  */
 #include <xcb/xcb.h>
+#include <xcb/xkb.h>
 #include <xcb/xproto.h>
 #include <xcb/xcb_aux.h>
 
@@ -63,8 +64,7 @@ static i3Font font;
 int bar_height;
 
 /* These are only relevant for XKB, which we only need for grabbing modifiers */
-Display *xkb_dpy;
-int xkb_event_base;
+int xkb_base;
 int mod_pressed = 0;
 
 /* Because the statusline is the same on all outputs, we have
@@ -854,7 +854,62 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
     }
 
     while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
-        switch (event->response_type & ~0x80) {
+        int type = (event->response_type & ~0x80);
+
+        if (type == xkb_base && xkb_base > -1) {
+            DLOG("received an xkb event\n");
+
+            xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
+            if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
+                int modstate = state->mods & config.modifier;
+
+#define DLOGMOD(modmask, status)                        \
+    do {                                                \
+        switch (modmask) {                              \
+            case ShiftMask:                             \
+                DLOG("ShiftMask got " #status "!\n");   \
+                break;                                  \
+            case ControlMask:                           \
+                DLOG("ControlMask got " #status "!\n"); \
+                break;                                  \
+            case Mod1Mask:                              \
+                DLOG("Mod1Mask got " #status "!\n");    \
+                break;                                  \
+            case Mod2Mask:                              \
+                DLOG("Mod2Mask got " #status "!\n");    \
+                break;                                  \
+            case Mod3Mask:                              \
+                DLOG("Mod3Mask got " #status "!\n");    \
+                break;                                  \
+            case Mod4Mask:                              \
+                DLOG("Mod4Mask got " #status "!\n");    \
+                break;                                  \
+            case Mod5Mask:                              \
+                DLOG("Mod5Mask got " #status "!\n");    \
+                break;                                  \
+        }                                               \
+    } while (0)
+
+                if (modstate != mod_pressed) {
+                    if (modstate == 0) {
+                        DLOGMOD(config.modifier, released);
+                        if (!activated_mode)
+                            hide_bars();
+                    } else {
+                        DLOGMOD(config.modifier, pressed);
+                        activated_mode = false;
+                        unhide_bars();
+                    }
+                    mod_pressed = modstate;
+                }
+#undef DLOGMOD
+            }
+
+            free(event);
+            continue;
+        }
+
+        switch (type) {
             case XCB_EXPOSE:
                 /* Expose-events happen, when the window needs to be redrawn */
                 redraw_bars();
@@ -900,76 +955,6 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
 void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
 }
 
-/*
- * We need to bind to the modifier per XKB. Sadly, XCB does not implement this
- *
- */
-void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
-    XkbEvent ev;
-    int modstate = 0;
-
-    DLOG("Got XKB-Event!\n");
-
-    while (XPending(xkb_dpy)) {
-        XNextEvent(xkb_dpy, (XEvent *)&ev);
-
-        if (ev.type != xkb_event_base) {
-            ELOG("No Xkb-Event!\n");
-            continue;
-        }
-
-        if (ev.any.xkb_type != XkbStateNotify) {
-            ELOG("No State Notify!\n");
-            continue;
-        }
-
-        unsigned int mods = ev.state.mods;
-        modstate = mods & config.modifier;
-    }
-
-#define DLOGMOD(modmask, status)                        \
-    do {                                                \
-        switch (modmask) {                              \
-            case ShiftMask:                             \
-                DLOG("ShiftMask got " #status "!\n");   \
-                break;                                  \
-            case ControlMask:                           \
-                DLOG("ControlMask got " #status "!\n"); \
-                break;                                  \
-            case Mod1Mask:                              \
-                DLOG("Mod1Mask got " #status "!\n");    \
-                break;                                  \
-            case Mod2Mask:                              \
-                DLOG("Mod2Mask got " #status "!\n");    \
-                break;                                  \
-            case Mod3Mask:                              \
-                DLOG("Mod3Mask got " #status "!\n");    \
-                break;                                  \
-            case Mod4Mask:                              \
-                DLOG("Mod4Mask got " #status "!\n");    \
-                break;                                  \
-            case Mod5Mask:                              \
-                DLOG("Mod5Mask got " #status "!\n");    \
-                break;                                  \
-        }                                               \
-    } while (0)
-
-    if (modstate != mod_pressed) {
-        if (modstate == 0) {
-            DLOGMOD(config.modifier, released);
-            if (!activated_mode)
-                hide_bars();
-        } else {
-            DLOGMOD(config.modifier, pressed);
-            activated_mode = false;
-            unhide_bars();
-        }
-        mod_pressed = modstate;
-    }
-
-#undef DLOGMOD
-}
-
 /*
  * Early initialization of the connection to X11: Everything which does not
  * depend on 'config'.
@@ -1053,44 +1038,23 @@ char *init_xcb_early() {
  *
  */
 void register_xkb_keyevents() {
-    if (xkb_dpy == NULL) {
-        int xkb_major, xkb_minor, xkb_errbase, xkb_err;
-        xkb_major = XkbMajorVersion;
-        xkb_minor = XkbMinorVersion;
-
-        xkb_dpy = XkbOpenDisplay(NULL,
-                                 &xkb_event_base,
-                                 &xkb_errbase,
-                                 &xkb_major,
-                                 &xkb_minor,
-                                 &xkb_err);
-
-        if (xkb_dpy == NULL) {
-            ELOG("No XKB!\n");
-            exit(EXIT_FAILURE);
-        }
-
-        if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
-            ELOG("Could not set FD_CLOEXEC on xkbdpy: %s\n", strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-
-        int i1;
-        if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
-            ELOG("XKB not supported by X-server!\n");
-            exit(EXIT_FAILURE);
-        }
-
-        if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) {
-            ELOG("Could not grab Key!\n");
-            exit(EXIT_FAILURE);
-        }
-
-        xkb_io = smalloc(sizeof(ev_io));
-        ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
-        ev_io_start(main_loop, xkb_io);
-        XFlush(xkb_dpy);
+    const xcb_query_extension_reply_t *extreply;
+    extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
+    if (!extreply->present) {
+        ELOG("xkb is not present on this server\n");
+        exit(EXIT_FAILURE);
     }
+    DLOG("initializing xcb-xkb\n");
+    xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
+    xcb_xkb_select_events(conn,
+                          XCB_XKB_ID_USE_CORE_KBD,
+                          XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+                          0,
+                          XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+                          0xff,
+                          0xff,
+                          NULL);
+    xkb_base = extreply->first_event;
 }
 
 /*
@@ -1098,13 +1062,14 @@ void register_xkb_keyevents() {
  *
  */
 void deregister_xkb_keyevents() {
-    if (xkb_dpy != NULL) {
-        ev_io_stop(main_loop, xkb_io);
-        XCloseDisplay(xkb_dpy);
-        close(xkb_io->fd);
-        FREE(xkb_io);
-        xkb_dpy = NULL;
-    }
+    xcb_xkb_select_events(conn,
+                          XCB_XKB_ID_USE_CORE_KBD,
+                          0,
+                          0,
+                          0,
+                          0xff,
+                          0xff,
+                          NULL);
 }
 
 /*
index db7d06b5fec7e172b1b8a488ec0752c7faf0987c..82f6b982fc43474c5717e5c48748a04409e8102c 100644 (file)
@@ -13,6 +13,7 @@
 #include <xcb/randr.h>
 
 extern int randr_base;
+extern int xkb_base;
 
 /**
  * Adds the given sequence to the list of events which are ignored.
index c70a5c5b4c8986847bb7d98ad3a7d2645cc20785..5ca875417bb163c386e5da8d462ffcc6d1a2c486 100644 (file)
@@ -13,6 +13,7 @@
 #include <sys/resource.h>
 
 #include <xcb/xcb_keysyms.h>
+#include <xcb/xkb.h>
 
 #include <X11/XKBlib.h>
 
index a7039bf2fbd1ae6e88fb3d6823a2a47245c1559d..cd91a3989c452c4d45189f474c39ea8994507b53 100644 (file)
@@ -8,6 +8,8 @@
  */
 #include "all.h"
 
+#include <xkbcommon/xkbcommon.h>
+
 pid_t command_error_nagbar_pid = -1;
 
 /*
@@ -263,8 +265,8 @@ void translate_keysyms(void) {
             continue;
 
         /* We need to translate the symbol to a keycode */
-        keysym = XStringToKeysym(bind->symbol);
-        if (keysym == NoSymbol) {
+        keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
+        if (keysym == XKB_KEY_NoSymbol) {
             ELOG("Could not translate string to key symbol: \"%s\"\n",
                  bind->symbol);
             continue;
index 781ff6b83b7a00cf12f2dc084941381cbb1b0ed0..ec084bb19d8282844edba1cdd8cbdb9fc803a7cb 100644 (file)
@@ -11,9 +11,7 @@
  *
  */
 #include "all.h"
-
-/* We need Xlib for XStringToKeysym */
-#include <X11/Xlib.h>
+#include <xkbcommon/xkbcommon.h>
 
 char *current_configpath = NULL;
 Config config;
index ba9b12117ad53a1727f164783b8ba8df3552985d..80297c7981084bf134871c77e39b95169b6bcbf4 100644 (file)
@@ -21,6 +21,8 @@
 #include <libsn/sn-monitor.h>
 
 int randr_base = -1;
+int xkb_base = -1;
+int xkb_current_group;
 
 /* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
    since it’d trigger an infinite loop of switching between the different windows when
@@ -1115,12 +1117,50 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom)
  *
  */
 void handle_event(int type, xcb_generic_event_t *event) {
+    DLOG("event type %d, xkb_base %d\n", type, xkb_base);
     if (randr_base > -1 &&
         type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
         handle_screen_change(event);
         return;
     }
 
+    if (xkb_base > -1 && type == xkb_base) {
+        DLOG("xkb event, need to handle it.\n");
+
+        xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
+        if (state->xkbType == XCB_XKB_MAP_NOTIFY) {
+            if (event_is_ignored(event->sequence, type)) {
+                DLOG("Ignoring map notify event for sequence %d.\n", state->sequence);
+            } else {
+                DLOG("xkb map notify, sequence %d, time %d\n", state->sequence, state->time);
+                add_ignore_event(event->sequence, type);
+                ungrab_all_keys(conn);
+                translate_keysyms();
+                grab_all_keys(conn, false);
+            }
+        } else if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
+            DLOG("xkb state group = %d\n", state->group);
+
+            /* See The XKB Extension: Library Specification, section 14.1 */
+            /* We check if the current group (each group contains
+             * two levels) has been changed. Mode_switch activates
+             * group XkbGroup2Index */
+            if (xkb_current_group == state->group)
+                return;
+            xkb_current_group = state->group;
+            if (state->group == XCB_XKB_GROUP_1) {
+                DLOG("Mode_switch disabled\n");
+                ungrab_all_keys(conn);
+                grab_all_keys(conn, false);
+            } else {
+                DLOG("Mode_switch enabled\n");
+                grab_all_keys(conn, false);
+            }
+        }
+
+        return;
+    }
+
     switch (type) {
         case XCB_KEY_PRESS:
         case XCB_KEY_RELEASE:
index 0325d82a95ee765ea17ab5bc846857c96f2e4353..f1105a04514c172b066daea470888d0522320d96 100644 (file)
--- a/src/i3.mk
+++ b/src/i3.mk
@@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
 i3_SOURCES           := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
 i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
 i3_HEADERS           := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
-i3_CFLAGS             = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
-i3_LIBS               = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
+i3_CFLAGS             = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
+i3_LIBS               = $(XKB_COMMON_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
 
 # When using clang, we use pre-compiled headers to speed up the build. With
 # gcc, this actually makes the build slower.
index 917ae1cba23982c5e996e0af77e24c72a50a885a..ac11244632b0a86ce05b618d70be2cfdc6018f31 100644 (file)
@@ -36,10 +36,6 @@ int listen_fds;
  * temporarily for drag_pointer(). */
 static struct ev_check *xcb_check;
 
-static int xkb_event_base;
-
-int xkb_current_group;
-
 extern Con *focused;
 
 char **start_argv;
@@ -70,9 +66,6 @@ struct ev_loop *main_loop;
 
 xcb_key_symbols_t *keysyms;
 
-/* Those are our connections to X11 for use with libXcursor and XKB */
-Display *xlibdpy, *xkbdpy;
-
 /* Default shmlog size if not set by user. */
 const int default_shmlog_size = 25 * 1024 * 1024;
 
@@ -94,7 +87,6 @@ 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 will be set to true when -C is used so that functions can behave
  * slightly differently. We don’t want i3-nagbar to be started when validating
@@ -166,73 +158,6 @@ void main_set_x11_cb(bool enable) {
     }
 }
 
-/*
- * When using xmodmap to change the keyboard mapping, this event
- * is only sent via XKB. Therefore, we need this special handler.
- *
- */
-static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
-    DLOG("Handling XKB event\n");
-    XkbEvent ev;
-
-    /* When using xmodmap, every change (!) gets an own event.
-     * Therefore, we just read all events and only handle the
-     * mapping_notify once. */
-    bool mapping_changed = false;
-    while (XPending(xkbdpy)) {
-        XNextEvent(xkbdpy, (XEvent *)&ev);
-        /* While we should never receive a non-XKB event,
-         * better do sanity checking */
-        if (ev.type != xkb_event_base)
-            continue;
-
-        if (ev.any.xkb_type == XkbMapNotify) {
-            mapping_changed = true;
-            continue;
-        }
-
-        if (ev.any.xkb_type != XkbStateNotify) {
-            ELOG("Unknown XKB event received (type %d)\n", ev.any.xkb_type);
-            continue;
-        }
-
-        /* See The XKB Extension: Library Specification, section 14.1 */
-        /* We check if the current group (each group contains
-         * two levels) has been changed. Mode_switch activates
-         * group XkbGroup2Index */
-        if (xkb_current_group == ev.state.group)
-            continue;
-
-        xkb_current_group = ev.state.group;
-
-        if (ev.state.group == XkbGroup2Index) {
-            DLOG("Mode_switch enabled\n");
-            grab_all_keys(conn, true);
-        }
-
-        if (ev.state.group == XkbGroup1Index) {
-            DLOG("Mode_switch disabled\n");
-            ungrab_all_keys(conn);
-            grab_all_keys(conn, false);
-        }
-    }
-
-    if (!mapping_changed)
-        return;
-
-    DLOG("Keyboard mapping changed, updating keybindings\n");
-    xcb_key_symbols_free(keysyms);
-    keysyms = xcb_key_symbols_alloc(conn);
-
-    xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
-
-    ungrab_all_keys(conn);
-    DLOG("Re-grabbing...\n");
-    translate_keysyms();
-    grab_all_keys(conn, (xkb_current_group == XkbGroup2Index));
-    DLOG("Done\n");
-}
-
 /*
  * Exit handler which destroys the main_loop. Will trigger cleanup handlers.
  *
@@ -593,21 +518,7 @@ int main(int argc, char *argv[]) {
 #include "atoms.xmacro"
 #undef xmacro
 
-    /* Initialize the Xlib connection */
-    xlibdpy = xkbdpy = XOpenDisplay(NULL);
-
-    /* Try to load the X cursors and initialize the XKB extension */
-    if (xlibdpy == NULL) {
-        ELOG("ERROR: XOpenDisplay() failed, disabling libXcursor/XKB support\n");
-        xcursor_supported = false;
-        xkb_supported = false;
-    } else if (fcntl(ConnectionNumber(xlibdpy), F_SETFD, FD_CLOEXEC) == -1) {
-        ELOG("Could not set FD_CLOEXEC on xkbdpy\n");
-        return 1;
-    } else {
-        xcursor_load_cursors();
-        /*init_xkb();*/
-    }
+    xcursor_load_cursors();
 
     /* Set a cursor for the root window (otherwise the root window will show no
        cursor until the first client is launched). */
@@ -616,27 +527,22 @@ int main(int argc, char *argv[]) {
     else
         xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
 
-    if (xkb_supported) {
-        int errBase,
-            major = XkbMajorVersion,
-            minor = XkbMinorVersion;
-
-        if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) {
-            fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n");
-            return 1;
-        }
-
-        int i1;
-        if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) {
-            fprintf(stderr, "XKB not supported by X-server\n");
-            xkb_supported = false;
-        }
-        /* end of ugliness */
-
-        if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) {
-            fprintf(stderr, "Could not set XKB event mask\n");
-            return 1;
-        }
+    const xcb_query_extension_reply_t *extreply;
+    extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
+    if (!extreply->present) {
+        DLOG("xkb is not present on this server\n");
+    } else {
+        DLOG("initializing xcb-xkb\n");
+        xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
+        xcb_xkb_select_events(conn,
+                              XCB_XKB_ID_USE_CORE_KBD,
+                              XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
+                              0,
+                              XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
+                              0xff,
+                              0xff,
+                              NULL);
+        xkb_base = extreply->first_event;
     }
 
     restore_connect();
@@ -770,21 +676,12 @@ int main(int argc, char *argv[]) {
     ewmh_update_number_of_desktops();
 
     struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
-    struct ev_io *xkb = scalloc(sizeof(struct ev_io));
     xcb_check = scalloc(sizeof(struct ev_check));
     struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
 
     ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
     ev_io_start(main_loop, xcb_watcher);
 
-    if (xkb_supported) {
-        ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
-        ev_io_start(main_loop, xkb);
-
-        /* Flush the buffer so that libev can properly get new events */
-        XFlush(xkbdpy);
-    }
-
     ev_check_init(xcb_check, xcb_check_cb);
     ev_check_start(main_loop, xcb_check);