aboutsummaryrefslogtreecommitdiff
path: root/sway/commands/focus.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/focus.c')
-rw-r--r--sway/commands/focus.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 6659a683..f342e524 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -6,9 +6,11 @@
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/tree/arrange.h"
+#include "sway/tree/root.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "stringop.h"
+#include "util.h"
static bool parse_movement_direction(const char *name,
enum movement_direction *out) {
@@ -31,6 +33,199 @@ static bool parse_movement_direction(const char *name,
return true;
}
+/**
+ * Get swayc in the direction of newly entered output.
+ */
+static struct sway_container *get_swayc_in_output_direction(
+ struct sway_container *output, enum movement_direction dir,
+ struct sway_seat *seat) {
+ if (!output) {
+ return NULL;
+ }
+
+ struct sway_container *ws = seat_get_focus_inactive(seat, output);
+ if (ws->type != C_WORKSPACE) {
+ ws = container_parent(ws, C_WORKSPACE);
+ }
+
+ if (ws == NULL) {
+ wlr_log(WLR_ERROR, "got an output without a workspace");
+ return NULL;
+ }
+
+ if (ws->children->length > 0) {
+ switch (dir) {
+ case MOVE_LEFT:
+ if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
+ // get most right child of new output
+ return ws->children->items[ws->children->length-1];
+ } else {
+ return seat_get_focus_inactive(seat, ws);
+ }
+ case MOVE_RIGHT:
+ if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
+ // get most left child of new output
+ return ws->children->items[0];
+ } else {
+ return seat_get_focus_inactive(seat, ws);
+ }
+ case MOVE_UP:
+ case MOVE_DOWN: {
+ struct sway_container *focused =
+ seat_get_focus_inactive(seat, ws);
+ if (focused && focused->parent) {
+ struct sway_container *parent = focused->parent;
+ if (parent->layout == L_VERT) {
+ if (dir == MOVE_UP) {
+ // get child furthest down on new output
+ int idx = parent->children->length - 1;
+ return parent->children->items[idx];
+ } else if (dir == MOVE_DOWN) {
+ // get child furthest up on new output
+ return parent->children->items[0];
+ }
+ }
+ return focused;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return ws;
+}
+
+static struct sway_container *container_get_in_direction(
+ struct sway_container *container, struct sway_seat *seat,
+ enum movement_direction dir) {
+ struct sway_container *parent = container->parent;
+
+ if (dir == MOVE_CHILD) {
+ return seat_get_focus_inactive(seat, container);
+ }
+ if (container->is_fullscreen) {
+ if (dir == MOVE_PARENT) {
+ return NULL;
+ }
+ container = container_parent(container, C_OUTPUT);
+ parent = container->parent;
+ } else {
+ if (dir == MOVE_PARENT) {
+ if (parent->type == C_OUTPUT || container_is_floating(container)) {
+ return NULL;
+ } else {
+ return parent;
+ }
+ }
+ }
+
+ struct sway_container *wrap_candidate = NULL;
+ while (true) {
+ bool can_move = false;
+ int desired;
+ int idx = list_find(container->parent->children, container);
+ if (idx == -1) {
+ return NULL;
+ }
+ if (parent->type == C_ROOT) {
+ enum wlr_direction wlr_dir = 0;
+ if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
+ "got invalid direction: %d", dir)) {
+ return NULL;
+ }
+ int lx = container->x + container->width / 2;
+ int ly = container->y + container->height / 2;
+ struct wlr_output_layout *layout =
+ root_container.sway_root->output_layout;
+ struct wlr_output *wlr_adjacent =
+ wlr_output_layout_adjacent_output(layout, wlr_dir,
+ container->sway_output->wlr_output, lx, ly);
+ struct sway_container *adjacent =
+ output_from_wlr_output(wlr_adjacent);
+
+ if (!adjacent || adjacent == container) {
+ if (!wrap_candidate) {
+ return NULL;
+ }
+ return seat_get_focus_inactive_view(seat, wrap_candidate);
+ }
+ struct sway_container *next =
+ get_swayc_in_output_direction(adjacent, dir, seat);
+ if (next == NULL) {
+ return NULL;
+ }
+ struct sway_container *next_workspace = next;
+ if (next_workspace->type != C_WORKSPACE) {
+ next_workspace = container_parent(next_workspace, C_WORKSPACE);
+ }
+ sway_assert(next_workspace, "Next container has no workspace");
+ if (next_workspace->sway_workspace->fullscreen) {
+ return seat_get_focus_inactive(seat,
+ next_workspace->sway_workspace->fullscreen);
+ }
+ if (next->children && next->children->length) {
+ // TODO consider floating children as well
+ return seat_get_focus_inactive_view(seat, next);
+ } else {
+ return next;
+ }
+ } else {
+ if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
+ if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_LEFT ? -1 : 1);
+ }
+ } else {
+ if (parent->layout == L_VERT || parent->layout == L_STACKED) {
+ can_move = true;
+ desired = idx + (dir == MOVE_UP ? -1 : 1);
+ }
+ }
+ }
+
+ if (can_move) {
+ // TODO handle floating
+ if (desired < 0 || desired >= parent->children->length) {
+ can_move = false;
+ int len = parent->children->length;
+ if (config->focus_wrapping != WRAP_NO && !wrap_candidate
+ && len > 1) {
+ if (desired < 0) {
+ wrap_candidate = parent->children->items[len-1];
+ } else {
+ wrap_candidate = parent->children->items[0];
+ }
+ if (config->focus_wrapping == WRAP_FORCE) {
+ return seat_get_focus_inactive_view(seat,
+ wrap_candidate);
+ }
+ }
+ } else {
+ struct sway_container *desired_con =
+ parent->children->items[desired];
+ wlr_log(WLR_DEBUG,
+ "cont %d-%p dir %i sibling %d: %p", idx,
+ container, dir, desired, desired_con);
+ return seat_get_focus_inactive_view(seat, desired_con);
+ }
+ }
+
+ if (!can_move) {
+ container = parent;
+ parent = parent->parent;
+ if (!parent) {
+ // wrapping is the last chance
+ if (!wrap_candidate) {
+ return NULL;
+ }
+ return seat_get_focus_inactive_view(seat, wrap_candidate);
+ }
+ }
+ }
+}
+
static struct cmd_results *focus_mode(struct sway_container *con,
struct sway_seat *seat, bool floating) {
struct sway_container *ws = con->type == C_WORKSPACE ?