aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/resize.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/commands/resize.c')
-rw-r--r--sway/commands/resize.c410
1 files changed, 353 insertions, 57 deletions
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 6357343e..e657864c 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -1,4 +1,5 @@
1#include <errno.h> 1#include <errno.h>
2#include <limits.h>
2#include <math.h> 3#include <math.h>
3#include <stdbool.h> 4#include <stdbool.h>
4#include <stdlib.h> 5#include <stdlib.h>
@@ -7,6 +8,7 @@
7#include <wlr/util/log.h> 8#include <wlr/util/log.h>
8#include "sway/commands.h" 9#include "sway/commands.h"
9#include "sway/tree/arrange.h" 10#include "sway/tree/arrange.h"
11#include "sway/tree/view.h"
10#include "log.h" 12#include "log.h"
11 13
12static const int MIN_SANE_W = 100, MIN_SANE_H = 60; 14static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
@@ -21,9 +23,18 @@ enum resize_unit {
21enum resize_axis { 23enum resize_axis {
22 RESIZE_AXIS_HORIZONTAL, 24 RESIZE_AXIS_HORIZONTAL,
23 RESIZE_AXIS_VERTICAL, 25 RESIZE_AXIS_VERTICAL,
26 RESIZE_AXIS_UP,
27 RESIZE_AXIS_DOWN,
28 RESIZE_AXIS_LEFT,
29 RESIZE_AXIS_RIGHT,
24 RESIZE_AXIS_INVALID, 30 RESIZE_AXIS_INVALID,
25}; 31};
26 32
33struct resize_amount {
34 int amount;
35 enum resize_unit unit;
36};
37
27static enum resize_unit parse_resize_unit(const char *unit) { 38static enum resize_unit parse_resize_unit(const char *unit) {
28 if (strcasecmp(unit, "px") == 0) { 39 if (strcasecmp(unit, "px") == 0) {
29 return RESIZE_UNIT_PX; 40 return RESIZE_UNIT_PX;
@@ -37,6 +48,69 @@ static enum resize_unit parse_resize_unit(const char *unit) {
37 return RESIZE_UNIT_INVALID; 48 return RESIZE_UNIT_INVALID;
38} 49}
39 50
51// Parse arguments such as "10", "10px" or "10 px".
52// Returns the number of arguments consumed.
53static int parse_resize_amount(int argc, char **argv,
54 struct resize_amount *amount) {
55 char *err;
56 amount->amount = (int)strtol(argv[0], &err, 10);
57 if (*err) {
58 // e.g. 10px
59 amount->unit = parse_resize_unit(err);
60 return 1;
61 }
62 if (argc == 1) {
63 amount->unit = RESIZE_UNIT_DEFAULT;
64 return 1;
65 }
66 // Try the second argument
67 amount->unit = parse_resize_unit(argv[1]);
68 if (amount->unit == RESIZE_UNIT_INVALID) {
69 amount->unit = RESIZE_UNIT_DEFAULT;
70 return 1;
71 }
72 return 2;
73}
74
75static void calculate_constraints(int *min_width, int *max_width,
76 int *min_height, int *max_height) {
77 struct sway_container *con = config->handler_context.current_container;
78
79 if (config->floating_minimum_width == -1) { // no minimum
80 *min_width = 0;
81 } else if (config->floating_minimum_width == 0) { // automatic
82 *min_width = 75;
83 } else {
84 *min_width = config->floating_minimum_width;
85 }
86
87 if (config->floating_minimum_height == -1) { // no minimum
88 *min_height = 0;
89 } else if (config->floating_minimum_height == 0) { // automatic
90 *min_height = 50;
91 } else {
92 *min_height = config->floating_minimum_height;
93 }
94
95 if (config->floating_maximum_width == -1) { // no maximum
96 *max_width = INT_MAX;
97 } else if (config->floating_maximum_width == 0) { // automatic
98 struct sway_container *ws = container_parent(con, C_WORKSPACE);
99 *max_width = ws->width;
100 } else {
101 *max_width = config->floating_maximum_width;
102 }
103
104 if (config->floating_maximum_height == -1) { // no maximum
105 *max_height = INT_MAX;
106 } else if (config->floating_maximum_height == 0) { // automatic
107 struct sway_container *ws = container_parent(con, C_WORKSPACE);
108 *max_height = ws->height;
109 } else {
110 *max_height = config->floating_maximum_height;
111 }
112}
113
40static enum resize_axis parse_resize_axis(const char *axis) { 114static enum resize_axis parse_resize_axis(const char *axis) {
41 if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { 115 if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {
42 return RESIZE_AXIS_HORIZONTAL; 116 return RESIZE_AXIS_HORIZONTAL;
@@ -44,6 +118,18 @@ static enum resize_axis parse_resize_axis(const char *axis) {
44 if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { 118 if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {
45 return RESIZE_AXIS_VERTICAL; 119 return RESIZE_AXIS_VERTICAL;
46 } 120 }
121 if (strcasecmp(axis, "up") == 0) {
122 return RESIZE_AXIS_UP;
123 }
124 if (strcasecmp(axis, "down") == 0) {
125 return RESIZE_AXIS_DOWN;
126 }
127 if (strcasecmp(axis, "left") == 0) {
128 return RESIZE_AXIS_LEFT;
129 }
130 if (strcasecmp(axis, "right") == 0) {
131 return RESIZE_AXIS_RIGHT;
132 }
47 return RESIZE_AXIS_INVALID; 133 return RESIZE_AXIS_INVALID;
48} 134}
49 135
@@ -95,7 +181,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {
95 return; 181 return;
96 } 182 }
97 183
98 wlr_log(L_DEBUG, 184 wlr_log(WLR_DEBUG,
99 "Found the proper parent: %p. It has %d l conts, and %d r conts", 185 "Found the proper parent: %p. It has %d l conts, and %d r conts",
100 parent->parent, minor_weight, major_weight); 186 parent->parent, minor_weight, major_weight);
101 187
@@ -182,105 +268,315 @@ static void resize_tiled(int amount, enum resize_axis axis) {
182 } 268 }
183 } 269 }
184 270
185 arrange_and_commit(parent->parent); 271 arrange_windows(parent->parent);
186} 272}
187 273
188static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { 274/**
189 struct sway_container *current = config->handler_context.current_container; 275 * Implement `resize <grow|shrink>` for a floating container.
190 if (unit == RESIZE_UNIT_DEFAULT) { 276 */
191 // Default for tiling; TODO floating should be px 277static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
192 unit = RESIZE_UNIT_PPT; 278 struct resize_amount *amount) {
279 struct sway_container *con = config->handler_context.current_container;
280 int grow_width = 0, grow_height = 0;
281 switch (axis) {
282 case RESIZE_AXIS_HORIZONTAL:
283 case RESIZE_AXIS_LEFT:
284 case RESIZE_AXIS_RIGHT:
285 grow_width = amount->amount;
286 break;
287 case RESIZE_AXIS_VERTICAL:
288 case RESIZE_AXIS_UP:
289 case RESIZE_AXIS_DOWN:
290 grow_height = amount->amount;
291 break;
292 case RESIZE_AXIS_INVALID:
293 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
294 }
295 // Make sure we're not adjusting beyond floating min/max size
296 int min_width, max_width, min_height, max_height;
297 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
298 if (con->width + grow_width < min_width) {
299 grow_width = min_width - con->width;
300 } else if (con->width + grow_width > max_width) {
301 grow_width = max_width - con->width;
193 } 302 }
303 if (con->height + grow_height < min_height) {
304 grow_height = min_height - con->height;
305 } else if (con->height + grow_height > max_height) {
306 grow_height = max_height - con->height;
307 }
308 int grow_x = 0, grow_y = 0;
309 switch (axis) {
310 case RESIZE_AXIS_HORIZONTAL:
311 grow_x = -grow_width / 2;
312 break;
313 case RESIZE_AXIS_VERTICAL:
314 grow_y = -grow_height / 2;
315 break;
316 case RESIZE_AXIS_UP:
317 grow_y = -grow_height;
318 break;
319 case RESIZE_AXIS_LEFT:
320 grow_x = -grow_width;
321 break;
322 case RESIZE_AXIS_DOWN:
323 case RESIZE_AXIS_RIGHT:
324 break;
325 case RESIZE_AXIS_INVALID:
326 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
327 }
328 con->x += grow_x;
329 con->y += grow_y;
330 con->width += grow_width;
331 con->height += grow_height;
332
333 if (con->type == C_VIEW) {
334 struct sway_view *view = con->sway_view;
335 view->x += grow_x;
336 view->y += grow_y;
337 view->width += grow_width;
338 view->height += grow_height;
339 }
340
341 arrange_windows(con);
342
343 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
344}
194 345
195 if (unit == RESIZE_UNIT_PPT) { 346/**
196 float pct = amount / 100.0f; 347 * Implement `resize <grow|shrink>` for a tiled container.
348 */
349static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
350 struct resize_amount *amount) {
351 struct sway_container *current = config->handler_context.current_container;
352
353 if (amount->unit == RESIZE_UNIT_DEFAULT) {
354 amount->unit = RESIZE_UNIT_PPT;
355 }
356 if (amount->unit == RESIZE_UNIT_PPT) {
357 float pct = amount->amount / 100.0f;
358 // TODO: Make left/right/up/down resize in that direction?
197 switch (axis) { 359 switch (axis) {
360 case RESIZE_AXIS_LEFT:
361 case RESIZE_AXIS_RIGHT:
198 case RESIZE_AXIS_HORIZONTAL: 362 case RESIZE_AXIS_HORIZONTAL:
199 amount = (float)current->width * pct; 363 amount->amount = (float)current->width * pct;
200 break; 364 break;
365 case RESIZE_AXIS_UP:
366 case RESIZE_AXIS_DOWN:
201 case RESIZE_AXIS_VERTICAL: 367 case RESIZE_AXIS_VERTICAL:
202 amount = (float)current->height * pct; 368 amount->amount = (float)current->height * pct;
203 break; 369 break;
204 default: 370 case RESIZE_AXIS_INVALID:
205 sway_assert(0, "invalid resize axis"); 371 return cmd_results_new(CMD_INVALID, "resize",
206 return; 372 "Invalid resize axis/direction");
207 } 373 }
208 } 374 }
209 375
210 return resize_tiled(amount, axis); 376 resize_tiled(amount->amount, axis);
377 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
211} 378}
212 379
213struct cmd_results *cmd_resize(int argc, char **argv) { 380/**
214 struct sway_container *current = config->handler_context.current_container; 381 * Implement `resize set` for a tiled container.
215 if (!current) { 382 */
216 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); 383static struct cmd_results *resize_set_tiled(struct sway_container *con,
217 } 384 struct resize_amount *width, struct resize_amount *height) {
218 if (current->type != C_VIEW && current->type != C_CONTAINER) { 385 return cmd_results_new(CMD_INVALID, "resize",
219 return cmd_results_new(CMD_INVALID, "resize", 386 "'resize set' is not implemented for tiled views");
220 "Can only resize views/containers"); 387}
388
389/**
390 * Implement `resize set` for a floating container.
391 */
392static struct cmd_results *resize_set_floating(struct sway_container *con,
393 struct resize_amount *width, struct resize_amount *height) {
394 int min_width, max_width, min_height, max_height;
395 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
396 width->amount = fmax(min_width, fmin(width->amount, max_width));
397 height->amount = fmax(min_height, fmin(height->amount, max_height));
398 int grow_width = width->amount - con->width;
399 int grow_height = height->amount - con->height;
400 con->x -= grow_width / 2;
401 con->y -= grow_height / 2;
402 con->width = width->amount;
403 con->height = height->amount;
404
405 if (con->type == C_VIEW) {
406 struct sway_view *view = con->sway_view;
407 view->x -= grow_width / 2;
408 view->y -= grow_height / 2;
409 view->width += grow_width;
410 view->height += grow_height;
221 } 411 }
222 412
413 arrange_windows(con);
414
415 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
416}
417
418/**
419 * resize set <args>
420 *
421 * args: <width> [px|ppt] <height> [px|ppt]
422 */
423static struct cmd_results *cmd_resize_set(int argc, char **argv) {
223 struct cmd_results *error; 424 struct cmd_results *error;
224 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 425 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
225 return error; 426 return error;
226 } 427 }
227 428 const char *usage = "Expected 'resize set <width> <height>'";
228 if (strcasecmp(argv[0], "set") == 0) { 429
229 // TODO 430 // Width
230 //return cmd_resize_set(argc - 1, &argv[1]); 431 struct resize_amount width;
231 return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); 432 int num_consumed_args = parse_resize_amount(argc, argv, &width);
433 argc -= num_consumed_args;
434 argv += num_consumed_args;
435 if (width.unit == RESIZE_UNIT_INVALID) {
436 return cmd_results_new(CMD_INVALID, "resize", usage);
437 }
438 if (!argc) {
439 return cmd_results_new(CMD_INVALID, "resize", usage);
232 } 440 }
233 441
234 // TODO: resize grow|shrink left|right|up|down 442 // Height
443 struct resize_amount height;
444 num_consumed_args = parse_resize_amount(argc, argv, &height);
445 argc -= num_consumed_args;
446 argv += num_consumed_args;
447 if (height.unit == RESIZE_UNIT_INVALID) {
448 return cmd_results_new(CMD_INVALID, "resize", usage);
449 }
235 450
236 const char *usage = "Expected 'resize <shrink|grow> " 451 // If 0, don't resize that dimension
237 "<width|height> [<amount>] [px|ppt]'"; 452 struct sway_container *con = config->handler_context.current_container;
453 if (width.amount <= 0) {
454 width.amount = con->width;
455 }
456 if (height.amount <= 0) {
457 height.amount = con->height;
458 }
238 459
239 int multiplier = 0; 460 if (container_is_floating(con)) {
240 if (strcasecmp(*argv, "grow") == 0) { 461 return resize_set_floating(con, &width, &height);
241 multiplier = 1;
242 } else if (strcasecmp(*argv, "shrink") == 0) {
243 multiplier = -1;
244 } else {
245 return cmd_results_new(CMD_INVALID, "resize", usage);
246 } 462 }
247 --argc; ++argv; 463 return resize_set_tiled(con, &width, &height);
464}
248 465
466/**
467 * resize <grow|shrink> <args>
468 *
469 * args: <direction>
470 * args: <direction> <amount> <unit>
471 * args: <direction> <amount> <unit> or <amount> <other_unit>
472 */
473static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
474 int multiplier) {
475 const char *usage = "Expected 'resize grow|shrink <direction> "
476 "[<amount> px|ppt [or <amount> px|ppt]]'";
249 enum resize_axis axis = parse_resize_axis(*argv); 477 enum resize_axis axis = parse_resize_axis(*argv);
250 if (axis == RESIZE_AXIS_INVALID) { 478 if (axis == RESIZE_AXIS_INVALID) {
251 return cmd_results_new(CMD_INVALID, "resize", usage); 479 return cmd_results_new(CMD_INVALID, "resize", usage);
252 } 480 }
253 --argc; ++argv; 481 --argc; ++argv;
254 482
255 int amount = 10; // Default amount 483 // First amount
256 enum resize_unit unit = RESIZE_UNIT_DEFAULT; 484 struct resize_amount first_amount;
257
258 if (argc) { 485 if (argc) {
259 char *err; 486 int num_consumed_args = parse_resize_amount(argc, argv, &first_amount);
260 amount = (int)strtol(*argv, &err, 10); 487 argc -= num_consumed_args;
261 if (*err) { 488 argv += num_consumed_args;
262 // e.g. `resize grow width 10px` 489 if (first_amount.unit == RESIZE_UNIT_INVALID) {
263 unit = parse_resize_unit(err); 490 return cmd_results_new(CMD_INVALID, "resize", usage);
264 if (unit == RESIZE_UNIT_INVALID) {
265 return cmd_results_new(CMD_INVALID, "resize", usage);
266 }
267 } 491 }
268 --argc; ++argv; 492 } else {
493 first_amount.amount = 10;
494 first_amount.unit = RESIZE_UNIT_DEFAULT;
269 } 495 }
270 496
497 // "or"
271 if (argc) { 498 if (argc) {
272 unit = parse_resize_unit(*argv); 499 if (strcmp(*argv, "or") != 0) {
273 if (unit == RESIZE_UNIT_INVALID) {
274 return cmd_results_new(CMD_INVALID, "resize", usage); 500 return cmd_results_new(CMD_INVALID, "resize", usage);
275 } 501 }
276 --argc; ++argv; 502 --argc; ++argv;
277 } 503 }
278 504
505 // Second amount
506 struct resize_amount second_amount;
279 if (argc) { 507 if (argc) {
280 // Provied too many args, the bastard 508 int num_consumed_args = parse_resize_amount(argc, argv, &second_amount);
281 return cmd_results_new(CMD_INVALID, "resize", usage); 509 argc -= num_consumed_args;
510 argv += num_consumed_args;
511 if (second_amount.unit == RESIZE_UNIT_INVALID) {
512 return cmd_results_new(CMD_INVALID, "resize", usage);
513 }
514 } else {
515 second_amount.unit = RESIZE_UNIT_INVALID;
282 } 516 }
283 517
284 resize(amount * multiplier, axis, unit); 518 first_amount.amount *= multiplier;
285 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 519 second_amount.amount *= multiplier;
520
521 struct sway_container *con = config->handler_context.current_container;
522 if (container_is_floating(con)) {
523 // Floating containers can only resize in px. Choose an amount which
524 // uses px, with fallback to an amount that specified no unit.
525 if (first_amount.unit == RESIZE_UNIT_PX) {
526 return resize_adjust_floating(axis, &first_amount);
527 } else if (second_amount.unit == RESIZE_UNIT_PX) {
528 return resize_adjust_floating(axis, &second_amount);
529 } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) {
530 return resize_adjust_floating(axis, &first_amount);
531 } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) {
532 return resize_adjust_floating(axis, &second_amount);
533 } else {
534 return cmd_results_new(CMD_INVALID, "resize",
535 "Floating containers cannot use ppt measurements");
536 }
537 }
538
539 // For tiling, prefer ppt -> default -> px
540 if (first_amount.unit == RESIZE_UNIT_PPT) {
541 return resize_adjust_tiled(axis, &first_amount);
542 } else if (second_amount.unit == RESIZE_UNIT_PPT) {
543 return resize_adjust_tiled(axis, &second_amount);
544 } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) {
545 return resize_adjust_tiled(axis, &first_amount);
546 } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) {
547 return resize_adjust_tiled(axis, &second_amount);
548 } else {
549 return resize_adjust_tiled(axis, &first_amount);
550 }
551}
552
553struct cmd_results *cmd_resize(int argc, char **argv) {
554 struct sway_container *current = config->handler_context.current_container;
555 if (!current) {
556 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
557 }
558 if (current->type != C_VIEW && current->type != C_CONTAINER) {
559 return cmd_results_new(CMD_INVALID, "resize",
560 "Can only resize views/containers");
561 }
562
563 struct cmd_results *error;
564 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
565 return error;
566 }
567
568 if (strcasecmp(argv[0], "set") == 0) {
569 return cmd_resize_set(argc - 1, &argv[1]);
570 }
571 if (strcasecmp(argv[0], "grow") == 0) {
572 return cmd_resize_adjust(argc - 1, &argv[1], 1);
573 }
574 if (strcasecmp(argv[0], "shrink") == 0) {
575 return cmd_resize_adjust(argc - 1, &argv[1], -1);
576 }
577
578 const char *usage = "Expected 'resize <shrink|grow> "
579 "<width|height|up|down|left|right> [<amount>] [px|ppt]'";
580
581 return cmd_results_new(CMD_INVALID, "resize", usage);
286} 582}