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.c264
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 */
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 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
100static struct sway_container *container_get_in_direction( 91static 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
229static 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
168static 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
258static struct cmd_results *focus_output(struct sway_container *con, 186static 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 }