2011-02-14 23:05:20 +01:00
|
|
|
|
/*
|
|
|
|
|
* vim:ts=4:sw=4:expandtab
|
2011-10-23 00:40:02 +02:00
|
|
|
|
*
|
|
|
|
|
* i3 - an improved dynamic tiling window manager
|
2015-04-04 02:17:56 +02:00
|
|
|
|
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
|
2011-10-23 00:40:02 +02:00
|
|
|
|
*
|
|
|
|
|
* move.c: Moving containers into some direction.
|
|
|
|
|
*
|
2011-02-14 23:05:20 +01:00
|
|
|
|
*/
|
|
|
|
|
#include "all.h"
|
2011-10-23 00:40:02 +02:00
|
|
|
|
|
2014-06-15 19:07:02 +02:00
|
|
|
|
typedef enum { BEFORE,
|
|
|
|
|
AFTER } position_t;
|
2011-02-14 23:05:20 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function detaches 'con' from its parent and inserts it either before or
|
|
|
|
|
* after 'target'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static void insert_con_into(Con *con, Con *target, position_t position) {
|
|
|
|
|
Con *parent = target->parent;
|
|
|
|
|
/* We need to preserve the old con->parent. While it might still be used to
|
|
|
|
|
* insert the entry before/after it, we call the on_remove_child callback
|
|
|
|
|
* afterwards which might then close the con if it is empty. */
|
|
|
|
|
Con *old_parent = con->parent;
|
|
|
|
|
|
|
|
|
|
con_detach(con);
|
|
|
|
|
con_fix_percent(con->parent);
|
|
|
|
|
|
2011-06-02 17:21:38 +02:00
|
|
|
|
/* When moving to a workspace, we respect the user’s configured
|
|
|
|
|
* workspace_layout */
|
|
|
|
|
if (parent->type == CT_WORKSPACE) {
|
|
|
|
|
Con *split = workspace_attach_to(parent);
|
|
|
|
|
if (split != parent) {
|
|
|
|
|
DLOG("Got a new split con, using that one instead\n");
|
|
|
|
|
con->parent = split;
|
|
|
|
|
con_attach(con, split, false);
|
|
|
|
|
DLOG("attached\n");
|
|
|
|
|
con->percent = 0.0;
|
|
|
|
|
con_fix_percent(split);
|
|
|
|
|
con = split;
|
|
|
|
|
DLOG("ok, continuing with con %p instead\n", con);
|
|
|
|
|
con_detach(con);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
con->parent = parent;
|
|
|
|
|
|
|
|
|
|
if (position == BEFORE) {
|
|
|
|
|
TAILQ_INSERT_BEFORE(target, con, nodes);
|
|
|
|
|
TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
|
|
|
|
|
} else if (position == AFTER) {
|
|
|
|
|
TAILQ_INSERT_AFTER(&(parent->nodes_head), target, con, nodes);
|
|
|
|
|
TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pretend the con was just opened with regards to size percent values.
|
|
|
|
|
* Since the con is moved to a completely different con, the old value
|
|
|
|
|
* does not make sense anyways. */
|
|
|
|
|
con->percent = 0.0;
|
|
|
|
|
con_fix_percent(parent);
|
|
|
|
|
|
|
|
|
|
CALL(old_parent, on_remove_child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2013-11-13 09:39:32 +01:00
|
|
|
|
* This function detaches 'con' from its parent and puts it in the given
|
|
|
|
|
* workspace. Position is determined by the direction of movement into the
|
|
|
|
|
* workspace container.
|
2011-02-14 23:05:20 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2013-11-13 09:39:32 +01:00
|
|
|
|
static void attach_to_workspace(Con *con, Con *ws, direction_t direction) {
|
2011-02-14 23:05:20 +01:00
|
|
|
|
con_detach(con);
|
|
|
|
|
con_fix_percent(con->parent);
|
|
|
|
|
|
|
|
|
|
CALL(con->parent, on_remove_child);
|
|
|
|
|
|
|
|
|
|
con->parent = ws;
|
|
|
|
|
|
2013-11-13 09:39:32 +01:00
|
|
|
|
if (direction == D_RIGHT || direction == D_DOWN) {
|
|
|
|
|
TAILQ_INSERT_HEAD(&(ws->nodes_head), con, nodes);
|
|
|
|
|
TAILQ_INSERT_HEAD(&(ws->focus_head), con, focused);
|
|
|
|
|
} else {
|
|
|
|
|
TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes);
|
|
|
|
|
TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused);
|
|
|
|
|
}
|
2011-02-14 23:05:20 +01:00
|
|
|
|
|
|
|
|
|
/* Pretend the con was just opened with regards to size percent values.
|
|
|
|
|
* Since the con is moved to a completely different con, the old value
|
|
|
|
|
* does not make sense anyways. */
|
|
|
|
|
con->percent = 0.0;
|
|
|
|
|
con_fix_percent(ws);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-13 09:39:32 +01:00
|
|
|
|
/*
|
|
|
|
|
* Moves the given container to the closest output in the given direction if
|
|
|
|
|
* such an output exists.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static void move_to_output_directed(Con *con, direction_t direction) {
|
2014-01-13 21:36:11 +01:00
|
|
|
|
Con *old_ws = con_get_workspace(con);
|
2016-09-30 17:28:02 +02:00
|
|
|
|
Output *current_output = get_output_for_con(con);
|
2013-11-13 09:39:32 +01:00
|
|
|
|
Output *output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
|
|
|
|
|
|
|
|
|
|
if (!output) {
|
|
|
|
|
DLOG("No output in this direction found. Not moving.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Con *ws = NULL;
|
|
|
|
|
GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
|
|
|
|
|
|
|
|
|
|
if (!ws) {
|
|
|
|
|
DLOG("No workspace on output in this direction found. Not moving.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attach_to_workspace(con, ws, direction);
|
2014-01-13 21:36:11 +01:00
|
|
|
|
|
|
|
|
|
/* fix the focus stack */
|
|
|
|
|
con_focus(con);
|
|
|
|
|
|
|
|
|
|
/* force re-painting the indicators */
|
|
|
|
|
FREE(con->deco_render_params);
|
|
|
|
|
|
|
|
|
|
tree_flatten(croot);
|
|
|
|
|
|
2014-12-20 05:43:47 +01:00
|
|
|
|
ipc_send_workspace_event("focus", ws, old_ws);
|
2013-11-13 09:39:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
/*
|
2014-06-19 14:09:31 +02:00
|
|
|
|
* Moves the given container in the given direction (D_LEFT, D_RIGHT,
|
2012-01-27 23:11:03 +01:00
|
|
|
|
* D_UP, D_DOWN).
|
2011-02-14 23:05:20 +01:00
|
|
|
|
*
|
|
|
|
|
*/
|
2014-06-19 14:09:31 +02:00
|
|
|
|
void tree_move(Con *con, int direction) {
|
2013-11-26 11:46:10 +01:00
|
|
|
|
position_t position;
|
|
|
|
|
Con *target;
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
DLOG("Moving in direction %d\n", direction);
|
2013-11-26 11:46:10 +01:00
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
/* 1: get the first parent with the same orientation */
|
|
|
|
|
|
|
|
|
|
if (con->type == CT_WORKSPACE) {
|
|
|
|
|
DLOG("Not moving workspace\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1) {
|
2013-11-13 09:39:32 +01:00
|
|
|
|
/* This is the only con on this workspace */
|
|
|
|
|
move_to_output_directed(con, direction);
|
2014-01-13 21:36:11 +01:00
|
|
|
|
return;
|
2011-02-14 23:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 23:11:03 +01:00
|
|
|
|
orientation_t o = (direction == D_LEFT || direction == D_RIGHT ? HORIZ : VERT);
|
2011-02-14 23:05:20 +01:00
|
|
|
|
|
|
|
|
|
Con *same_orientation = con_parent_with_orientation(con, o);
|
2011-02-19 20:11:47 +01:00
|
|
|
|
/* The do {} while is used to 'restart' at this point with a different
|
|
|
|
|
* same_orientation, see the very last lines before the end of this block
|
|
|
|
|
* */
|
|
|
|
|
do {
|
|
|
|
|
/* There is no parent container with the same orientation */
|
|
|
|
|
if (!same_orientation) {
|
|
|
|
|
if (con_is_floating(con)) {
|
|
|
|
|
/* this is a floating con, we just disable floating */
|
|
|
|
|
floating_disable(con, true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (con_inside_floating(con)) {
|
|
|
|
|
/* 'con' should be moved out of a floating container */
|
|
|
|
|
DLOG("Inside floating, moving to workspace\n");
|
2013-11-13 09:39:32 +01:00
|
|
|
|
attach_to_workspace(con, con_get_workspace(con), direction);
|
2011-02-19 20:11:47 +01:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
DLOG("Force-changing orientation\n");
|
|
|
|
|
ws_force_orientation(con_get_workspace(con), o);
|
|
|
|
|
same_orientation = con_parent_with_orientation(con, o);
|
2011-02-14 23:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-02-19 20:11:47 +01:00
|
|
|
|
/* easy case: the move is within this container */
|
|
|
|
|
if (same_orientation == con->parent) {
|
|
|
|
|
DLOG("We are in the same container\n");
|
|
|
|
|
Con *swap;
|
2014-06-15 19:07:02 +02:00
|
|
|
|
if ((swap = (direction == D_LEFT || direction == D_UP ? TAILQ_PREV(con, nodes_head, nodes) : TAILQ_NEXT(con, nodes)))) {
|
2011-02-19 20:11:47 +01:00
|
|
|
|
if (!con_is_leaf(swap)) {
|
2013-11-26 11:46:10 +01:00
|
|
|
|
DLOG("Moving into our bordering branch\n");
|
|
|
|
|
target = con_descend_direction(swap, direction);
|
|
|
|
|
position = (con_orientation(target->parent) != o ||
|
2014-06-15 19:07:02 +02:00
|
|
|
|
direction == D_UP ||
|
|
|
|
|
direction == D_LEFT
|
|
|
|
|
? AFTER
|
|
|
|
|
: BEFORE);
|
2013-11-26 11:46:10 +01:00
|
|
|
|
insert_con_into(con, target, position);
|
2011-02-19 20:11:47 +01:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
2012-01-27 23:11:03 +01:00
|
|
|
|
if (direction == D_LEFT || direction == D_UP)
|
2011-02-19 20:11:47 +01:00
|
|
|
|
TAILQ_SWAP(swap, con, &(swap->parent->nodes_head), nodes);
|
2014-06-15 19:07:02 +02:00
|
|
|
|
else
|
|
|
|
|
TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes);
|
2011-02-19 20:11:47 +01:00
|
|
|
|
|
|
|
|
|
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
|
|
|
|
TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
|
|
|
|
|
|
|
|
|
|
DLOG("Swapped.\n");
|
2014-06-19 10:58:03 +02:00
|
|
|
|
ipc_send_window_event("move", con);
|
Handle the EWMH atom _NET_WM_DESKTOP.
We already claim _NET_WM_DESKTOP support in _NET_SUPPORTED since around 2009,
but haven't actually done anything with it. However, especially pagers like
gnome-panel rely on this property to be updated and many tools, like GTK, want
to use the corresponding client messages to make a window sticky, move it
around etc.
This patch implements full support according to the EWMH spec. This means:
* We set the property on all windows when managing it.
* We keep the property updated on all windows at all times.
* We read and respect the property upon managing a window if it was set before
mapping the window.
* We react to client messages for it.
* We remove the property on withdrawn windows.
Note that the special value 0xFFFFFFFF, according to the spec, means that the
window shall be shown on all workspaces. We do this by making it sticky and
float it. This shows it on all workspaces at least on the output it is on.
Furthermore, the spec gives us the freedom to ignore _NET_WM_DESKTOP when
managing a window if we have good reason to. In our case, we give window
swallowing a higher priority since the user would likely expect that and we
want to keep placeholder windows only around for as long as we have to.
However, we do prioritize this property over, for example, startup
notifications.
fixes #2153
fixes #1507
fixes #938
2016-01-11 20:53:26 +01:00
|
|
|
|
ewmh_update_wm_desktop();
|
2011-02-19 20:11:47 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-13 09:39:32 +01:00
|
|
|
|
if (con->parent == con_get_workspace(con)) {
|
|
|
|
|
/* If we couldn't find a place to move it on this workspace,
|
|
|
|
|
* try to move it to a workspace on a different output */
|
|
|
|
|
move_to_output_directed(con, direction);
|
2014-06-19 10:58:03 +02:00
|
|
|
|
ipc_send_window_event("move", con);
|
Handle the EWMH atom _NET_WM_DESKTOP.
We already claim _NET_WM_DESKTOP support in _NET_SUPPORTED since around 2009,
but haven't actually done anything with it. However, especially pagers like
gnome-panel rely on this property to be updated and many tools, like GTK, want
to use the corresponding client messages to make a window sticky, move it
around etc.
This patch implements full support according to the EWMH spec. This means:
* We set the property on all windows when managing it.
* We keep the property updated on all windows at all times.
* We read and respect the property upon managing a window if it was set before
mapping the window.
* We react to client messages for it.
* We remove the property on withdrawn windows.
Note that the special value 0xFFFFFFFF, according to the spec, means that the
window shall be shown on all workspaces. We do this by making it sticky and
float it. This shows it on all workspaces at least on the output it is on.
Furthermore, the spec gives us the freedom to ignore _NET_WM_DESKTOP when
managing a window if we have good reason to. In our case, we give window
swallowing a higher priority since the user would likely expect that and we
want to keep placeholder windows only around for as long as we have to.
However, we do prioritize this property over, for example, startup
notifications.
fixes #2153
fixes #1507
fixes #938
2016-01-11 20:53:26 +01:00
|
|
|
|
ewmh_update_wm_desktop();
|
2014-01-13 21:36:11 +01:00
|
|
|
|
return;
|
2013-11-13 09:39:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there was no con with which we could swap the current one,
|
|
|
|
|
* search again, but starting one level higher. */
|
2011-02-19 20:11:47 +01:00
|
|
|
|
same_orientation = con_parent_with_orientation(con->parent, o);
|
2011-02-14 23:05:20 +01:00
|
|
|
|
}
|
2011-02-19 20:11:47 +01:00
|
|
|
|
} while (same_orientation == NULL);
|
2011-02-14 23:05:20 +01:00
|
|
|
|
|
|
|
|
|
/* this time, we have to move to another container */
|
2011-06-02 17:21:38 +02:00
|
|
|
|
/* This is the container *above* 'con' (an ancestor of con) which is inside
|
|
|
|
|
* 'same_orientation' */
|
2011-02-14 23:05:20 +01:00
|
|
|
|
Con *above = con;
|
|
|
|
|
while (above->parent != same_orientation)
|
|
|
|
|
above = above->parent;
|
|
|
|
|
|
2012-05-27 02:45:15 +02:00
|
|
|
|
/* Enforce the fullscreen focus restrictions. */
|
|
|
|
|
if (!con_fullscreen_permits_focusing(above->parent)) {
|
|
|
|
|
LOG("Cannot move out of fullscreen container\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
DLOG("above = %p\n", above);
|
|
|
|
|
|
2014-06-15 19:07:02 +02:00
|
|
|
|
Con *next = (direction == D_UP || direction == D_LEFT ? TAILQ_PREV(above, nodes_head, nodes) : TAILQ_NEXT(above, nodes));
|
2013-11-26 11:46:10 +01:00
|
|
|
|
|
|
|
|
|
if (next && !con_is_leaf(next)) {
|
|
|
|
|
DLOG("Moving into the bordering branch of our adjacent container\n");
|
|
|
|
|
target = con_descend_direction(next, direction);
|
|
|
|
|
position = (con_orientation(target->parent) != o ||
|
2014-06-15 19:07:02 +02:00
|
|
|
|
direction == D_UP ||
|
|
|
|
|
direction == D_LEFT
|
|
|
|
|
? AFTER
|
|
|
|
|
: BEFORE);
|
2013-11-26 11:46:10 +01:00
|
|
|
|
insert_con_into(con, target, position);
|
2017-09-14 17:41:48 +02:00
|
|
|
|
} else if (!next &&
|
|
|
|
|
con->parent->parent->type == CT_WORKSPACE &&
|
2015-03-31 19:29:43 +02:00
|
|
|
|
con->parent->layout != L_DEFAULT &&
|
|
|
|
|
con_num_children(con->parent) == 1) {
|
|
|
|
|
/* Con is the lone child of a non-default layout container at the edge
|
|
|
|
|
* of the workspace. Treat it as though the workspace is its parent
|
|
|
|
|
* and move it to the next output. */
|
|
|
|
|
DLOG("Grandparent is workspace\n");
|
|
|
|
|
move_to_output_directed(con, direction);
|
2013-11-26 11:46:10 +01:00
|
|
|
|
} else {
|
|
|
|
|
DLOG("Moving into container above\n");
|
|
|
|
|
position = (direction == D_UP || direction == D_LEFT ? BEFORE : AFTER);
|
2011-02-14 23:05:20 +01:00
|
|
|
|
insert_con_into(con, above, position);
|
2013-11-26 11:46:10 +01:00
|
|
|
|
}
|
2011-02-14 23:05:20 +01:00
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
/* We need to call con_focus() to fix the focus stack "above" the container
|
|
|
|
|
* we just inserted the focused container into (otherwise, the parent
|
|
|
|
|
* container(s) would still point to the old container(s)). */
|
|
|
|
|
con_focus(con);
|
|
|
|
|
|
2012-01-22 16:15:35 +01:00
|
|
|
|
/* force re-painting the indicators */
|
|
|
|
|
FREE(con->deco_render_params);
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
tree_flatten(croot);
|
2014-06-19 10:58:03 +02:00
|
|
|
|
ipc_send_window_event("move", con);
|
Handle the EWMH atom _NET_WM_DESKTOP.
We already claim _NET_WM_DESKTOP support in _NET_SUPPORTED since around 2009,
but haven't actually done anything with it. However, especially pagers like
gnome-panel rely on this property to be updated and many tools, like GTK, want
to use the corresponding client messages to make a window sticky, move it
around etc.
This patch implements full support according to the EWMH spec. This means:
* We set the property on all windows when managing it.
* We keep the property updated on all windows at all times.
* We read and respect the property upon managing a window if it was set before
mapping the window.
* We react to client messages for it.
* We remove the property on withdrawn windows.
Note that the special value 0xFFFFFFFF, according to the spec, means that the
window shall be shown on all workspaces. We do this by making it sticky and
float it. This shows it on all workspaces at least on the output it is on.
Furthermore, the spec gives us the freedom to ignore _NET_WM_DESKTOP when
managing a window if we have good reason to. In our case, we give window
swallowing a higher priority since the user would likely expect that and we
want to keep placeholder windows only around for as long as we have to.
However, we do prioritize this property over, for example, startup
notifications.
fixes #2153
fixes #1507
fixes #938
2016-01-11 20:53:26 +01:00
|
|
|
|
ewmh_update_wm_desktop();
|
2011-02-14 23:05:20 +01:00
|
|
|
|
}
|