Switch to xcb-xkb and libxkbcommon

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.)
This commit is contained in:
Michael Stapelberg 2014-01-02 08:40:03 +01:00
parent cf6cc134b8
commit 3f5a0f0024
13 changed files with 200 additions and 258 deletions

View File

@ -92,6 +92,7 @@ else
XCB_CFLAGS += $(call cflags_for_lib, xcb-util) XCB_CFLAGS += $(call cflags_for_lib, xcb-util)
XCB_LIBS += $(call ldflags_for_lib, xcb-util) XCB_LIBS += $(call ldflags_for_lib, xcb-util)
endif endif
XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb)
# XCB keyboard stuff # XCB keyboard stuff
XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms) 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-xinerama,xcb-xinerama)
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
# Xlib XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
X11_CFLAGS := $(call cflags_for_lib, x11) XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
X11_LIBS := $(call ldflags_for_lib, x11,X11) 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
XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor) XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)

3
debian/control vendored
View File

@ -10,6 +10,9 @@ Build-Depends: debhelper (>= 7.0.50~),
libxcb-randr0-dev, libxcb-randr0-dev,
libxcb-icccm4-dev, libxcb-icccm4-dev,
libxcb-cursor-dev, libxcb-cursor-dev,
libxcb-xkb-dev,
libxkbcommon-dev,
libxkbcommon-x11-dev,
asciidoc (>= 8.4.4), asciidoc (>= 8.4.4),
xmlto, xmlto,
docbook-xml, docbook-xml,

View File

@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-config-wizard
i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c) i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c)
i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) 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_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS)
i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS) 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) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o)

View File

@ -43,6 +43,9 @@
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap;
static xcb_gcontext_t pixmap_gc; static xcb_gcontext_t pixmap_gc;
static xcb_key_symbols_t *symbols; static xcb_key_symbols_t *symbols;
xcb_window_t root; 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(); 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 * This reduces a lot of confusion for users who switch keyboard
* layouts from qwerty to qwertz or other slight variations of * layouts from qwerty to qwertz or other slight variations of
* qwerty (yes, that happens quite often). */ * qwerty (yes, that happens quite often). */
KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0); const xkb_keysym_t *syms;
if (!keysym_used_on_other_key(sym, keycode)) 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; 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"); const char *release = get_string("release");
char *res; char *res;
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers)); 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() { static void finish() {
printf("creating \"%s\"...\n", config_path); printf("creating \"%s\"...\n", config_path);
if (!(dpy = XOpenDisplay(NULL))) struct xkb_context *xkb_context;
errx(1, "Could not connect to X11");
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"); FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
if (kc_config == NULL) if (kc_config == NULL)
@ -797,6 +820,16 @@ int main(int argc, char *argv[]) {
xcb_connection_has_error(conn)) xcb_connection_has_error(conn))
errx(1, "Cannot open display\n"); 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) if (socket_path == NULL)
socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen); socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);

View File

@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_SOURCES := $(wildcard i3bar/src/*.c)
i3bar_HEADERS := $(wildcard i3bar/include/*.h) i3bar_HEADERS := $(wildcard i3bar/include/*.h)
i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)

View File

@ -8,6 +8,7 @@
* *
*/ */
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xkb.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
@ -63,8 +64,7 @@ static i3Font font;
int bar_height; int bar_height;
/* These are only relevant for XKB, which we only need for grabbing modifiers */ /* These are only relevant for XKB, which we only need for grabbing modifiers */
Display *xkb_dpy; int xkb_base;
int xkb_event_base;
int mod_pressed = 0; int mod_pressed = 0;
/* Because the statusline is the same on all outputs, we have /* 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) { 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: case XCB_EXPOSE:
/* Expose-events happen, when the window needs to be redrawn */ /* Expose-events happen, when the window needs to be redrawn */
redraw_bars(); 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) { 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 * Early initialization of the connection to X11: Everything which does not
* depend on 'config'. * depend on 'config'.
@ -1053,44 +1038,23 @@ char *init_xcb_early() {
* *
*/ */
void register_xkb_keyevents() { void register_xkb_keyevents() {
if (xkb_dpy == NULL) { const xcb_query_extension_reply_t *extreply;
int xkb_major, xkb_minor, xkb_errbase, xkb_err; extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
xkb_major = XkbMajorVersion; if (!extreply->present) {
xkb_minor = XkbMinorVersion; ELOG("xkb is not present on this server\n");
exit(EXIT_FAILURE);
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);
} }
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() { void deregister_xkb_keyevents() {
if (xkb_dpy != NULL) { xcb_xkb_select_events(conn,
ev_io_stop(main_loop, xkb_io); XCB_XKB_ID_USE_CORE_KBD,
XCloseDisplay(xkb_dpy); 0,
close(xkb_io->fd); 0,
FREE(xkb_io); 0,
xkb_dpy = NULL; 0xff,
} 0xff,
NULL);
} }
/* /*

View File

@ -13,6 +13,7 @@
#include <xcb/randr.h> #include <xcb/randr.h>
extern int randr_base; extern int randr_base;
extern int xkb_base;
/** /**
* Adds the given sequence to the list of events which are ignored. * Adds the given sequence to the list of events which are ignored.

View File

@ -13,6 +13,7 @@
#include <sys/resource.h> #include <sys/resource.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <xcb/xkb.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>

View File

@ -8,6 +8,8 @@
*/ */
#include "all.h" #include "all.h"
#include <xkbcommon/xkbcommon.h>
pid_t command_error_nagbar_pid = -1; pid_t command_error_nagbar_pid = -1;
/* /*
@ -263,8 +265,8 @@ void translate_keysyms(void) {
continue; continue;
/* We need to translate the symbol to a keycode */ /* We need to translate the symbol to a keycode */
keysym = XStringToKeysym(bind->symbol); keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
if (keysym == NoSymbol) { if (keysym == XKB_KEY_NoSymbol) {
ELOG("Could not translate string to key symbol: \"%s\"\n", ELOG("Could not translate string to key symbol: \"%s\"\n",
bind->symbol); bind->symbol);
continue; continue;

View File

@ -11,9 +11,7 @@
* *
*/ */
#include "all.h" #include "all.h"
#include <xkbcommon/xkbcommon.h>
/* We need Xlib for XStringToKeysym */
#include <X11/Xlib.h>
char *current_configpath = NULL; char *current_configpath = NULL;
Config config; Config config;

View File

@ -21,6 +21,8 @@
#include <libsn/sn-monitor.h> #include <libsn/sn-monitor.h>
int randr_base = -1; int randr_base = -1;
int xkb_base = -1;
int xkb_current_group;
/* After mapping/unmapping windows, a notify event is generated. However, we dont want it, /* After mapping/unmapping windows, a notify event is generated. However, we dont want it,
since itd trigger an infinite loop of switching between the different windows when since itd 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) { 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 && if (randr_base > -1 &&
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
handle_screen_change(event); handle_screen_change(event);
return; 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) { switch (type) {
case XCB_KEY_PRESS: case XCB_KEY_PRESS:
case XCB_KEY_RELEASE: case XCB_KEY_RELEASE:

View File

@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.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_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_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_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 # When using clang, we use pre-compiled headers to speed up the build. With
# gcc, this actually makes the build slower. # gcc, this actually makes the build slower.

View File

@ -36,10 +36,6 @@ int listen_fds;
* temporarily for drag_pointer(). */ * temporarily for drag_pointer(). */
static struct ev_check *xcb_check; static struct ev_check *xcb_check;
static int xkb_event_base;
int xkb_current_group;
extern Con *focused; extern Con *focused;
char **start_argv; char **start_argv;
@ -70,9 +66,6 @@ struct ev_loop *main_loop;
xcb_key_symbols_t *keysyms; 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. */ /* Default shmlog size if not set by user. */
const int default_shmlog_size = 25 * 1024 * 1024; 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 */ /* We hope that those are supported and set them to true */
bool xcursor_supported = true; bool xcursor_supported = true;
bool xkb_supported = true;
/* This will be set to true when -C is used so that functions can behave /* This will be set to true when -C is used so that functions can behave
* slightly differently. We dont want i3-nagbar to be started when validating * slightly differently. We dont 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. * Exit handler which destroys the main_loop. Will trigger cleanup handlers.
* *
@ -593,21 +518,7 @@ int main(int argc, char *argv[]) {
#include "atoms.xmacro" #include "atoms.xmacro"
#undef xmacro #undef xmacro
/* Initialize the Xlib connection */ xcursor_load_cursors();
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();*/
}
/* Set a cursor for the root window (otherwise the root window will show no /* Set a cursor for the root window (otherwise the root window will show no
cursor until the first client is launched). */ cursor until the first client is launched). */
@ -616,27 +527,22 @@ int main(int argc, char *argv[]) {
else else
xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
if (xkb_supported) { const xcb_query_extension_reply_t *extreply;
int errBase, extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
major = XkbMajorVersion, if (!extreply->present) {
minor = XkbMinorVersion; DLOG("xkb is not present on this server\n");
} else {
if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) { DLOG("initializing xcb-xkb\n");
fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n"); xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
return 1; xcb_xkb_select_events(conn,
} XCB_XKB_ID_USE_CORE_KBD,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
int i1; 0,
if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) { XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
fprintf(stderr, "XKB not supported by X-server\n"); 0xff,
xkb_supported = false; 0xff,
} NULL);
/* end of ugliness */ xkb_base = extreply->first_event;
if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) {
fprintf(stderr, "Could not set XKB event mask\n");
return 1;
}
} }
restore_connect(); restore_connect();
@ -770,21 +676,12 @@ int main(int argc, char *argv[]) {
ewmh_update_number_of_desktops(); ewmh_update_number_of_desktops();
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); 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)); xcb_check = scalloc(sizeof(struct ev_check));
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); 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_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
ev_io_start(main_loop, xcb_watcher); 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_init(xcb_check, xcb_check_cb);
ev_check_start(main_loop, xcb_check); ev_check_start(main_loop, xcb_check);