X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fhandlers.c;h=d1b3a73cd0cb42a3e1e6d5cc3c631a553bd6ff4a;hb=ede954128afca118025db6a04d4d9d259473c70f;hp=6cbc54f230d0dfc2864be17ed6fcf35e45e73461;hpb=bc126aaeafa2c5f18dadd8cb0d7e57329c722273;p=i3%2Fi3 diff --git a/src/handlers.c b/src/handlers.c index 6cbc54f2..d1b3a73c 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "handlers.c" /* * vim:ts=4:sw=4:expandtab * @@ -148,18 +146,15 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { enter_child = true; } - /* If not, then the user moved their cursor to the root window. In that case, we adjust c_ws */ - if (con == NULL) { + /* If we cannot find the container, the user moved their cursor to the root + * window. In this case and if they used it to a dock, we need to focus the + * workspace on the correct output. */ + if (con == NULL || con->parent->type == CT_DOCKAREA) { DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y); check_crossing_screen_boundary(event->root_x, event->root_y); return; } - if (con->parent->type == CT_DOCKAREA) { - DLOG("Ignoring, this is a dock client\n"); - return; - } - /* see if the user entered the window on a certain window decoration */ layout_t layout = (enter_child ? con->parent->layout : con->layout); if (layout == L_DEFAULT) { @@ -172,16 +167,6 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { } } -#if 0 - if (client->workspace != c_ws && client->workspace->output == c_ws->output) { - /* This can happen when a client gets assigned to a different workspace than - * the current one (see src/mainx.c:reparent_window). Shortly after it was created, - * an enter_notify will follow. */ - DLOG("enter_notify for a client on a different workspace but the same screen, ignoring\n"); - return 1; - } -#endif - if (config.disable_focus_follows_mouse) return; @@ -199,8 +184,6 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { focused_id = XCB_NONE; con_focus(con_descend_focused(con)); tree_render(); - - return; } /* @@ -264,8 +247,6 @@ static void handle_mapping_notify(xcb_mapping_notify_event_t *event) { ungrab_all_keys(conn); translate_keysyms(); grab_all_keys(conn); - - return; } /* @@ -281,7 +262,6 @@ static void handle_map_request(xcb_map_request_event_t *event) { add_ignore_event(event->sequence, -1); manage_window(event->window, cookie, false); - return; } /* @@ -330,16 +310,12 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { DLOG("Configure request!\n"); - Con *workspace = con_get_workspace(con), - *fullscreen = NULL; - - /* There might not be a corresponding workspace for dock cons, therefore we - * have to be careful here. */ - if (workspace) { - fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT); - if (!fullscreen) - fullscreen = con_get_fullscreen_con(workspace, CF_GLOBAL); + Con *workspace = con_get_workspace(con); + if (workspace && (strcmp(workspace->name, "__i3_scratch") == 0)) { + DLOG("This is a scratchpad container, ignoring ConfigureRequest\n"); + goto out; } + Con *fullscreen = con_get_fullscreen_covering_ws(workspace); if (fullscreen != con && con_is_floating(con) && con_is_leaf(con)) { /* find the height for the decorations */ @@ -352,12 +328,6 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { bsr.height -= deco_height; } Con *floatingcon = con->parent; - - if (strcmp(con_get_workspace(floatingcon)->name, "__i3_scratch") == 0) { - DLOG("This is a scratchpad container, ignoring ConfigureRequest\n"); - return; - } - Rect newrect = floatingcon->rect; if (event->value_mask & XCB_CONFIG_WINDOW_X) { @@ -403,7 +373,7 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { Con *current_output = con_get_output(con); Output *target = get_output_containing(x, y); if (target != NULL && current_output != target->con) { - DLOG("Dock client is requested to be moved to output %s, moving it there.\n", target->name); + DLOG("Dock client is requested to be moved to output %s, moving it there.\n", output_primary_name(target)); Match *match; Con *nc = con_for_window(target->con, con->window, &match); DLOG("Dock client will be moved to container %p.\n", nc); @@ -415,28 +385,46 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { DLOG("Dock client will not be moved, we only support moving it to another output.\n"); } } + goto out; } - fake_absolute_configure_notify(con); + if (event->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { + DLOG("window 0x%08x wants to be stacked %d\n", event->window, event->stack_mode); - return; -} -#if 0 + /* Emacs and IntelliJ Idea “request focus” by stacking their window + * above all others. */ + if (event->stack_mode != XCB_STACK_MODE_ABOVE) { + DLOG("stack_mode != XCB_STACK_MODE_ABOVE, ignoring ConfigureRequest\n"); + goto out; + } -/* - * Configuration notifies are only handled because we need to set up ignore for - * the following enter notify events. - * - */ -int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event) { - DLOG("configure_event, sequence %d\n", event->sequence); - /* We ignore this sequence twice because events for child and frame should be ignored */ - add_ignore_event(event->sequence); - add_ignore_event(event->sequence); + if (fullscreen || !con_is_leaf(con)) { + DLOG("fullscreen or not a leaf, ignoring ConfigureRequest\n"); + goto out; + } + + if (workspace == NULL) { + DLOG("Window is not being managed, ignoring ConfigureRequest\n"); + goto out; + } + + if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(workspace))) { + DLOG("Focusing con = %p\n", con); + workspace_show(workspace); + con_activate(con); + tree_render(); + } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(workspace))) { + DLOG("Marking con = %p urgent\n", con); + con_set_urgency(con, true); + tree_render(); + } else { + DLOG("Ignoring request for con = %p.\n", con); + } + } - return 1; +out: + fake_absolute_configure_notify(con); } -#endif /* * Gets triggered upon a RandR screen change event, that is when the user @@ -464,8 +452,6 @@ static void handle_screen_change(xcb_generic_event_t *e) { scratchpad_fix_resolution(); ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}"); - - return; } /* @@ -503,7 +489,12 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) { goto ignore_end; } - tree_close_internal(con, DONT_KILL_WINDOW, false, false); + /* Since we close the container, we need to unset _NET_WM_DESKTOP and + * _NET_WM_STATE according to the spec. */ + xcb_delete_property(conn, event->window, A__NET_WM_DESKTOP); + xcb_delete_property(conn, event->window, A__NET_WM_STATE); + + tree_close_internal(con, DONT_KILL_WINDOW, false); tree_render(); ignore_end: @@ -625,23 +616,6 @@ static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t return true; } -#if 0 -/* - * Updates the client’s WM_CLASS property - * - */ -static int handle_windowclass_change(void *data, xcb_connection_t *conn, uint8_t state, - 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 1; - - window_update_class(con->window, prop, false); - - return 0; -} -#endif - /* * Expose event means we should redraw our windows (= title bar) * @@ -657,14 +631,10 @@ static void handle_expose_event(xcb_expose_event_t *event) { } /* Since we render to our surface on every change anyways, expose events - * only tell us that the X server lost (parts of) the window contents. We - * can handle that by copying the appropriate part from our surface to the - * window. */ - draw_util_copy_surface(conn, &(parent->frame_buffer), &(parent->frame), - event->x, event->y, event->x, event->y, - event->width, event->height); + * only tell us that the X server lost (parts of) the window contents. */ + draw_util_copy_surface(&(parent->frame_buffer), &(parent->frame), + 0, 0, 0, 0, parent->rect.width, parent->rect.height); xcb_flush(conn); - return; } #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 @@ -680,6 +650,11 @@ static void handle_expose_event(xcb_expose_event_t *event) { #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ #define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ +#define _NET_MOVERESIZE_WINDOW_X (1 << 8) +#define _NET_MOVERESIZE_WINDOW_Y (1 << 9) +#define _NET_MOVERESIZE_WINDOW_WIDTH (1 << 10) +#define _NET_MOVERESIZE_WINDOW_HEIGHT (1 << 11) + /* * Handle client messages (EWMH) * @@ -735,7 +710,9 @@ static void handle_client_message(xcb_client_message_event_t *event) { con->sticky = !con->sticky; DLOG("New sticky status for con = %p is %i.\n", con, con->sticky); + ewmh_update_sticky(con->window->id, con->sticky); output_push_sticky_windows(focused); + ewmh_update_wm_desktop(); } tree_render(); @@ -752,14 +729,13 @@ static void handle_client_message(xcb_client_message_event_t *event) { } Con *ws = con_get_workspace(con); - if (ws == NULL) { DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n"); return; } - if (con_is_internal(ws)) { - DLOG("Workspace is internal, ignoring _NET_ACTIVE_WINDOW\n"); + if (con_is_internal(ws) && ws != workspace_get("__i3_scratch", NULL)) { + DLOG("Workspace is internal but not scratchpad, ignoring _NET_ACTIVE_WINDOW\n"); return; } @@ -768,15 +744,26 @@ static void handle_client_message(xcb_client_message_event_t *event) { /* 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); + + if (con_is_internal(ws)) { + scratchpad_show(con); + } else { + workspace_show(ws); + /* Re-set focus, even if unchanged from i3’s perspective. */ + focused_id = XCB_NONE; + con_activate(con); + } } else { /* Request is from an application. */ + if (con_is_internal(ws)) { + DLOG("Ignoring request to make con = %p active because it's on an internal workspace.\n", con); + return; + } 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); + con_activate(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); @@ -788,21 +775,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { } else if (event->type == A_I3_SYNC) { xcb_window_t window = event->data.data32[0]; uint32_t rnd = event->data.data32[1]; - DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window); - - void *reply = scalloc(32, 1); - xcb_client_message_event_t *ev = reply; - - ev->response_type = XCB_CLIENT_MESSAGE; - ev->window = window; - ev->type = A_I3_SYNC; - ev->format = 32; - ev->data.data32[0] = window; - ev->data.data32[1] = rnd; - - xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev); - xcb_flush(conn); - free(reply); + sync_respond(window, rnd); } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) { /* * A client can request an estimate for the frame size which the window @@ -822,7 +795,7 @@ 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 */ + render_deco_height(), /* top */ config.default_border_width /* bottom */ }; xcb_change_property( @@ -837,39 +810,55 @@ static void handle_client_message(xcb_client_message_event_t *event) { /* 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 + * https://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]); + Con *ws = ewmh_get_workspace_by_index(event->data.data32[0]); + if (ws == NULL) { + ELOG("Could not determine workspace for this index, ignoring request.\n"); + return; + } - 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(); + } else if (event->type == A__NET_WM_DESKTOP) { + uint32_t index = event->data.data32[0]; + DLOG("Request to move window %d to EWMH desktop index %d\n", event->window, index); - DLOG("Handling request to focus workspace %s\n", ws->name); + Con *con = con_by_window_id(event->window); + if (con == NULL) { + DLOG("Couldn't find con for window %d, ignoring the request.\n", event->window); + return; + } - workspace_show(ws); - tree_render(); + if (index == NET_WM_DESKTOP_ALL) { + /* The window is requesting to be visible on all workspaces, so + * let's float it and make it sticky. */ + DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n"); - return; - } + floating_enable(con, false); - ++idx; + con->sticky = true; + ewmh_update_sticky(con->window->id, true); + output_push_sticky_windows(focused); + } else { + Con *ws = ewmh_get_workspace_by_index(index); + if (ws == NULL) { + ELOG("Could not determine workspace for this index, ignoring request.\n"); + return; } + + con_move_to_workspace(con, ws, true, false, false); } + + tree_render(); + ewmh_update_wm_desktop(); } 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 + * https://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896 */ Con *con = con_by_window_id(event->window); if (con) { @@ -878,7 +867,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { if (event->data.data32[0]) last_timestamp = event->data.data32[0]; - tree_close_internal(con, KILL_WINDOW, false, false); + tree_close_internal(con, KILL_WINDOW, false); tree_render(); } else { DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window); @@ -914,9 +903,37 @@ static void handle_client_message(xcb_client_message_event_t *event) { DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction); break; } + } else if (event->type == A__NET_MOVERESIZE_WINDOW) { + DLOG("Received _NET_MOVE_RESIZE_WINDOW. Handling by faking a configure request.\n"); + + void *_generated_event = scalloc(32, 1); + xcb_configure_request_event_t *generated_event = _generated_event; + + generated_event->window = event->window; + generated_event->response_type = XCB_CONFIGURE_REQUEST; + + generated_event->value_mask = 0; + if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_X) { + generated_event->value_mask |= XCB_CONFIG_WINDOW_X; + generated_event->x = event->data.data32[1]; + } + if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_Y) { + generated_event->value_mask |= XCB_CONFIG_WINDOW_Y; + generated_event->y = event->data.data32[2]; + } + if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_WIDTH) { + generated_event->value_mask |= XCB_CONFIG_WINDOW_WIDTH; + generated_event->width = event->data.data32[3]; + } + if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_HEIGHT) { + generated_event->value_mask |= XCB_CONFIG_WINDOW_HEIGHT; + generated_event->height = event->data.data32[4]; + } + + handle_configure_request(generated_event); + FREE(generated_event); } else { - DLOG("unhandled clientmessage\n"); - return; + DLOG("Skipping client message for unhandled type %d\n", event->type); } } @@ -947,54 +964,72 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat xcb_size_hints_t size_hints; - //CLIENT_LOG(client); - /* If the hints were already in this event, use them, if not, request them */ - if (reply != NULL) + if (reply != NULL) { xcb_icccm_get_wm_size_hints_from_reply(&size_hints, reply); - else + } else { xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints_unchecked(conn, con->window->id), &size_hints, NULL); + } + + int win_width = con->window_rect.width; + int win_height = con->window_rect.height; 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); + + con->window->min_width = size_hints.min_width; + con->window->min_height = size_hints.min_height; + } + + if (con_is_floating(con)) { + win_width = MAX(win_width, con->window->min_width); + win_height = MAX(win_height, con->window->min_height); } bool changed = false; 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 (con->window->width_increment != size_hints.width_inc) { con->window->width_increment = size_hints.width_inc; changed = true; } - if (size_hints.height_inc > 0 && size_hints.height_inc < 0xFFFF) + } + + if (size_hints.height_inc > 0 && size_hints.height_inc < 0xFFFF) { if (con->window->height_increment != size_hints.height_inc) { con->window->height_increment = size_hints.height_inc; changed = true; } + } - if (changed) + if (changed) { DLOG("resize increments changed\n"); + } } - int base_width = 0, base_height = 0; + bool has_base_size = false; + int base_width = 0; + int base_height = 0; - /* 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 */ + /* The base width / height is the desired size of the window. */ 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_ICCCM_SIZE_HINT_P_MIN_SIZE) { - /* TODO: is this right? icccm says not */ + has_base_size = true; + } + + /* If the window didn't specify a base size, the ICCCM tells us to fall + * back to the minimum size instead, if available. */ + if (!has_base_size && size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) { base_width = size_hints.min_width; base_height = size_hints.min_height; } - if (base_width != con->window->base_width || - base_height != con->window->base_height) { + // TODO XXX Should we only do this is the base size is > 0? + if (base_width != con->window->base_width || base_height != con->window->base_height) { con->window->base_width = base_width; con->window->base_height = base_height; + DLOG("client's base_height changed to %d\n", base_height); DLOG("client's base_width changed to %d\n", base_width); changed = true; @@ -1007,9 +1042,13 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat goto render_and_return; } - /* XXX: do we really use rect here, not window_rect? */ - double width = con->rect.width - base_width; - double height = con->rect.height - base_height; + /* The ICCCM says to subtract the base size from the window size for aspect + * ratio calculations. However, unlike determining the base size itself we + * must not fall back to using the minimum size in this case according to + * the ICCCM. */ + double width = win_width - base_width * has_base_size; + double height = win_height - base_height * has_base_size; + /* Convert numerator/denominator to a double */ double min_aspect = (double)size_hints.min_aspect_num / size_hints.min_aspect_den; double max_aspect = (double)size_hints.max_aspect_num / size_hints.min_aspect_den; @@ -1018,8 +1057,9 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat DLOG("width = %f, height = %f\n", width, height); /* Sanity checks, this is user-input, in a way */ - if (max_aspect <= 0 || min_aspect <= 0 || height == 0 || (width / height) <= 0) + if (max_aspect <= 0 || min_aspect <= 0 || height == 0 || (width / height) <= 0) { goto render_and_return; + } /* Check if we need to set proportional_* variables using the correct ratio */ double aspect_ratio = 0.0; @@ -1027,8 +1067,9 @@ 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 + } else { goto render_and_return; + } if (fabs(con->window->aspect_ratio - aspect_ratio) > DBL_EPSILON) { con->window->aspect_ratio = aspect_ratio; @@ -1036,8 +1077,10 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat } render_and_return: - if (changed) + if (changed) { tree_render(); + } + FREE(reply); return true; } @@ -1081,8 +1124,7 @@ 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), + prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32), NULL); if (prop == NULL) return false; @@ -1105,8 +1147,7 @@ static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8 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), + prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32), NULL); if (prop == NULL) return false; @@ -1125,6 +1166,14 @@ static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8 */ static void handle_focus_in(xcb_focus_in_event_t *event) { DLOG("focus change in, for window 0x%08x\n", event->event); + + if (event->event == root) { + DLOG("Received focus in for root window, refocusing the focused window.\n"); + con_focus(focused); + focused_id = XCB_NONE; + x_push_changes(croot); + } + Con *con; if ((con = con_by_window_id(event->event)) == NULL || con->window == NULL) return; @@ -1141,7 +1190,9 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } - if (focused_id == event->event) { + /* Floating windows should be refocused to ensure that they are on top of + * other windows. */ + if (focused_id == event->event && !con_inside_floating(con)) { DLOG("focus matches the currently focused window, not doing anything\n"); return; } @@ -1152,7 +1203,7 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } - DLOG("focus is different, updating decorations\n"); + DLOG("focus is different / refocusing floating window: updating decorations\n"); /* Get the currently focused workspace to check if the focus change also * involves changing workspaces. If so, we need to call workspace_show() to @@ -1161,11 +1212,28 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { if (ws != con_get_workspace(focused)) workspace_show(ws); - con_focus(con); + con_activate(con); /* We update focused_id because we don’t need to set focus again */ focused_id = event->event; - x_push_changes(croot); - return; + tree_render(); +} + +/* + * Handles ConfigureNotify events for the root window, which are generated when + * the monitor configuration changed. + * + */ +static void handle_configure_notify(xcb_configure_notify_event_t *event) { + if (event->event != root) { + DLOG("ConfigureNotify for non-root window 0x%08x, ignoring\n", event->event); + return; + } + DLOG("ConfigureNotify for root window 0x%08x\n", event->event); + + if (force_xinerama) { + return; + } + randr_query_outputs(); } /* @@ -1179,8 +1247,7 @@ static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t stat 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), + 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) @@ -1192,6 +1259,37 @@ static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t stat return true; } +/* + * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings. + * + */ +static bool handle_motif_hints_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, A__MOTIF_WM_HINTS, XCB_GET_PROPERTY_TYPE_ANY, 0, 5 * sizeof(uint64_t)), + NULL); + + if (prop == NULL) + return false; + } + + border_style_t motif_border_style; + window_update_motif_hints(con->window, prop, &motif_border_style); + + if (motif_border_style != con->border_style && motif_border_style != BS_NORMAL) { + DLOG("Update border style of con %p to %d\n", con, motif_border_style); + con_set_border_style(con, motif_border_style, con->current_border_width); + + x_push_changes(croot); + } + + return true; +} + /* * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients. * @@ -1293,7 +1391,8 @@ static struct property_handler_t property_handlers[] = { {0, 128, handle_windowrole_change}, {0, 128, handle_class_change}, {0, UINT_MAX, handle_strut_partial_change}, - {0, UINT_MAX, handle_window_type}}; + {0, UINT_MAX, handle_window_type}, + {0, 5 * sizeof(uint64_t), handle_motif_hints_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -1314,13 +1413,14 @@ void property_handlers_init(void) { property_handlers[7].atom = XCB_ATOM_WM_CLASS; property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; property_handlers[9].atom = A__NET_WM_WINDOW_TYPE; + property_handlers[10].atom = A__MOTIF_WM_HINTS; } static void 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; - for (size_t c = 0; c < sizeof(property_handlers) / sizeof(struct property_handler_t); c++) { + for (size_t c = 0; c < NUM_HANDLERS; c++) { if (property_handlers[c].atom != atom) continue; @@ -1420,7 +1520,10 @@ void handle_event(int type, xcb_generic_event_t *event) { break; case XCB_EXPOSE: - handle_expose_event((xcb_expose_event_t *)event); + if (((xcb_expose_event_t *)event)->count == 0) { + handle_expose_event((xcb_expose_event_t *)event); + } + break; case XCB_MOTION_NOTIFY: @@ -1460,6 +1563,10 @@ void handle_event(int type, xcb_generic_event_t *event) { break; } + case XCB_CONFIGURE_NOTIFY: + handle_configure_notify((xcb_configure_notify_event_t *)event); + break; + default: //DLOG("Unhandled event of type %d\n", type); break;