diff options
Diffstat (limited to 'sway/commands/focus.c')
-rw-r--r-- | sway/commands/focus.c | 284 |
1 files changed, 121 insertions, 163 deletions
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index f342e524..83b8c64a 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -34,58 +34,49 @@ static bool parse_movement_direction(const char *name, | |||
34 | } | 34 | } |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * Get swayc in the direction of newly entered output. | 37 | * Get node in the direction of newly entered output. |
38 | */ | 38 | */ |
39 | static struct sway_container *get_swayc_in_output_direction( | 39 | static struct sway_node *get_node_in_output_direction( |
40 | struct sway_container *output, enum movement_direction dir, | 40 | struct sway_output *output, enum movement_direction dir) { |
41 | struct sway_seat *seat) { | 41 | struct sway_seat *seat = config->handler_context.seat; |
42 | if (!output) { | 42 | struct sway_workspace *ws = output_get_active_workspace(output); |
43 | return NULL; | 43 | if (ws->fullscreen) { |
44 | } | 44 | return seat_get_focus_inactive(seat, &ws->fullscreen->node); |
45 | |||
46 | struct sway_container *ws = seat_get_focus_inactive(seat, output); | ||
47 | if (ws->type != C_WORKSPACE) { | ||
48 | ws = container_parent(ws, C_WORKSPACE); | ||
49 | } | ||
50 | |||
51 | if (ws == NULL) { | ||
52 | wlr_log(WLR_ERROR, "got an output without a workspace"); | ||
53 | return NULL; | ||
54 | } | 45 | } |
46 | struct sway_container *container = NULL; | ||
55 | 47 | ||
56 | if (ws->children->length > 0) { | 48 | if (ws->tiling->length > 0) { |
57 | switch (dir) { | 49 | switch (dir) { |
58 | case MOVE_LEFT: | 50 | case MOVE_LEFT: |
59 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { | 51 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { |
60 | // get most right child of new output | 52 | // get most right child of new output |
61 | return ws->children->items[ws->children->length-1]; | 53 | container = ws->tiling->items[ws->tiling->length-1]; |
62 | } else { | 54 | } else { |
63 | return seat_get_focus_inactive(seat, ws); | 55 | container = seat_get_focus_inactive_tiling(seat, ws); |
64 | } | 56 | } |
57 | break; | ||
65 | case MOVE_RIGHT: | 58 | case MOVE_RIGHT: |
66 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { | 59 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { |
67 | // get most left child of new output | 60 | // get most left child of new output |
68 | return ws->children->items[0]; | 61 | container = ws->tiling->items[0]; |
69 | } else { | 62 | } else { |
70 | return seat_get_focus_inactive(seat, ws); | 63 | container = seat_get_focus_inactive_tiling(seat, ws); |
71 | } | 64 | } |
65 | break; | ||
72 | case MOVE_UP: | 66 | case MOVE_UP: |
67 | if (ws->layout == L_VERT || ws->layout == L_STACKED) { | ||
68 | // get most bottom child of new output | ||
69 | container = ws->tiling->items[ws->tiling->length-1]; | ||
70 | } else { | ||
71 | container = seat_get_focus_inactive_tiling(seat, ws); | ||
72 | } | ||
73 | break; | ||
73 | case MOVE_DOWN: { | 74 | case MOVE_DOWN: { |
74 | struct sway_container *focused = | 75 | if (ws->layout == L_VERT || ws->layout == L_STACKED) { |
75 | seat_get_focus_inactive(seat, ws); | 76 | // get most top child of new output |
76 | if (focused && focused->parent) { | 77 | container = ws->tiling->items[0]; |
77 | struct sway_container *parent = focused->parent; | 78 | } else { |
78 | if (parent->layout == L_VERT) { | 79 | container = seat_get_focus_inactive_tiling(seat, ws); |
79 | if (dir == MOVE_UP) { | ||
80 | // get child furthest down on new output | ||
81 | int idx = parent->children->length - 1; | ||
82 | return parent->children->items[idx]; | ||
83 | } else if (dir == MOVE_DOWN) { | ||
84 | // get child furthest up on new output | ||
85 | return parent->children->items[0]; | ||
86 | } | ||
87 | } | ||
88 | return focused; | ||
89 | } | 80 | } |
90 | break; | 81 | break; |
91 | } | 82 | } |
@@ -94,151 +85,90 @@ static struct sway_container *get_swayc_in_output_direction( | |||
94 | } | 85 | } |
95 | } | 86 | } |
96 | 87 | ||
97 | return ws; | 88 | if (container) { |
98 | } | 89 | container = seat_get_focus_inactive_view(seat, &container->node); |
90 | return &container->node; | ||
91 | } | ||
99 | 92 | ||
100 | static struct sway_container *container_get_in_direction( | 93 | return &ws->node; |
101 | struct sway_container *container, struct sway_seat *seat, | 94 | } |
102 | enum movement_direction dir) { | ||
103 | struct sway_container *parent = container->parent; | ||
104 | 95 | ||
105 | if (dir == MOVE_CHILD) { | 96 | static struct sway_node *node_get_in_direction(struct sway_container *container, |
106 | return seat_get_focus_inactive(seat, container); | 97 | struct sway_seat *seat, enum movement_direction dir) { |
107 | } | ||
108 | if (container->is_fullscreen) { | 98 | if (container->is_fullscreen) { |
109 | if (dir == MOVE_PARENT) { | 99 | if (dir == MOVE_PARENT) { |
110 | return NULL; | 100 | return NULL; |
111 | } | 101 | } |
112 | container = container_parent(container, C_OUTPUT); | 102 | // Fullscreen container with a direction - go straight to outputs |
113 | parent = container->parent; | 103 | struct sway_output *output = container->workspace->output; |
114 | } else { | 104 | struct sway_output *new_output = output_get_in_direction(output, dir); |
115 | if (dir == MOVE_PARENT) { | 105 | return get_node_in_output_direction(new_output, dir); |
116 | if (parent->type == C_OUTPUT || container_is_floating(container)) { | 106 | } |
117 | return NULL; | 107 | if (dir == MOVE_PARENT) { |
118 | } else { | 108 | return node_get_parent(&container->node); |
119 | return parent; | ||
120 | } | ||
121 | } | ||
122 | } | 109 | } |
123 | 110 | ||
124 | struct sway_container *wrap_candidate = NULL; | 111 | struct sway_container *wrap_candidate = NULL; |
125 | while (true) { | 112 | struct sway_container *current = container; |
113 | while (current) { | ||
126 | bool can_move = false; | 114 | bool can_move = false; |
127 | int desired; | 115 | int desired; |
128 | int idx = list_find(container->parent->children, container); | 116 | int idx = container_sibling_index(current); |
129 | if (idx == -1) { | 117 | enum sway_container_layout parent_layout = |
130 | return NULL; | 118 | container_parent_layout(current); |
131 | } | 119 | list_t *siblings = container_get_siblings(current); |
132 | if (parent->type == C_ROOT) { | ||
133 | enum wlr_direction wlr_dir = 0; | ||
134 | if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), | ||
135 | "got invalid direction: %d", dir)) { | ||
136 | return NULL; | ||
137 | } | ||
138 | int lx = container->x + container->width / 2; | ||
139 | int ly = container->y + container->height / 2; | ||
140 | struct wlr_output_layout *layout = | ||
141 | root_container.sway_root->output_layout; | ||
142 | struct wlr_output *wlr_adjacent = | ||
143 | wlr_output_layout_adjacent_output(layout, wlr_dir, | ||
144 | container->sway_output->wlr_output, lx, ly); | ||
145 | struct sway_container *adjacent = | ||
146 | output_from_wlr_output(wlr_adjacent); | ||
147 | 120 | ||
148 | if (!adjacent || adjacent == container) { | 121 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { |
149 | if (!wrap_candidate) { | 122 | if (parent_layout == L_HORIZ || parent_layout == L_TABBED) { |
150 | return NULL; | 123 | can_move = true; |
151 | } | 124 | desired = idx + (dir == MOVE_LEFT ? -1 : 1); |
152 | return seat_get_focus_inactive_view(seat, wrap_candidate); | ||
153 | } | ||
154 | struct sway_container *next = | ||
155 | get_swayc_in_output_direction(adjacent, dir, seat); | ||
156 | if (next == NULL) { | ||
157 | return NULL; | ||
158 | } | ||
159 | struct sway_container *next_workspace = next; | ||
160 | if (next_workspace->type != C_WORKSPACE) { | ||
161 | next_workspace = container_parent(next_workspace, C_WORKSPACE); | ||
162 | } | ||
163 | sway_assert(next_workspace, "Next container has no workspace"); | ||
164 | if (next_workspace->sway_workspace->fullscreen) { | ||
165 | return seat_get_focus_inactive(seat, | ||
166 | next_workspace->sway_workspace->fullscreen); | ||
167 | } | ||
168 | if (next->children && next->children->length) { | ||
169 | // TODO consider floating children as well | ||
170 | return seat_get_focus_inactive_view(seat, next); | ||
171 | } else { | ||
172 | return next; | ||
173 | } | 125 | } |
174 | } else { | 126 | } else { |
175 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | 127 | if (parent_layout == L_VERT || parent_layout == L_STACKED) { |
176 | if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { | 128 | can_move = true; |
177 | can_move = true; | 129 | desired = idx + (dir == MOVE_UP ? -1 : 1); |
178 | desired = idx + (dir == MOVE_LEFT ? -1 : 1); | ||
179 | } | ||
180 | } else { | ||
181 | if (parent->layout == L_VERT || parent->layout == L_STACKED) { | ||
182 | can_move = true; | ||
183 | desired = idx + (dir == MOVE_UP ? -1 : 1); | ||
184 | } | ||
185 | } | 130 | } |
186 | } | 131 | } |
187 | 132 | ||
188 | if (can_move) { | 133 | if (can_move) { |
189 | // TODO handle floating | 134 | if (desired < 0 || desired >= siblings->length) { |
190 | if (desired < 0 || desired >= parent->children->length) { | ||
191 | can_move = false; | 135 | can_move = false; |
192 | int len = parent->children->length; | 136 | int len = siblings->length; |
193 | if (config->focus_wrapping != WRAP_NO && !wrap_candidate | 137 | if (config->focus_wrapping != WRAP_NO && !wrap_candidate |
194 | && len > 1) { | 138 | && len > 1) { |
195 | if (desired < 0) { | 139 | if (desired < 0) { |
196 | wrap_candidate = parent->children->items[len-1]; | 140 | wrap_candidate = siblings->items[len-1]; |
197 | } else { | 141 | } else { |
198 | wrap_candidate = parent->children->items[0]; | 142 | wrap_candidate = siblings->items[0]; |
199 | } | 143 | } |
200 | if (config->focus_wrapping == WRAP_FORCE) { | 144 | if (config->focus_wrapping == WRAP_FORCE) { |
201 | return seat_get_focus_inactive_view(seat, | 145 | struct sway_container *c = seat_get_focus_inactive_view( |
202 | wrap_candidate); | 146 | seat, &wrap_candidate->node); |
147 | return &c->node; | ||
203 | } | 148 | } |
204 | } | 149 | } |
205 | } else { | 150 | } else { |
206 | struct sway_container *desired_con = | 151 | struct sway_container *desired_con = siblings->items[desired]; |
207 | parent->children->items[desired]; | 152 | struct sway_container *c = seat_get_focus_inactive_view( |
208 | wlr_log(WLR_DEBUG, | 153 | seat, &desired_con->node); |
209 | "cont %d-%p dir %i sibling %d: %p", idx, | 154 | return &c->node; |
210 | container, dir, desired, desired_con); | ||
211 | return seat_get_focus_inactive_view(seat, desired_con); | ||
212 | } | 155 | } |
213 | } | 156 | } |
214 | 157 | ||
215 | if (!can_move) { | 158 | current = current->parent; |
216 | container = parent; | ||
217 | parent = parent->parent; | ||
218 | if (!parent) { | ||
219 | // wrapping is the last chance | ||
220 | if (!wrap_candidate) { | ||
221 | return NULL; | ||
222 | } | ||
223 | return seat_get_focus_inactive_view(seat, wrap_candidate); | ||
224 | } | ||
225 | } | ||
226 | } | 159 | } |
227 | } | ||
228 | |||
229 | static struct cmd_results *focus_mode(struct sway_container *con, | ||
230 | struct sway_seat *seat, bool floating) { | ||
231 | struct sway_container *ws = con->type == C_WORKSPACE ? | ||
232 | con : container_parent(con, C_WORKSPACE); | ||
233 | 160 | ||
234 | // If the container is in a floating split container, | 161 | // Check a different output |
235 | // operate on the split container instead of the child. | 162 | struct sway_output *output = container->workspace->output; |
236 | if (container_is_floating_or_child(con)) { | 163 | struct sway_output *new_output = output_get_in_direction(output, dir); |
237 | while (con->parent->type != C_WORKSPACE) { | 164 | if (new_output) { |
238 | con = con->parent; | 165 | return get_node_in_output_direction(new_output, dir); |
239 | } | ||
240 | } | 166 | } |
167 | return NULL; | ||
168 | } | ||
241 | 169 | ||
170 | static struct cmd_results *focus_mode(struct sway_workspace *ws, | ||
171 | struct sway_seat *seat, bool floating) { | ||
242 | struct sway_container *new_focus = NULL; | 172 | struct sway_container *new_focus = NULL; |
243 | if (floating) { | 173 | if (floating) { |
244 | new_focus = seat_get_focus_inactive_floating(seat, ws); | 174 | new_focus = seat_get_focus_inactive_floating(seat, ws); |
@@ -246,7 +176,7 @@ static struct cmd_results *focus_mode(struct sway_container *con, | |||
246 | new_focus = seat_get_focus_inactive_tiling(seat, ws); | 176 | new_focus = seat_get_focus_inactive_tiling(seat, ws); |
247 | } | 177 | } |
248 | if (new_focus) { | 178 | if (new_focus) { |
249 | seat_set_focus(seat, new_focus); | 179 | seat_set_focus(seat, &new_focus->node); |
250 | } else { | 180 | } else { |
251 | return cmd_results_new(CMD_FAILURE, "focus", | 181 | return cmd_results_new(CMD_FAILURE, "focus", |
252 | "Failed to find a %s container in workspace", | 182 | "Failed to find a %s container in workspace", |
@@ -255,14 +185,14 @@ static struct cmd_results *focus_mode(struct sway_container *con, | |||
255 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 185 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
256 | } | 186 | } |
257 | 187 | ||
258 | static struct cmd_results *focus_output(struct sway_container *con, | 188 | static struct cmd_results *focus_output(struct sway_seat *seat, |
259 | struct sway_seat *seat, int argc, char **argv) { | 189 | int argc, char **argv) { |
260 | if (!argc) { | 190 | if (!argc) { |
261 | return cmd_results_new(CMD_INVALID, "focus", | 191 | return cmd_results_new(CMD_INVALID, "focus", |
262 | "Expected 'focus output <direction|name>'"); | 192 | "Expected 'focus output <direction|name>'"); |
263 | } | 193 | } |
264 | char *identifier = join_args(argv, argc); | 194 | char *identifier = join_args(argv, argc); |
265 | struct sway_container *output = output_by_name(identifier); | 195 | struct sway_output *output = output_by_name(identifier); |
266 | 196 | ||
267 | if (!output) { | 197 | if (!output) { |
268 | enum movement_direction direction; | 198 | enum movement_direction direction; |
@@ -272,14 +202,13 @@ static struct cmd_results *focus_output(struct sway_container *con, | |||
272 | return cmd_results_new(CMD_INVALID, "focus", | 202 | return cmd_results_new(CMD_INVALID, "focus", |
273 | "There is no output with that name"); | 203 | "There is no output with that name"); |
274 | } | 204 | } |
275 | struct sway_container *focus = seat_get_focus(seat); | 205 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
276 | focus = container_parent(focus, C_OUTPUT); | 206 | output = output_get_in_direction(ws->output, direction); |
277 | output = container_get_in_direction(focus, seat, direction); | ||
278 | } | 207 | } |
279 | 208 | ||
280 | free(identifier); | 209 | free(identifier); |
281 | if (output) { | 210 | if (output) { |
282 | seat_set_focus(seat, seat_get_focus_inactive(seat, output)); | 211 | seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node)); |
283 | } | 212 | } |
284 | 213 | ||
285 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 214 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
@@ -289,29 +218,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
289 | if (config->reading || !config->active) { | 218 | if (config->reading || !config->active) { |
290 | return cmd_results_new(CMD_DEFER, NULL, NULL); | 219 | return cmd_results_new(CMD_DEFER, NULL, NULL); |
291 | } | 220 | } |
292 | struct sway_container *con = config->handler_context.current_container; | 221 | struct sway_node *node = config->handler_context.node; |
222 | struct sway_container *container = config->handler_context.container; | ||
223 | struct sway_workspace *workspace = config->handler_context.workspace; | ||
293 | struct sway_seat *seat = config->handler_context.seat; | 224 | struct sway_seat *seat = config->handler_context.seat; |
294 | if (con->type < C_WORKSPACE) { | 225 | if (node->type < N_WORKSPACE) { |
295 | return cmd_results_new(CMD_FAILURE, "focus", | 226 | return cmd_results_new(CMD_FAILURE, "focus", |
296 | "Command 'focus' cannot be used above the workspace level"); | 227 | "Command 'focus' cannot be used above the workspace level"); |
297 | } | 228 | } |
298 | 229 | ||
299 | if (argc == 0) { | 230 | if (argc == 0) { |
300 | seat_set_focus(seat, con); | 231 | seat_set_focus(seat, node); |
301 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 232 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
302 | } | 233 | } |
303 | 234 | ||
304 | if (strcmp(argv[0], "floating") == 0) { | 235 | if (strcmp(argv[0], "floating") == 0) { |
305 | return focus_mode(con, seat, true); | 236 | return focus_mode(workspace, seat, true); |
306 | } else if (strcmp(argv[0], "tiling") == 0) { | 237 | } else if (strcmp(argv[0], "tiling") == 0) { |
307 | return focus_mode(con, seat, false); | 238 | return focus_mode(workspace, seat, false); |
308 | } else if (strcmp(argv[0], "mode_toggle") == 0) { | 239 | } else if (strcmp(argv[0], "mode_toggle") == 0) { |
309 | return focus_mode(con, seat, !container_is_floating_or_child(con)); | 240 | bool floating = container && container_is_floating_or_child(container); |
241 | return focus_mode(workspace, seat, !floating); | ||
310 | } | 242 | } |
311 | 243 | ||
312 | if (strcmp(argv[0], "output") == 0) { | 244 | if (strcmp(argv[0], "output") == 0) { |
313 | argc--; argv++; | 245 | argc--; argv++; |
314 | return focus_output(con, seat, argc, argv); | 246 | return focus_output(seat, argc, argv); |
315 | } | 247 | } |
316 | 248 | ||
317 | enum movement_direction direction = 0; | 249 | enum movement_direction direction = 0; |
@@ -321,8 +253,34 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
321 | "or 'focus output <direction|name>'"); | 253 | "or 'focus output <direction|name>'"); |
322 | } | 254 | } |
323 | 255 | ||
324 | struct sway_container *next_focus = container_get_in_direction( | 256 | if (direction == MOVE_CHILD) { |
325 | con, seat, direction); | 257 | struct sway_node *focus = seat_get_active_child(seat, node); |
258 | if (focus) { | ||
259 | seat_set_focus(seat, focus); | ||
260 | } | ||
261 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
262 | } | ||
263 | |||
264 | if (node->type == N_WORKSPACE) { | ||
265 | if (direction == MOVE_PARENT) { | ||
266 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
267 | } | ||
268 | |||
269 | // Jump to the next output | ||
270 | struct sway_output *new_output = | ||
271 | output_get_in_direction(workspace->output, direction); | ||
272 | if (!new_output) { | ||
273 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
274 | } | ||
275 | |||
276 | struct sway_node *node = | ||
277 | get_node_in_output_direction(new_output, direction); | ||
278 | seat_set_focus(seat, node); | ||
279 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
280 | } | ||
281 | |||
282 | struct sway_node *next_focus = | ||
283 | node_get_in_direction(container, seat, direction); | ||
326 | if (next_focus) { | 284 | if (next_focus) { |
327 | seat_set_focus(seat, next_focus); | 285 | seat_set_focus(seat, next_focus); |
328 | } | 286 | } |