Make i3 compatible with the very latest xcb

This involves:
 • Compiling with xcb-util instead of xcb-{atom,aux} (they merged the libraries)
 • Not using xcb-{event,property} anymore (code removed upstream)
 • Not using the predefined WINDOW, CARDINEL, … atoms (removed upstream)
 • Using the new xcb_icccm_* data types/functions instead of just xcb_*
   (for example xcb_icccm_get_wm_hints instead of xcb_get_wm_hints)

Also I refactored the atoms to use x-macros.
This commit is contained in:
Michael Stapelberg 2011-03-18 16:07:02 +01:00
parent f5afe2f67e
commit 86117db434
19 changed files with 400 additions and 241 deletions

View File

@ -40,8 +40,13 @@ LDFLAGS += -lm
LDFLAGS += -lxcb-event LDFLAGS += -lxcb-event
LDFLAGS += -lxcb-property LDFLAGS += -lxcb-property
LDFLAGS += -lxcb-keysyms LDFLAGS += -lxcb-keysyms
ifeq ($(shell pkg-config --exists xcb-util || echo 1),1)
CFLAGS += -DXCB_COMPAT
LDFLAGS += -lxcb-atom LDFLAGS += -lxcb-atom
LDFLAGS += -lxcb-aux LDFLAGS += -lxcb-aux
else
LDFLAGS += -lxcb-util
endif
LDFLAGS += -lxcb-icccm LDFLAGS += -lxcb-icccm
LDFLAGS += -lxcb-xinerama LDFLAGS += -lxcb-xinerama
LDFLAGS += -lxcb-randr LDFLAGS += -lxcb-randr

View File

@ -308,13 +308,6 @@ int main(int argc, char *argv[]) {
die("Cannot open display\n"); die("Cannot open display\n");
/* Set up event handlers for key press and key release */ /* Set up event handlers for key press and key release */
xcb_event_handlers_t evenths;
memset(&evenths, 0, sizeof(xcb_event_handlers_t));
xcb_event_handlers_init(conn, &evenths);
xcb_event_set_key_press_handler(&evenths, handle_key_press, NULL);
xcb_event_set_key_release_handler(&evenths, handle_key_release, NULL);
xcb_event_set_expose_handler(&evenths, handle_expose, NULL);
modeswitchmask = get_mod_mask(conn, XK_Mode_switch); modeswitchmask = get_mod_mask(conn, XK_Mode_switch);
numlockmask = get_mod_mask(conn, XK_Num_Lock); numlockmask = get_mod_mask(conn, XK_Num_Lock);
symbols = xcb_key_symbols_alloc(conn); symbols = xcb_key_symbols_alloc(conn);
@ -362,7 +355,33 @@ int main(int argc, char *argv[]) {
xcb_flush(conn); xcb_flush(conn);
xcb_event_wait_for_event_loop(&evenths); xcb_generic_event_t *event;
while ((event = xcb_wait_for_event(conn)) != NULL) {
if (event->response_type == 0) {
fprintf(stderr, "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);
switch (type) {
case XCB_KEY_PRESS:
handle_key_press(NULL, conn, (xcb_key_press_event_t*)event);
break;
case XCB_KEY_RELEASE:
handle_key_release(NULL, conn, (xcb_key_release_event_t*)event);
break;
case XCB_EXPOSE:
handle_expose(NULL, conn, (xcb_expose_event_t*)event);
break;
}
free(event);
}
return 0; return 0;
} }

31
include/atoms.xmacro Normal file
View File

@ -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)

View File

@ -13,6 +13,28 @@
#include <xcb/randr.h> #include <xcb/randr.h>
extern int randr_base;
/**
* 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);
/**
* Requests the property and invokes the appropriate callback.
*
*/
int property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom);
/**
* 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 * There was a key press. We compare this key code with our bindings table and
* pass the bound action to parse_command(). * pass the bound action to parse_command().

View File

@ -10,7 +10,6 @@
*/ */
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_property.h> #include <xcb/xcb_property.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
@ -21,8 +20,6 @@
#ifndef _I3_H #ifndef _I3_H
#define _I3_H #define _I3_H
#define NUM_ATOMS 22
extern xcb_connection_t *global_conn; extern xcb_connection_t *global_conn;
extern xcb_key_symbols_t *keysyms; extern xcb_key_symbols_t *keysyms;
extern char **start_argv; extern char **start_argv;
@ -32,10 +29,8 @@ extern TAILQ_HEAD(bindings_head, Binding) *bindings;
extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
extern TAILQ_HEAD(assignments_head, Assignment) assignments; extern TAILQ_HEAD(assignments_head, Assignment) assignments;
extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
extern xcb_event_handlers_t evenths;
extern uint8_t root_depth; extern uint8_t root_depth;
extern bool xkb_supported; extern bool xkb_supported;
extern xcb_atom_t atoms[NUM_ATOMS];
extern xcb_window_t root; extern xcb_window_t root;
#endif #endif

View File

@ -20,8 +20,7 @@
* manage them * manage them
* *
*/ */
void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t void manage_existing_windows(xcb_connection_t *conn, xcb_window_t root);
*prophs, xcb_window_t root);
/** /**
* Restores the geometry of each window by reparenting it to the root window * Restores the geometry of each window by reparenting it to the root window
@ -37,7 +36,7 @@ void restore_geometry(xcb_connection_t *conn);
* Do some sanity checks and then reparent the window. * Do some sanity checks and then reparent the window.
* *
*/ */
void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn, void manage_window(xcb_connection_t *conn,
xcb_window_t window, xcb_window_t window,
xcb_get_window_attributes_cookie_t cookie, xcb_get_window_attributes_cookie_t cookie,
bool needs_to_be_mapped); bool needs_to_be_mapped);

View File

@ -43,30 +43,9 @@
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \
XCB_EVENT_MASK_ENTER_WINDOW) /* …user moves cursor inside our window */ XCB_EVENT_MASK_ENTER_WINDOW) /* …user moves cursor inside our window */
#define xmacro(atom) xcb_atom_t A_ ## atom;
enum { _NET_SUPPORTED = 0, #include "atoms.xmacro"
_NET_SUPPORTING_WM_CHECK, #undef xmacro
_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
};
extern unsigned int xcb_numlock_mask; extern unsigned int xcb_numlock_mask;

26
include/xcb_compat.h Normal file
View File

@ -0,0 +1,26 @@
#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
#define xcb_icccm_get_wm_transient_for_reply xcb_get_wm_transient_for_reply
#define xcb_icccm_get_wm_transient_for_unchecked xcb_get_wm_transient_for_unchecked
#endif

View File

@ -19,6 +19,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <math.h> #include <math.h>
/* Contains compatibility definitions for old libxcb versions */
#ifdef XCB_COMPAT
#include "xcb_compat.h"
#endif
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_atom.h> #include <xcb/xcb_atom.h>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>

View File

@ -15,6 +15,11 @@
#include <assert.h> #include <assert.h>
#include <limits.h> #include <limits.h>
/* Contains compatibility definitions for old libxcb versions */
#ifdef XCB_COMPAT
#include "xcb_compat.h"
#endif
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
@ -71,11 +76,11 @@ void client_warp_pointer_into(xcb_connection_t *conn, Client *client) {
*/ */
static bool client_supports_protocol(xcb_connection_t *conn, Client *client, xcb_atom_t atom) { static bool client_supports_protocol(xcb_connection_t *conn, Client *client, xcb_atom_t atom) {
xcb_get_property_cookie_t cookie; xcb_get_property_cookie_t cookie;
xcb_get_wm_protocols_reply_t protocols; xcb_icccm_get_wm_protocols_reply_t protocols;
bool result = false; bool result = false;
cookie = xcb_get_wm_protocols_unchecked(conn, client->child, atoms[WM_PROTOCOLS]); cookie = xcb_icccm_get_wm_protocols_unchecked(conn, client->child, A_WM_PROTOCOLS);
if (xcb_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1) if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1)
return false; return false;
/* Check if the clients protocols have the requested atom set */ /* Check if the clients protocols have the requested atom set */
@ -83,7 +88,7 @@ static bool client_supports_protocol(xcb_connection_t *conn, Client *client, xcb
if (protocols.atoms[i] == atom) if (protocols.atoms[i] == atom)
result = true; result = true;
xcb_get_wm_protocols_reply_wipe(&protocols); xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
return result; return result;
} }
@ -94,7 +99,7 @@ static bool client_supports_protocol(xcb_connection_t *conn, Client *client, xcb
*/ */
void client_kill(xcb_connection_t *conn, Client *window) { void client_kill(xcb_connection_t *conn, Client *window) {
/* If the client does not support WM_DELETE_WINDOW, we kill it the hard way */ /* If the client does not support WM_DELETE_WINDOW, we kill it the hard way */
if (!client_supports_protocol(conn, window, atoms[WM_DELETE_WINDOW])) { if (!client_supports_protocol(conn, window, A_WM_DELETE_WINDOW)) {
LOG("Killing window the hard way\n"); LOG("Killing window the hard way\n");
xcb_kill_client(conn, window->child); xcb_kill_client(conn, window->child);
return; return;
@ -106,9 +111,9 @@ void client_kill(xcb_connection_t *conn, Client *window) {
ev.response_type = XCB_CLIENT_MESSAGE; ev.response_type = XCB_CLIENT_MESSAGE;
ev.window = window->child; ev.window = window->child;
ev.type = atoms[WM_PROTOCOLS]; ev.type = A_WM_PROTOCOLS;
ev.format = 32; 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; ev.data.data32[1] = XCB_CURRENT_TIME;
LOG("Sending WM_DELETE to the client\n"); LOG("Sending WM_DELETE to the client\n");
@ -229,8 +234,8 @@ void client_enter_fullscreen(xcb_connection_t *conn, Client *client, bool global
xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_STACK_MODE, values); xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
/* Update _NET_WM_STATE */ /* Update _NET_WM_STATE */
values[0] = atoms[_NET_WM_STATE_FULLSCREEN]; values[0] = A__NET_WM_STATE_FULLSCREEN;
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[_NET_WM_STATE], ATOM, 32, 1, values); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, A__NET_WM_STATE, A_ATOM, 32, 1, values);
fake_configure_notify(conn, r, client->child); fake_configure_notify(conn, r, client->child);
@ -267,7 +272,7 @@ void client_leave_fullscreen(xcb_connection_t *conn, Client *client) {
} }
/* Update _NET_WM_STATE */ /* Update _NET_WM_STATE */
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, atoms[_NET_WM_STATE], ATOM, 32, 0, NULL); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, A__NET_WM_STATE, A_ATOM, 32, 0, NULL);
xcb_flush(conn); xcb_flush(conn);
} }
@ -401,8 +406,8 @@ void client_change_border(xcb_connection_t *conn, Client *client, char border_ty
*/ */
void client_unmap(xcb_connection_t *conn, Client *client) { void client_unmap(xcb_connection_t *conn, Client *client) {
/* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */ /* 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, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, A_WM_STATE, A_WM_STATE, 32, 2, data);
xcb_unmap_window(conn, client->frame); xcb_unmap_window(conn, client->frame);
} }
@ -414,8 +419,8 @@ void client_unmap(xcb_connection_t *conn, Client *client) {
void client_map(xcb_connection_t *conn, Client *client) { void client_map(xcb_connection_t *conn, Client *client) {
/* Set WM_STATE_NORMAL because GTK applications dont want to drag & drop if we dont. /* Set WM_STATE_NORMAL because GTK applications dont want to drag & drop if we dont.
* Also, xprop(1) needs that to work. */ * Also, xprop(1) needs that to work. */
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, client->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->child, A_WM_STATE, A_WM_STATE, 32, 2, data);
xcb_map_window(conn, client->frame); xcb_map_window(conn, client->frame);
} }

View File

@ -245,6 +245,6 @@ int format_event(xcb_generic_event_t *e) {
return 1; return 1;
} }
int handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e) { int dbg_handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e) {
return format_event(e); return format_event(e);
} }

View File

@ -31,7 +31,7 @@
void ewmh_update_current_desktop() { void ewmh_update_current_desktop() {
uint32_t current_desktop = c_ws->num; uint32_t current_desktop = c_ws->num;
xcb_change_property(global_conn, XCB_PROP_MODE_REPLACE, root, xcb_change_property(global_conn, XCB_PROP_MODE_REPLACE, root,
atoms[_NET_CURRENT_DESKTOP], CARDINAL, 32, 1, A__NET_CURRENT_DESKTOP, A_CARDINAL, 32, 1,
&current_desktop); &current_desktop);
} }
@ -44,7 +44,7 @@ void ewmh_update_current_desktop() {
*/ */
void ewmh_update_active_window(xcb_window_t window) { void ewmh_update_active_window(xcb_window_t window) {
xcb_change_property(global_conn, XCB_PROP_MODE_REPLACE, root, xcb_change_property(global_conn, XCB_PROP_MODE_REPLACE, root,
atoms[_NET_ACTIVE_WINDOW], WINDOW, 32, 1, &window); A__NET_ACTIVE_WINDOW, A_WINDOW, 32, 1, &window);
} }
/* /*
@ -95,7 +95,7 @@ void ewmh_update_workarea() {
memcpy(&last_rect, &(ws->rect), sizeof(Rect)); memcpy(&last_rect, &(ws->rect), sizeof(Rect));
} }
xcb_change_property(global_conn, XCB_PROP_MODE_REPLACE, root, xcb_change_property(global_conn, XCB_PROP_MODE_REPLACE, root,
atoms[_NET_WORKAREA], CARDINAL, 32, A__NET_WORKAREA, A_CARDINAL, 32,
num_workspaces * (sizeof(Rect) / sizeof(uint32_t)), num_workspaces * (sizeof(Rect) / sizeof(uint32_t)),
workarea); workarea);
free(workarea); free(workarea);

View File

@ -15,19 +15,18 @@
#include <assert.h> #include <assert.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include "i3.h" #include "i3.h"
#include "config.h" #include "config.h"
#include "data.h" #include "data.h"
#include "util.h" #include "util.h"
#include "xcb.h" #include "xcb.h"
#include "debug.h"
#include "layout.h" #include "layout.h"
#include "client.h" #include "client.h"
#include "floating.h" #include "floating.h"
#include "workspace.h" #include "workspace.h"
#include "log.h" #include "log.h"
#include "handlers.h"
/* /*
* Toggles floating mode for the given client. * Toggles floating mode for the given client.
@ -404,19 +403,15 @@ void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event
while ((inside_event = xcb_wait_for_event(conn))) { while ((inside_event = xcb_wait_for_event(conn))) {
/* We now handle all events we can get using xcb_poll_for_event */ /* We now handle all events we can get using xcb_poll_for_event */
do { do {
/* Same as get_event_handler in xcb */ /* skip x11 errors */
int nr = inside_event->response_type; if (inside_event->response_type == 0) {
if (nr == 0) {
/* An error occured */
handle_event(NULL, conn, inside_event);
free(inside_event); free(inside_event);
continue; continue;
} }
assert(nr < 256); /* Strip off the highest bit (set if the event is generated) */
nr &= XCB_EVENT_RESPONSE_TYPE_MASK; int type = (inside_event->response_type & 0x7F);
assert(nr >= 2);
switch (nr) { switch (type) {
case XCB_BUTTON_RELEASE: case XCB_BUTTON_RELEASE:
goto done; goto done;
@ -428,13 +423,13 @@ void drag_pointer(xcb_connection_t *conn, Client *client, xcb_button_press_event
case XCB_UNMAP_NOTIFY: case XCB_UNMAP_NOTIFY:
DLOG("Unmap-notify, aborting\n"); DLOG("Unmap-notify, aborting\n");
xcb_event_handle(&evenths, inside_event); handle_event(type, inside_event);
goto done; goto done;
default: default:
DLOG("Passing to original handler\n"); DLOG("Passing to original handler\n");
/* Use original handler */ /* Use original handler */
xcb_event_handle(&evenths, inside_event); handle_event(type, inside_event);
break; break;
} }
if (last_motion_notify != inside_event) if (last_motion_notify != inside_event)

View File

@ -13,6 +13,12 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <limits.h>
/* Contains compatibility definitions for old libxcb versions */
#ifdef XCB_COMPAT
#include "xcb_compat.h"
#endif
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_atom.h> #include <xcb/xcb_atom.h>
@ -22,7 +28,7 @@
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#include "i3.h" #include "i3.h"
#include "debug.h" #include "handlers.h"
#include "table.h" #include "table.h"
#include "layout.h" #include "layout.h"
#include "commands.h" #include "commands.h"
@ -41,6 +47,8 @@
#include "container.h" #include "container.h"
#include "ipc.h" #include "ipc.h"
int randr_base = -1;
/* 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
changing workspaces */ changing workspaces */
@ -82,6 +90,154 @@ static bool event_is_ignored(const int sequence) {
return false; 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, global_conn, event);
return;
}
switch (type) {
case XCB_KEY_PRESS:
handle_key_press(NULL, global_conn, (xcb_key_press_event_t*)event);
break;
case XCB_BUTTON_PRESS:
handle_button_press(NULL, global_conn, (xcb_button_press_event_t*)event);
break;
case XCB_MAP_REQUEST:
handle_map_request(NULL, global_conn, (xcb_map_request_event_t*)event);
break;
case XCB_UNMAP_NOTIFY:
handle_unmap_notify_event(NULL, global_conn, (xcb_unmap_notify_event_t*)event);
break;
case XCB_DESTROY_NOTIFY:
handle_destroy_notify_event(NULL, global_conn, (xcb_destroy_notify_event_t*)event);
break;
case XCB_EXPOSE:
handle_expose_event(NULL, global_conn, (xcb_expose_event_t*)event);
break;
case XCB_MOTION_NOTIFY:
handle_motion_notify(NULL, global_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, global_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, global_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, global_conn, (xcb_configure_request_event_t*)event);
break;
case XCB_CONFIGURE_NOTIFY:
handle_configure_event(NULL, global_conn, (xcb_configure_notify_event_t*)event);
break;
/* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */
case XCB_MAPPING_NOTIFY:
handle_mapping_notify(NULL, global_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 },
{ 0, UINT_MAX, handle_windowclass_change }
};
#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;
property_handlers[6].atom = A_WM_CLASS;
}
/*
* Requests the property and invokes the appropriate callback.
*
*/
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(global_conn, 0, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, handler->long_len);
propr = xcb_get_property_reply(global_conn, cookie, 0);
}
ret = handler->cb(NULL, global_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 * There was a key press. We compare this key code with our bindings table and pass
* the bound action to parse_command(). * the bound action to parse_command().
@ -271,7 +427,7 @@ int handle_mapping_notify(void *ignored, xcb_connection_t *conn, xcb_mapping_not
* A new window appeared on the screen (=was mapped), so lets manage it. * A new window appeared on the screen (=was mapped), so lets manage it.
* *
*/ */
int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_event_t *event) { int handle_map_request(void *invalid, xcb_connection_t *conn, xcb_map_request_event_t *event) {
xcb_get_window_attributes_cookie_t cookie; xcb_get_window_attributes_cookie_t cookie;
cookie = xcb_get_window_attributes_unchecked(conn, event->window); cookie = xcb_get_window_attributes_unchecked(conn, event->window);
@ -279,7 +435,7 @@ int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_eve
DLOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence); DLOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence);
add_ignore_event(event->sequence); add_ignore_event(event->sequence);
manage_window(prophs, conn, event->window, cookie, false); manage_window(conn, event->window, cookie, false);
return 1; return 1;
} }
@ -836,8 +992,8 @@ 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) { int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event) {
if (event->type == atoms[_NET_WM_STATE]) { if (event->type == A__NET_WM_STATE) {
if (event->format != 32 || event->data.data32[1] != atoms[_NET_WM_STATE_FULLSCREEN]) if (event->format != 32 || event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN)
return 0; return 0;
Client *client = table_get(&by_child, event->window); Client *client = table_get(&by_child, event->window);
@ -888,17 +1044,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 the hints were already in this event, use them, if not, request them */
if (reply != NULL) 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 else
xcb_get_wm_normal_hints_reply(conn, xcb_get_wm_normal_hints_unchecked(conn, client->child), &size_hints, NULL); xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints_unchecked(conn, client->child), &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 // TODO: Minimum size is not yet implemented
DLOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height); DLOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height);
} }
bool changed = false; 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 (size_hints.width_inc > 0 && size_hints.width_inc < 0xFFFF)
if (client->width_increment != size_hints.width_inc) { if (client->width_increment != size_hints.width_inc) {
client->width_increment = size_hints.width_inc; client->width_increment = size_hints.width_inc;
@ -919,10 +1075,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. /* base_width/height are the desired size of the window.
We check if either the program-specified size or the program-specified We check if either the program-specified size or the program-specified
min-size is available */ 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_width = size_hints.base_width;
base_height = size_hints.base_height; 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 */ /* TODO: is this right? icccm says not */
base_width = size_hints.min_width; base_width = size_hints.min_width;
base_height = size_hints.min_height; base_height = size_hints.min_height;
@ -947,7 +1103,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 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_num <= 0) ||
(size_hints.min_aspect_den <= 0)) { (size_hints.min_aspect_den <= 0)) {
return 1; return 1;
@ -996,13 +1152,13 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
DLOG("Received WM_HINTS for unknown client\n"); DLOG("Received WM_HINTS for unknown client\n");
return 1; return 1;
} }
xcb_wm_hints_t hints; xcb_icccm_wm_hints_t hints;
if (reply != NULL) { if (reply != NULL) {
if (!xcb_get_wm_hints_from_reply(&hints, reply)) if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply))
return 1; return 1;
} else { } else {
if (!xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, client->child), &hints, NULL)) if (!xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints_unchecked(conn, client->child), &hints, NULL))
return 1; return 1;
} }
@ -1013,7 +1169,7 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
} }
/* Update the flag on the client directly */ /* Update the flag on the client directly */
client->urgent = (xcb_wm_hints_get_urgency(&hints) != 0); client->urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0);
CLIENT_LOG(client); CLIENT_LOG(client);
LOG("Urgency flag changed to %d\n", client->urgent); LOG("Urgency flag changed to %d\n", client->urgent);
@ -1050,10 +1206,10 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_
xcb_window_t transient_for; xcb_window_t transient_for;
if (reply != NULL) { if (reply != NULL) {
if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply)) if (!xcb_icccm_get_wm_transient_for_from_reply(&transient_for, reply))
return 1; return 1;
} else { } else {
if (!xcb_get_wm_transient_for_reply(conn, xcb_get_wm_transient_for_unchecked(conn, window), if (!xcb_icccm_get_wm_transient_for_reply(conn, xcb_icccm_get_wm_transient_for_unchecked(conn, window),
&transient_for, NULL)) &transient_for, NULL))
return 1; return 1;
} }
@ -1075,7 +1231,7 @@ int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state
xcb_atom_t name, xcb_get_property_reply_t *prop) { xcb_atom_t name, xcb_get_property_reply_t *prop) {
if (prop == NULL) { if (prop == NULL) {
prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, 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) if (prop == NULL)
return 1; return 1;
} }

View File

@ -26,8 +26,6 @@
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_atom.h> #include <xcb/xcb_atom.h>
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_property.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
@ -35,7 +33,6 @@
#include "config.h" #include "config.h"
#include "data.h" #include "data.h"
#include "debug.h"
#include "handlers.h" #include "handlers.h"
#include "click.h" #include "click.h"
#include "i3.h" #include "i3.h"
@ -77,11 +74,6 @@ struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments);
/* This is a list of Stack_Windows, global, for easier/faster access on expose events */ /* This is a list of Stack_Windows, global, for easier/faster access on expose events */
struct stack_wins_head stack_wins = SLIST_HEAD_INITIALIZER(stack_wins); struct stack_wins_head stack_wins = SLIST_HEAD_INITIALIZER(stack_wins);
/* The event handlers need to be global because they are accessed by our custom event handler
in handle_button_press(), needed for graphical resizing */
xcb_event_handlers_t evenths;
xcb_atom_t atoms[NUM_ATOMS];
xcb_window_t root; xcb_window_t root;
int num_screens = 0; int num_screens = 0;
@ -105,7 +97,7 @@ static void xcb_got_event(EV_P_ struct ev_io *w, int revents) {
* *
*/ */
static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) { static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
xcb_flush(evenths.c); xcb_flush(global_conn);
} }
/* /*
@ -116,8 +108,17 @@ static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
static void xcb_check_cb(EV_P_ ev_check *w, int revents) { static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
xcb_generic_event_t *event; xcb_generic_event_t *event;
while ((event = xcb_poll_for_event(evenths.c)) != NULL) { while ((event = xcb_poll_for_event(global_conn)) != NULL) {
xcb_event_handle(&evenths, 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); free(event);
} }
} }
@ -191,14 +192,12 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
int main(int argc, char *argv[], char *env[]) { int main(int argc, char *argv[], char *env[]) {
int i, screens, opt; int screens, opt;
char *override_configpath = NULL; char *override_configpath = NULL;
bool autostart = true; bool autostart = true;
bool only_check_config = false; bool only_check_config = false;
bool force_xinerama = false; bool force_xinerama = false;
xcb_connection_t *conn; xcb_connection_t *conn;
xcb_property_handlers_t prophs;
xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
static struct option long_options[] = { static struct option long_options[] = {
{"no-autostart", no_argument, 0, 'a'}, {"no-autostart", no_argument, 0, 'a'},
{"config", required_argument, 0, 'c'}, {"config", required_argument, 0, 'c'},
@ -275,9 +274,6 @@ int main(int argc, char *argv[], char *env[]) {
/* Initialize the table data structures for each workspace */ /* Initialize the table data structures for each workspace */
init_table(); init_table();
memset(&evenths, 0, sizeof(xcb_event_handlers_t));
memset(&prophs, 0, sizeof(xcb_property_handlers_t));
conn = global_conn = xcb_connect(NULL, &screens); conn = global_conn = xcb_connect(NULL, &screens);
if (xcb_connection_has_error(conn)) if (xcb_connection_has_error(conn))
@ -303,30 +299,10 @@ int main(int argc, char *argv[], char *env[]) {
expand_table_rows(TAILQ_FIRST(workspaces)); expand_table_rows(TAILQ_FIRST(workspaces));
/* Place requests for the atoms we need as soon as possible */ /* 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); #define xmacro(atom) \
xcb_intern_atom_cookie_t atom ## _cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
REQUEST_ATOM(_NET_SUPPORTED); #include "atoms.xmacro"
REQUEST_ATOM(_NET_WM_STATE_FULLSCREEN); #undef xmacro
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);
/* TODO: this has to be more beautiful somewhen */ /* TODO: this has to be more beautiful somewhen */
int major, minor, error; int major, minor, error;
@ -392,15 +368,7 @@ int main(int argc, char *argv[], char *env[]) {
/* Grab the server to delay any events until we enter the eventloop */ /* Grab the server to delay any events until we enter the eventloop */
xcb_grab_server(conn); xcb_grab_server(conn);
xcb_event_handlers_init(conn, &evenths); #if 0
/* DEBUG: Trap all events and print them */
for (i = 2; i < 128; ++i)
xcb_event_set_handler(&evenths, i, handle_event, 0);
for (i = 0; i < 256; ++i)
xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0);
/* Expose = an Application should redraw itself, in this case its our titlebars. */ /* Expose = an Application should redraw itself, in this case its our titlebars. */
xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL); xcb_event_set_expose_handler(&evenths, handle_expose_event, NULL);
@ -440,12 +408,7 @@ int main(int argc, char *argv[], char *env[]) {
/* Client message are sent to the root window. The only interesting client message /* 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 */ for us is _NET_WM_STATE, we honour _NET_WM_STATE_FULLSCREEN */
xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL); xcb_event_set_client_message_handler(&evenths, handle_client_message, NULL);
#endif
/* Initialize the property handlers */
xcb_property_handlers_init(&prophs, &evenths);
/* Watch size hints (to obey correct aspect ratio) */
xcb_property_set_handler(&prophs, WM_NORMAL_HINTS, UINT_MAX, handle_normal_hints, NULL);
/* set event mask */ /* set event mask */
uint32_t mask = XCB_CW_EVENT_MASK; uint32_t mask = XCB_CW_EVENT_MASK;
@ -461,66 +424,34 @@ int main(int argc, char *argv[], char *env[]) {
check_error(conn, cookie, "Another window manager seems to be running"); check_error(conn, cookie, "Another window manager seems to be running");
/* Setup NetWM atoms */ /* Setup NetWM atoms */
#define GET_ATOM(name) { \ #define xmacro(name) \
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, atom_cookies[name], NULL); \ do { \
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name ## _cookie, NULL); \
if (!reply) { \ if (!reply) { \
ELOG("Could not get atom " #name "\n"); \ ELOG("Could not get atom " #name "\n"); \
exit(-1); \ exit(-1); \
} \ } \
atoms[name] = reply->atom; \ A_ ## name = reply->atom; \
free(reply); \ free(reply); \
} } while (0);
#include "atoms.xmacro"
#undef xmacro
GET_ATOM(_NET_SUPPORTED); property_handlers_init();
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);
xcb_property_set_handler(&prophs, atoms[_NET_WM_WINDOW_TYPE], UINT_MAX, handle_window_type, NULL);
/* TODO: In order to comply with EWMH, we have to watch _NET_WM_STRUT_PARTIAL */
/* Watch _NET_WM_NAME (= title of the window in UTF-8) property */
xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_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);
/* Watch WM_NAME (= title of the window in compound text) property for legacy applications */
xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL);
/* Watch WM_CLASS (= class of the window) */
xcb_property_set_handler(&prophs, WM_CLASS, 128, handle_windowclass_change, 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_HINTS (contains the urgent property) */
xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL);
/* Set up the atoms we support */ /* Set up the atoms we support */
check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], xcb_atom_t supported_atoms[] = {
ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED"); #define xmacro(atom) A_ ## atom,
#include "atoms.xmacro"
#undef xmacro
};
/* Set up the atoms we support */
check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED,
A_ATOM, 32, 7, supported_atoms), "Could not set _NET_SUPPORTED");
/* Set up the window managers name */ /* Set up the window managers 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, A__NET_SUPPORTING_WM_CHECK, A_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_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
keysyms = xcb_key_symbols_alloc(conn); keysyms = xcb_key_symbols_alloc(conn);
@ -529,18 +460,11 @@ int main(int argc, char *argv[], char *env[]) {
translate_keysyms(); translate_keysyms();
grab_all_keys(conn, false); grab_all_keys(conn, false);
int randr_base = -1;
if (force_xinerama) { if (force_xinerama) {
initialize_xinerama(conn); initialize_xinerama(conn);
} else { } else {
DLOG("Checking for XRandR...\n"); DLOG("Checking for XRandR...\n");
initialize_randr(conn, &randr_base); initialize_randr(conn, &randr_base);
if (randr_base != -1)
xcb_event_set_handler(&evenths,
randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY,
handle_screen_change,
NULL);
} }
xcb_flush(conn); xcb_flush(conn);
@ -562,7 +486,7 @@ int main(int argc, char *argv[], char *env[]) {
DLOG("Starting on %p\n", screen->current_workspace); DLOG("Starting on %p\n", screen->current_workspace);
c_ws = screen->current_workspace; c_ws = screen->current_workspace;
manage_existing_windows(conn, &prophs, root); manage_existing_windows(conn, root);
/* Create the UNIX domain socket for IPC */ /* Create the UNIX domain socket for IPC */
if (config.ipc_socket_path != NULL) { if (config.ipc_socket_path != NULL) {

View File

@ -37,7 +37,7 @@
* Go through all existing windows (if the window manager is restarted) and manage them * Go through all existing windows (if the window manager is restarted) and manage them
* *
*/ */
void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *prophs, xcb_window_t root) { void manage_existing_windows(xcb_connection_t *conn, xcb_window_t root) {
xcb_query_tree_reply_t *reply; xcb_query_tree_reply_t *reply;
int i, len; int i, len;
xcb_window_t *children; xcb_window_t *children;
@ -57,7 +57,7 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *pr
/* Call manage_window with the attributes for every window */ /* Call manage_window with the attributes for every window */
for (i = 0; i < len; ++i) for (i = 0; i < len; ++i)
manage_window(prophs, conn, children[i], cookies[i], true); manage_window(conn, children[i], cookies[i], true);
free(reply); free(reply);
free(cookies); free(cookies);
@ -89,7 +89,7 @@ void restore_geometry(xcb_connection_t *conn) {
* Do some sanity checks and then reparent the window. * Do some sanity checks and then reparent the window.
* *
*/ */
void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn, void manage_window(xcb_connection_t *conn,
xcb_window_t window, xcb_get_window_attributes_cookie_t cookie, xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
bool needs_to_be_mapped) { bool needs_to_be_mapped) {
xcb_drawable_t d = { window }; xcb_drawable_t d = { window };
@ -127,13 +127,13 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
geom->border_width); geom->border_width);
/* Generate callback events for every property we watch */ /* Generate callback events for every property we watch */
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS); property_notify(XCB_PROPERTY_NEW_VALUE, window, A_WM_CLASS);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME); property_notify(XCB_PROPERTY_NEW_VALUE, window, A_WM_NAME);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS); property_notify(XCB_PROPERTY_NEW_VALUE, window, A_WM_NORMAL_HINTS);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_HINTS); property_notify(XCB_PROPERTY_NEW_VALUE, window, A_WM_HINTS);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR); property_notify(XCB_PROPERTY_NEW_VALUE, window, A_WM_TRANSIENT_FOR);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]); property_notify(XCB_PROPERTY_NEW_VALUE, window, A_WM_CLIENT_LEADER);
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]); property_notify(XCB_PROPERTY_NEW_VALUE, window, A__NET_WM_NAME);
free(geom); free(geom);
out: out:
@ -167,13 +167,13 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
xcb_change_window_attributes(conn, child, mask, values); xcb_change_window_attributes(conn, child, mask, values);
/* Place requests for properties ASAP */ /* Place requests for properties ASAP */
wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX); wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, A__NET_WM_WINDOW_TYPE, UINT32_MAX);
strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX); strut_cookie = xcb_get_any_property_unchecked(conn, false, child, A__NET_WM_STRUT_PARTIAL, UINT32_MAX);
state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX); state_cookie = xcb_get_any_property_unchecked(conn, false, child, A__NET_WM_STATE, UINT32_MAX);
utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128); utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, A__NET_WM_NAME, 128);
leader_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[WM_CLIENT_LEADER], UINT32_MAX); leader_cookie = xcb_get_any_property_unchecked(conn, false, child, A_WM_CLIENT_LEADER, UINT32_MAX);
title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128); title_cookie = xcb_get_any_property_unchecked(conn, false, child, A_WM_NAME, 128);
class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128); class_cookie = xcb_get_any_property_unchecked(conn, false, child, A_WM_CLASS, 128);
Client *new = table_get(&by_child, child); Client *new = table_get(&by_child, child);
@ -270,7 +270,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL); xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) { if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
for (int i = 0; i < xcb_get_property_value_length(preply); i++) for (int i = 0; i < xcb_get_property_value_length(preply); i++)
if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) { if (atom[i] == A__NET_WM_WINDOW_TYPE_DOCK) {
DLOG("Window is a dock.\n"); DLOG("Window is a dock.\n");
Output *t_out = get_output_containing(x, y); Output *t_out = get_output_containing(x, y);
if (t_out == NULL) if (t_out == NULL)
@ -289,10 +289,10 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
/* If its a dock we cant make it float, so we break */ /* If its a dock we cant make it float, so we break */
new->floating = FLOATING_AUTO_OFF; new->floating = FLOATING_AUTO_OFF;
break; break;
} else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] || } else if (atom[i] == A__NET_WM_WINDOW_TYPE_DIALOG ||
atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] || atom[i] == A__NET_WM_WINDOW_TYPE_UTILITY ||
atom[i] == atoms[_NET_WM_WINDOW_TYPE_TOOLBAR] || atom[i] == A__NET_WM_WINDOW_TYPE_TOOLBAR ||
atom[i] == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) { atom[i] == A__NET_WM_WINDOW_TYPE_SPLASH) {
/* Set the dialog window to automatically floating, will be used below */ /* Set the dialog window to automatically floating, will be used below */
new->floating = FLOATING_AUTO_ON; new->floating = FLOATING_AUTO_ON;
DLOG("dialog/utility/toolbar/splash window, automatically floating\n"); DLOG("dialog/utility/toolbar/splash window, automatically floating\n");
@ -337,16 +337,16 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
* changes. It is important that the client was already inserted into the by_child table, * changes. It is important that the client was already inserted into the by_child table,
* because the callbacks wont work otherwise. */ * because the callbacks wont work otherwise. */
preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL); preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL);
handle_windowname_change(NULL, conn, 0, new->child, atoms[_NET_WM_NAME], preply); handle_windowname_change(NULL, conn, 0, new->child, A__NET_WM_NAME, preply);
preply = xcb_get_property_reply(conn, title_cookie, NULL); preply = xcb_get_property_reply(conn, title_cookie, NULL);
handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply); handle_windowname_change_legacy(NULL, conn, 0, new->child, A_WM_NAME, preply);
preply = xcb_get_property_reply(conn, class_cookie, NULL); preply = xcb_get_property_reply(conn, class_cookie, NULL);
handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply); handle_windowclass_change(NULL, conn, 0, new->child, A_WM_CLASS, preply);
preply = xcb_get_property_reply(conn, leader_cookie, NULL); preply = xcb_get_property_reply(conn, leader_cookie, NULL);
handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply); handle_clientleader_change(NULL, conn, 0, new->child, A_WM_CLIENT_LEADER, preply);
/* if WM_CLIENT_LEADER is set, we put the new window on the /* if WM_CLIENT_LEADER is set, we put the new window on the
* same window as its leader. This might be overwritten by * same window as its leader. This might be overwritten by
@ -485,7 +485,7 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
(state = xcb_get_property_value(preply)) != NULL) (state = xcb_get_property_value(preply)) != NULL)
/* Check all set _NET_WM_STATEs */ /* Check all set _NET_WM_STATEs */
for (int i = 0; i < xcb_get_property_value_length(preply); i++) { for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN]) if (state[i] != A__NET_WM_STATE_FULLSCREEN)
continue; continue;
/* If the window got the fullscreen state, we just toggle fullscreen /* If the window got the fullscreen state, we just toggle fullscreen
and dont event bother to redraw the layout that would not change and dont event bother to redraw the layout that would not change

View File

@ -15,7 +15,6 @@
#include <assert.h> #include <assert.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include "i3.h" #include "i3.h"
#include "data.h" #include "data.h"

View File

@ -21,7 +21,6 @@
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <X11/keysym.h> #include <X11/keysym.h>

View File

@ -233,9 +233,9 @@ void take_focus(xcb_connection_t *conn, Client *client) {
ev.response_type = XCB_CLIENT_MESSAGE; ev.response_type = XCB_CLIENT_MESSAGE;
ev.window = client->child; ev.window = client->child;
ev.type = atoms[WM_PROTOCOLS]; ev.type = A_WM_PROTOCOLS;
ev.format = 32; 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; ev.data.data32[1] = XCB_CURRENT_TIME;
DLOG("Sending WM_TAKE_FOCUS to the client\n"); DLOG("Sending WM_TAKE_FOCUS to the client\n");