From a547365a8874015ae65fb94896120d7641f1b4e5 Mon Sep 17 00:00:00 2001 From: Peter Bui Date: Sat, 6 Aug 2011 12:28:05 -0400 Subject: [PATCH 1/4] Implement switching focus across screens. Modify _tree_next() so that when we reach the workspace container: 1. Find the next corresponding output (screen) using the added get_output_next(). 2. If there is another output, find the visible workspace. 3. Call workspace_show on found workspace. 4. Find the appropriate window to focus (leftmost/rightmost, etc.) using con_descend_direction, and then focus it. I've only tested on horizontal monitors (left/right). --- include/con.h | 8 ++++++++ include/randr.h | 6 ++++++ src/con.c | 38 ++++++++++++++++++++++++++++++++++++++ src/randr.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/tree.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 136 insertions(+), 2 deletions(-) diff --git a/include/con.h b/include/con.h index 6ce7bf84..7d828408 100644 --- a/include/con.h +++ b/include/con.h @@ -177,6 +177,14 @@ Con *con_descend_focused(Con *con); */ Con *con_descend_tiling_focused(Con *con); +/* + * Returns the leftmost, rightmost, etc. container in sub-tree. For example, if + * direction is D_LEFT, then we return the rightmost container and if direction + * is D_RIGHT, we return the leftmost container. This is because if we are + * moving D_LEFT, and thus want the rightmost container. + */ +Con *con_descend_direction(Con *con, direction_t direction); + /** * Returns a "relative" Rect which contains the amount of pixels that need to * be added to the original Rect to get the final position (obviously the diff --git a/include/randr.h b/include/randr.h index d14d0478..9c09f2b1 100644 --- a/include/randr.h +++ b/include/randr.h @@ -91,4 +91,10 @@ Output *get_output_containing(int x, int y); */ Output *get_output_most(direction_t direction, Output *current); +/** + * Gets the output which is the next one in the given direction. + * + */ +Output *get_output_next(direction_t direction, Output *current); + #endif diff --git a/src/con.c b/src/con.c index 1cf1779e..e42e8c77 100644 --- a/src/con.c +++ b/src/con.c @@ -773,6 +773,44 @@ Con *con_descend_tiling_focused(Con *con) { return next; } +/* + * Recursively walk tree of nodes and check all nodes for condition. Returns + * container that matches condition (i.e. leftmost, rightmost, etc.). + * + */ +Con *_con_descend_direction(Con *con, Con *next, direction_t direction) { + #define DESCEND_DIRECTION(condition) \ + if (TAILQ_EMPTY(&(con->nodes_head))) \ + if (!next || condition) \ + next = con; \ + NODES_FOREACH(con) \ + next = _con_descend_direction(child, next, direction); \ + break; + + switch (direction) { + case D_LEFT: + DESCEND_DIRECTION(next->rect.x < con->rect.x) + case D_RIGHT: + DESCEND_DIRECTION(next->rect.x > con->rect.x) + case D_UP: + DESCEND_DIRECTION(next->rect.y > con->rect.y) + case D_DOWN: + DESCEND_DIRECTION(next->rect.y < con->rect.y) + } + + return next; +} + +/* + * Returns the leftmost, rightmost, etc. container in sub-tree. For example, if + * direction is D_LEFT, then we return the rightmost container and if direction + * is D_RIGHT, we return the leftmost container. This is because if we are + * moving D_LEFT, and thus want the rightmost container. + * + */ +Con *con_descend_direction(Con *con, direction_t direction) { + return _con_descend_direction(con, NULL, direction); +} /* * Returns a "relative" Rect which contains the amount of pixels that need to diff --git a/src/randr.c b/src/randr.c index e48e2065..bd887630 100644 --- a/src/randr.c +++ b/src/randr.c @@ -143,6 +143,48 @@ Output *get_output_most(direction_t direction, Output *current) { return candidate; } +/* + * Gets the output which is the next one in the given direction. + * + */ +Output *get_output_next(direction_t direction, Output *current) { + Output *output, *candidate = NULL; + + TAILQ_FOREACH(output, &outputs, outputs) { + if (!output->active) + continue; + + if (((direction == D_UP) || (direction == D_DOWN)) && + (current->rect.x != output->rect.x)) + continue; + + if (((direction == D_LEFT) || (direction == D_RIGHT)) && + (current->rect.y != output->rect.y)) + continue; + + switch (direction) { + case D_UP: + if (current->rect.y < output->rect.y && (!candidate || output->rect.y < candidate->rect.y)) + candidate = output; + break; + case D_DOWN: + if (current->rect.y > output->rect.y && (!candidate || output->rect.y > candidate->rect.y)) + candidate = output; + break; + case D_LEFT: + if (current->rect.x > output->rect.x && (!candidate || output->rect.x > candidate->rect.x)) + candidate = output; + break; + case D_RIGHT: + if (current->rect.x < output->rect.x && (!candidate || output->rect.x < candidate->rect.x)) + candidate = output; + break; + } + } + + return candidate; +} + /* * Disables RandR support by creating exactly one output with the size of the * X11 screen. diff --git a/src/tree.c b/src/tree.c index 272276f4..fb6d6cc7 100644 --- a/src/tree.c +++ b/src/tree.c @@ -376,9 +376,49 @@ void tree_render() { * */ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) { - /* Stop recursing at workspaces */ - if (con->type == CT_WORKSPACE) + /* Stop recursing at workspaces after attempting to switch to next + * workspace if possible. */ + if (con->type == CT_WORKSPACE) { + Output *current_output = get_output_containing(con->rect.x, con->rect.y); + Output *next_output; + + if (!current_output) + return false; + DLOG("Current output is %s\n", current_output->name); + + /* Try to find next output */ + direction_t direction; + if (way == 'n' && orientation == HORIZ) + direction = D_RIGHT; + else if (way == 'p' && orientation == HORIZ) + direction = D_LEFT; + else if (way == 'n' && orientation == VERT) + direction = D_UP; + else if (way == 'p' && orientation == VERT) + direction = D_DOWN; + else + return false; + + next_output = get_output_next(direction, current_output); + if (!next_output) + return false; + DLOG("Next output is %s\n", next_output->name); + + /* Find visible workspace on next output */ + Con* workspace = NULL; + GREP_FIRST(workspace, output_get_content(next_output->con), workspace_is_visible(child)); + + /* Show next workspace and focus appropriate container if possible. */ + if (workspace) { + workspace_show(workspace->name); + Con* focus = con_descend_direction(workspace, direction); + if (focus) + con_focus(focus); + return true; + } + return false; + } if (con->type == CT_FLOATING_CON) { /* TODO: implement focus for floating windows */ From 692d65b0fd7ab8c8618de09a8f8f5fba031bd4b0 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 7 Aug 2011 15:24:51 +0200 Subject: [PATCH 2/4] little style fixes --- src/tree.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/tree.c b/src/tree.c index fb6d6cc7..c76a47ee 100644 --- a/src/tree.c +++ b/src/tree.c @@ -405,19 +405,18 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) DLOG("Next output is %s\n", next_output->name); /* Find visible workspace on next output */ - Con* workspace = NULL; + Con *workspace = NULL; GREP_FIRST(workspace, output_get_content(next_output->con), workspace_is_visible(child)); /* Show next workspace and focus appropriate container if possible. */ - if (workspace) { - workspace_show(workspace->name); - Con* focus = con_descend_direction(workspace, direction); - if (focus) - con_focus(focus); - return true; - } + if (!workspace) + return false; - return false; + workspace_show(workspace->name); + Con *focus = con_descend_direction(workspace, direction); + if (focus) + con_focus(focus); + return true; } if (con->type == CT_FLOATING_CON) { From 99ba193ce76b020f22fd17858f4fecdc1e75ef58 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 7 Aug 2011 15:46:24 +0200 Subject: [PATCH 3/4] Bugfix: the up/down directions were swapped Also compare 'output' and 'current' in the same order in both parts of the condition to make the comparison more clear. --- src/randr.c | 12 ++++++++---- src/tree.c | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/randr.c b/src/randr.c index bd887630..aa2ea351 100644 --- a/src/randr.c +++ b/src/randr.c @@ -164,19 +164,23 @@ Output *get_output_next(direction_t direction, Output *current) { switch (direction) { case D_UP: - if (current->rect.y < output->rect.y && (!candidate || output->rect.y < candidate->rect.y)) + if (output->rect.y < current->rect.y && + (!candidate || output->rect.y < candidate->rect.y)) candidate = output; break; case D_DOWN: - if (current->rect.y > output->rect.y && (!candidate || output->rect.y > candidate->rect.y)) + if (output->rect.y > current->rect.y && + (!candidate || output->rect.y > candidate->rect.y)) candidate = output; break; case D_LEFT: - if (current->rect.x > output->rect.x && (!candidate || output->rect.x > candidate->rect.x)) + if (output->rect.x < current->rect.x && + (!candidate || output->rect.x > candidate->rect.x)) candidate = output; break; case D_RIGHT: - if (current->rect.x < output->rect.x && (!candidate || output->rect.x < candidate->rect.x)) + if (output->rect.x > current->rect.x && + (!candidate || output->rect.x < candidate->rect.x)) candidate = output; break; } diff --git a/src/tree.c b/src/tree.c index c76a47ee..55bf27d8 100644 --- a/src/tree.c +++ b/src/tree.c @@ -393,9 +393,9 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) else if (way == 'p' && orientation == HORIZ) direction = D_LEFT; else if (way == 'n' && orientation == VERT) - direction = D_UP; - else if (way == 'p' && orientation == VERT) direction = D_DOWN; + else if (way == 'p' && orientation == VERT) + direction = D_UP; else return false; From d39261a1f0552b08ae16112455a3fede1c04d4fb Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 7 Aug 2011 15:57:36 +0200 Subject: [PATCH 4/4] rewrite con_descend_orientation It now uses the container orientation (if it is appropriate, the last focused one otherwise) to recurse. This works better if the target workspace is in vertical orientation when you use right/left or if it is in horizontal orientation but you use up/down. --- src/con.c | 72 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/src/con.c b/src/con.c index e42e8c77..5b5acc1b 100644 --- a/src/con.c +++ b/src/con.c @@ -773,34 +773,6 @@ Con *con_descend_tiling_focused(Con *con) { return next; } -/* - * Recursively walk tree of nodes and check all nodes for condition. Returns - * container that matches condition (i.e. leftmost, rightmost, etc.). - * - */ -Con *_con_descend_direction(Con *con, Con *next, direction_t direction) { - #define DESCEND_DIRECTION(condition) \ - if (TAILQ_EMPTY(&(con->nodes_head))) \ - if (!next || condition) \ - next = con; \ - NODES_FOREACH(con) \ - next = _con_descend_direction(child, next, direction); \ - break; - - switch (direction) { - case D_LEFT: - DESCEND_DIRECTION(next->rect.x < con->rect.x) - case D_RIGHT: - DESCEND_DIRECTION(next->rect.x > con->rect.x) - case D_UP: - DESCEND_DIRECTION(next->rect.y > con->rect.y) - case D_DOWN: - DESCEND_DIRECTION(next->rect.y < con->rect.y) - } - - return next; -} - /* * Returns the leftmost, rightmost, etc. container in sub-tree. For example, if * direction is D_LEFT, then we return the rightmost container and if direction @@ -809,7 +781,49 @@ Con *_con_descend_direction(Con *con, Con *next, direction_t direction) { * */ Con *con_descend_direction(Con *con, direction_t direction) { - return _con_descend_direction(con, NULL, direction); + Con *most; + DLOG("con_descend_direction(%p, %d)\n", con, direction); + if (direction == D_LEFT || direction == D_RIGHT) { + if (con->orientation == HORIZ) { + /* If the direction is horizontal, we can use either the first + * (D_RIGHT) or the last con (D_LEFT) */ + if (direction == D_RIGHT) + most = TAILQ_FIRST(&(con->nodes_head)); + else most = TAILQ_LAST(&(con->nodes_head), nodes_head); + } else if (con->orientation == VERT) { + /* 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)); + } else { + /* If the con has no orientation set, it’s not a split container + * but a container with a client window, so stop recursing */ + return con; + } + } + + if (direction == D_UP || direction == D_DOWN) { + if (con->orientation == VERT) { + /* If the direction is vertical, we can use either the first + * (D_DOWN) or the last con (D_UP) */ + if (direction == D_UP) + most = TAILQ_LAST(&(con->nodes_head), nodes_head); + else most = TAILQ_FIRST(&(con->nodes_head)); + } else if (con->orientation == HORIZ) { + /* 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)); + } else { + /* If the con has no orientation set, it’s not a split container + * but a container with a client window, so stop recursing */ + return con; + } + } + + if (!most) + return con; + return con_descend_direction(most, direction); } /*