X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fx.c;h=02fc338fb12826e655109633a00dd1b6530d1010;hb=524f20b8a02b2b243f92e01c17c1680f12c851c3;hp=fcb63c35bdef19765540800ecb610011ced1941a;hpb=2c249b6949566fe9e2414d86b26bf5d29c7c1a6b;p=i3%2Fi3 diff --git a/src/x.c b/src/x.c index fcb63c35..02fc338f 100644 --- a/src/x.c +++ b/src/x.c @@ -15,10 +15,10 @@ /* Stores the X11 window ID of the currently focused window */ xcb_window_t focused_id = XCB_NONE; -/* The bottom-to-top window stack of all windows which are managed by i3. - * Used for x_get_window_stack(). */ -static xcb_window_t *btt_stack; -static int btt_stack_num; +/* Because 'focused_id' might be reset to force input focus (after click to + * raise), we separately keep track of the X11 window ID to be able to always + * tell whether the focused window actually changed. */ +static xcb_window_t last_focused = XCB_NONE; /* Stores coordinates to warp mouse pointer to if set */ static Rect *warp_to; @@ -55,6 +55,7 @@ typedef struct con_state { CIRCLEQ_ENTRY(con_state) state; CIRCLEQ_ENTRY(con_state) old_state; + TAILQ_ENTRY(con_state) initial_mapping_order; } con_state; CIRCLEQ_HEAD(state_head, con_state) state_head = @@ -63,6 +64,9 @@ CIRCLEQ_HEAD(state_head, con_state) state_head = CIRCLEQ_HEAD(old_state_head, con_state) old_state_head = CIRCLEQ_HEAD_INITIALIZER(old_state_head); +TAILQ_HEAD(initial_mapping_head, con_state) initial_mapping_head = + TAILQ_HEAD_INITIALIZER(initial_mapping_head); + /* * Returns the container state for the given frame. This function always * returns a container state (otherwise, there is a bug in the code and the @@ -146,8 +150,10 @@ void x_con_init(Con *con, uint16_t depth) { state->id = con->frame; state->mapped = false; state->initial = true; + DLOG("Adding window 0x%08x to lists\n", state->id); CIRCLEQ_INSERT_HEAD(&state_head, state, state); CIRCLEQ_INSERT_HEAD(&old_state_head, state, old_state); + TAILQ_INSERT_TAIL(&initial_mapping_head, state, initial_mapping_order); DLOG("adding new state for window id 0x%08x\n", state->id); } @@ -228,11 +234,12 @@ void x_con_kill(Con *con) { state = state_for_frame(con->frame); CIRCLEQ_REMOVE(&state_head, state, state); CIRCLEQ_REMOVE(&old_state_head, state, old_state); + TAILQ_REMOVE(&initial_mapping_head, state, initial_mapping_order); FREE(state->name); free(state); /* Invalidate focused_id to correctly focus new windows with the same ID */ - focused_id = XCB_NONE; + focused_id = last_focused = XCB_NONE; } /* @@ -350,7 +357,7 @@ void x_draw_decoration(Con *con) { p->con_deco_rect = con->deco_rect; p->background = config.client.background; p->con_is_leaf = con_is_leaf(con); - p->parent_orientation = con_orientation(parent); + p->parent_layout = con->parent->layout; if (con->deco_render_params != NULL && (con->window == NULL || !con->window->name_x_changed) && @@ -445,10 +452,10 @@ void x_draw_decoration(Con *con) { TAILQ_PREV(con, nodes_head, nodes) == NULL && con->parent->type != CT_FLOATING_CON) { xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->indicator }); - if (p->parent_orientation == HORIZ) + if (p->parent_layout == L_SPLITH) xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){ { r->width + br.width + br.x, br.y, r->width, r->height + br.height } }); - else + else if (p->parent_layout == L_SPLITV) xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){ { br.x, r->height + br.height + br.y, r->width - (2 * br.x), r->height } }); } @@ -667,9 +674,24 @@ void x_push_node(Con *con) { /* As the pixmap only depends on the size and not on the position, it * is enough to check if width/height have changed. Also, we don’t * create a pixmap at all when the window is actually not visible - * (height == 0). */ - if ((state->rect.width != rect.width || - state->rect.height != rect.height)) { + * (height == 0) or when it is not needed. */ + bool has_rect_changed = (state->rect.width != rect.width || state->rect.height != rect.height); + + /* The pixmap of a borderless leaf container will not be used except + * for the titlebar in a stack or tabs (issue #1013). */ + bool is_pixmap_needed = (con->border_style != BS_NONE || + !con_is_leaf(con) || + con->parent->layout == L_STACKED || + con->parent->layout == L_TABBED); + + /* Check if the container has an unneeded pixmap left over from + * previously having a border or titlebar. */ + if (!is_pixmap_needed && con->pixmap != XCB_NONE) { + xcb_free_pixmap(conn, con->pixmap); + con->pixmap = XCB_NONE; + } + + if (has_rect_changed && is_pixmap_needed) { if (con->pixmap == 0) { con->pixmap = xcb_generate_id(conn); con->pm_gc = xcb_generate_id(conn); @@ -731,10 +753,9 @@ void x_push_node(Con *con) { } /* Map if map state changed, also ensure that the child window - * is changed if we are mapped *and* in initial state (meaning the - * container was empty before, but now got a child). Unmaps are handled in - * x_push_node_unmaps(). */ - if ((state->mapped != con->mapped || (con->mapped && state->initial)) && + * is changed if we are mapped and there is a new, unmapped child window. + * Unmaps are handled in x_push_node_unmaps(). */ + if ((state->mapped != con->mapped || (con->window != NULL && !state->child_mapped)) && con->mapped) { xcb_void_cookie_t cookie; @@ -834,6 +855,24 @@ static void x_push_node_unmaps(Con *con) { x_push_node_unmaps(current); } +/* + * Returns true if the given container is currently attached to its parent. + * + * TODO: Remove once #1185 has been fixed + */ +static bool is_con_attached(Con *con) { + if (con->parent == NULL) + return false; + + Con *current; + TAILQ_FOREACH(current, &(con->parent->nodes_head), nodes) { + if (current == con) + return true; + } + + return false; +} + /* * Pushes all changes (state of each node, see x_push_node() and the window * stack) to X11. @@ -872,12 +911,17 @@ void x_push_changes(Con *con) { if (state->con && state->con->window) cnt++; - if (cnt != btt_stack_num) { - btt_stack = srealloc(btt_stack, sizeof(xcb_window_t) * cnt); - btt_stack_num = cnt; + /* The bottom-to-top window stack of all windows which are managed by i3. + * Used for x_get_window_stack(). */ + static xcb_window_t *client_list_windows = NULL; + static int client_list_count = 0; + + if (cnt != client_list_count) { + client_list_windows = srealloc(client_list_windows, sizeof(xcb_window_t) * cnt); + client_list_count = cnt; } - xcb_window_t *walk = btt_stack; + xcb_window_t *walk = client_list_windows; /* X11 correctly represents the stack if we push it from bottom to top */ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { @@ -903,9 +947,21 @@ void x_push_changes(Con *con) { } /* If we re-stacked something (or a new window appeared), we need to update - * the _NET_CLIENT_LIST_STACKING hint */ - if (stacking_changed) - ewmh_update_client_list_stacking(btt_stack, btt_stack_num); + * the _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING hints */ + if (stacking_changed) { + DLOG("Client list changed (%i clients)\n", cnt); + ewmh_update_client_list_stacking(client_list_windows, client_list_count); + + walk = client_list_windows; + + /* reorder by initial mapping */ + TAILQ_FOREACH(state, &initial_mapping_head, initial_mapping_order) { + if (state->con && state->con->window) + *walk++ = state->con->window->id; + } + + ewmh_update_client_list(client_list_windows, client_list_count); + } DLOG("PUSHING CHANGES\n"); x_push_node(con); @@ -950,17 +1006,16 @@ void x_push_changes(Con *con) { /* Invalidate focused_id to correctly focus new windows with the same ID */ focused_id = XCB_NONE; } else { - bool set_focus = true; if (focused->window != NULL && - focused->window->needs_take_focus) { + focused->window->needs_take_focus && + focused->window->doesnt_accept_focus) { DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x (focused: %p / %s)\n", to_focus, focused, focused->name); - send_take_focus(to_focus); - set_focus = !focused->window->doesnt_accept_focus; - DLOG("set_focus = %d\n", set_focus); - } + send_take_focus(to_focus, last_timestamp); - if (set_focus) { + if (to_focus != last_focused && is_con_attached(focused)) + ipc_send_window_event("focus", focused); + } else { DLOG("Updating focus (focused: %p / %s) to X11 window 0x%08x\n", focused, focused->name, to_focus); /* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get * no focus change events for our own focus changes. We only want @@ -976,9 +1031,12 @@ void x_push_changes(Con *con) { } ewmh_update_active_window(to_focus); + + if (to_focus != XCB_NONE && to_focus != last_focused && focused->window != NULL && is_con_attached(focused)) + ipc_send_window_event("focus", focused); } - focused_id = to_focus; + focused_id = last_focused = to_focus; } } @@ -1052,6 +1110,16 @@ void x_set_name(Con *con, const char *name) { state->name = sstrdup(name); } +/* + * Set up the I3_SHMLOG_PATH atom. + * + */ +void update_shmlog_atom() { + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, + A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, + strlen(shmlogname), shmlogname); +} + /* * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH) * @@ -1064,8 +1132,7 @@ void x_set_i3_atoms(void) { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_PID, XCB_ATOM_CARDINAL, 32, 1, &pid); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8, strlen(current_configpath), current_configpath); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, - strlen(shmlogname), shmlogname); + update_shmlog_atom(); } /* @@ -1075,7 +1142,8 @@ void x_set_i3_atoms(void) { */ void x_set_warp_to(Rect *rect) { - warp_to = rect; + if (!config.disable_focus_follows_mouse) + warp_to = rect; } /*