diff options
Diffstat (limited to 'sway/commands/focus.c')
-rw-r--r-- | sway/commands/focus.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 6659a683..a9fa9a0f 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -31,6 +31,199 @@ static bool parse_movement_direction(const char *name, | |||
31 | return true; | 31 | return true; |
32 | } | 32 | } |
33 | 33 | ||
34 | /** | ||
35 | * Get swayc in the direction of newly entered output. | ||
36 | */ | ||
37 | static struct sway_container *get_swayc_in_output_direction( | ||
38 | struct sway_container *output, enum movement_direction dir, | ||
39 | struct sway_seat *seat) { | ||
40 | if (!output) { | ||
41 | return NULL; | ||
42 | } | ||
43 | |||
44 | struct sway_container *ws = seat_get_focus_inactive(seat, output); | ||
45 | if (ws->type != C_WORKSPACE) { | ||
46 | ws = container_parent(ws, C_WORKSPACE); | ||
47 | } | ||
48 | |||
49 | if (ws == NULL) { | ||
50 | wlr_log(WLR_ERROR, "got an output without a workspace"); | ||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | if (ws->children->length > 0) { | ||
55 | switch (dir) { | ||
56 | case MOVE_LEFT: | ||
57 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { | ||
58 | // get most right child of new output | ||
59 | return ws->children->items[ws->children->length-1]; | ||
60 | } else { | ||
61 | return seat_get_focus_inactive(seat, ws); | ||
62 | } | ||
63 | case MOVE_RIGHT: | ||
64 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { | ||
65 | // get most left child of new output | ||
66 | return ws->children->items[0]; | ||
67 | } else { | ||
68 | return seat_get_focus_inactive(seat, ws); | ||
69 | } | ||
70 | case MOVE_UP: | ||
71 | case MOVE_DOWN: { | ||
72 | struct sway_container *focused = | ||
73 | seat_get_focus_inactive(seat, ws); | ||
74 | if (focused && focused->parent) { | ||
75 | struct sway_container *parent = focused->parent; | ||
76 | if (parent->layout == L_VERT) { | ||
77 | if (dir == MOVE_UP) { | ||
78 | // get child furthest down on new output | ||
79 | int idx = parent->children->length - 1; | ||
80 | return parent->children->items[idx]; | ||
81 | } else if (dir == MOVE_DOWN) { | ||
82 | // get child furthest up on new output | ||
83 | return parent->children->items[0]; | ||
84 | } | ||
85 | } | ||
86 | return focused; | ||
87 | } | ||
88 | break; | ||
89 | } | ||
90 | default: | ||
91 | break; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return ws; | ||
96 | } | ||
97 | |||
98 | static struct sway_container *container_get_in_direction( | ||
99 | struct sway_container *container, struct sway_seat *seat, | ||
100 | enum movement_direction dir) { | ||
101 | struct sway_container *parent = container->parent; | ||
102 | |||
103 | if (dir == MOVE_CHILD) { | ||
104 | return seat_get_focus_inactive(seat, container); | ||
105 | } | ||
106 | if (container->is_fullscreen) { | ||
107 | if (dir == MOVE_PARENT) { | ||
108 | return NULL; | ||
109 | } | ||
110 | container = container_parent(container, C_OUTPUT); | ||
111 | parent = container->parent; | ||
112 | } else { | ||
113 | if (dir == MOVE_PARENT) { | ||
114 | if (parent->type == C_OUTPUT || container_is_floating(container)) { | ||
115 | return NULL; | ||
116 | } else { | ||
117 | return parent; | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | struct sway_container *wrap_candidate = NULL; | ||
123 | while (true) { | ||
124 | bool can_move = false; | ||
125 | int desired; | ||
126 | int idx = list_find(container->parent->children, container); | ||
127 | if (idx == -1) { | ||
128 | return NULL; | ||
129 | } | ||
130 | if (parent->type == C_ROOT) { | ||
131 | enum wlr_direction wlr_dir = 0; | ||
132 | if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), | ||
133 | "got invalid direction: %d", dir)) { | ||
134 | return NULL; | ||
135 | } | ||
136 | int lx = container->x + container->width / 2; | ||
137 | int ly = container->y + container->height / 2; | ||
138 | struct wlr_output_layout *layout = | ||
139 | root_container.sway_root->output_layout; | ||
140 | struct wlr_output *wlr_adjacent = | ||
141 | wlr_output_layout_adjacent_output(layout, wlr_dir, | ||
142 | container->sway_output->wlr_output, lx, ly); | ||
143 | struct sway_container *adjacent = | ||
144 | output_from_wlr_output(wlr_adjacent); | ||
145 | |||
146 | if (!adjacent || adjacent == container) { | ||
147 | if (!wrap_candidate) { | ||
148 | return NULL; | ||
149 | } | ||
150 | return seat_get_focus_inactive_view(seat, wrap_candidate); | ||
151 | } | ||
152 | struct sway_container *next = | ||
153 | get_swayc_in_output_direction(adjacent, dir, seat); | ||
154 | if (next == NULL) { | ||
155 | return NULL; | ||
156 | } | ||
157 | struct sway_container *next_workspace = next; | ||
158 | if (next_workspace->type != C_WORKSPACE) { | ||
159 | next_workspace = container_parent(next_workspace, C_WORKSPACE); | ||
160 | } | ||
161 | sway_assert(next_workspace, "Next container has no workspace"); | ||
162 | if (next_workspace->sway_workspace->fullscreen) { | ||
163 | return seat_get_focus_inactive(seat, | ||
164 | next_workspace->sway_workspace->fullscreen); | ||
165 | } | ||
166 | if (next->children && next->children->length) { | ||
167 | // TODO consider floating children as well | ||
168 | return seat_get_focus_inactive_view(seat, next); | ||
169 | } else { | ||
170 | return next; | ||
171 | } | ||
172 | } else { | ||
173 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | ||
174 | if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { | ||
175 | can_move = true; | ||
176 | desired = idx + (dir == MOVE_LEFT ? -1 : 1); | ||
177 | } | ||
178 | } else { | ||
179 | if (parent->layout == L_VERT || parent->layout == L_STACKED) { | ||
180 | can_move = true; | ||
181 | desired = idx + (dir == MOVE_UP ? -1 : 1); | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | if (can_move) { | ||
187 | // TODO handle floating | ||
188 | if (desired < 0 || desired >= parent->children->length) { | ||
189 | can_move = false; | ||
190 | int len = parent->children->length; | ||
191 | if (config->focus_wrapping != WRAP_NO && !wrap_candidate | ||
192 | && len > 1) { | ||
193 | if (desired < 0) { | ||
194 | wrap_candidate = parent->children->items[len-1]; | ||
195 | } else { | ||
196 | wrap_candidate = parent->children->items[0]; | ||
197 | } | ||
198 | if (config->focus_wrapping == WRAP_FORCE) { | ||
199 | return seat_get_focus_inactive_view(seat, | ||
200 | wrap_candidate); | ||
201 | } | ||
202 | } | ||
203 | } else { | ||
204 | struct sway_container *desired_con = | ||
205 | parent->children->items[desired]; | ||
206 | wlr_log(WLR_DEBUG, | ||
207 | "cont %d-%p dir %i sibling %d: %p", idx, | ||
208 | container, dir, desired, desired_con); | ||
209 | return seat_get_focus_inactive_view(seat, desired_con); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | if (!can_move) { | ||
214 | container = parent; | ||
215 | parent = parent->parent; | ||
216 | if (!parent) { | ||
217 | // wrapping is the last chance | ||
218 | if (!wrap_candidate) { | ||
219 | return NULL; | ||
220 | } | ||
221 | return seat_get_focus_inactive_view(seat, wrap_candidate); | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | |||
34 | static struct cmd_results *focus_mode(struct sway_container *con, | 227 | static struct cmd_results *focus_mode(struct sway_container *con, |
35 | struct sway_seat *seat, bool floating) { | 228 | struct sway_seat *seat, bool floating) { |
36 | struct sway_container *ws = con->type == C_WORKSPACE ? | 229 | struct sway_container *ws = con->type == C_WORKSPACE ? |