From a547365a8874015ae65fb94896120d7641f1b4e5 Mon Sep 17 00:00:00 2001 From: Peter Bui Date: Sat, 6 Aug 2011 12:28:05 -0400 Subject: [PATCH] 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 */