diff --git a/common.mk b/common.mk index a9bab084..db887cac 100644 --- a/common.mk +++ b/common.mk @@ -11,6 +11,10 @@ endif GIT_VERSION:="$(shell git describe --tags --always) ($(shell git log --pretty=format:%cd --date=short -n1), branch $(shell [ -f .git/HEAD ] && sed 's/ref: refs\/heads\/\(.*\)/\\\\\\"\1\\\\\\"/g' .git/HEAD || echo 'unknown'))" VERSION:=$(shell git describe --tags --abbrev=0) +ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1) +$(error "pkg-config was not found") +endif + # An easier way to get CFLAGS and LDFLAGS falling back in case there's # no pkg-config support for certain libraries cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1)) @@ -24,11 +28,14 @@ CFLAGS += -Wall CFLAGS += -Wunused-value CFLAGS += -Iinclude CFLAGS += -I/usr/local/include -CFLAGS += $(call cflags_for_lib, xcb-event) -CFLAGS += $(call cflags_for_lib, xcb-property) CFLAGS += $(call cflags_for_lib, xcb-keysyms) +ifeq ($(shell pkg-config --exists xcb-util || echo 1),1) +CFLAGS += -DXCB_COMPAT CFLAGS += $(call cflags_for_lib, xcb-atom) CFLAGS += $(call cflags_for_lib, xcb-aux) +else +CFLAGS += $(call cflags_for_lib, xcb-util) +endif CFLAGS += $(call cflags_for_lib, xcb-icccm) CFLAGS += $(call cflags_for_lib, xcb-xinerama) CFLAGS += $(call cflags_for_lib, xcb-randr) @@ -44,8 +51,12 @@ LDFLAGS += -lm LDFLAGS += $(call ldflags_for_lib, xcb-event, xcb-event) LDFLAGS += $(call ldflags_for_lib, xcb-property, xcb-property) LDFLAGS += $(call ldflags_for_lib, xcb-keysyms, xcb-keysyms) +ifeq ($(shell pkg-config --exists xcb-util || echo 1),1) LDFLAGS += $(call ldflags_for_lib, xcb-atom, xcb-atom) LDFLAGS += $(call ldflags_for_lib, xcb-aux, xcb-aux) +else +LDFLAGS += $(call ldflags_for_lib, xcb-util) +endif LDFLAGS += $(call ldflags_for_lib, xcb-icccm, xcb-icccm) LDFLAGS += $(call ldflags_for_lib, xcb-xinerama, xcb-xinerama) LDFLAGS += $(call ldflags_for_lib, xcb-randr, xcb-randr) diff --git a/include/all.h b/include/all.h index 6251932c..9d13bb95 100644 --- a/include/all.h +++ b/include/all.h @@ -26,10 +26,14 @@ #include #include -#include #include #include +/* Contains compatibility definitions for old libxcb versions */ +#ifdef XCB_COMPAT +#include "xcb_compat.h" +#endif + #include "util.h" #include "ipc.h" #include "tree.h" diff --git a/include/atoms.xmacro b/include/atoms.xmacro new file mode 100644 index 00000000..2543ffd2 --- /dev/null +++ b/include/atoms.xmacro @@ -0,0 +1,31 @@ +xmacro(_NET_SUPPORTED) +xmacro(_NET_SUPPORTING_WM_CHECK) +xmacro(_NET_WM_NAME) +xmacro(_NET_WM_STATE_FULLSCREEN) +xmacro(_NET_WM_STATE) +xmacro(_NET_WM_WINDOW_TYPE) +xmacro(_NET_WM_WINDOW_TYPE_DOCK) +xmacro(_NET_WM_WINDOW_TYPE_DIALOG) +xmacro(_NET_WM_WINDOW_TYPE_UTILITY) +xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR) +xmacro(_NET_WM_WINDOW_TYPE_SPLASH) +xmacro(_NET_WM_DESKTOP) +xmacro(_NET_WM_STRUT_PARTIAL) +xmacro(WM_PROTOCOLS) +xmacro(WM_DELETE_WINDOW) +xmacro(UTF8_STRING) +xmacro(WM_STATE) +xmacro(WM_CLIENT_LEADER) +xmacro(_NET_CURRENT_DESKTOP) +xmacro(_NET_ACTIVE_WINDOW) +xmacro(_NET_WORKAREA) +xmacro(WM_TAKE_FOCUS) +xmacro(WM_HINTS) +xmacro(WM_NORMAL_HINTS) +xmacro(WM_TRANSIENT_FOR) +xmacro(ATOM) +xmacro(WINDOW) +xmacro(WM_NAME) +xmacro(WM_CLASS) +xmacro(STRING) +xmacro(CARDINAL) diff --git a/include/data.h b/include/data.h index 7cbf3dd6..8beb91ab 100644 --- a/include/data.h +++ b/include/data.h @@ -7,7 +7,6 @@ * include/data.h: This file defines all data structures used by i3 * */ -#include #include #include #include diff --git a/include/handlers.h b/include/handlers.h index 76ba3eea..1c8aa283 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -13,9 +13,24 @@ #include +extern int randr_base; void add_ignore_event(const int sequence); +/** + * Takes an xcb_generic_event_t and calls the appropriate handler, based on the + * event type. + * + */ +void handle_event(int type, xcb_generic_event_t *event); + +/** + * Sets the appropriate atoms for the property handlers after the atoms were + * received from X11 + * + */ +void property_handlers_init(); + /** * There was a key press. We compare this key code with our bindings table and * pass the bound action to parse_command(). diff --git a/include/i3.h b/include/i3.h index 3c238c46..060a0cf8 100644 --- a/include/i3.h +++ b/include/i3.h @@ -8,9 +8,6 @@ * See file LICENSE for license information. * */ -#include -#include -#include #include #include @@ -31,11 +28,8 @@ extern TAILQ_HEAD(bindings_head, Binding) *bindings; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; extern TAILQ_HEAD(assignments_head, Assignment) assignments; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; -extern xcb_event_handlers_t evenths; -extern xcb_property_handlers_t prophs; extern uint8_t root_depth; extern bool xcursor_supported, xkb_supported; -extern xcb_atom_t atoms[NUM_ATOMS]; extern xcb_window_t root; #endif diff --git a/include/manage.h b/include/manage.h index 555cefbe..e23eccf3 100644 --- a/include/manage.h +++ b/include/manage.h @@ -8,7 +8,6 @@ * See file LICENSE for license information. * */ -#include #include "data.h" diff --git a/include/util.h b/include/util.h index 064a4792..514e10bd 100644 --- a/include/util.h +++ b/include/util.h @@ -8,7 +8,6 @@ * See file LICENSE for license information. * */ -#include #include #include "data.h" diff --git a/include/workspace.h b/include/workspace.h index ae43e2f1..2132a792 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -8,7 +8,6 @@ * See file LICENSE for license information. * */ -#include #include "data.h" #include "tree.h" diff --git a/include/xcb.h b/include/xcb.h index 47eec76b..bec06b06 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -44,32 +44,9 @@ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \ XCB_EVENT_MASK_ENTER_WINDOW) /* …user moves cursor inside our window */ - -enum { - _NET_SUPPORTED = 0, - _NET_SUPPORTING_WM_CHECK, - _NET_WM_NAME, - _NET_WM_STATE_FULLSCREEN, - _NET_WM_STATE, - _NET_WM_WINDOW_TYPE, - _NET_WM_WINDOW_TYPE_DOCK, - _NET_WM_WINDOW_TYPE_DIALOG, - _NET_WM_WINDOW_TYPE_UTILITY, - _NET_WM_WINDOW_TYPE_TOOLBAR, - _NET_WM_WINDOW_TYPE_SPLASH, - _NET_WM_DESKTOP, - _NET_WM_STRUT_PARTIAL, - WM_PROTOCOLS, - WM_DELETE_WINDOW, - UTF8_STRING, - WM_STATE, - WM_CLIENT_LEADER, - _NET_CURRENT_DESKTOP, - _NET_ACTIVE_WINDOW, - _NET_WORKAREA, - WM_TAKE_FOCUS, - NUM_ATOMS -}; +#define xmacro(atom) xcb_atom_t A_ ## atom; +#include "atoms.xmacro" +#undef xmacro extern unsigned int xcb_numlock_mask; diff --git a/include/xcb_compat.h b/include/xcb_compat.h new file mode 100644 index 00000000..736cbb99 --- /dev/null +++ b/include/xcb_compat.h @@ -0,0 +1,24 @@ +#ifndef _XCB_COMPAT_H +#define _XCB_COMPAT_H + +#define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t +#define xcb_icccm_get_wm_protocols_unchecked xcb_get_wm_protocols_unchecked +#define xcb_icccm_get_wm_protocols_reply xcb_get_wm_protocols_reply +#define xcb_icccm_get_wm_protocols_reply_wipe xcb_get_wm_protocols_reply_wipe +#define XCB_ICCCM_WM_STATE_NORMAL XCB_WM_STATE_NORMAL +#define XCB_ICCCM_WM_STATE_WITHDRAWN XCB_WM_STATE_WITHDRAWN +#define xcb_icccm_get_wm_size_hints_from_reply xcb_get_wm_size_hints_from_reply +#define xcb_icccm_get_wm_normal_hints_reply xcb_get_wm_normal_hints_reply +#define xcb_icccm_get_wm_normal_hints_unchecked xcb_get_wm_normal_hints_unchecked +#define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE XCB_SIZE_HINT_P_MIN_SIZE +#define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC XCB_SIZE_HINT_P_RESIZE_INC +#define XCB_ICCCM_SIZE_HINT_BASE_SIZE XCB_SIZE_HINT_BASE_SIZE +#define XCB_ICCCM_SIZE_HINT_P_ASPECT XCB_SIZE_HINT_P_ASPECT +#define xcb_icccm_wm_hints_t xcb_wm_hints_t +#define xcb_icccm_get_wm_hints_from_reply xcb_get_wm_hints_from_reply +#define xcb_icccm_get_wm_hints_reply xcb_get_wm_hints_reply +#define xcb_icccm_get_wm_hints_unchecked xcb_get_wm_hints_unchecked +#define xcb_icccm_wm_hints_get_urgency xcb_wm_hints_get_urgency +#define xcb_icccm_get_wm_transient_for_from_reply xcb_get_wm_transient_for_from_reply + +#endif diff --git a/src/con.c b/src/con.c index e1359069..8c0a8303 100644 --- a/src/con.c +++ b/src/con.c @@ -504,10 +504,10 @@ void con_toggle_fullscreen(Con *con) { unsigned int num = 0; if (con->fullscreen_mode != CF_NONE) - values[num++] = atoms[_NET_WM_STATE_FULLSCREEN]; + values[num++] = A__NET_WM_STATE_FULLSCREEN; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, - atoms[_NET_WM_STATE], ATOM, 32, num, values); + A__NET_WM_STATE, A_ATOM, 32, num, values); } /* diff --git a/src/ewmh.c b/src/ewmh.c index 67daa022..7b2cc3e4 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -31,7 +31,7 @@ void ewmh_update_current_desktop() { TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { if (ws == focused_ws) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, - atoms[_NET_CURRENT_DESKTOP], CARDINAL, 32, 1, &idx); + A__NET_CURRENT_DESKTOP, A_CARDINAL, 32, 1, &idx); return; } ++idx; @@ -48,7 +48,7 @@ void ewmh_update_current_desktop() { */ void ewmh_update_active_window(xcb_window_t window) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, - atoms[_NET_ACTIVE_WINDOW], WINDOW, 32, 1, &window); + A__NET_ACTIVE_WINDOW, A_WINDOW, 32, 1, &window); } /* @@ -104,7 +104,7 @@ void ewmh_update_workarea() { } } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, - atoms[_NET_WORKAREA], CARDINAL, 32, + A__NET_WORKAREA, A_CARDINAL, 32, num_workspaces * (sizeof(Rect) / sizeof(uint32_t)), workarea); free(workarea); diff --git a/src/floating.c b/src/floating.c index ef500bf6..9d4e1cf8 100644 --- a/src/floating.c +++ b/src/floating.c @@ -374,19 +374,15 @@ void drag_pointer(Con *con, xcb_button_press_event_t *event, xcb_window_t while ((inside_event = xcb_wait_for_event(conn))) { /* We now handle all events we can get using xcb_poll_for_event */ do { - /* Same as get_event_handler in xcb */ - int nr = inside_event->response_type; - if (nr == 0) { - /* An error occured */ - //handle_event(NULL, conn, inside_event); + /* skip x11 errors */ + if (inside_event->response_type == 0) { free(inside_event); continue; } - assert(nr < 256); - nr &= XCB_EVENT_RESPONSE_TYPE_MASK; - assert(nr >= 2); + /* Strip off the highest bit (set if the event is generated) */ + int type = (inside_event->response_type & 0x7F); - switch (nr) { + switch (type) { case XCB_BUTTON_RELEASE: goto done; @@ -398,13 +394,13 @@ void drag_pointer(Con *con, xcb_button_press_event_t *event, xcb_window_t case XCB_UNMAP_NOTIFY: DLOG("Unmap-notify, aborting\n"); - xcb_event_handle(&evenths, inside_event); + handle_event(type, inside_event); goto done; default: DLOG("Passing to original handler\n"); /* Use original handler */ - xcb_event_handle(&evenths, inside_event); + handle_event(type, inside_event); break; } if (last_motion_notify != inside_event) diff --git a/src/handlers.c b/src/handlers.c index 6e6626a9..111d7c24 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -6,14 +6,19 @@ * */ #include +#include -#include #include #include #include "all.h" +int randr_base = -1; + +/* forward declaration for property_notify */ +static int property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom); + /* 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 changing workspaces */ @@ -59,6 +64,143 @@ static bool event_is_ignored(const int sequence) { return false; } +/* + * Takes an xcb_generic_event_t and calls the appropriate handler, based on the + * event type. + * + */ +void handle_event(int type, xcb_generic_event_t *event) { + /* XXX: remove the NULL and conn parameters as soon as this version of libxcb is required */ + + if (randr_base > -1 && + type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { + handle_screen_change(NULL, conn, event); + return; + } + + switch (type) { + case XCB_KEY_PRESS: + handle_key_press(NULL, conn, (xcb_key_press_event_t*)event); + break; + + case XCB_BUTTON_PRESS: + handle_button_press(NULL, conn, (xcb_button_press_event_t*)event); + break; + + case XCB_MAP_REQUEST: + handle_map_request(NULL, conn, (xcb_map_request_event_t*)event); + break; + + case XCB_UNMAP_NOTIFY: + handle_unmap_notify_event(NULL, conn, (xcb_unmap_notify_event_t*)event); + break; + + case XCB_DESTROY_NOTIFY: + handle_destroy_notify_event(NULL, conn, (xcb_destroy_notify_event_t*)event); + break; + + case XCB_EXPOSE: + handle_expose_event(NULL, conn, (xcb_expose_event_t*)event); + break; + + case XCB_MOTION_NOTIFY: + handle_motion_notify(NULL, conn, (xcb_motion_notify_event_t*)event); + break; + + /* Enter window = user moved his mouse over the window */ + case XCB_ENTER_NOTIFY: + handle_enter_notify(NULL, conn, (xcb_enter_notify_event_t*)event); + break; + + /* Client message are sent to the root window. The only interesting + * client message for us is _NET_WM_STATE, we honour + * _NET_WM_STATE_FULLSCREEN */ + case XCB_CLIENT_MESSAGE: + handle_client_message(NULL, conn, (xcb_client_message_event_t*)event); + break; + + /* Configure request = window tried to change size on its own */ + case XCB_CONFIGURE_REQUEST: + handle_configure_request(NULL, conn, (xcb_configure_request_event_t*)event); + break; + + /* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */ + case XCB_MAPPING_NOTIFY: + handle_mapping_notify(NULL, conn, (xcb_mapping_notify_event_t*)event); + break; + + case XCB_PROPERTY_NOTIFY: + DLOG("Property notify\n"); + xcb_property_notify_event_t *e = (xcb_property_notify_event_t*)event; + property_notify(e->state, e->window, e->atom); + break; + + default: + DLOG("Unhandled event of type %d\n", type); + break; + } +} + +typedef int (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property); + +struct property_handler_t { + xcb_atom_t atom; + uint32_t long_len; + cb_property_handler_t cb; +}; + +static struct property_handler_t property_handlers[] = { + { 0, 128, handle_windowname_change }, + { 0, UINT_MAX, handle_hints }, + { 0, 128, handle_windowname_change_legacy }, + { 0, UINT_MAX, handle_normal_hints }, + { 0, UINT_MAX, handle_clientleader_change }, + { 0, UINT_MAX, handle_transient_for } +}; +#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) + +/* + * Sets the appropriate atoms for the property handlers after the atoms were + * received from X11 + * + */ +void property_handlers_init() { + property_handlers[0].atom = A__NET_WM_NAME; + property_handlers[1].atom = A_WM_HINTS; + property_handlers[2].atom = A_WM_NAME; + property_handlers[3].atom = A_WM_NORMAL_HINTS; + property_handlers[4].atom = A_WM_CLIENT_LEADER; + property_handlers[5].atom = A_WM_TRANSIENT_FOR; +} + +static int property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { + struct property_handler_t *handler = NULL; + xcb_get_property_reply_t *propr = NULL; + int ret; + + for (int c = 0; c < sizeof(property_handlers) / sizeof(struct property_handler_t); c++) { + if (property_handlers[c].atom != atom) + continue; + + handler = &property_handlers[c]; + break; + } + + if (handler == NULL) { + DLOG("Unhandled property notify for atom %d (0x%08x)\n", atom, atom); + return 0; + } + + if (state != XCB_PROPERTY_DELETE) { + xcb_get_property_cookie_t cookie = xcb_get_property(conn, 0, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, handler->long_len); + propr = xcb_get_property_reply(conn, cookie, 0); + } + + ret = handler->cb(NULL, conn, state, window, atom, propr); + FREE(propr); + return ret; +} + /* * There was a key press. We compare this key code with our bindings table and pass * the bound action to parse_command(). @@ -627,10 +769,10 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * */ int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event) { LOG("ClientMessage for window 0x%08x\n", event->window); - if (event->type == atoms[_NET_WM_STATE]) { - if (event->format != 32 || event->data.data32[1] != atoms[_NET_WM_STATE_FULLSCREEN]) { + if (event->type == A__NET_WM_STATE) { + if (event->format != 32 || event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN) { DLOG("atom in clientmessage is %d, fullscreen is %d\n", - event->data.data32[1], atoms[_NET_WM_STATE_FULLSCREEN]); + event->data.data32[1], A__NET_WM_STATE_FULLSCREEN); DLOG("not about fullscreen atom\n"); return 0; } @@ -693,17 +835,17 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w /* If the hints were already in this event, use them, if not, request them */ if (reply != NULL) - xcb_get_wm_size_hints_from_reply(&size_hints, reply); + xcb_icccm_get_wm_size_hints_from_reply(&size_hints, reply); else - xcb_get_wm_normal_hints_reply(conn, xcb_get_wm_normal_hints_unchecked(conn, con->window->id), &size_hints, NULL); + xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints_unchecked(conn, con->window->id), &size_hints, NULL); - if ((size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE)) { + if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)) { // TODO: Minimum size is not yet implemented DLOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height); } bool changed = false; - if ((size_hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)) { + if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)) { if (size_hints.width_inc > 0 && size_hints.width_inc < 0xFFFF) if (con->width_increment != size_hints.width_inc) { con->width_increment = size_hints.width_inc; @@ -724,10 +866,10 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w /* base_width/height are the desired size of the window. We check if either the program-specified size or the program-specified min-size is available */ - if (size_hints.flags & XCB_SIZE_HINT_BASE_SIZE) { + if (size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) { base_width = size_hints.base_width; base_height = size_hints.base_height; - } else if (size_hints.flags & XCB_SIZE_HINT_P_MIN_SIZE) { + } else if (size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) { /* TODO: is this right? icccm says not */ base_width = size_hints.min_width; base_height = size_hints.min_height; @@ -742,7 +884,7 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w } /* If no aspect ratio was set or if it was invalid, we ignore the hints */ - if (!(size_hints.flags & XCB_SIZE_HINT_P_ASPECT) || + if (!(size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT) || (size_hints.min_aspect_num <= 0) || (size_hints.min_aspect_den <= 0)) { goto render_and_return; @@ -788,13 +930,13 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t return 1; } - xcb_wm_hints_t hints; + xcb_icccm_wm_hints_t hints; if (reply != NULL) { - if (!xcb_get_wm_hints_from_reply(&hints, reply)) + if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply)) return 1; } else { - if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, con->window->id), &hints, NULL)) + if (!xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints_unchecked(conn, con->window->id), &hints, NULL)) return 1; } @@ -804,7 +946,7 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t } /* Update the flag on the client directly */ - con->urgent = (xcb_wm_hints_get_urgency(&hints) != 0); + con->urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0); //CLIENT_LOG(con); LOG("Urgency flag changed to %d\n", con->urgent); @@ -841,7 +983,7 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_ if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, - false, window, WM_TRANSIENT_FOR, WINDOW, 0, 32), NULL); + false, window, A_WM_TRANSIENT_FOR, A_WINDOW, 0, 32), NULL); if (prop == NULL) return 1; } @@ -872,7 +1014,7 @@ int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, - false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL); + false, window, A_WM_CLIENT_LEADER, A_WINDOW, 0, 32), NULL); if (prop == NULL) return 1; } diff --git a/src/main.c b/src/main.c index 002eb6a5..16df027b 100644 --- a/src/main.c +++ b/src/main.c @@ -15,9 +15,6 @@ extern Con *focused; char **start_argv; xcb_connection_t *conn; -xcb_event_handlers_t evenths; -xcb_property_handlers_t prophs; -xcb_atom_t atoms[NUM_ATOMS]; xcb_window_t root; uint8_t root_depth; @@ -66,8 +63,17 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { xcb_generic_event_t *event; while ((event = xcb_poll_for_event(conn)) != NULL) { - xcb_event_handle(&evenths, event); - free(event); + if (event->response_type == 0) { + ELOG("X11 Error received! sequence %x\n", event->sequence); + continue; + } + + /* Strip off the highest bit (set if the event is generated) */ + int type = (event->response_type & 0x7F); + + handle_event(type, event); + + free(event); } } @@ -149,7 +155,6 @@ int main(int argc, char *argv[]) { bool only_check_config = false; bool force_xinerama = false; bool disable_signalhandler = false; - xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS]; static struct option long_options[] = { {"no-autostart", no_argument, 0, 'a'}, {"config", required_argument, 0, 'c'}, @@ -272,30 +277,10 @@ int main(int argc, char *argv[]) { check_error(conn, cookie, "Another window manager seems to be running"); /* Place requests for the atoms we need as soon as possible */ - #define REQUEST_ATOM(name) atom_cookies[name] = xcb_intern_atom(conn, 0, strlen(#name), #name); - - REQUEST_ATOM(_NET_SUPPORTED); - REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN); - REQUEST_ATOM(_NET_SUPPORTING_WM_CHECK); - REQUEST_ATOM(_NET_WM_NAME); - REQUEST_ATOM(_NET_WM_STATE); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE); - REQUEST_ATOM(_NET_WM_DESKTOP); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DOCK); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - REQUEST_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); - REQUEST_ATOM(_NET_WM_STRUT_PARTIAL); - REQUEST_ATOM(WM_PROTOCOLS); - REQUEST_ATOM(WM_DELETE_WINDOW); - REQUEST_ATOM(UTF8_STRING); - REQUEST_ATOM(WM_STATE); - REQUEST_ATOM(WM_CLIENT_LEADER); - REQUEST_ATOM(_NET_CURRENT_DESKTOP); - REQUEST_ATOM(_NET_ACTIVE_WINDOW); - REQUEST_ATOM(_NET_WORKAREA); - REQUEST_ATOM(WM_TAKE_FOCUS); + #define xmacro(atom) \ + xcb_intern_atom_cookie_t atom ## _cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom); + #include "atoms.xmacro" + #undef xmacro /* Initialize the Xlib connection */ xlibdpy = xkbdpy = XOpenDisplay(NULL); @@ -338,95 +323,32 @@ int main(int argc, char *argv[]) { } } - memset(&evenths, 0, sizeof(xcb_event_handlers_t)); - memset(&prophs, 0, sizeof(xcb_property_handlers_t)); - - xcb_event_handlers_init(conn, &evenths); - xcb_property_handlers_init(&prophs, &evenths); - xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL); - - xcb_event_set_button_press_handler(&evenths, handle_button_press, NULL); - - xcb_event_set_map_request_handler(&evenths, handle_map_request, NULL); - - xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL); - xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL); - - xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL); - - xcb_event_set_motion_notify_handler(&evenths, handle_motion_notify, NULL); - - /* Enter window = user moved his mouse over the window */ - xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, NULL); - - /* Client message are sent to the root window. The only interesting client message - for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */ - xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL); - - /* Configure request = window tried to change size on its own */ - xcb_event_set_configure_request_handler(&evenths, handle_configure_request, NULL); - /* Setup NetWM atoms */ - #define GET_ATOM(name) \ + #define xmacro(name) \ do { \ - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \ + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name ## _cookie, NULL); \ if (!reply) { \ ELOG("Could not get atom " #name "\n"); \ exit(-1); \ } \ - atoms[name] = reply->atom; \ + A_ ## name = reply->atom; \ free(reply); \ - } while (0) + } while (0); + #include "atoms.xmacro" + #undef xmacro - GET_ATOM(_NET_SUPPORTED); - GET_ATOM(_NET_WM_STATE_FULLSCREEN); - GET_ATOM(_NET_SUPPORTING_WM_CHECK); - GET_ATOM(_NET_WM_NAME); - GET_ATOM(_NET_WM_STATE); - GET_ATOM(_NET_WM_WINDOW_TYPE); - GET_ATOM(_NET_WM_DESKTOP); - GET_ATOM(_NET_WM_WINDOW_TYPE_DOCK); - GET_ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - GET_ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - GET_ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - GET_ATOM(_NET_WM_WINDOW_TYPE_SPLASH); - GET_ATOM(_NET_WM_STRUT_PARTIAL); - GET_ATOM(WM_PROTOCOLS); - GET_ATOM(WM_DELETE_WINDOW); - GET_ATOM(UTF8_STRING); - GET_ATOM(WM_STATE); - GET_ATOM(WM_CLIENT_LEADER); - GET_ATOM(_NET_CURRENT_DESKTOP); - GET_ATOM(_NET_ACTIVE_WINDOW); - GET_ATOM(_NET_WORKAREA); - GET_ATOM(WM_TAKE_FOCUS); - - /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */ - xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL); - - /* Watch WM_HINTS (contains the urgent property) */ - xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL); - - /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */ - xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL); - - /* Watch WM_NORMAL_HINTS (aspect ratio, size increments, …) */ - xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL); - - /* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */ - xcb_property_set_handler(&prophs, atoms[WM_CLIENT_LEADER], UINT_MAX, handle_clientleader_change, NULL); - - /* Watch WM_TRANSIENT_FOR property (to which client this popup window belongs) */ - xcb_property_set_handler(&prophs, WM_TRANSIENT_FOR, UINT_MAX, handle_transient_for, NULL); - - /* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */ - xcb_event_set_mapping_notify_handler(&evenths, handle_mapping_notify, NULL); + property_handlers_init(); /* Set up the atoms we support */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms); + xcb_atom_t supported_atoms[] = { +#define xmacro(atom) A_ ## atom, +#include "atoms.xmacro" +#undef xmacro + }; + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, A_ATOM, 32, 7, supported_atoms); /* Set up the window manager’s name */ - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, strlen("i3"), "i3"); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, A_WINDOW, 32, 1, &root); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); keysyms = xcb_key_symbols_alloc(conn); @@ -446,17 +368,11 @@ int main(int argc, char *argv[]) { if (needs_tree_init) tree_init(); - int randr_base; if (force_xinerama) { xinerama_init(); } else { DLOG("Checking for XRandR...\n"); randr_init(&randr_base); - - xcb_event_set_handler(&evenths, - randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY, - handle_screen_change, - NULL); } tree_render(); diff --git a/src/manage.c b/src/manage.c index c4d6172b..4821e28f 100644 --- a/src/manage.c +++ b/src/manage.c @@ -85,15 +85,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki utf8_title_cookie, title_cookie, class_cookie, leader_cookie, transient_cookie; - wm_type_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX); - strut_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX); - state_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STATE], UINT32_MAX); - utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_NAME], 128); - leader_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[WM_CLIENT_LEADER], UINT32_MAX); - transient_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_TRANSIENT_FOR, UINT32_MAX); - title_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_NAME, 128); - class_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_CLASS, 128); - /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */ geomc = xcb_get_geometry(conn, d); @@ -126,6 +117,18 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki goto out; } +#define GET_PROPERTY(atom, len) xcb_get_property_unchecked(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len) + + wm_type_cookie = GET_PROPERTY(A__NET_WM_WINDOW_TYPE, UINT32_MAX); + strut_cookie = GET_PROPERTY(A__NET_WM_STRUT_PARTIAL, UINT32_MAX); + state_cookie = GET_PROPERTY(A__NET_WM_STATE, UINT32_MAX); + utf8_title_cookie = GET_PROPERTY(A__NET_WM_NAME, 128); + leader_cookie = GET_PROPERTY(A_WM_CLIENT_LEADER, UINT32_MAX); + transient_cookie = GET_PROPERTY(A_WM_TRANSIENT_FOR, UINT32_MAX); + title_cookie = GET_PROPERTY(A_WM_NAME, 128); + class_cookie = GET_PROPERTY(A_WM_CLASS, 128); + /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */ + DLOG("reparenting!\n"); uint32_t mask = 0; uint32_t values[1]; @@ -157,7 +160,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki Con *search_at = croot; xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, wm_type_cookie, NULL); - if (xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_DOCK])) { + if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DOCK)) { LOG("This window is of type dock\n"); Output *output = get_output_containing(geom->x, geom->y); if (output != NULL) { @@ -254,10 +257,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki /* set floating if necessary */ bool want_floating = false; - if (xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_DIALOG]) || - xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_UTILITY]) || - xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_TOOLBAR]) || - xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_SPLASH])) { + if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DIALOG) || + xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_UTILITY) || + xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) || + xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_SPLASH)) { LOG("This window is a dialog window, setting floating\n"); want_floating = true; } @@ -308,7 +311,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki xcb_change_window_attributes(conn, window, mask, values); reply = xcb_get_property_reply(conn, state_cookie, NULL); - if (xcb_reply_contains_atom(reply, atoms[_NET_WM_STATE_FULLSCREEN])) + if (xcb_reply_contains_atom(reply, A__NET_WM_STATE_FULLSCREEN)) con_toggle_fullscreen(nc); /* Put the client inside the save set. Upon termination (whether killed or diff --git a/src/sighandler.c b/src/sighandler.c index d6128b5b..1d3e4ab7 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -26,12 +26,7 @@ #include -#include "i3.h" -#include "util.h" -#include "xcb.h" -#include "log.h" -#include "config.h" -#include "randr.h" +#include "all.h" static xcb_gcontext_t pixmap_gc; static xcb_pixmap_t pixmap; @@ -159,12 +154,6 @@ void handle_signal(int sig, siginfo_t *info, void *data) { sigaction(sig, &action, NULL); raised_signal = sig; - /* setup event handler for key presses */ - xcb_event_handlers_t sig_evenths; - memset(&sig_evenths, 0, sizeof(xcb_event_handlers_t)); - xcb_event_handlers_init(conn, &sig_evenths); - xcb_event_set_key_press_handler(&sig_evenths, sig_handle_key_press, NULL); - /* width and height of the popup window, so that the text fits in */ int crash_text_num = sizeof(crash_text) / sizeof(char*); int height = 13 + (crash_text_num * config.font.height); @@ -203,7 +192,16 @@ void handle_signal(int sig, siginfo_t *info, void *data) { xcb_flush(conn); } - xcb_event_wait_for_event_loop(&sig_evenths); + xcb_generic_event_t *event; + /* Yay, more own eventhandlers… */ + while ((event = xcb_wait_for_event(conn))) { + /* Strip off the highest bit (set if the event is generated) */ + int type = (event->response_type & 0x7F); + if (type == XCB_KEY_PRESS) { + sig_handle_key_press(NULL, conn, (xcb_key_press_event_t*)event); + } + free(event); + } } /* diff --git a/src/window.c b/src/window.c index 11be7c6f..ffc73cd0 100644 --- a/src/window.c +++ b/src/window.c @@ -136,7 +136,7 @@ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) } xcb_window_t transient_for; - if (!xcb_get_wm_transient_for_from_reply(&transient_for, prop)) + if (!xcb_icccm_get_wm_transient_for_from_reply(&transient_for, prop)) return; DLOG("Transient for changed to %08x\n", transient_for); diff --git a/src/x.c b/src/x.c index 7a3385e5..374fd162 100644 --- a/src/x.c +++ b/src/x.c @@ -180,11 +180,11 @@ void x_con_kill(Con *con) { */ static bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom) { xcb_get_property_cookie_t cookie; - xcb_get_wm_protocols_reply_t protocols; + xcb_icccm_get_wm_protocols_reply_t protocols; bool result = false; - cookie = xcb_get_wm_protocols_unchecked(conn, window, atoms[WM_PROTOCOLS]); - if (xcb_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1) + cookie = xcb_icccm_get_wm_protocols_unchecked(conn, window, A_WM_PROTOCOLS); + if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1) return false; /* Check if the client’s protocols have the requested atom set */ @@ -192,7 +192,7 @@ static bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom) { if (protocols.atoms[i] == atom) result = true; - xcb_get_wm_protocols_reply_wipe(&protocols); + xcb_icccm_get_wm_protocols_reply_wipe(&protocols); return result; } @@ -203,7 +203,7 @@ static bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom) { */ void x_window_kill(xcb_window_t window) { /* if this window does not support WM_DELETE_WINDOW, we kill it the hard way */ - if (!window_supports_protocol(window, atoms[WM_DELETE_WINDOW])) { + if (!window_supports_protocol(window, A_WM_DELETE_WINDOW)) { LOG("Killing window the hard way\n"); xcb_kill_client(conn, window); return; @@ -215,9 +215,9 @@ void x_window_kill(xcb_window_t window) { ev.response_type = XCB_CLIENT_MESSAGE; ev.window = window; - ev.type = atoms[WM_PROTOCOLS]; + ev.type = A_WM_PROTOCOLS; ev.format = 32; - ev.data.data32[0] = atoms[WM_DELETE_WINDOW]; + ev.data.data32[0] = A_WM_DELETE_WINDOW; ev.data.data32[1] = XCB_CURRENT_TIME; LOG("Sending WM_DELETE to the client\n"); @@ -389,7 +389,7 @@ static void x_push_node(Con *con) { DLOG("pushing name %s for con %p\n", state->name, con); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->frame, - WM_NAME, STRING, 8, strlen(state->name), state->name); + A_WM_NAME, A_STRING, 8, strlen(state->name), state->name); FREE(state->name); } @@ -468,9 +468,9 @@ static void x_push_node(Con *con) { if (con->window != NULL) { /* Set WM_STATE_NORMAL because GTK applications don’t want to * drag & drop if we don’t. Also, xprop(1) needs it. */ - long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE }; + long data[] = { XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE }; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, - atoms[WM_STATE], atoms[WM_STATE], 32, 2, data); + A_WM_STATE, A_WM_STATE, 32, 2, data); } if (!state->child_mapped && con->window != NULL) { @@ -528,9 +528,9 @@ static void x_push_node_unmaps(Con *con) { xcb_void_cookie_t cookie; if (con->window != NULL) { /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */ - long data[] = { XCB_WM_STATE_WITHDRAWN, XCB_NONE }; + long data[] = { XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE }; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, - atoms[WM_STATE], atoms[WM_STATE], 32, 2, data); + A_WM_STATE, A_WM_STATE, 32, 2, data); } cookie = xcb_unmap_window(conn, con->frame); @@ -624,9 +624,9 @@ void x_push_changes(Con *con) { ev.response_type = XCB_CLIENT_MESSAGE; ev.window = to_focus; - ev.type = atoms[WM_PROTOCOLS]; + ev.type = A_WM_PROTOCOLS; ev.format = 32; - ev.data.data32[0] = atoms[WM_TAKE_FOCUS]; + ev.data.data32[0] = A_WM_TAKE_FOCUS; ev.data.data32[1] = XCB_CURRENT_TIME; DLOG("Sending WM_TAKE_FOCUS to the client\n");