X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fx.c;h=9e3e55bc5453ab258333ed1b0553f595b2ae2774;hb=56c284af8e281d9d5657e55b3edb7f7c673fc747;hp=27d92acad13687499c207414ac0b15f7c2fb99d3;hpb=78e99440f6ff144bb6842f3fb33c1f65faadc4df;p=i3%2Fi3 diff --git a/src/x.c b/src/x.c index 27d92aca..9e3e55bc 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; @@ -36,7 +36,6 @@ typedef struct con_state { bool mapped; bool unmap_now; bool child_mapped; - bool above_all; /** The con for which this state is. */ Con *con; @@ -56,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 = @@ -64,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 @@ -73,7 +76,7 @@ CIRCLEQ_HEAD(old_state_head, con_state) old_state_head = */ static con_state *state_for_frame(xcb_window_t window) { con_state *state; - CIRCLEQ_FOREACH(state, &state_head, state) + CIRCLEQ_FOREACH (state, &state_head, state) if (state->id == window) return state; @@ -137,7 +140,7 @@ void x_con_init(Con *con, uint16_t depth) { values[2] = colormap; } - Rect dims = { -15, -15, 10, 10 }; + Rect dims = {-15, -15, 10, 10}; con->frame = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); if (win_colormap != XCB_NONE) @@ -147,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); } @@ -209,7 +214,7 @@ void x_move_win(Con *src, Con *dest) { state_dest->con = state_src->con; state_src->con = NULL; - Rect zero = { 0, 0, 0, 0 }; + Rect zero = {0, 0, 0, 0}; if (memcmp(&(state_dest->window_rect), &(zero), sizeof(Rect)) == 0) { memcpy(&(state_dest->window_rect), &(state_src->window_rect), sizeof(Rect)); DLOG("COPYING RECT\n"); @@ -229,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; } /* @@ -290,7 +296,7 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) { ev->data.data32[1] = XCB_CURRENT_TIME; LOG("Sending WM_DELETE to the client\n"); - 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(event); } @@ -346,8 +352,8 @@ void x_draw_decoration(Con *con) { Rect *r = &(con->rect); Rect *w = &(con->window_rect); - p->con_rect = (struct width_height){ r->width, r->height }; - p->con_window_rect = (struct width_height){ w->width, w->height }; + p->con_rect = (struct width_height) {r->width, r->height}; + p->con_window_rect = (struct width_height) {w->width, w->height}; p->con_deco_rect = con->deco_rect; p->background = config.client.background; p->con_is_leaf = con_is_leaf(con); @@ -380,14 +386,13 @@ void x_draw_decoration(Con *con) { if (con->window != NULL) { xcb_rectangle_t background[] = { /* top area */ - { 0, 0, r->width, w->y }, + {0, 0, r->width, w->y}, /* bottom area */ - { 0, (w->y + w->height), r->width, r->height - (w->y + w->height) }, + {0, (w->y + w->height), r->width, r->height - (w->y + w->height)}, /* left area */ - { 0, 0, w->x, r->height }, + {0, 0, w->x, r->height}, /* right area */ - { w->x + w->width, 0, r->width - (w->x + w->width), r->height } - }; + {w->x + w->width, 0, r->width - (w->x + w->width), r->height}}; #if 0 for (int i = 0; i < 4; i++) DLOG("rect is (%d, %d) with %d x %d\n", @@ -398,7 +403,7 @@ void x_draw_decoration(Con *con) { ); #endif - xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) { config.client.background }); + xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {config.client.background}); xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background); } @@ -419,22 +424,22 @@ void x_draw_decoration(Con *con) { * (left, bottom and right part). We don’t just fill the whole * rectangle because some childs are not freely resizable and we want * their background color to "shine through". */ - xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->background }); + xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background}); if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) { - xcb_rectangle_t leftline = { 0, 0, br.x, r->height }; + xcb_rectangle_t leftline = {0, 0, br.x, r->height}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline); } if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) { - xcb_rectangle_t rightline = { r->width + br.width + br.x, 0, r->width, r->height }; + xcb_rectangle_t rightline = {r->width + br.width + br.x, 0, r->width, r->height}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline); } if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) { - xcb_rectangle_t bottomline = { 0, r->height + br.height + br.y, r->width, r->height }; + xcb_rectangle_t bottomline = {0, r->height + br.height + br.y, r->width, r->height}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); } /* 1pixel border needs an additional line at the top */ if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { - xcb_rectangle_t topline = { br.x, 0, con->rect.width + br.width + br.x, br.y }; + xcb_rectangle_t topline = {br.x, 0, con->rect.width + br.width + br.x, br.y}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline); } @@ -445,15 +450,14 @@ void x_draw_decoration(Con *con) { if (TAILQ_NEXT(con, nodes) == NULL && 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 }); + xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator}); 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 } }); + 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 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 } }); + 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}}); } - } /* if this is a borderless/1pixel window, we don’t need to render the @@ -462,12 +466,12 @@ void x_draw_decoration(Con *con) { goto copy_pixmaps; /* 4: paint the bar */ - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->background }); - xcb_rectangle_t drect = { con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height }; + xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background}); + xcb_rectangle_t drect = {con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height}; xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect); /* 5: draw two unconnected horizontal lines in border color */ - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->border }); + xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border}); Rect *dr = &(con->deco_rect); int deco_diff_l = 2; int deco_diff_r = 2; @@ -478,12 +482,10 @@ void x_draw_decoration(Con *con) { deco_diff_r = 0; } xcb_segment_t segments[] = { - { dr->x, dr->y, - dr->x + dr->width - 1, dr->y }, - - { dr->x + deco_diff_l, dr->y + dr->height - 1, - dr->x - deco_diff_r + dr->width - 1, dr->y + dr->height - 1 } - }; + {dr->x, dr->y, + dr->x + dr->width - 1, dr->y}, + {dr->x + deco_diff_l, dr->y + dr->height - 1, + dr->x - deco_diff_r + dr->width - 1, dr->y + dr->height - 1}}; xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments); /* 6: draw the title */ @@ -501,9 +503,9 @@ void x_draw_decoration(Con *con) { free(tree); draw_text_ascii(title, - parent->pixmap, parent->pm_gc, - con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, - con->deco_rect.width - 2); + parent->pixmap, parent->pm_gc, + con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, + con->deco_rect.width - 2); free(title); goto after_title; @@ -530,9 +532,9 @@ void x_draw_decoration(Con *con) { int indent_px = (indent_level * 5) * indent_mult; draw_text(win->name, - parent->pixmap, parent->pm_gc, - con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y, - con->deco_rect.width - 2 - indent_px); + parent->pixmap, parent->pm_gc, + con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y, + con->deco_rect.width - 2 - indent_px); after_title: /* Since we don’t clip the text at all, it might in some cases be painted @@ -543,21 +545,21 @@ after_title: /* Draw a 1px separator line before and after every tab, so that tabs can * be easily distinguished. */ if (parent->layout == L_TABBED) { - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->border }); + xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border}); } else { - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->background }); + xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background}); } xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, parent->pixmap, parent->pm_gc, 6, - (xcb_point_t[]){ - { dr->x + dr->width, dr->y }, - { dr->x + dr->width, dr->y + dr->height }, - { dr->x + dr->width - 1, dr->y }, - { dr->x + dr->width - 1, dr->y + dr->height }, - { dr->x, dr->y + dr->height }, - { dr->x, dr->y }, + (xcb_point_t[]) { + {dr->x + dr->width, dr->y}, + {dr->x + dr->width, dr->y + dr->height}, + {dr->x + dr->width - 1, dr->y}, + {dr->x + dr->width - 1, dr->y + dr->height}, + {dr->x, dr->y + dr->height}, + {dr->x, dr->y}, }); - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->border }); + xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border}); xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments); copy_pixmaps: @@ -577,10 +579,10 @@ void x_deco_recurse(Con *con) { con_state *state = state_for_frame(con->frame); if (!leaf) { - TAILQ_FOREACH(current, &(con->nodes_head), nodes) + TAILQ_FOREACH (current, &(con->nodes_head), nodes) x_deco_recurse(current); - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) x_deco_recurse(current); if (state->mapped) @@ -618,7 +620,7 @@ void x_push_node(Con *con) { /* Calculate the height of all window decorations which will be drawn on to * this frame. */ uint32_t max_y = 0, max_height = 0; - TAILQ_FOREACH(current, &(con->nodes_head), nodes) { + TAILQ_FOREACH (current, &(con->nodes_head), nodes) { Rect *dr = &(current->deco_rect); if (dr->y >= max_y && dr->height >= max_height) { max_y = dr->y; @@ -638,7 +640,7 @@ void x_push_node(Con *con) { /* Temporarily set the event masks to XCB_NONE so that we won’t get * UnmapNotify events (otherwise the handler would close the container). * These events are generated automatically when reparenting. */ - uint32_t values[] = { XCB_NONE }; + uint32_t values[] = {XCB_NONE}; xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values); xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values); @@ -654,13 +656,21 @@ void x_push_node(Con *con) { con->ignore_unmap++; DLOG("ignore_unmap for reparenting of con %p (win 0x%08x) is now %d\n", - con, con->window->id, con->ignore_unmap); + con, con->window->id, con->ignore_unmap); } + /* 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); + bool fake_notify = false; - /* Set new position if rect changed (and if height > 0) */ - if (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 && - rect.height > 0) { + /* Set new position if rect changed (and if height > 0) or if the pixmap + * needs to be recreated */ + if ((is_pixmap_needed && con->pixmap == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 && + rect.height > 0)) { /* We first create the new pixmap, then render to it, set it as the * background and only afterwards change the window size. This reduces * flickering. */ @@ -668,9 +678,17 @@ 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); + + /* 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 (is_pixmap_needed && (has_rect_changed || con->pixmap == XCB_NONE)) { if (con->pixmap == 0) { con->pixmap = xcb_generate_id(conn); con->pm_gc = xcb_generate_id(conn); @@ -690,7 +708,7 @@ void x_push_node(Con *con) { * properly due to parts of the source being unmapped or otherwise * unavailable. Since we always copy from pixmaps to windows, this * is not a concern for us. */ - uint32_t values[] = { 0 }; + uint32_t values[] = {0}; xcb_create_gc(conn, con->pm_gc, con->pixmap, XCB_GC_GRAPHICS_EXPOSURES, values); con->pixmap_recreated = true; @@ -725,24 +743,23 @@ void x_push_node(Con *con) { if (con->window != NULL && memcmp(&(state->window_rect), &(con->window_rect), sizeof(Rect)) != 0) { DLOG("setting window rect (%d, %d, %d, %d)\n", - con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height); + con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height); xcb_set_window_rect(conn, con->window->id, con->window_rect); memcpy(&(state->window_rect), &(con->window_rect), sizeof(Rect)); fake_notify = true; } /* 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; if (con->window != NULL) { /* Set WM_STATE_NORMAL because GTK applications don’t want to * drag & drop if we don’t. Also, xprop(1) needs it. */ - long data[] = { XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE }; + long data[] = {XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE}; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data); } @@ -783,7 +800,7 @@ void x_push_node(Con *con) { /* Handle all children and floating windows of this node. We recurse * in focus order to display the focused client in a stack first when * switching workspaces (reduces flickering). */ - TAILQ_FOREACH(current, &(con->focus_head), focused) + TAILQ_FOREACH (current, &(con->focus_head), focused) x_push_node(current); } @@ -810,7 +827,7 @@ static void x_push_node_unmaps(Con *con) { xcb_void_cookie_t cookie; if (con->window != NULL) { /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */ - long data[] = { XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE }; + long data[] = {XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE}; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data); } @@ -828,13 +845,31 @@ static void x_push_node_unmaps(Con *con) { } /* handle all children and floating windows of this node */ - TAILQ_FOREACH(current, &(con->nodes_head), nodes) + TAILQ_FOREACH (current, &(con->nodes_head), nodes) x_push_node_unmaps(current); - TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + TAILQ_FOREACH (current, &(con->floating_head), floating_windows) 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. @@ -857,8 +892,8 @@ void x_push_changes(Con *con) { DLOG("-- PUSHING WINDOW STACK --\n"); //DLOG("Disabling EnterNotify\n"); - uint32_t values[1] = { XCB_NONE }; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + uint32_t values[1] = {XCB_NONE}; + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } @@ -869,20 +904,25 @@ void x_push_changes(Con *con) { /* count first, necessary to (re)allocate memory for the bottom-to-top * stack afterwards */ int cnt = 0; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) - if (state->con && state->con->window) + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) + if (con_has_managed_window(state->con)) 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) { - if (state->con && state->con->window) + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + if (con_has_managed_window(state->con)) memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t)); //DLOG("stack: 0x%08x\n", state->id); @@ -900,17 +940,25 @@ void x_push_changes(Con *con) { xcb_configure_window(conn, prev->id, mask, values); } - if (state->above_all) { - DLOG("above all: 0x%08x\n", state->id); - xcb_configure_window(conn, state->id, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){ XCB_STACK_MODE_ABOVE }); - } state->initial = false; } /* 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 (con_has_managed_window(state->con)) + *walk++ = state->con->window->id; + } + + ewmh_update_client_list(client_list_windows, client_list_count); + } DLOG("PUSHING CHANGES\n"); x_push_node(con); @@ -927,9 +975,9 @@ void x_push_changes(Con *con) { Output *target = get_output_containing(mid_x, mid_y); if (current != target) { /* Ignore MotionNotify events generated by warping */ - xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT }); + xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT}); xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, mid_x, mid_y); - xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ ROOT_EVENT_MASK }); + xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {ROOT_EVENT_MASK}); } } warp_to = NULL; @@ -937,7 +985,7 @@ void x_push_changes(Con *con) { //DLOG("Re-enabling EnterNotify\n"); values[0] = FRAME_EVENT_MASK; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } @@ -955,17 +1003,18 @@ 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) { + ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE)); + + 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 @@ -974,22 +1023,26 @@ void x_push_changes(Con *con) { values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE); xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values); } - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp); if (focused->window != NULL) { values[0] = CHILD_EVENT_MASK; xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values); } - ewmh_update_active_window(to_focus); + ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE)); + + 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; } } if (focused_id == XCB_NONE) { DLOG("Still no window focused, better set focus to the root window\n"); - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, last_timestamp); + ewmh_update_active_window(XCB_WINDOW_NONE); focused_id = root; } @@ -1004,7 +1057,7 @@ void x_push_changes(Con *con) { * unmapped, the second one appears under the cursor and therefore gets an * EnterNotify event. */ values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (!state->unmap_now) continue; xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); @@ -1014,7 +1067,7 @@ void x_push_changes(Con *con) { x_push_node_unmaps(con); /* save the current stack as old stack */ - CIRCLEQ_FOREACH(state, &state_head, state) { + CIRCLEQ_FOREACH (state, &state_head, state) { CIRCLEQ_REMOVE(&old_state_head, state, old_state); CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state); } @@ -1029,18 +1082,12 @@ void x_push_changes(Con *con) { * Raises the specified container in the internal stack of X windows. The * next call to x_push_changes() will make the change visible in X11. * - * If above_all is true, the X11 window will be raised to the top - * of the stack. This should only be used for precisely one fullscreen - * window per output. - * */ -void x_raise_con(Con *con, bool above_all) { +void x_raise_con(Con *con) { con_state *state; state = state_for_frame(con->frame); //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id); - state->above_all = above_all; - CIRCLEQ_REMOVE(&state_head, state, state); CIRCLEQ_INSERT_HEAD(&state_head, state, state); } @@ -1069,8 +1116,8 @@ void x_set_name(Con *con, const char *name) { */ void update_shmlog_atom() { xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, - A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, - strlen(shmlogname), shmlogname); + A_I3_SHMLOG_PATH, A_UTF8_STRING, 8, + strlen(shmlogname), shmlogname); } /* @@ -1093,9 +1140,9 @@ void x_set_i3_atoms(void) { * x_push_changes(). * */ -void x_set_warp_to(Rect *rect) -{ - warp_to = rect; +void x_set_warp_to(Rect *rect) { + if (config.mouse_warping != POINTER_WARPING_NONE) + warp_to = rect; } /* @@ -1105,10 +1152,10 @@ void x_set_warp_to(Rect *rect) * */ void x_mask_event_mask(uint32_t mask) { - uint32_t values[] = { FRAME_EVENT_MASK & mask }; + uint32_t values[] = {FRAME_EVENT_MASK & mask}; con_state *state; - CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); }