aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/focus.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/focus.c')
-rw-r--r--sway/commands/focus.c284
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 */
39static struct sway_container *get_swayc_in_output_direction( 39static 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
100static 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) { 96static 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
229static 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
170static 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
258static struct cmd_results *focus_output(struct sway_container *con, 188static 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 }