X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fhandlers.c;h=532ab18966f3fc25b66a8ccc4a94b4e68fd069aa;hb=b1974a469f2dec2af753ea4735f1b1c5d37161da;hp=312372a7f08bea0af817eb92d203b19efa130cea;hpb=cd0cd0c3d22d7acba240ca10d8594621cc67ee34;p=i3%2Fi3 diff --git a/src/handlers.c b/src/handlers.c index 312372a7..532ab189 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -4,7 +4,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * * handlers.c: Small handlers for various events (keypresses, focus changes, * …). @@ -21,6 +21,8 @@ #include int randr_base = -1; +int xkb_base = -1; +int xkb_current_group; /* After mapping/unmapping windows, a notify event is generated. However, we don’t want it, since it’d trigger an infinite loop of switching between the different windows when @@ -58,7 +60,8 @@ bool event_is_ignored(const int sequence, const int response_type) { event = SLIST_NEXT(event, ignore_events); SLIST_REMOVE(&ignore_events, save, Ignore_Event, ignore_events); free(save); - } else event = SLIST_NEXT(event, ignore_events); + } else + event = SLIST_NEXT(event, ignore_events); } SLIST_FOREACH(event, &ignore_events, ignore_events) { @@ -103,7 +106,7 @@ static void check_crossing_screen_boundary(uint32_t x, uint32_t y) { return; } - /* Focus the output on which the user moved his cursor */ + /* Focus the output on which the user moved their cursor */ Con *old_focused = focused; Con *next = con_descend_focused(output_get_content(output->con)); /* Since we are switching outputs, this *must* be a different workspace, so @@ -146,7 +149,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { enter_child = true; } - /* If not, then the user moved his cursor to the root window. In that case, we adjust c_ws */ + /* If not, then the user moved their cursor to the root window. In that case, we adjust c_ws */ if (con == NULL) { DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y); check_crossing_screen_boundary(event->root_x, event->root_y); @@ -163,11 +166,11 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { if (layout == L_DEFAULT) { Con *child; TAILQ_FOREACH(child, &(con->nodes_head), nodes) - if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { - LOG("using child %p / %s instead!\n", child, child->name); - con = child; - break; - } + if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { + LOG("using child %p / %s instead!\n", child, child->name); + con = child; + break; + } } #if 0 @@ -183,6 +186,10 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { if (config.disable_focus_follows_mouse) return; + /* if this container is already focused, there is nothing to do. */ + if (con == focused) + return; + /* Get the currently focused workspace to check if the focus change also * involves changing workspaces. If so, we need to call workspace_show() to * correctly update state and send the IPC event. */ @@ -204,7 +211,6 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { * */ static void handle_motion_notify(xcb_motion_notify_event_t *event) { - last_timestamp = event->time; /* Skip events where the pointer was over a child window, we are only @@ -278,7 +284,6 @@ static void handle_map_request(xcb_map_request_event_t *event) { add_ignore_event(event->sequence, -1); manage_window(event->window, cookie, false); - x_push_changes(croot); return; } @@ -294,7 +299,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { Con *con; DLOG("window 0x%08x wants to be at %dx%d with %dx%d\n", - event->window, event->x, event->y, event->width, event->height); + event->window, event->x, event->y, event->width, event->height); /* For unmanaged windows, we just execute the configure request. As soon as * it gets mapped, we will take over anyways. */ @@ -304,12 +309,13 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { uint32_t mask = 0; uint32_t values[7]; int c = 0; -#define COPY_MASK_MEMBER(mask_member, event_member) do { \ - if (event->value_mask & mask_member) { \ - mask |= mask_member; \ - values[c++] = event->event_member; \ - } \ -} while (0) +#define COPY_MASK_MEMBER(mask_member, event_member) \ + do { \ + if (event->value_mask & mask_member) { \ + mask |= mask_member; \ + values[c++] = event->event_member; \ + } \ + } while (0) COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_X, x); COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_Y, y); @@ -482,7 +488,6 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) { tree_close(con, DONT_KILL_WINDOW, false, false); tree_render(); - x_push_changes(croot); ignore_end: /* If the client (as opposed to i3) destroyed or unmapped a window, an @@ -528,20 +533,38 @@ static void handle_destroy_notify_event(xcb_destroy_notify_event_t *event) { handle_unmap_notify_event(&unmap); } +static bool window_name_changed(i3Window *window, char *old_name) { + if ((old_name == NULL) && (window->name == NULL)) + return false; + + /* Either the old or the new one is NULL, but not both. */ + if ((old_name == NULL) ^ (window->name == NULL)) + return true; + + return (strcmp(old_name, i3string_as_utf8(window->name)) != 0); +} + /* * Called when a window changes its title * */ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; + char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); + window_update_name(con->window, prop, false); x_push_changes(croot); + if (window_name_changed(con->window, old_name)) + ipc_send_window_event("title", con); + + FREE(old_name); + return true; } @@ -551,15 +574,22 @@ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t * */ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; + char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); + window_update_name_legacy(con->window, prop, false); x_push_changes(croot); + if (window_name_changed(con->window, old_name)) + ipc_send_window_event("title", con); + + FREE(old_name); + return true; } @@ -621,6 +651,19 @@ static void handle_expose_event(xcb_expose_event_t *event) { return; } +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ +#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ + /* * Handle client messages (EWMH) * @@ -628,7 +671,7 @@ static void handle_expose_event(xcb_expose_event_t *event) { static void handle_client_message(xcb_client_message_event_t *event) { /* If this is a startup notification ClientMessage, the library will handle * it and call our monitor_event() callback. */ - if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t*)event)) + if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t *)event)) return; LOG("ClientMessage for window 0x%08x\n", event->window); @@ -669,7 +712,11 @@ static void handle_client_message(xcb_client_message_event_t *event) { tree_render(); } else if (event->type == A__NET_ACTIVE_WINDOW) { + if (event->format != 32) + return; + DLOG("_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window); + Con *con = con_by_window_id(event->window); if (con == NULL) { DLOG("Could not get window for client message\n"); @@ -677,8 +724,9 @@ static void handle_client_message(xcb_client_message_event_t *event) { } Con *ws = con_get_workspace(con); - if (!workspace_is_visible(ws)) { - DLOG("Workspace not visible, ignoring _NET_ACTIVE_WINDOW\n"); + + if (ws == NULL) { + DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n"); return; } @@ -687,10 +735,27 @@ static void handle_client_message(xcb_client_message_event_t *event) { return; } - if (ws != con_get_workspace(focused)) + /* data32[0] indicates the source of the request (application or pager) */ + if (event->data.data32[0] == 2) { + /* Always focus the con if it is from a pager, because this is most + * likely from some user action */ + DLOG("This request came from a pager. Focusing con = %p\n", con); workspace_show(ws); + con_focus(con); + } else { + /* Request is from an application. */ + + if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) { + DLOG("Focusing con = %p\n", con); + workspace_show(ws); + con_focus(con); + } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) { + DLOG("Marking con = %p urgent\n", con); + con_set_urgency(con, true); + } else + DLOG("Ignoring request for con = %p", con); + } - con_focus(con); tree_render(); } else if (event->type == A_I3_SYNC) { xcb_window_t window = event->data.data32[0]; @@ -707,7 +772,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { ev->data.data32[0] = window; ev->data.data32[1] = rnd; - xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)ev); + xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev); xcb_flush(conn); free(reply); } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) { @@ -729,17 +794,98 @@ static void handle_client_message(xcb_client_message_event_t *event) { Rect r = { config.default_border_width, /* left */ config.default_border_width, /* right */ - config.font.height + 5, /* top */ - config.default_border_width /* bottom */ + config.font.height + 5, /* top */ + config.default_border_width /* bottom */ }; xcb_change_property( - conn, - XCB_PROP_MODE_REPLACE, - event->window, - A__NET_FRAME_EXTENTS, - XCB_ATOM_CARDINAL, 32, 4, - &r); + conn, + XCB_PROP_MODE_REPLACE, + event->window, + A__NET_FRAME_EXTENTS, + XCB_ATOM_CARDINAL, 32, 4, + &r); xcb_flush(conn); + } else if (event->type == A__NET_CURRENT_DESKTOP) { + /* This request is used by pagers and bars to change the current + * desktop likely as a result of some user action. We interpret this as + * a request to focus the given workspace. See + * http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008 + * */ + Con *output; + uint32_t idx = 0; + DLOG("Request to change current desktop to index %d\n", event->data.data32[0]); + + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + + if (idx == event->data.data32[0]) { + /* data32[1] is a timestamp used to prevent focus race conditions */ + if (event->data.data32[1]) + last_timestamp = event->data.data32[1]; + + DLOG("Handling request to focus workspace %s\n", ws->name); + + workspace_show(ws); + tree_render(); + + return; + } + + ++idx; + } + } + } else if (event->type == A__NET_CLOSE_WINDOW) { + /* + * Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW + * client message request to the root window. + * http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896 + */ + Con *con = con_by_window_id(event->window); + if (con) { + DLOG("Handling _NET_CLOSE_WINDOW request (con = %p)\n", con); + + if (event->data.data32[0]) + last_timestamp = event->data.data32[0]; + + tree_close(con, KILL_WINDOW, false, false); + tree_render(); + } else { + DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window); + } + } else if (event->type == A__NET_WM_MOVERESIZE) { + /* + * Client-side decorated Gtk3 windows emit this signal when being + * dragged by their GtkHeaderBar + */ + Con *con = con_by_window_id(event->window); + if (!con || !con_is_floating(con)) { + DLOG("Couldn't find con for _NET_WM_MOVERESIZE request, or con not floating (window = %d)\n", event->window); + return; + } + DLOG("Handling _NET_WM_MOVERESIZE request (con = %p)\n", con); + uint32_t direction = event->data.data32[2]; + uint32_t x_root = event->data.data32[0]; + uint32_t y_root = event->data.data32[1]; + /* construct fake xcb_button_press_event_t */ + xcb_button_press_event_t fake = { + .root_x = x_root, + .root_y = y_root, + .event_x = x_root - (con->rect.x), + .event_y = y_root - (con->rect.y)}; + switch (direction) { + case _NET_WM_MOVERESIZE_MOVE: + floating_drag_window(con->parent, &fake); + break; + case _NET_WM_MOVERESIZE_SIZE_TOPLEFT... _NET_WM_MOVERESIZE_SIZE_LEFT: + floating_resize_window(con->parent, false, &fake); + break; + default: + DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction); + break; + } } else { DLOG("unhandled clientmessage\n"); return; @@ -764,7 +910,7 @@ int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_wi * */ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { + xcb_atom_t name, xcb_get_property_reply_t *reply) { Con *con = con_by_window_id(window); if (con == NULL) { DLOG("Received WM_NORMAL_HINTS for unknown client\n"); @@ -773,7 +919,7 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat xcb_size_hints_t size_hints; - //CLIENT_LOG(client); + //CLIENT_LOG(client); /* If the hints were already in this event, use them, if not, request them */ if (reply != NULL) @@ -853,7 +999,8 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat aspect_ratio = min_aspect; } else if ((width / height) > max_aspect) { aspect_ratio = max_aspect; - } else goto render_and_return; + } else + goto render_and_return; if (fabs(con->aspect_ratio - aspect_ratio) > DBL_EPSILON) { con->aspect_ratio = aspect_ratio; @@ -872,7 +1019,7 @@ render_and_return: * */ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *reply) { + xcb_atom_t name, xcb_get_property_reply_t *reply) { Con *con = con_by_window_id(window); if (con == NULL) { DLOG("Received WM_HINTS for unknown client\n"); @@ -897,7 +1044,7 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_ * */ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { + xcb_atom_t name, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { @@ -907,7 +1054,8 @@ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t sta if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, - false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), NULL); + false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), + NULL); if (prop == NULL) return false; } @@ -923,14 +1071,15 @@ static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t sta * */ static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, - xcb_atom_t name, xcb_get_property_reply_t *prop) { + xcb_atom_t name, xcb_get_property_reply_t *prop) { Con *con; if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; if (prop == NULL) { prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, - false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), NULL); + false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), + NULL); if (prop == NULL) return false; } @@ -991,6 +1140,111 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } +/* + * Handles the WM_CLASS property for assignments and criteria selection. + * + */ +static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, + xcb_atom_t name, xcb_get_property_reply_t *prop) { + Con *con; + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) + return false; + + if (prop == NULL) { + prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, + false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32), + NULL); + + if (prop == NULL) + return false; + } + + window_update_class(con->window, prop, false); + + return true; +} + +/* + * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients. + * + */ +static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, + xcb_atom_t name, xcb_get_property_reply_t *prop) { + DLOG("strut partial change for window 0x%08x\n", window); + + Con *con; + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) { + return false; + } + + if (prop == NULL) { + xcb_generic_error_t *err = NULL; + xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL, + XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX); + prop = xcb_get_property_reply(conn, strut_cookie, &err); + + if (err != NULL) { + DLOG("got error when getting strut partial property: %d\n", err->error_code); + free(err); + return false; + } + + if (prop == NULL) { + return false; + } + } + + DLOG("That is con %p / %s\n", con, con->name); + + window_update_strut_partial(con->window, prop); + + /* we only handle this change for dock clients */ + if (con->parent == NULL || con->parent->type != CT_DOCKAREA) { + return true; + } + + Con *search_at = croot; + Con *output = con_get_output(con); + if (output != NULL) { + DLOG("Starting search at output %s\n", output->name); + search_at = output; + } + + /* find out the desired position of this dock window */ + if (con->window->reserved.top > 0 && con->window->reserved.bottom == 0) { + DLOG("Top dock client\n"); + con->window->dock = W_DOCK_TOP; + } else if (con->window->reserved.top == 0 && con->window->reserved.bottom > 0) { + DLOG("Bottom dock client\n"); + con->window->dock = W_DOCK_BOTTOM; + } else { + DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n"); + if (con->geometry.y < (search_at->rect.height / 2)) { + DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n", + con->geometry.y, (search_at->rect.height / 2)); + con->window->dock = W_DOCK_TOP; + } else { + DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n", + con->geometry.y, (search_at->rect.height / 2)); + con->window->dock = W_DOCK_BOTTOM; + } + } + + /* find the dockarea */ + Con *dockarea = con_for_window(search_at, con->window, NULL); + assert(dockarea != NULL); + + /* attach the dock to the dock area */ + con_detach(con); + con->parent = dockarea; + TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused); + TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes); + + tree_render(); + + return true; +} + /* Returns false if the event could not be processed (e.g. the window could not * be found), true otherwise */ typedef bool (*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); @@ -1002,14 +1256,15 @@ struct property_handler_t { }; 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, 128, handle_windowrole_change } -}; + {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, 128, handle_windowrole_change}, + {0, 128, handle_class_change}, + {0, UINT_MAX, handle_strut_partial_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -1018,7 +1273,6 @@ static struct property_handler_t property_handlers[] = { * */ void property_handlers_init(void) { - sn_monitor_context_new(sndisplay, conn_screen, startup_monitor_event, NULL, NULL); property_handlers[0].atom = A__NET_WM_NAME; @@ -1028,6 +1282,8 @@ void property_handlers_init(void) { property_handlers[4].atom = A_WM_CLIENT_LEADER; property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR; property_handlers[6].atom = A_WM_WINDOW_ROLE; + property_handlers[7].atom = XCB_ATOM_WM_CLASS; + property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { @@ -1063,70 +1319,118 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) * */ void handle_event(int type, xcb_generic_event_t *event) { + DLOG("event type %d, xkb_base %d\n", type, xkb_base); if (randr_base > -1 && type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { handle_screen_change(event); return; } + if (xkb_base > -1 && type == xkb_base) { + DLOG("xkb event, need to handle it.\n"); + + xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event; + if (state->xkbType == XCB_XKB_NEW_KEYBOARD_NOTIFY) { + DLOG("xkb new keyboard notify, sequence %d, time %d\n", state->sequence, state->time); + xcb_key_symbols_free(keysyms); + keysyms = xcb_key_symbols_alloc(conn); + ungrab_all_keys(conn); + translate_keysyms(); + grab_all_keys(conn, false); + } else 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); + xcb_key_symbols_free(keysyms); + keysyms = xcb_key_symbols_alloc(conn); + 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 XCB_XKB_GROUP_2 */ + 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, true); + } + } + + return; + } + switch (type) { case XCB_KEY_PRESS: case XCB_KEY_RELEASE: - handle_key_press((xcb_key_press_event_t*)event); + handle_key_press((xcb_key_press_event_t *)event); break; case XCB_BUTTON_PRESS: - handle_button_press((xcb_button_press_event_t*)event); + case XCB_BUTTON_RELEASE: + handle_button_press((xcb_button_press_event_t *)event); break; case XCB_MAP_REQUEST: - handle_map_request((xcb_map_request_event_t*)event); + handle_map_request((xcb_map_request_event_t *)event); break; case XCB_UNMAP_NOTIFY: - handle_unmap_notify_event((xcb_unmap_notify_event_t*)event); + handle_unmap_notify_event((xcb_unmap_notify_event_t *)event); break; case XCB_DESTROY_NOTIFY: - handle_destroy_notify_event((xcb_destroy_notify_event_t*)event); + handle_destroy_notify_event((xcb_destroy_notify_event_t *)event); break; case XCB_EXPOSE: - handle_expose_event((xcb_expose_event_t*)event); + handle_expose_event((xcb_expose_event_t *)event); break; case XCB_MOTION_NOTIFY: - handle_motion_notify((xcb_motion_notify_event_t*)event); + handle_motion_notify((xcb_motion_notify_event_t *)event); break; - /* Enter window = user moved his mouse over the window */ + /* Enter window = user moved their mouse over the window */ case XCB_ENTER_NOTIFY: - handle_enter_notify((xcb_enter_notify_event_t*)event); + handle_enter_notify((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 and _NET_WM_STATE_DEMANDS_ATTENTION */ case XCB_CLIENT_MESSAGE: - handle_client_message((xcb_client_message_event_t*)event); + handle_client_message((xcb_client_message_event_t *)event); break; /* Configure request = window tried to change size on its own */ case XCB_CONFIGURE_REQUEST: - handle_configure_request((xcb_configure_request_event_t*)event); + handle_configure_request((xcb_configure_request_event_t *)event); break; /* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */ case XCB_MAPPING_NOTIFY: - handle_mapping_notify((xcb_mapping_notify_event_t*)event); + handle_mapping_notify((xcb_mapping_notify_event_t *)event); break; case XCB_FOCUS_IN: - handle_focus_in((xcb_focus_in_event_t*)event); + handle_focus_in((xcb_focus_in_event_t *)event); break; case XCB_PROPERTY_NOTIFY: { - xcb_property_notify_event_t *e = (xcb_property_notify_event_t*)event; + xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)event; last_timestamp = e->time; property_notify(e->state, e->window, e->atom); break;