/* 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;
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 =
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
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);
}
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;
}
/*
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) &&
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 } });
}
/* 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);
}
/* 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;
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.
* stack afterwards */
int cnt = 0;
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state)
- if (state->con && state->con->window)
+ 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)
+ if (con_has_managed_window(state->con))
memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t));
//DLOG("stack: 0x%08x\n", state->id);
}
/* 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);
/* 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
}
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;
}
}
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)
*
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();
}
/*
*/
void x_set_warp_to(Rect *rect)
{
- warp_to = rect;
+ if (!config.disable_focus_follows_mouse)
+ warp_to = rect;
}
/*