Con *parent = con;
while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
- if (parent->split)
+ if (!con_is_leaf(parent))
FREE(parent->deco_render_params);
parent = parent->parent;
}
/*
* Create a new container (and attach it to the given parent, if not NULL).
- * This function initializes the data structures and creates the appropriate
- * X11 IDs using x_con_init().
+ * This function only initializes the data structures.
*
*/
-Con *con_new(Con *parent, i3Window *window) {
+Con *con_new_skeleton(Con *parent, i3Window *window) {
Con *new = scalloc(sizeof(Con));
new->on_remove_child = con_on_remove_child;
TAILQ_INSERT_TAIL(&all_cons, new, all_cons);
+ new->aspect_ratio = 0.0;
new->type = CT_CON;
new->window = window;
new->border_style = config.default_border;
new->current_border_width = -1;
+ if (window)
+ new->depth = window->depth;
+ else
+ new->depth = XCB_COPY_FROM_PARENT;
static int cnt = 0;
DLOG("opening window %d\n", cnt);
cnt++;
if ((cnt % (sizeof(colors) / sizeof(char*))) == 0)
cnt = 0;
- if (window)
- x_con_init(new, window->depth);
- else
- x_con_init(new, XCB_COPY_FROM_PARENT);
TAILQ_INIT(&(new->floating_head));
TAILQ_INIT(&(new->nodes_head));
return new;
}
+/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
+ *
+ */
+Con *con_new(Con *parent, i3Window *window) {
+ Con *new = con_new_skeleton(parent, window);
+ x_con_init(new, new->depth);
+ return new;
+}
+
/*
* Attaches the given container to the given parent. This happens when moving
* a container or when inserting a new container at a specific place in the
return TAILQ_EMPTY(&(con->nodes_head));
}
+/**
+ * Returns true if this node has regular or floating children.
+ *
+ */
+bool con_has_children(Con *con) {
+ return (!con_is_leaf(con) || !TAILQ_EMPTY(&(con->floating_head)));
+}
+
+/*
+ * Returns true if a container should be considered split.
+ *
+ */
+bool con_is_split(Con *con) {
+ if (con_is_leaf(con))
+ return false;
+
+ switch (con->layout) {
+ case L_DOCKAREA:
+ case L_OUTPUT:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
/*
* Returns true if this node accepts a window (if the node swallows windows,
* it might already have swallowed enough and cannot hold any more).
if (con->type == CT_WORKSPACE)
return false;
- if (con->split) {
+ if (con_is_split(con)) {
DLOG("container %p does not accept windows, it is a split container.\n", con);
return false;
}
}
/*
- * Toggles fullscreen mode for the given container. Fullscreen mode will not be
- * entered when there already is a fullscreen container on this workspace.
+ * Toggles fullscreen mode for the given container. If there already is a
+ * fullscreen container on this workspace, fullscreen will be disabled and then
+ * enabled for the container the user wants to have in fullscreen mode.
*
*/
void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
*
*/
void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
- if (con->type == CT_WORKSPACE) {
- DLOG("Moving workspaces is not yet implemented.\n");
- return;
- }
-
/* Prevent moving if this would violate the fullscreen focus restrictions. */
if (!con_fullscreen_permits_focusing(workspace)) {
LOG("Cannot move out of a fullscreen container");
return;
}
+ if (con->type == CT_WORKSPACE) {
+ /* Re-parent all of the old workspace's floating windows. */
+ Con *child;
+ while (!TAILQ_EMPTY(&(source_ws->floating_head))) {
+ child = TAILQ_FIRST(&(source_ws->floating_head));
+ con_move_to_workspace(child, workspace, true, true);
+ }
+
+ /* If there are no non-floating children, ignore the workspace. */
+ if (con_is_leaf(con))
+ return;
+
+ con = workspace_encapsulate(con);
+ if (con == NULL) {
+ ELOG("Workspace failed to move its contents into a container!\n");
+ return;
+ }
+ }
+
/* Save the current workspace. So we can call workspace_show() by the end
* of this function. */
Con *current_ws = con_get_workspace(focused);
}
}
+ /* If moving a fullscreen container and the destination already has a
+ * fullscreen window on it, un-fullscreen the target's fullscreen con. */
+ Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
+ if (con->fullscreen_mode != CF_NONE && fullscreen != NULL) {
+ con_toggle_fullscreen(fullscreen, CF_OUTPUT);
+ fullscreen = NULL;
+ }
+
DLOG("Re-attaching container to %p / %s\n", next, next->name);
/* 5: re-attach the con to the parent of this focused container */
Con *parent = con->parent;
con->percent = 0.0;
con_fix_percent(next);
- /* 7: focus the con on the target workspace (the X focus is only updated by
- * calling tree_render(), so for the "real" focus this is a no-op).
+ /* 7: focus the con on the target workspace, but only within that
+ * workspace, that is, don’t move focus away if the target workspace is
+ * invisible.
* We don’t focus the con for i3 pseudo workspaces like __i3_scratch and
* we don’t focus when there is a fullscreen con on that workspace. */
- if (!con_is_internal(workspace) &&
- con_get_fullscreen_con(workspace, CF_OUTPUT) == NULL)
+ if (!con_is_internal(workspace) && !fullscreen) {
+ /* We need to save the focused workspace on the output in case the
+ * new workspace is hidden and it's necessary to immediately switch
+ * back to the originally-focused workspace. */
+ Con *old_focus = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
con_focus(con_descend_focused(con));
- /* 8: when moving to a visible workspace on a different output, we keep the
- * con focused. Otherwise, we leave the focus on the current workspace as we
- * don’t want to focus invisible workspaces */
- if (source_output != dest_output &&
- workspace_is_visible(workspace) &&
- !con_is_internal(workspace)) {
- DLOG("Moved to a different output, focusing target\n");
- } else {
- /* Descend focus stack in case focus_next is a workspace which can
- * occur if we move to the same workspace. Also show current workspace
- * to ensure it is focused. */
- workspace_show(current_ws);
-
- /* Set focus only if con was on current workspace before moving.
- * Otherwise we would give focus to some window on different workspace. */
- if (source_ws == current_ws)
+ /* Restore focus if the output's focused workspace has changed. */
+ if (con_get_workspace(focused) != old_focus)
+ con_focus(old_focus);
+ }
+
+ /* 8: when moving to another workspace, we leave the focus on the current
+ * workspace. (see also #809) */
+
+ /* Descend focus stack in case focus_next is a workspace which can
+ * occur if we move to the same workspace. Also show current workspace
+ * to ensure it is focused. */
+ workspace_show(current_ws);
+
+ /* Set focus only if con was on current workspace before moving.
+ * Otherwise we would give focus to some window on different workspace. */
+ if (source_ws == current_ws)
con_focus(con_descend_focused(focus_next));
+
+ /* If anything within the container is associated with a startup sequence,
+ * delete it so child windows won't be created on the old workspace. */
+ struct Startup_Sequence *sequence;
+ xcb_get_property_cookie_t cookie;
+ xcb_get_property_reply_t *startup_id_reply;
+
+ if (!con_is_leaf(con)) {
+ Con *child;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
+ if (!child->window)
+ continue;
+
+ cookie = xcb_get_property(conn, false, child->window->id,
+ A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
+ startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
+
+ sequence = startup_sequence_get(child->window, startup_id_reply, true);
+ if (sequence != NULL)
+ startup_sequence_delete(sequence);
+ }
+ }
+
+ if (con->window) {
+ cookie = xcb_get_property(conn, false, con->window->id,
+ A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
+ startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
+
+ sequence = startup_sequence_get(con->window, startup_id_reply, true);
+ if (sequence != NULL)
+ startup_sequence_delete(sequence);
}
CALL(parent, on_remove_child);
*/
Con *con_descend_direction(Con *con, direction_t direction) {
Con *most = NULL;
+ Con *current;
int orientation = con_orientation(con);
DLOG("con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
if (direction == D_LEFT || direction == D_RIGHT) {
/* Wrong orientation. We use the last focused con. Within that con,
* we recurse to chose the left/right con or at least the last
* focused one. */
- most = TAILQ_FIRST(&(con->focus_head));
+ TAILQ_FOREACH(current, &(con->focus_head), focused) {
+ if (current->type != CT_FLOATING_CON) {
+ most = current;
+ break;
+ }
+ }
} else {
/* If the con has no orientation set, it’s not a split container
* but a container with a client window, so stop recursing */
/* Wrong orientation. We use the last focused con. Within that con,
* we recurse to chose the top/bottom con or at least the last
* focused one. */
- most = TAILQ_FIRST(&(con->focus_head));
+ TAILQ_FOREACH(current, &(con->focus_head), focused) {
+ if (current->type != CT_FLOATING_CON) {
+ most = current;
+ break;
+ }
+ }
} else {
/* If the con has no orientation set, it’s not a split container
* but a container with a client window, so stop recursing */
} else {
result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
}
+
+ /* Floating windows are never adjacent to any other window, so
+ don’t hide their border(s). This prevents bug #998. */
+ if (con_is_floating(con))
+ return result;
+
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
result.x -= border_width;
result.width += border_width;
con->current_border_width = border_width;
bsr = con_border_style_rect(con);
int deco_height =
- (con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
+ (con->border_style == BS_NORMAL ? render_deco_height() : 0);
con->rect.x -= bsr.x;
con->rect.y -= bsr.y;
* new split container before).
*
*/
-void con_set_layout(Con *con, int layout) {
+void con_set_layout(Con *con, layout_t layout) {
DLOG("con_set_layout(%p, %d), con->type = %d\n",
con, layout, con->type);
* split. */
new->layout = layout;
new->last_split_layout = con->last_split_layout;
- new->split = true;
Con *old_focused = TAILQ_FIRST(&(con->focus_head));
if (old_focused == TAILQ_END(&(con->focus_head)))
* not be closed when the last child was removed */
if (con->type == CT_OUTPUT ||
con->type == CT_ROOT ||
- con->type == CT_DOCKAREA) {
- DLOG("not handling, type = %d\n", con->type);
+ con->type == CT_DOCKAREA ||
+ (con->parent != NULL && con->parent->type == CT_OUTPUT)) {
+ DLOG("not handling, type = %d, name = %s\n", con->type, con->name);
return;
}
}
con_force_split_parents_redraw(con);
+ con->urgent = con_has_urgent_child(con);
+ con_update_parents_urgency(con);
/* TODO: check if this container would swallow any other client and
* don’t close it automatically. */
/* For horizontal/vertical split containers we sum up the width (h-split)
* or height (v-split) and use the maximum of the height (h-split) or width
* (v-split) as minimum size. */
- if (con->split) {
+ if (con_is_split(con)) {
uint32_t width = 0, height = 0;
Con *child;
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
}
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
- con->type, con->layout, con->split);
+ con->type, con->layout, con_is_split(con));
assert(false);
}
Con *parent = con->parent;
bool new_urgency_value = con->urgent;
- while (parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
+ while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
if (new_urgency_value) {
parent->urgent = true;
} else {
}
}
+/*
+ * Set urgency flag to the container, all the parent containers and the workspace.
+ *
+ */
+void con_set_urgency(Con *con, bool urgent) {
+ if (focused == con) {
+ DLOG("Ignoring urgency flag for current client\n");
+ con->window->urgent.tv_sec = 0;
+ con->window->urgent.tv_usec = 0;
+ return;
+ }
+
+ if (con->urgency_timer == NULL) {
+ con->urgent = urgent;
+ } else
+ DLOG("Discarding urgency WM_HINT because timer is running\n");
+
+ //CLIENT_LOG(con);
+ if (con->window) {
+ if (con->urgent) {
+ gettimeofday(&con->window->urgent, NULL);
+ } else {
+ con->window->urgent.tv_sec = 0;
+ con->window->urgent.tv_usec = 0;
+ }
+ }
+
+ con_update_parents_urgency(con);
+
+ if (con->urgent == urgent)
+ LOG("Urgency flag changed to %d\n", con->urgent);
+
+ Con *ws;
+ /* Set the urgency flag on the workspace, if a workspace could be found
+ * (for dock clients, that is not the case). */
+ if ((ws = con_get_workspace(con)) != NULL)
+ workspace_update_urgent_flag(ws);
+}
+
/*
* Create a string representing the subtree under con.
*
buf = sstrdup("T[");
else if (con->layout == L_STACKED)
buf = sstrdup("S[");
+ else {
+ ELOG("BUG: Code not updated to account for new layout type\n");
+ assert(false);
+ }
/* 2) append representation of children */
Con *child;