aboutsummaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2018-04-04 21:32:31 -0400
committerDrew DeVault <sir@cmpwn.com>2018-04-05 00:03:20 -0400
commitf77986338fc4186d003908012685c12d718ed647 (patch)
treef8b1ec4af069ab2123453d713251dcd7c51767d1 /sway
parent21aedf15052df4e7f8ee72922fa0e214d690facc (diff)
Implement resize command
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/resize.c284
-rw-r--r--sway/meson.build46
-rw-r--r--sway/tree/layout.c21
4 files changed, 333 insertions, 19 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 2786a879..22decef3 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 00000000..93c1fe7d
--- /dev/null
+++ b/sway/commands/resize.c
@@ -0,0 +1,284 @@
+#include <errno.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <wlr/util/log.h>
+#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 <shrink|grow> "
+ "<width|height> [<amount>] [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 f210c195..ec7b4c42 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 5abdbc32..7e2c61ed 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);
+ }
+ }
+}