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..5b5acc1b 100644 --- a/src/con.c +++ b/src/con.c @@ -773,6 +773,58 @@ Con *con_descend_tiling_focused(Con *con) { 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) { + 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); +} /* * 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..aa2ea351 100644 --- a/src/randr.c +++ b/src/randr.c @@ -143,6 +143,52 @@ 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 (output->rect.y < current->rect.y && + (!candidate || output->rect.y < candidate->rect.y)) + candidate = output; + break; + case D_DOWN: + if (output->rect.y > current->rect.y && + (!candidate || output->rect.y > candidate->rect.y)) + candidate = output; + break; + case D_LEFT: + if (output->rect.x < current->rect.x && + (!candidate || output->rect.x > candidate->rect.x)) + candidate = output; + break; + case D_RIGHT: + if (output->rect.x > current->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..55bf27d8 100644 --- a/src/tree.c +++ b/src/tree.c @@ -376,9 +376,48 @@ 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) - return false; + /* 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_DOWN; + else if (way == 'p' && orientation == VERT) + direction = D_UP; + 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) + 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) { /* TODO: implement focus for floating windows */