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
|
|
|
|
|
2017-09-23 18:23:49 +02:00
|
|
|
|
/*
|
|
|
|
|
* Returns the lowest container in the tree that has both a and b as descendants.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static Con *lowest_common_ancestor(Con *a, Con *b) {
|
|
|
|
|
Con *parent_a = a;
|
|
|
|
|
while (parent_a) {
|
|
|
|
|
Con *parent_b = b;
|
|
|
|
|
while (parent_b) {
|
|
|
|
|
if (parent_a == parent_b) {
|
|
|
|
|
return parent_a;
|
|
|
|
|
}
|
|
|
|
|
parent_b = parent_b->parent;
|
|
|
|
|
}
|
|
|
|
|
parent_a = parent_a->parent;
|
|
|
|
|
}
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Returns the direct child of ancestor that contains con.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static Con *child_containing_con_recursively(Con *ancestor, Con *con) {
|
|
|
|
|
Con *child = con;
|
|
|
|
|
while (child && child->parent != ancestor) {
|
|
|
|
|
child = child->parent;
|
|
|
|
|
assert(child->parent);
|
|
|
|
|
}
|
|
|
|
|
return child;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Returns true if the given container is the focused descendant of ancestor, recursively.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static bool is_focused_descendant(Con *con, Con *ancestor) {
|
|
|
|
|
Con *current = con;
|
|
|
|
|
while (current != ancestor) {
|
|
|
|
|
if (TAILQ_FIRST(&(current->parent->focus_head)) != current) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
current = current->parent;
|
|
|
|
|
assert(current->parent);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
/*
|
|
|
|
|
* This function detaches 'con' from its parent and inserts it either before or
|
|
|
|
|
* after 'target'.
|
|
|
|
|
*
|
|
|
|
|
*/
|
2017-09-13 02:54:40 +02:00
|
|
|
|
void insert_con_into(Con *con, Con *target, position_t position) {
|
2011-02-14 23:05:20 +01:00
|
|
|
|
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;
|
|
|
|
|
|
2017-09-23 18:23:49 +02:00
|
|
|
|
/* We compare the focus order of the children of the lowest common ancestor. If con or
|
|
|
|
|
* its ancestor is before target's ancestor then con should be placed before the target
|
|
|
|
|
* in the focus stack. */
|
|
|
|
|
Con *lca = lowest_common_ancestor(con, parent);
|
|
|
|
|
if (lca == con) {
|
|
|
|
|
ELOG("Container is being inserted into one of its descendants.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Con *con_ancestor = child_containing_con_recursively(lca, con);
|
|
|
|
|
Con *target_ancestor = child_containing_con_recursively(lca, target);
|
|
|
|
|
bool moves_focus_from_ancestor = is_focused_descendant(con, con_ancestor);
|
|
|
|
|
bool focus_before;
|
|
|
|
|
|
|
|
|
|
/* Determine if con is going to be placed before or after target in the parent's focus stack. */
|
|
|
|
|
if (con_ancestor == target_ancestor) {
|
|
|
|
|
/* Happens when the target is con's old parent. Eg with layout V [ A H [ B C ] ],
|
|
|
|
|
* if we move C up. Target will be H. */
|
|
|
|
|
focus_before = moves_focus_from_ancestor;
|
|
|
|
|
} else {
|
|
|
|
|
/* Look at the focus stack order of the children of the lowest common ancestor. */
|
|
|
|
|
Con *current;
|
|
|
|
|
TAILQ_FOREACH(current, &(lca->focus_head), focused) {
|
|
|
|
|
if (current == con_ancestor || current == target_ancestor) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
focus_before = (current == con_ancestor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If con is the focused container in our old ancestor we place the new ancestor
|
|
|
|
|
* before the old ancestor in the focus stack. Example:
|
|
|
|
|
* Consider the layout [ H [ V1 [ A* B ] V2 [ C ] ] ] where A is focused. We move to
|
|
|
|
|
* a second workspace and from there we move A to the right and switch back to the
|
|
|
|
|
* original workspace. Without the change focus would move to B instead of staying
|
|
|
|
|
* with A. */
|
|
|
|
|
if (moves_focus_from_ancestor && focus_before) {
|
|
|
|
|
Con *place = TAILQ_PREV(con_ancestor, focus_head, focused);
|
|
|
|
|
TAILQ_REMOVE(&(lca->focus_head), target_ancestor, focused);
|
|
|
|
|
if (place) {
|
|
|
|
|
TAILQ_INSERT_AFTER(&(lca->focus_head), place, target_ancestor, focused);
|
|
|
|
|
} else {
|
|
|
|
|
TAILQ_INSERT_HEAD(&(lca->focus_head), target_ancestor, focused);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
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;
|
|
|
|
|
|
2017-09-23 18:23:49 +02:00
|
|
|
|
if (parent == lca) {
|
|
|
|
|
if (focus_before) {
|
|
|
|
|
/* Example layout: H [ A B* ], we move A up/down. 'target' will be H. */
|
|
|
|
|
TAILQ_INSERT_BEFORE(target, con, focused);
|
|
|
|
|
} else {
|
|
|
|
|
/* Example layout: H [ A B* ], we move A up/down. 'target' will be H. */
|
|
|
|
|
TAILQ_INSERT_AFTER(&(parent->focus_head), target, con, focused);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (focus_before) {
|
|
|
|
|
/* Example layout: V [ H [ A B ] C* ], we move C up. 'target' will be A. */
|
|
|
|
|
TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
|
|
|
|
|
} else {
|
|
|
|
|
/* Example layout: V [ H [ A* B ] C ], we move C up. 'target' will be A. */
|
|
|
|
|
TAILQ_INSERT_TAIL(&(parent->focus_head), con, focused);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-14 23:05:20 +01:00
|
|
|
|
if (position == BEFORE) {
|
|
|
|
|
TAILQ_INSERT_BEFORE(target, con, nodes);
|
|
|
|
|
} else if (position == AFTER) {
|
|
|
|
|
TAILQ_INSERT_AFTER(&(parent->nodes_head), target, con, nodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
2018-12-12 18:09:39 +01:00
|
|
|
|
Con *old_parent = con->parent;
|
2011-02-14 23:05:20 +01:00
|
|
|
|
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);
|
|
|
|
|
} else {
|
|
|
|
|
TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes);
|
|
|
|
|
}
|
2018-10-09 20:45:59 +02:00
|
|
|
|
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);
|
2018-12-12 18:09:39 +01:00
|
|
|
|
|
|
|
|
|
con_fix_percent(old_parent);
|
|
|
|
|
CALL(old_parent, on_remove_child);
|
2011-02-14 23:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
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) {
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 20:45:59 +02:00
|
|
|
|
Con *old_ws = con_get_workspace(con);
|
|
|
|
|
const bool moves_focus = (focused == con);
|
2013-11-13 09:39:32 +01:00
|
|
|
|
attach_to_workspace(con, ws, direction);
|
2018-10-09 20:45:59 +02:00
|
|
|
|
if (moves_focus) {
|
|
|
|
|
/* workspace_show will not correctly update the active workspace because
|
|
|
|
|
* the focused container, con, is now a child of ws. To work around this
|
|
|
|
|
* and still produce the correct workspace focus events (see
|
|
|
|
|
* 517-regress-move-direction-ipc.t) we need to temporarily set focused
|
|
|
|
|
* to the old workspace. */
|
|
|
|
|
focused = old_ws;
|
|
|
|
|
workspace_show(ws);
|
|
|
|
|
con_focus(con);
|
|
|
|
|
}
|
2014-01-13 21:36:11 +01:00
|
|
|
|
|
|
|
|
|
/* force re-painting the indicators */
|
|
|
|
|
FREE(con->deco_render_params);
|
|
|
|
|
|
|
|
|
|
tree_flatten(croot);
|
2018-10-09 20:45:59 +02:00
|
|
|
|
ipc_send_window_event("move", con);
|
|
|
|
|
ewmh_update_wm_desktop();
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 14:46:40 +01:00
|
|
|
|
if (con->fullscreen_mode == CF_GLOBAL) {
|
|
|
|
|
DLOG("Not moving fullscreen global container\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((con->fullscreen_mode == CF_OUTPUT) ||
|
|
|
|
|
(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
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 14:36:23 +02:00
|
|
|
|
orientation_t o = orientation_from_direction(direction);
|
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) {
|
2018-10-09 21:39:38 +02:00
|
|
|
|
Con *swap = (direction == D_LEFT || direction == D_UP)
|
|
|
|
|
? TAILQ_PREV(con, nodes_head, nodes)
|
|
|
|
|
: TAILQ_NEXT(con, nodes);
|
|
|
|
|
if (swap) {
|
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;
|
|
|
|
|
}
|
2018-10-09 21:39:38 +02:00
|
|
|
|
|
|
|
|
|
DLOG("Swapping with sibling.\n");
|
|
|
|
|
if (direction == D_LEFT || direction == D_UP) {
|
2011-02-19 20:11:47 +01:00
|
|
|
|
TAILQ_SWAP(swap, con, &(swap->parent->nodes_head), nodes);
|
2018-10-09 21:39:38 +02:00
|
|
|
|
} else {
|
2014-06-15 19:07:02 +02:00
|
|
|
|
TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes);
|
2018-10-09 21:39:38 +02:00
|
|
|
|
}
|
2011-02-19 20:11:47 +01:00
|
|
|
|
|
2014-06-19 10:58:03 +02:00
|
|
|
|
ipc_send_window_event("move", con);
|
2011-02-19 20:11:47 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-13 09:39:32 +01:00
|
|
|
|
if (con->parent == con_get_workspace(con)) {
|
2018-10-09 20:45:59 +02:00
|
|
|
|
/* If we couldn't find a place to move it on this workspace, try
|
|
|
|
|
* to move it to a workspace on a different output */
|
2013-11-13 09:39:32 +01:00
|
|
|
|
move_to_output_directed(con, direction);
|
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);
|
2018-10-09 20:45:59 +02:00
|
|
|
|
return;
|
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:
|
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
|
|
|
|
}
|