diff --git a/sway/commands/resize.c b/sway/commands/resize.c index afb369c2..3f243cf7 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -29,6 +29,11 @@ enum resize_axis { RESIZE_AXIS_INVALID, }; +struct resize_amount { + int amount; + enum resize_unit unit; +}; + static enum resize_unit parse_resize_unit(const char *unit) { if (strcasecmp(unit, "px") == 0) { return RESIZE_UNIT_PX; @@ -42,6 +47,30 @@ static enum resize_unit parse_resize_unit(const char *unit) { return RESIZE_UNIT_INVALID; } +// Parse arguments such as "10", "10px" or "10 px". +// Returns the number of arguments consumed. +static int parse_resize_amount(int argc, char **argv, + struct resize_amount *amount) { + char *err; + amount->amount = (int)strtol(argv[0], &err, 10); + if (*err) { + // e.g. 10px + amount->unit = parse_resize_unit(err); + return 1; + } + if (argc == 1) { + amount->unit = RESIZE_UNIT_DEFAULT; + return 1; + } + // Try the second argument + amount->unit = parse_resize_unit(argv[1]); + if (amount->unit == RESIZE_UNIT_INVALID) { + amount->unit = RESIZE_UNIT_DEFAULT; + return 1; + } + return 2; +} + static enum resize_axis parse_resize_axis(const char *axis) { if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { return RESIZE_AXIS_HORIZONTAL; @@ -202,36 +231,39 @@ static void resize_tiled(int amount, enum resize_axis axis) { arrange_and_commit(parent->parent); } -static void resize_floating(int amount, enum resize_axis axis) { +/** + * Implement `resize ` for a floating container. + */ +static struct cmd_results *resize_adjust_floating(enum resize_axis axis, + struct resize_amount *amount) { struct sway_container *con = config->handler_context.current_container; int grow_x = 0, grow_y = 0; int grow_width = 0, grow_height = 0; switch (axis) { case RESIZE_AXIS_HORIZONTAL: - grow_x = -amount / 2; - grow_width = amount; + grow_x = -amount->amount / 2; + grow_width = amount->amount; break; case RESIZE_AXIS_VERTICAL: - grow_y = -amount / 2; - grow_height = amount; + grow_y = -amount->amount / 2; + grow_height = amount->amount; break; case RESIZE_AXIS_UP: - grow_y = -amount; - grow_height = amount; + grow_y = -amount->amount; + grow_height = amount->amount; break; case RESIZE_AXIS_LEFT: - grow_x = -amount; - grow_width = amount; + grow_x = -amount->amount; + grow_width = amount->amount; break; case RESIZE_AXIS_DOWN: - grow_height = amount; + grow_height = amount->amount; break; case RESIZE_AXIS_RIGHT: - grow_width = amount; + grow_width = amount->amount; break; case RESIZE_AXIS_INVALID: - sway_assert(false, "Didn't expect RESIZE_AXIS_INVALID"); - return; + return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); } con->x += grow_x; con->y += grow_y; @@ -247,80 +279,64 @@ static void resize_floating(int amount, enum resize_axis axis) { } arrange_and_commit(con); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { +/** + * Implement `resize ` for a tiled container. + */ +static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, + struct resize_amount *amount) { struct sway_container *current = config->handler_context.current_container; - if (container_is_floating(current)) { - return resize_floating(amount, axis); - } - if (unit == RESIZE_UNIT_DEFAULT) { - unit = RESIZE_UNIT_PPT; + if (amount->unit == RESIZE_UNIT_DEFAULT) { + amount->unit = RESIZE_UNIT_PPT; } - if (unit == RESIZE_UNIT_PPT) { - float pct = amount / 100.0f; + if (amount->unit == RESIZE_UNIT_PPT) { + float pct = amount->amount / 100.0f; + // TODO: Make left/right/up/down resize in that direction? switch (axis) { + case RESIZE_AXIS_LEFT: + case RESIZE_AXIS_RIGHT: case RESIZE_AXIS_HORIZONTAL: - amount = (float)current->width * pct; + amount->amount = (float)current->width * pct; break; + case RESIZE_AXIS_UP: + case RESIZE_AXIS_DOWN: case RESIZE_AXIS_VERTICAL: - amount = (float)current->height * pct; + amount->amount = (float)current->height * pct; break; - default: - sway_assert(0, "invalid resize axis"); - return; + case RESIZE_AXIS_INVALID: + return cmd_results_new(CMD_INVALID, "resize", + "Invalid resize axis/direction"); } } - return resize_tiled(amount, axis); + resize_tiled(amount->amount, axis); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -// resize set [px|ppt] [px|ppt] -static struct cmd_results *cmd_resize_set(int argc, char **argv) { - struct cmd_results *error; - if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { - return error; - } - struct sway_container *con = config->handler_context.current_container; - if (!container_is_floating(con)) { - return cmd_results_new(CMD_INVALID, "resize", - "resize set is not currently supported for tiled containers"); - } - const char *usage = "Expected 'resize set '"; - - char *err; - size_t width = (int)strtol(*argv, &err, 10); - if (*err) { - // e.g. `resize set width 500px` - enum resize_unit unit = parse_resize_unit(err); - if (unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - } - --argc; ++argv; - if (parse_resize_unit(argv[0]) != RESIZE_UNIT_INVALID) { - --argc; ++argv; - } - - if (!argc) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - size_t height = (int)strtol(*argv, &err, 10); - if (*err) { - // e.g. `resize set height 500px` - enum resize_unit unit = parse_resize_unit(err); - if (unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } - } +/** + * Implement `resize set` for a tiled container. + */ +static struct cmd_results *resize_set_tiled(struct sway_container *con, + struct resize_amount *width, struct resize_amount *height) { + return cmd_results_new(CMD_INVALID, "resize", + "'resize set' is not implemented for tiled views"); +} - int grow_width = width - con->width; - int grow_height = height - con->height; +/** + * Implement `resize set` for a floating container. + */ +static struct cmd_results *resize_set_floating(struct sway_container *con, + struct resize_amount *width, struct resize_amount *height) { + int grow_width = width->amount - con->width; + int grow_height = height->amount - con->height; con->x -= grow_width / 2; con->y -= grow_height / 2; - con->width = width; - con->height = height; + con->width = width->amount; + con->height = height->amount; if (con->type == C_VIEW) { struct sway_view *view = con->sway_view; @@ -335,73 +351,168 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -struct cmd_results *cmd_resize(int argc, char **argv) { - struct sway_container *current = config->handler_context.current_container; - if (!current) { - return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); - } - if (current->type != C_VIEW && current->type != C_CONTAINER) { - return cmd_results_new(CMD_INVALID, "resize", - "Can only resize views/containers"); - } - +/** + * resize set + * + * args: [px|ppt] [px|ppt] + */ +static struct cmd_results *cmd_resize_set(int argc, char **argv) { struct cmd_results *error; if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { return error; } + const char *usage = "Expected 'resize set '"; - if (strcasecmp(argv[0], "set") == 0) { - return cmd_resize_set(argc - 1, &argv[1]); + // Width + struct resize_amount width; + int num_consumed_args = parse_resize_amount(argc, argv, &width); + argc -= num_consumed_args; + argv += num_consumed_args; + if (width.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "resize", usage); } - const char *usage = "Expected 'resize " - " [] [px|ppt]'"; - - int multiplier = 0; - if (strcasecmp(*argv, "grow") == 0) { - multiplier = 1; - } else if (strcasecmp(*argv, "shrink") == 0) { - multiplier = -1; - } else { + // Height + struct resize_amount height; + num_consumed_args = parse_resize_amount(argc, argv, &height); + argc -= num_consumed_args; + argv += num_consumed_args; + if (height.unit == RESIZE_UNIT_INVALID) { return cmd_results_new(CMD_INVALID, "resize", usage); } - --argc; ++argv; + // If 0, don't resize that dimension + struct sway_container *con = config->handler_context.current_container; + if (width.amount <= 0) { + width.amount = con->width; + } + if (height.amount <= 0) { + height.amount = con->height; + } + + if (container_is_floating(con)) { + return resize_set_floating(con, &width, &height); + } + return resize_set_tiled(con, &width, &height); +} + +/** + * resize + * + * args: + * args: + * args: or + */ +static struct cmd_results *cmd_resize_adjust(int argc, char **argv, + int multiplier) { + const char *usage = "Expected 'resize grow|shrink " + "[ px|ppt [or px|ppt]]'"; enum resize_axis axis = parse_resize_axis(*argv); if (axis == RESIZE_AXIS_INVALID) { return cmd_results_new(CMD_INVALID, "resize", usage); } --argc; ++argv; - int amount = 10; // Default amount - enum resize_unit unit = RESIZE_UNIT_DEFAULT; - + // First amount + struct resize_amount first_amount; if (argc) { - char *err; - amount = (int)strtol(*argv, &err, 10); - if (*err) { - // e.g. `resize grow width 10px` - unit = parse_resize_unit(err); - if (unit == RESIZE_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, "resize", usage); - } + int num_consumed_args = parse_resize_amount(argc, argv, &first_amount); + argc -= num_consumed_args; + argv += num_consumed_args; + if (first_amount.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); } - --argc; ++argv; + } else { + first_amount.amount = 10; + first_amount.unit = RESIZE_UNIT_DEFAULT; } + // "or" if (argc) { - unit = parse_resize_unit(*argv); - if (unit == RESIZE_UNIT_INVALID) { + if (strcmp(*argv, "or") != 0) { return cmd_results_new(CMD_INVALID, "resize", usage); } --argc; ++argv; } + // Second amount + struct resize_amount second_amount; if (argc) { - // Provied too many args, the bastard - return cmd_results_new(CMD_INVALID, "resize", usage); + int num_consumed_args = parse_resize_amount(argc, argv, &second_amount); + argc -= num_consumed_args; + argv += num_consumed_args; + if (second_amount.unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + } else { + second_amount.unit = RESIZE_UNIT_INVALID; } - resize(amount * multiplier, axis, unit); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + first_amount.amount *= multiplier; + second_amount.amount *= multiplier; + + struct sway_container *con = config->handler_context.current_container; + if (container_is_floating(con)) { + // Floating containers can only resize in px. Choose an amount which + // uses px, with fallback to an amount that specified no unit. + if (first_amount.unit == RESIZE_UNIT_PX) { + return resize_adjust_floating(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_PX) { + return resize_adjust_floating(axis, &second_amount); + } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_floating(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_floating(axis, &second_amount); + } else { + return cmd_results_new(CMD_INVALID, "resize", + "Floating containers cannot use ppt measurements"); + } + } + + // For tiling, prefer ppt -> default -> px + if (first_amount.unit == RESIZE_UNIT_PPT) { + return resize_adjust_tiled(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_PPT) { + return resize_adjust_tiled(axis, &second_amount); + } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_tiled(axis, &first_amount); + } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) { + return resize_adjust_tiled(axis, &second_amount); + } else { + return resize_adjust_tiled(axis, &first_amount); + } +} + +struct cmd_results *cmd_resize(int argc, char **argv) { + struct sway_container *current = config->handler_context.current_container; + if (!current) { + return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); + } + if (current->type != C_VIEW && current->type != C_CONTAINER) { + return cmd_results_new(CMD_INVALID, "resize", + "Can only resize views/containers"); + } + + struct cmd_results *error; + if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { + return error; + } + + if (strcasecmp(argv[0], "set") == 0) { + return cmd_resize_set(argc - 1, &argv[1]); + } + if (strcasecmp(argv[0], "grow") == 0) { + return cmd_resize_adjust(argc - 1, &argv[1], 1); + } + if (strcasecmp(argv[0], "shrink") == 0) { + return cmd_resize_adjust(argc - 1, &argv[1], -1); + } + + const char *usage = "Expected 'resize " + " [] [px|ppt]'"; + + return cmd_results_new(CMD_INVALID, "resize", usage); }