}
#endif
+/*
+ * XXX: we need to clean up all this recursive walking code.
+ *
+ */
+Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
+ Con *current;
+
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
+ if (current != exclude &&
+ current->sticky_group != NULL &&
+ current->window != NULL &&
+ strcmp(current->sticky_group, sticky_group) == 0)
+ return current;
+
+ Con *recurse = _get_sticky(current, sticky_group, exclude);
+ if (recurse != NULL)
+ return recurse;
+ }
+
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
+ if (current != exclude &&
+ current->sticky_group != NULL &&
+ current->window != NULL &&
+ strcmp(current->sticky_group, sticky_group) == 0)
+ return current;
+
+ Con *recurse = _get_sticky(current, sticky_group, exclude);
+ if (recurse != NULL)
+ return recurse;
+ }
+
+ return NULL;
+}
+
+/*
+ * Reassigns all child windows in sticky containers. Called when the user
+ * changes workspaces.
+ *
+ * XXX: what about sticky containers which contain containers?
+ *
+ */
+static void workspace_reassign_sticky(Con *con) {
+ Con *current;
+ /* 1: go through all containers */
+
+ /* handle all children and floating windows of this node */
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
+ if (current->sticky_group == NULL) {
+ workspace_reassign_sticky(current);
+ continue;
+ }
+
+ LOG("Ah, this one is sticky: %s / %p\n", current->name, current);
+ /* 2: find a window which we can re-assign */
+ Con *output = con_get_output(current);
+ Con *src = _get_sticky(output, current->sticky_group, current);
+
+ if (src == NULL) {
+ LOG("No window found for this sticky group\n");
+ workspace_reassign_sticky(current);
+ continue;
+ }
+
+ x_move_win(src, current);
+ current->window = src->window;
+ current->mapped = true;
+ src->window = NULL;
+ src->mapped = false;
+
+ x_reparent_child(current, src);
+
+ LOG("re-assigned window from src %p to dest %p\n", src, current);
+ }
+
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+ workspace_reassign_sticky(current);
+}
/*
* Switches to the given workspace
TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes)
current->fullscreen_mode = CF_NONE;
+ workspace_reassign_sticky(workspace);
+
LOG("switching to %p\n", workspace);
Con *next = workspace;
typedef struct con_state {
xcb_window_t id;
bool mapped;
+
+ /* 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). */
+ bool need_reparent;
+ xcb_window_t old_frame;
+
Rect rect;
Rect window_rect;
memset(&(state->window_rect), 0, sizeof(Rect));
}
+/*
+ * Reparents the child window of the given container (necessary for sticky
+ * containers). The reparenting happens in the next call of x_push_changes().
+ *
+ */
+void x_reparent_child(Con *con, Con *old) {
+ struct con_state *state;
+ if ((state = state_for_frame(con->frame)) == NULL) {
+ ELOG("window state for con not found\n");
+ return;
+ }
+
+ state->need_reparent = true;
+ state->old_frame = old->frame;
+}
+
+/*
+ * Moves a child window from Container src to Container dest.
+ *
+ */
+void x_move_win(Con *src, Con *dest) {
+ struct con_state *state_src, *state_dest;
+
+ if ((state_src = state_for_frame(src->frame)) == NULL) {
+ ELOG("window state for src not found\n");
+ return;
+ }
+
+ if ((state_dest = state_for_frame(dest->frame)) == NULL) {
+ ELOG("window state for dest not found\n");
+ return;
+ }
+
+ Rect zero;
+ memset(&zero, 0, sizeof(Rect));
+ if (memcmp(&(state_dest->window_rect), &(zero), sizeof(Rect)) == 0) {
+ memcpy(&(state_dest->window_rect), &(state_src->window_rect), sizeof(Rect));
+ LOG("COPYING RECT\n");
+ }
+}
+
/*
* Kills the window decoration associated with the given container.
*
LOG("Pushing changes for node %p / %s\n", con, con->name);
state = state_for_frame(con->frame);
+ /* reparent the child window (when the window was moved due to a sticky
+ * container) */
+ if (state->need_reparent && con->window != NULL) {
+ LOG("Reparenting child window\n");
+
+ /* 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 };
+ 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);
+
+ xcb_reparent_window(conn, con->window->id, con->frame, 0, 0);
+
+ values[0] = FRAME_EVENT_MASK;
+ xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values);
+ values[0] = CHILD_EVENT_MASK;
+ xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values);
+
+ state->old_frame = XCB_NONE;
+ state->need_reparent = false;
+ }
+
/* map/unmap 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) */
}
/* dito, but for child windows */
- if (memcmp(&(state->window_rect), &(con->window_rect), sizeof(Rect)) != 0) {
+ if (con->window != NULL &&
+ memcmp(&(state->window_rect), &(con->window_rect), sizeof(Rect)) != 0) {
LOG("setting window rect (%d, %d, %d, %d)\n",
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);