diff options
Diffstat (limited to 'sway/commands/focus.c')
-rw-r--r-- | sway/commands/focus.c | 264 |
1 files changed, 102 insertions, 162 deletions
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index f342e524..e31898af 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -34,211 +34,139 @@ 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 | return &container->node; | ||
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 | return &container->node; | ||
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 | return &container->node; | ||
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 | return &container->node; |
91 | } | 82 | } |
92 | default: | 83 | default: |
93 | break; | 84 | break; |
94 | } | 85 | } |
95 | } | 86 | } |
96 | 87 | ||
97 | return ws; | 88 | return &ws->node; |
98 | } | 89 | } |
99 | 90 | ||
100 | static struct sway_container *container_get_in_direction( | 91 | static struct sway_node *node_get_in_direction(struct sway_container *container, |
101 | struct sway_container *container, struct sway_seat *seat, | 92 | struct sway_seat *seat, enum movement_direction dir) { |
102 | enum movement_direction dir) { | ||
103 | struct sway_container *parent = container->parent; | ||
104 | |||
105 | if (dir == MOVE_CHILD) { | 93 | if (dir == MOVE_CHILD) { |
106 | return seat_get_focus_inactive(seat, container); | 94 | return seat_get_active_child(seat, &container->node); |
107 | } | 95 | } |
108 | if (container->is_fullscreen) { | 96 | if (container->is_fullscreen) { |
109 | if (dir == MOVE_PARENT) { | 97 | if (dir == MOVE_PARENT) { |
110 | return NULL; | 98 | return NULL; |
111 | } | 99 | } |
112 | container = container_parent(container, C_OUTPUT); | 100 | // Fullscreen container with a direction - go straight to outputs |
113 | parent = container->parent; | 101 | struct sway_output *output = container->workspace->output; |
114 | } else { | 102 | struct sway_output *new_output = output_get_in_direction(output, dir); |
115 | if (dir == MOVE_PARENT) { | 103 | return get_node_in_output_direction(new_output, dir); |
116 | if (parent->type == C_OUTPUT || container_is_floating(container)) { | 104 | } |
117 | return NULL; | 105 | if (dir == MOVE_PARENT) { |
118 | } else { | 106 | return node_get_parent(&container->node); |
119 | return parent; | ||
120 | } | ||
121 | } | ||
122 | } | 107 | } |
123 | 108 | ||
124 | struct sway_container *wrap_candidate = NULL; | 109 | struct sway_container *wrap_candidate = NULL; |
125 | while (true) { | 110 | struct sway_container *current = container; |
111 | while (current) { | ||
126 | bool can_move = false; | 112 | bool can_move = false; |
127 | int desired; | 113 | int desired; |
128 | int idx = list_find(container->parent->children, container); | 114 | int idx = container_sibling_index(current); |
129 | if (idx == -1) { | 115 | enum sway_container_layout parent_layout = |
130 | return NULL; | 116 | container_parent_layout(current); |
131 | } | 117 | 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 | 118 | ||
148 | if (!adjacent || adjacent == container) { | 119 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { |
149 | if (!wrap_candidate) { | 120 | if (parent_layout == L_HORIZ || parent_layout == L_TABBED) { |
150 | return NULL; | 121 | can_move = true; |
151 | } | 122 | 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 | } | 123 | } |
174 | } else { | 124 | } else { |
175 | if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { | 125 | if (parent_layout == L_VERT || parent_layout == L_STACKED) { |
176 | if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { | 126 | can_move = true; |
177 | can_move = true; | 127 | 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 | } | 128 | } |
186 | } | 129 | } |
187 | 130 | ||
188 | if (can_move) { | 131 | if (can_move) { |
189 | // TODO handle floating | 132 | if (desired < 0 || desired >= siblings->length) { |
190 | if (desired < 0 || desired >= parent->children->length) { | ||
191 | can_move = false; | 133 | can_move = false; |
192 | int len = parent->children->length; | 134 | int len = siblings->length; |
193 | if (config->focus_wrapping != WRAP_NO && !wrap_candidate | 135 | if (config->focus_wrapping != WRAP_NO && !wrap_candidate |
194 | && len > 1) { | 136 | && len > 1) { |
195 | if (desired < 0) { | 137 | if (desired < 0) { |
196 | wrap_candidate = parent->children->items[len-1]; | 138 | wrap_candidate = siblings->items[len-1]; |
197 | } else { | 139 | } else { |
198 | wrap_candidate = parent->children->items[0]; | 140 | wrap_candidate = siblings->items[0]; |
199 | } | 141 | } |
200 | if (config->focus_wrapping == WRAP_FORCE) { | 142 | if (config->focus_wrapping == WRAP_FORCE) { |
201 | return seat_get_focus_inactive_view(seat, | 143 | struct sway_container *c = seat_get_focus_inactive_view( |
202 | wrap_candidate); | 144 | seat, &wrap_candidate->node); |
145 | return &c->node; | ||
203 | } | 146 | } |
204 | } | 147 | } |
205 | } else { | 148 | } else { |
206 | struct sway_container *desired_con = | 149 | struct sway_container *desired_con = siblings->items[desired]; |
207 | parent->children->items[desired]; | 150 | struct sway_container *c = seat_get_focus_inactive_view( |
208 | wlr_log(WLR_DEBUG, | 151 | seat, &desired_con->node); |
209 | "cont %d-%p dir %i sibling %d: %p", idx, | 152 | return &c->node; |
210 | container, dir, desired, desired_con); | ||
211 | return seat_get_focus_inactive_view(seat, desired_con); | ||
212 | } | 153 | } |
213 | } | 154 | } |
214 | 155 | ||
215 | if (!can_move) { | 156 | 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 | } | 157 | } |
227 | } | ||
228 | 158 | ||
229 | static struct cmd_results *focus_mode(struct sway_container *con, | 159 | // Check a different output |
230 | struct sway_seat *seat, bool floating) { | 160 | struct sway_output *output = container->workspace->output; |
231 | struct sway_container *ws = con->type == C_WORKSPACE ? | 161 | struct sway_output *new_output = output_get_in_direction(output, dir); |
232 | con : container_parent(con, C_WORKSPACE); | 162 | if (new_output) { |
233 | 163 | return get_node_in_output_direction(new_output, dir); | |
234 | // If the container is in a floating split container, | ||
235 | // operate on the split container instead of the child. | ||
236 | if (container_is_floating_or_child(con)) { | ||
237 | while (con->parent->type != C_WORKSPACE) { | ||
238 | con = con->parent; | ||
239 | } | ||
240 | } | 164 | } |
165 | return NULL; | ||
166 | } | ||
241 | 167 | ||
168 | static struct cmd_results *focus_mode(struct sway_workspace *ws, | ||
169 | struct sway_seat *seat, bool floating) { | ||
242 | struct sway_container *new_focus = NULL; | 170 | struct sway_container *new_focus = NULL; |
243 | if (floating) { | 171 | if (floating) { |
244 | new_focus = seat_get_focus_inactive_floating(seat, ws); | 172 | new_focus = seat_get_focus_inactive_floating(seat, ws); |
@@ -246,7 +174,7 @@ static struct cmd_results *focus_mode(struct sway_container *con, | |||
246 | new_focus = seat_get_focus_inactive_tiling(seat, ws); | 174 | new_focus = seat_get_focus_inactive_tiling(seat, ws); |
247 | } | 175 | } |
248 | if (new_focus) { | 176 | if (new_focus) { |
249 | seat_set_focus(seat, new_focus); | 177 | seat_set_focus(seat, &new_focus->node); |
250 | } else { | 178 | } else { |
251 | return cmd_results_new(CMD_FAILURE, "focus", | 179 | return cmd_results_new(CMD_FAILURE, "focus", |
252 | "Failed to find a %s container in workspace", | 180 | "Failed to find a %s container in workspace", |
@@ -255,14 +183,14 @@ static struct cmd_results *focus_mode(struct sway_container *con, | |||
255 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 183 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
256 | } | 184 | } |
257 | 185 | ||
258 | static struct cmd_results *focus_output(struct sway_container *con, | 186 | static struct cmd_results *focus_output(struct sway_seat *seat, |
259 | struct sway_seat *seat, int argc, char **argv) { | 187 | int argc, char **argv) { |
260 | if (!argc) { | 188 | if (!argc) { |
261 | return cmd_results_new(CMD_INVALID, "focus", | 189 | return cmd_results_new(CMD_INVALID, "focus", |
262 | "Expected 'focus output <direction|name>'"); | 190 | "Expected 'focus output <direction|name>'"); |
263 | } | 191 | } |
264 | char *identifier = join_args(argv, argc); | 192 | char *identifier = join_args(argv, argc); |
265 | struct sway_container *output = output_by_name(identifier); | 193 | struct sway_output *output = output_by_name(identifier); |
266 | 194 | ||
267 | if (!output) { | 195 | if (!output) { |
268 | enum movement_direction direction; | 196 | enum movement_direction direction; |
@@ -272,14 +200,13 @@ static struct cmd_results *focus_output(struct sway_container *con, | |||
272 | return cmd_results_new(CMD_INVALID, "focus", | 200 | return cmd_results_new(CMD_INVALID, "focus", |
273 | "There is no output with that name"); | 201 | "There is no output with that name"); |
274 | } | 202 | } |
275 | struct sway_container *focus = seat_get_focus(seat); | 203 | struct sway_workspace *ws = seat_get_focused_workspace(seat); |
276 | focus = container_parent(focus, C_OUTPUT); | 204 | output = output_get_in_direction(ws->output, direction); |
277 | output = container_get_in_direction(focus, seat, direction); | ||
278 | } | 205 | } |
279 | 206 | ||
280 | free(identifier); | 207 | free(identifier); |
281 | if (output) { | 208 | if (output) { |
282 | seat_set_focus(seat, seat_get_focus_inactive(seat, output)); | 209 | seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node)); |
283 | } | 210 | } |
284 | 211 | ||
285 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 212 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
@@ -289,29 +216,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
289 | if (config->reading || !config->active) { | 216 | if (config->reading || !config->active) { |
290 | return cmd_results_new(CMD_DEFER, NULL, NULL); | 217 | return cmd_results_new(CMD_DEFER, NULL, NULL); |
291 | } | 218 | } |
292 | struct sway_container *con = config->handler_context.current_container; | 219 | struct sway_node *node = config->handler_context.node; |
220 | struct sway_container *container = config->handler_context.container; | ||
221 | struct sway_workspace *workspace = config->handler_context.workspace; | ||
293 | struct sway_seat *seat = config->handler_context.seat; | 222 | struct sway_seat *seat = config->handler_context.seat; |
294 | if (con->type < C_WORKSPACE) { | 223 | if (node->type < N_WORKSPACE) { |
295 | return cmd_results_new(CMD_FAILURE, "focus", | 224 | return cmd_results_new(CMD_FAILURE, "focus", |
296 | "Command 'focus' cannot be used above the workspace level"); | 225 | "Command 'focus' cannot be used above the workspace level"); |
297 | } | 226 | } |
298 | 227 | ||
299 | if (argc == 0) { | 228 | if (argc == 0) { |
300 | seat_set_focus(seat, con); | 229 | seat_set_focus(seat, node); |
301 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 230 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
302 | } | 231 | } |
303 | 232 | ||
304 | if (strcmp(argv[0], "floating") == 0) { | 233 | if (strcmp(argv[0], "floating") == 0) { |
305 | return focus_mode(con, seat, true); | 234 | return focus_mode(workspace, seat, true); |
306 | } else if (strcmp(argv[0], "tiling") == 0) { | 235 | } else if (strcmp(argv[0], "tiling") == 0) { |
307 | return focus_mode(con, seat, false); | 236 | return focus_mode(workspace, seat, false); |
308 | } else if (strcmp(argv[0], "mode_toggle") == 0) { | 237 | } else if (strcmp(argv[0], "mode_toggle") == 0) { |
309 | return focus_mode(con, seat, !container_is_floating_or_child(con)); | 238 | bool floating = container && container_is_floating_or_child(container); |
239 | return focus_mode(workspace, seat, !floating); | ||
310 | } | 240 | } |
311 | 241 | ||
312 | if (strcmp(argv[0], "output") == 0) { | 242 | if (strcmp(argv[0], "output") == 0) { |
313 | argc--; argv++; | 243 | argc--; argv++; |
314 | return focus_output(con, seat, argc, argv); | 244 | return focus_output(seat, argc, argv); |
315 | } | 245 | } |
316 | 246 | ||
317 | enum movement_direction direction = 0; | 247 | enum movement_direction direction = 0; |
@@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
321 | "or 'focus output <direction|name>'"); | 251 | "or 'focus output <direction|name>'"); |
322 | } | 252 | } |
323 | 253 | ||
324 | struct sway_container *next_focus = container_get_in_direction( | 254 | if (node->type == N_WORKSPACE) { |
325 | con, seat, direction); | 255 | // A workspace is focused, so just jump to the next output |
256 | struct sway_output *new_output = | ||
257 | output_get_in_direction(workspace->output, direction); | ||
258 | struct sway_node *node = | ||
259 | get_node_in_output_direction(new_output, direction); | ||
260 | seat_set_focus(seat, node); | ||
261 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
262 | } | ||
263 | |||
264 | struct sway_node *next_focus = | ||
265 | node_get_in_direction(container, seat, direction); | ||
326 | if (next_focus) { | 266 | if (next_focus) { |
327 | seat_set_focus(seat, next_focus); | 267 | seat_set_focus(seat, next_focus); |
328 | } | 268 | } |