/* 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;
+
/*
* Describes the X11 state we may modify (map state, position, window stack).
* There is one entry per container. The state represents the current situation
bool unmap_now;
bool child_mapped;
+ /** The con for which this state is. */
+ Con *con;
+
/* For reparenting, we have a flag (need_reparent) and the X ID of the old
* frame this window was in. The latter is necessary because we need to
* ignore UnmapNotify events (by changing the window event mask). */
DLOG("resetting state %p to initial\n", state);
state->initial = true;
state->child_mapped = false;
+ state->con = con;
memset(&(state->window_rect), 0, sizeof(Rect));
}
return;
}
+ state_dest->con = state_src->con;
+ state_src->con = NULL;
+
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));
xcb_icccm_get_wm_protocols_reply_t protocols;
bool result = false;
- cookie = xcb_icccm_get_wm_protocols_unchecked(conn, window, A_WM_PROTOCOLS);
+ cookie = xcb_icccm_get_wm_protocols(conn, window, A_WM_PROTOCOLS);
if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1)
return false;
return;
}
- xcb_client_message_event_t ev;
+ /* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
+ * In order to properly initialize these bytes, we allocate 32 bytes even
+ * though we only need less for an xcb_configure_notify_event_t */
+ void *event = scalloc(32);
+ xcb_client_message_event_t *ev = event;
- memset(&ev, 0, sizeof(xcb_client_message_event_t));
-
- ev.response_type = XCB_CLIENT_MESSAGE;
- ev.window = window;
- ev.type = A_WM_PROTOCOLS;
- ev.format = 32;
- ev.data.data32[0] = A_WM_DELETE_WINDOW;
- ev.data.data32[1] = XCB_CURRENT_TIME;
+ ev->response_type = XCB_CLIENT_MESSAGE;
+ ev->window = window;
+ ev->type = A_WM_PROTOCOLS;
+ ev->format = 32;
+ ev->data.data32[0] = A_WM_DELETE_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);
}
/*
return;
}
+ /* Skip containers whose pixmap has not yet been created (can happen when
+ * decoration rendering happens recursively for a window for which
+ * x_push_node() was not yet called) */
+ if (con->pixmap == XCB_NONE) {
+ DLOG("pixmap not yet created, not rendering\n");
+ return;
+ }
+
/* 1: build deco_params and compare with cache */
struct deco_render_params *p = scalloc(sizeof(struct deco_render_params));
parent->pixmap_recreated = false;
con->pixmap_recreated = false;
- /* If the con is in fullscreen mode, the decoration height we work with is set to 0 */
- Rect deco_rect = con->deco_rect;
- if (con_get_fullscreen_con(parent, CF_OUTPUT) == con)
- deco_rect.height = 0;
-
/* 2: draw the client.background, but only for the parts around the client_rect */
if (con->window != NULL) {
xcb_rectangle_t background[] = {
* while drawing the decoration needs to happen in the actual order.
*
*/
-static void x_deco_recurse(Con *con) {
+void x_deco_recurse(Con *con) {
Con *current;
bool leaf = TAILQ_EMPTY(&(con->nodes_head)) &&
TAILQ_EMPTY(&(con->floating_head));
}
bool fake_notify = false;
- /* set new position if rect changed */
- if (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0) {
+ /* Set new position if rect changed (and if height > 0) */
+ if (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. */
* 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 (rect.height > 0 &&
- (state->rect.width != rect.width ||
+ if ((state->rect.width != rect.width ||
state->rect.height != rect.height)) {
DLOG("CACHE: creating new pixmap for con %p (old: %d x %d, new: %d x %d)\n",
con, state->rect.width, state->rect.height,
xcb_free_gc(conn, con->pm_gc);
}
xcb_create_pixmap(conn, root_depth, con->pixmap, con->frame, rect.width, rect.height);
- xcb_create_gc(conn, con->pm_gc, con->pixmap, 0, 0);
+ /* For the graphics context, we disable GraphicsExposure events.
+ * Those will be sent when a CopyArea request cannot be fulfilled
+ * 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 };
+ xcb_create_gc(conn, con->pm_gc, con->pixmap, XCB_GC_GRAPHICS_EXPOSURES, values);
con->pixmap_recreated = true;
- /* Render the decoration now to make the correct decoration visible
- * from the very first moment. Later calls will be cached, so this
- * doesn’t hurt performance. */
- x_deco_recurse(con);
+ /* Don’t render the decoration for windows inside a stack which are
+ * not visible right now */
+ if (!con->parent ||
+ con->parent->layout != L_STACKED ||
+ TAILQ_FIRST(&(con->parent->focus_head)) == con)
+ /* Render the decoration now to make the correct decoration visible
+ * from the very first moment. Later calls will be cached, so this
+ * doesn’t hurt performance. */
+ x_deco_recurse(con);
}
DLOG("setting rect (%d, %d, %d, %d)\n", rect.x, rect.y, rect.width, rect.height);
* fast as possible) */
xcb_flush(conn);
xcb_set_window_rect(conn, con->frame, rect);
- xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+ if (con->pixmap != XCB_NONE)
+ xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
xcb_flush(conn);
memcpy(&(state->rect), &rect, sizeof(Rect));
xcb_change_window_attributes(conn, con->frame, XCB_CW_EVENT_MASK, values);
/* copy the pixmap contents to the frame window immediately after mapping */
- xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+ if (con->pixmap != XCB_NONE)
+ xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
xcb_flush(conn);
DLOG("mapping container %08x (serial %d)\n", con->frame, cookie.sequence);
}
//DLOG("Done, EnterNotify disabled\n");
bool order_changed = false;
+
+ /* 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)
+ cnt++;
+
+ if (cnt != btt_stack_num) {
+ btt_stack = srealloc(btt_stack, sizeof(xcb_window_t) * cnt);
+ btt_stack_num = cnt;
+ }
+
+ xcb_window_t *walk = btt_stack;
+
/* 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)
+ memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t));
+
//DLOG("stack: 0x%08x\n", state->id);
con_state *prev = CIRCLEQ_PREV(state, state);
con_state *old_prev = CIRCLEQ_PREV(state, old_state);
- if (prev != old_prev)
+ if ((prev != old_prev || state->initial) && prev != CIRCLEQ_END(&state_head)) {
order_changed = true;
- if ((state->initial || order_changed) && prev != CIRCLEQ_END(&state_head)) {
DLOG("Stacking 0x%08x above 0x%08x\n", prev->id, state->id);
uint32_t mask = 0;
mask |= XCB_CONFIG_WINDOW_SIBLING;
}
state->initial = false;
}
+
+ /* If we re-stacked something (or a new window appeared), we need to update
+ * the _NET_CLIENT_LIST_STACKING hint */
+ if (order_changed)
+ ewmh_update_client_list_stacking(btt_stack, btt_stack_num);
+
//DLOG("Re-enabling EnterNotify\n");
values[0] = FRAME_EVENT_MASK;
CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {