diff --git a/config.in b/config.in index 086b66dc6..54817f2ac 100644 --- a/config.in +++ b/config.in @@ -164,16 +164,16 @@ mode "resize" { # right will grow the containers width # up will shrink the containers height # down will grow the containers height - bindsym $left resize shrink width 10 px or 10 ppt - bindsym $down resize grow height 10 px or 10 ppt - bindsym $up resize shrink height 10 px or 10 ppt - bindsym $right resize grow width 10 px or 10 ppt + bindsym $left resize shrink width 10px + bindsym $down resize grow height 10px + bindsym $up resize shrink height 10px + bindsym $right resize grow width 10px # ditto, with arrow keys - bindsym Left resize shrink width 10 px or 10 ppt - bindsym Down resize grow height 10 px or 10 ppt - bindsym Up resize shrink height 10 px or 10 ppt - bindsym Right resize grow width 10 px or 10 ppt + bindsym Left resize shrink width 10px + bindsym Down resize grow height 10px + bindsym Up resize shrink height 10px + bindsym Right resize grow width 10px # return to default mode bindsym Return mode "default" diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 784dcc9b1..fc5ce21f8 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -13,6 +13,13 @@ enum movement_direction { MOVE_CHILD, }; +enum resize_edge { + RESIZE_EDGE_LEFT, + RESIZE_EDGE_RIGHT, + RESIZE_EDGE_TOP, + RESIZE_EDGE_BOTTOM, +}; + struct sway_container; struct sway_root { @@ -63,4 +70,7 @@ struct sway_container *container_get_in_direction(struct sway_container struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout); +void container_recursive_resize(struct sway_container *container, + double amount, enum resize_edge edge); + #endif diff --git a/sway/commands.c b/sway/commands.c index 2786a8791..22decef36 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -165,6 +165,7 @@ static struct cmd_handler command_handlers[] = { { "move", cmd_move }, { "opacity", cmd_opacity }, { "reload", cmd_reload }, + { "resize", cmd_resize }, { "split", cmd_split }, { "splith", cmd_splith }, { "splitt", cmd_splitt }, diff --git a/sway/commands/resize.c b/sway/commands/resize.c new file mode 100644 index 000000000..93c1fe7de --- /dev/null +++ b/sway/commands/resize.c @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include +#include +#include "sway/commands.h" +#include "sway/tree/layout.h" +#include "log.h" + +static const int MIN_SANE_W = 100, MIN_SANE_H = 60; + +enum resize_unit { + RESIZE_UNIT_PX, + RESIZE_UNIT_PPT, + RESIZE_UNIT_DEFAULT, + RESIZE_UNIT_INVALID, +}; + +enum resize_axis { + RESIZE_AXIS_HORIZONTAL, + RESIZE_AXIS_VERTICAL, + RESIZE_AXIS_INVALID, +}; + +static enum resize_unit parse_resize_unit(const char *unit) { + if (strcasecmp(unit, "px") == 0) { + return RESIZE_UNIT_PX; + } + if (strcasecmp(unit, "ppt") == 0) { + return RESIZE_UNIT_PPT; + } + if (strcasecmp(unit, "default") == 0) { + return RESIZE_UNIT_DEFAULT; + } + return RESIZE_UNIT_INVALID; +} + +static enum resize_axis parse_resize_axis(const char *axis) { + if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { + return RESIZE_AXIS_HORIZONTAL; + } + if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { + return RESIZE_AXIS_VERTICAL; + } + return RESIZE_AXIS_INVALID; +} + +static int parallel_coord(struct sway_container *c, enum resize_axis a) { + return a == RESIZE_AXIS_HORIZONTAL ? c->x : c->y; +} + +static int parallel_size(struct sway_container *c, enum resize_axis a) { + return a == RESIZE_AXIS_HORIZONTAL ? c->width : c->height; +} + +static void resize_tiled(int amount, enum resize_axis axis) { + struct sway_container *parent = config->handler_context.current_container; + struct sway_container *focused = parent; + if (!parent) { + return; + } + + enum sway_container_layout parallel_layout = + axis == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT; + int minor_weight = 0; + int major_weight = 0; + while (parent->parent) { + struct sway_container *next = parent->parent; + if (next->layout == parallel_layout) { + for (int i = 0; i < next->children->length; i++) { + struct sway_container *sibling = next->children->items[i]; + + int sibling_pos = parallel_coord(sibling, axis); + int focused_pos = parallel_coord(focused, axis); + int parent_pos = parallel_coord(parent, axis); + + if (sibling_pos != focused_pos) { + if (sibling_pos < parent_pos) { + minor_weight++; + } else if (sibling_pos > parent_pos) { + major_weight++; + } + } + } + if (major_weight || minor_weight) { + break; + } + } + parent = next; + } + + if (parent->type == C_ROOT) { + return; + } + + wlr_log(L_DEBUG, + "Found the proper parent: %p. It has %d l conts, and %d r conts", + parent->parent, minor_weight, major_weight); + + int min_sane = axis == RESIZE_AXIS_HORIZONTAL ? MIN_SANE_W : MIN_SANE_H; + + //TODO: Ensure rounding is done in such a way that there are NO pixel leaks + // ^ ????? + + for (int i = 0; i < parent->parent->children->length; i++) { + struct sway_container *sibling = parent->parent->children->items[i]; + + int sibling_pos = parallel_coord(sibling, axis); + int focused_pos = parallel_coord(focused, axis); + int parent_pos = parallel_coord(parent, axis); + + int sibling_size = parallel_size(sibling, axis); + int parent_size = parallel_size(parent, axis); + + if (sibling_pos != focused_pos) { + if (sibling_pos < parent_pos) { + double pixels = -amount / minor_weight; + if (major_weight && (sibling_size + pixels / 2) < min_sane) { + return; // Too small + } else if ((sibling_size + pixels) < min_sane) { + return; // Too small + } + } else if (sibling_pos > parent_pos) { + double pixels = -amount / major_weight; + if (minor_weight && (sibling_size + pixels / 2) < min_sane) { + return; // Too small + } else if ((sibling_size + pixels) < min_sane) { + return; // Too small + } + } + } else { + double pixels = amount; + if (parent_size + pixels < min_sane) { + return; // Too small + } + } + } + + enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ? + RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP; + enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ? + RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM; + + for (int i = 0; i < parent->parent->children->length; i++) { + struct sway_container *sibling = parent->parent->children->items[i]; + + int sibling_pos = parallel_coord(sibling, axis); + int focused_pos = parallel_coord(focused, axis); + int parent_pos = parallel_coord(parent, axis); + + if (sibling_pos != focused_pos) { + if (sibling_pos < parent_pos) { + double pixels = -1 * amount; + pixels /= minor_weight; + if (major_weight) { + container_recursive_resize(sibling, pixels / 2, major_edge); + } else { + container_recursive_resize(sibling, pixels, major_edge); + } + } else if (sibling_pos > parent_pos) { + double pixels = -1 * amount; + pixels /= major_weight; + if (minor_weight) { + container_recursive_resize(sibling, pixels / 2, minor_edge); + } else { + container_recursive_resize(sibling, pixels, minor_edge); + } + } + } else { + if (major_weight != 0 && minor_weight != 0) { + double pixels = amount; + pixels /= 2; + container_recursive_resize(parent, pixels, minor_edge); + container_recursive_resize(parent, pixels, major_edge); + } else if (major_weight) { + container_recursive_resize(parent, amount, major_edge); + } else if (minor_weight) { + container_recursive_resize(parent, amount, minor_edge); + } + } + } + + arrange_windows(parent->parent, -1, -1); +} + +static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { + struct sway_container *current = config->handler_context.current_container; + if (unit == RESIZE_UNIT_DEFAULT) { + // Default for tiling; TODO floating should be px + unit = RESIZE_UNIT_PPT; + } + + if (unit == RESIZE_UNIT_PPT) { + float pct = amount / 100.0f; + switch (axis) { + case RESIZE_AXIS_HORIZONTAL: + amount = (float)current->width * pct; + break; + case RESIZE_AXIS_VERTICAL: + amount = (float)current->height * pct; + break; + default: + sway_assert(0, "invalid resize axis"); + return; + } + } + + return resize_tiled(amount, axis); +} + +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"); + } + if (strcasecmp(argv[0], "set") == 0) { + // TODO + //return cmd_resize_set(argc - 1, &argv[1]); + return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); + } + struct cmd_results *error; + if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { + return error; + } + + // TODO: resize grow|shrink left|right|up|down + + 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 { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + --argc; ++argv; + + 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; + + 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); + } + } + --argc; ++argv; + } + + if (argc) { + unit = parse_resize_unit(*argv); + if (unit == RESIZE_UNIT_INVALID) { + return cmd_results_new(CMD_INVALID, "resize", usage); + } + --argc; ++argv; + } + + if (argc) { + // Provied too many args, the bastard + return cmd_results_new(CMD_INVALID, "resize", usage); + } + + resize(amount * multiplier, axis, unit); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/meson.build b/sway/meson.build index f210c195b..ec7b4c42b 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -2,10 +2,28 @@ sway_sources = files( 'main.c', 'server.c', 'commands.c', + 'config.c', + 'criteria.c', + 'ipc-json.c', + 'ipc-server.c', + 'security.c', + + 'desktop/output.c', + 'desktop/layer_shell.c', + 'desktop/wl_shell.c', + 'desktop/xdg_shell_v6.c', + 'desktop/xwayland.c', + 'input/input-manager.c', 'input/seat.c', 'input/cursor.c', 'input/keyboard.c', + + 'config/bar.c', + 'config/output.c', + 'config/seat.c', + 'config/input.c', + 'commands/bar.c', 'commands/bind.c', 'commands/default_orientation.c', @@ -20,13 +38,19 @@ sway_sources = files( 'commands/input.c', 'commands/layout.c', 'commands/mode.c', - 'commands/split.c', + 'commands/mouse_warping.c', 'commands/move.c', + 'commands/output.c', + 'commands/reload.c', + 'commands/resize.c', 'commands/seat.c', 'commands/seat/attach.c', 'commands/seat/fallback.c', 'commands/set.c', + 'commands/split.c', 'commands/swaybg_command.c', + 'commands/workspace.c', + 'commands/bar/activate_button.c', 'commands/bar/binding_mode_indicator.c', 'commands/bar/bindsym.c', @@ -51,6 +75,7 @@ sway_sources = files( 'commands/bar/tray_padding.c', 'commands/bar/workspace_buttons.c', 'commands/bar/wrap_scroll.c', + 'commands/input/accel_profile.c', 'commands/input/click_method.c', 'commands/input/drag_lock.c', @@ -67,24 +92,7 @@ sway_sources = files( 'commands/input/xkb_options.c', 'commands/input/xkb_rules.c', 'commands/input/xkb_variant.c', - 'commands/mouse_warping.c', - 'commands/output.c', - 'commands/reload.c', - 'commands/workspace.c', - 'config.c', - 'config/bar.c', - 'config/output.c', - 'config/seat.c', - 'config/input.c', - 'criteria.c', - 'ipc-json.c', - 'ipc-server.c', - 'desktop/output.c', - 'desktop/layer_shell.c', - 'desktop/wl_shell.c', - 'desktop/xdg_shell_v6.c', - 'desktop/xwayland.c', - 'security.c', + 'tree/container.c', 'tree/layout.c', 'tree/view.c', diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1769609bd..65fd5d4a3 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -729,3 +729,24 @@ struct sway_container *container_split(struct sway_container *child, return cont; } + +void container_recursive_resize(struct sway_container *container, + double amount, enum resize_edge edge) { + bool layout_match = true; + wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); + if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { + container->width += amount; + layout_match = container->layout == L_HORIZ; + } else if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { + container->height += amount; + layout_match = container->layout == L_VERT; + } + if (container->children) { + for (int i = 0; i < container->children->length; i++) { + struct sway_container *child = container->children->items[i]; + double amt = layout_match ? + amount / container->children->length : amount; + container_recursive_resize(child, amt, edge); + } + } +}