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.c515
1 files changed, 212 insertions, 303 deletions
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ef52bb07..93c1fe7d 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -4,372 +4,281 @@
4#include <stdlib.h> 4#include <stdlib.h>
5#include <string.h> 5#include <string.h>
6#include <strings.h> 6#include <strings.h>
7#include <wlc/wlc.h> 7#include <wlr/util/log.h>
8#include "sway/commands.h" 8#include "sway/commands.h"
9#include "sway/layout.h" 9#include "sway/tree/layout.h"
10#include "sway/focus.h"
11#include "sway/input_state.h"
12#include "sway/handlers.h"
13#include "log.h" 10#include "log.h"
14 11
15enum resize_dim_types { 12static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
16 RESIZE_DIM_PX,
17 RESIZE_DIM_PPT,
18 RESIZE_DIM_DEFAULT,
19};
20
21static bool set_size_floating(int new_dimension, bool use_width) {
22 swayc_t *view = current_container;
23 if (view) {
24 if (use_width) {
25 int current_width = view->width;
26 view->desired_width = new_dimension;
27 floating_view_sane_size(view);
28
29 int new_x = view->x + (int)(((view->desired_width - current_width) / 2) * -1);
30 view->width = view->desired_width;
31 view->x = new_x;
32 13
33 update_geometry(view); 14enum resize_unit {
34 } else { 15 RESIZE_UNIT_PX,
35 int current_height = view->height; 16 RESIZE_UNIT_PPT,
36 view->desired_height = new_dimension; 17 RESIZE_UNIT_DEFAULT,
37 floating_view_sane_size(view); 18 RESIZE_UNIT_INVALID,
38 19};
39 int new_y = view->y + (int)(((view->desired_height - current_height) / 2) * -1);
40 view->height = view->desired_height;
41 view->y = new_y;
42 20
43 update_geometry(view); 21enum resize_axis {
44 } 22 RESIZE_AXIS_HORIZONTAL,
23 RESIZE_AXIS_VERTICAL,
24 RESIZE_AXIS_INVALID,
25};
45 26
46 return true; 27static enum resize_unit parse_resize_unit(const char *unit) {
28 if (strcasecmp(unit, "px") == 0) {
29 return RESIZE_UNIT_PX;
47 } 30 }
48 31 if (strcasecmp(unit, "ppt") == 0) {
49 return false; 32 return RESIZE_UNIT_PPT;
33 }
34 if (strcasecmp(unit, "default") == 0) {
35 return RESIZE_UNIT_DEFAULT;
36 }
37 return RESIZE_UNIT_INVALID;
50} 38}
51 39
52static bool resize_floating(int amount, bool use_width) { 40static enum resize_axis parse_resize_axis(const char *axis) {
53 swayc_t *view = current_container; 41 if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {
54 42 return RESIZE_AXIS_HORIZONTAL;
55 if (view) { 43 }
56 if (use_width) { 44 if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {
57 return set_size_floating(view->width + amount, true); 45 return RESIZE_AXIS_VERTICAL;
58 } else {
59 return set_size_floating(view->height + amount, false);
60 }
61 } 46 }
47 return RESIZE_AXIS_INVALID;
48}
62 49
63 return false; 50static int parallel_coord(struct sway_container *c, enum resize_axis a) {
51 return a == RESIZE_AXIS_HORIZONTAL ? c->x : c->y;
64} 52}
65 53
66static bool resize_tiled(int amount, bool use_width) { 54static int parallel_size(struct sway_container *c, enum resize_axis a) {
67 swayc_t *container = current_container; 55 return a == RESIZE_AXIS_HORIZONTAL ? c->width : c->height;
68 swayc_t *parent = container->parent; 56}
69 int idx_focused = 0;
70 bool use_major = false;
71 size_t nb_before = 0;
72 size_t nb_after = 0;
73
74 // 1. Identify a container ancestor that will allow the focused child to grow in the requested
75 // direction.
76 while (container->parent) {
77 parent = container->parent;
78 if ((parent->children && parent->children->length > 1)
79 && (is_auto_layout(parent->layout)
80 || (use_width ? parent->layout == L_HORIZ : parent->layout == L_VERT))) {
81 // check if container has siblings that can provide/absorb the space needed for
82 // the resize operation.
83 use_major = use_width
84 ? parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
85 : parent->layout == L_AUTO_TOP || parent->layout == L_AUTO_BOTTOM;
86 // Note: use_major will be false for L_HORIZ and L_VERT
87
88 idx_focused = index_child(container);
89 if (idx_focused < 0) {
90 sway_log(L_ERROR, "Something weird is happening, child container not "
91 "present in its parent's children list.");
92 continue;
93 }
94 if (use_major) {
95 nb_before = auto_group_index(parent, idx_focused);
96 nb_after = auto_group_count(parent) - nb_before - 1;
97 } else {
98 nb_before = idx_focused - auto_group_start_index(parent, idx_focused);
99 nb_after = auto_group_end_index(parent, idx_focused) - idx_focused - 1;
100 sway_log(L_DEBUG, "+++ focused: %d, start: %d, end: %d, before: %d, after: %d",
101 idx_focused,
102 (int)auto_group_start_index(parent, idx_focused),
103 (int)auto_group_end_index(parent, idx_focused),
104 (int)nb_before, (int)nb_after);
105 57
106 } 58static void resize_tiled(int amount, enum resize_axis axis) {
107 if (nb_before || nb_after) { 59 struct sway_container *parent = config->handler_context.current_container;
108 break; 60 struct sway_container *focused = parent;
109 } 61 if (!parent) {
110 } 62 return;
111 container = parent; /* continue up the tree to the next ancestor */
112 }
113 if (parent == &root_container) {
114 return true;
115 } 63 }
116 sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, " 64
117 "and %zu after conts", parent, nb_before, nb_after); 65 enum sway_container_layout parallel_layout =
118 // 2. Ensure that the resize operation will not make one of the resized containers drop 66 axis == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
119 // below the "sane" size threshold. 67 int minor_weight = 0;
120 bool valid = true; 68 int major_weight = 0;
121 swayc_t *focused = parent->children->items[idx_focused]; 69 while (parent->parent) {
122 int start = use_major ? 0 : auto_group_start_index(parent, idx_focused); 70 struct sway_container *next = parent->parent;
123 int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused); 71 if (next->layout == parallel_layout) {
124 sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end); 72 for (int i = 0; i < next->children->length; i++) {
125 for (int i = start; i < end; ) { 73 struct sway_container *sibling = next->children->items[i];
126 swayc_t *sibling = parent->children->items[i]; 74
127 double pixels = amount; 75 int sibling_pos = parallel_coord(sibling, axis);
128 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y; 76 int focused_pos = parallel_coord(focused, axis);
129 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y; 77 int parent_pos = parallel_coord(parent, axis);
130 if (is_before || is_after) { 78
131 pixels = -pixels; 79 if (sibling_pos != focused_pos) {
132 pixels /= is_before ? nb_before : nb_after; 80 if (sibling_pos < parent_pos) {
133 if (nb_after != 0 && nb_before != 0) { 81 minor_weight++;
134 pixels /= 2; 82 } else if (sibling_pos > parent_pos) {
135 } 83 major_weight++;
136 }
137 sway_log(L_DEBUG, "Check container %p: width %g vs %d, height %g vs %d", sibling, sibling->width + pixels, min_sane_w, sibling->height + pixels, min_sane_h);
138 if (use_width ?
139 sibling->width + pixels < min_sane_w :
140 sibling->height + pixels < min_sane_h) {
141 valid = false;
142 sway_log(L_DEBUG, "Container size no longer sane");
143 break;
144 }
145 i = use_major ? auto_group_end_index(parent, i) : (i + 1);
146 sway_log(L_DEBUG, "+++++ check %i", i);
147 }
148 // 3. Apply the size change
149 if (valid) {
150 for (int i = start; i < end; ) {
151 int next_i = use_major ? auto_group_end_index(parent, i) : (i + 1);
152 swayc_t *sibling = parent->children->items[i];
153 double pixels = amount;
154 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
155 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
156 if (is_before || is_after) {
157 pixels = -pixels;
158 pixels /= is_before ? nb_before : nb_after;
159 if (nb_after != 0 && nb_before != 0) {
160 pixels /= 2;
161 }
162 sway_log(L_DEBUG, "%p: %s", sibling, is_before ? "before" : "after");
163 if (use_major) {
164 for (int j = i; j < next_i; ++j) {
165 recursive_resize(parent->children->items[j], pixels,
166 use_width ?
167 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
168 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
169 }
170 } else {
171 recursive_resize(sibling, pixels,
172 use_width ?
173 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
174 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
175 }
176 } else {
177 if (use_major) {
178 for (int j = i; j < next_i; ++j) {
179 recursive_resize(parent->children->items[j], pixels / 2,
180 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
181 recursive_resize(parent->children->items[j], pixels / 2,
182 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
183 } 84 }
184 } else {
185 recursive_resize(sibling, pixels / 2,
186 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
187 recursive_resize(sibling, pixels / 2,
188 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
189 } 85 }
190 } 86 }
191 i = next_i; 87 if (major_weight || minor_weight) {
88 break;
89 }
192 } 90 }
193 // Recursive resize does not handle positions, let arrange_windows 91 parent = next;
194 // take care of that.
195 arrange_windows(swayc_active_workspace(), -1, -1);
196 } 92 }
197 return true;
198}
199
200static bool set_size_tiled(int amount, bool use_width) {
201 int desired;
202 swayc_t *focused = current_container;
203 93
204 if (use_width) { 94 if (parent->type == C_ROOT) {
205 desired = amount - focused->width; 95 return;
206 } else {
207 desired = amount - focused->height;
208 } 96 }
209 97
210 return resize_tiled(desired, use_width); 98 wlr_log(L_DEBUG,
211} 99 "Found the proper parent: %p. It has %d l conts, and %d r conts",
212 100 parent->parent, minor_weight, major_weight);
213static bool set_size(int dimension, bool use_width) {
214 swayc_t *focused = current_container;
215 101
216 if (focused) { 102 int min_sane = axis == RESIZE_AXIS_HORIZONTAL ? MIN_SANE_W : MIN_SANE_H;
217 if (focused->is_floating) {
218 return set_size_floating(dimension, use_width);
219 } else {
220 return set_size_tiled(dimension, use_width);
221 }
222 }
223 103
224 return false; 104 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
225} 105 // ^ ?????
226 106
227static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) { 107 for (int i = 0; i < parent->parent->children->length; i++) {
228 swayc_t *focused = current_container; 108 struct sway_container *sibling = parent->parent->children->items[i];
229 109
230 // translate "10 ppt" (10%) to appropriate # of pixels in case we need it 110 int sibling_pos = parallel_coord(sibling, axis);
231 float ppt_dim = (float)dimension / 100; 111 int focused_pos = parallel_coord(focused, axis);
112 int parent_pos = parallel_coord(parent, axis);
232 113
233 if (use_width) { 114 int sibling_size = parallel_size(sibling, axis);
234 ppt_dim = focused->width * ppt_dim; 115 int parent_size = parallel_size(parent, axis);
235 } else {
236 ppt_dim = focused->height * ppt_dim;
237 }
238 116
239 if (focused) { 117 if (sibling_pos != focused_pos) {
240 if (focused->is_floating) { 118 if (sibling_pos < parent_pos) {
241 // floating view resize dimensions should default to px, so only 119 double pixels = -amount / minor_weight;
242 // use ppt if specified 120 if (major_weight && (sibling_size + pixels / 2) < min_sane) {
243 if (dim_type == RESIZE_DIM_PPT) { 121 return; // Too small
244 dimension = (int)ppt_dim; 122 } else if ((sibling_size + pixels) < min_sane) {
123 return; // Too small
124 }
125 } else if (sibling_pos > parent_pos) {
126 double pixels = -amount / major_weight;
127 if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
128 return; // Too small
129 } else if ((sibling_size + pixels) < min_sane) {
130 return; // Too small
131 }
245 } 132 }
246
247 return resize_floating(dimension, use_width);
248 } else { 133 } else {
249 // tiled view resize dimensions should default to ppt, so only use 134 double pixels = amount;
250 // px if specified 135 if (parent_size + pixels < min_sane) {
251 if (dim_type != RESIZE_DIM_PX) { 136 return; // Too small
252 dimension = (int)ppt_dim;
253 } 137 }
254
255 return resize_tiled(dimension, use_width);
256 } 138 }
257 } 139 }
258 140
259 return false; 141 enum resize_edge minor_edge = axis == RESIZE_AXIS_HORIZONTAL ?
260} 142 RESIZE_EDGE_LEFT : RESIZE_EDGE_TOP;
143 enum resize_edge major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
144 RESIZE_EDGE_RIGHT : RESIZE_EDGE_BOTTOM;
261 145
262static struct cmd_results *cmd_resize_set(int argc, char **argv) { 146 for (int i = 0; i < parent->parent->children->length; i++) {
263 struct cmd_results *error = NULL; 147 struct sway_container *sibling = parent->parent->children->items[i];
264 if ((error = checkarg(argc, "resize set", EXPECTED_AT_LEAST, 2))) {
265 return error;
266 }
267 148
268 if (strcasecmp(argv[0], "width") == 0 || strcasecmp(argv[0], "height") == 0) { 149 int sibling_pos = parallel_coord(sibling, axis);
269 // handle `reset set width 100 px height 100 px` syntax, also allows 150 int focused_pos = parallel_coord(focused, axis);
270 // specifying only one dimension for a `resize set` 151 int parent_pos = parallel_coord(parent, axis);
271 int cmd_num = 0;
272 int dim;
273
274 while ((cmd_num + 1) < argc) {
275 dim = (int)strtol(argv[cmd_num + 1], NULL, 10);
276 if (errno == ERANGE || dim == 0) {
277 errno = 0;
278 return cmd_results_new(CMD_INVALID, "resize set",
279 "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'");
280 }
281 152
282 if (strcasecmp(argv[cmd_num], "width") == 0) { 153 if (sibling_pos != focused_pos) {
283 set_size(dim, true); 154 if (sibling_pos < parent_pos) {
284 } else if (strcasecmp(argv[cmd_num], "height") == 0) { 155 double pixels = -1 * amount;
285 set_size(dim, false); 156 pixels /= minor_weight;
286 } else { 157 if (major_weight) {
287 return cmd_results_new(CMD_INVALID, "resize set", 158 container_recursive_resize(sibling, pixels / 2, major_edge);
288 "Expected 'resize set <width|height> <amount> [px] [<width|height> <amount> [px]]'"); 159 } else {
160 container_recursive_resize(sibling, pixels, major_edge);
161 }
162 } else if (sibling_pos > parent_pos) {
163 double pixels = -1 * amount;
164 pixels /= major_weight;
165 if (minor_weight) {
166 container_recursive_resize(sibling, pixels / 2, minor_edge);
167 } else {
168 container_recursive_resize(sibling, pixels, minor_edge);
169 }
289 } 170 }
290 171 } else {
291 cmd_num += 2; 172 if (major_weight != 0 && minor_weight != 0) {
292 173 double pixels = amount;
293 if (cmd_num < argc && strcasecmp(argv[cmd_num], "px") == 0) { 174 pixels /= 2;
294 // if this was `resize set width 400 px height 300 px`, disregard the `px` arg 175 container_recursive_resize(parent, pixels, minor_edge);
295 cmd_num++; 176 container_recursive_resize(parent, pixels, major_edge);
177 } else if (major_weight) {
178 container_recursive_resize(parent, amount, major_edge);
179 } else if (minor_weight) {
180 container_recursive_resize(parent, amount, minor_edge);
296 } 181 }
297 } 182 }
298 } else { 183 }
299 // handle `reset set 100 px 100 px` syntax
300 int width = (int)strtol(argv[0], NULL, 10);
301 if (errno == ERANGE || width == 0) {
302 errno = 0;
303 return cmd_results_new(CMD_INVALID, "resize set",
304 "Expected 'resize set <width> [px] <height> [px]'");
305 }
306 184
307 int height_arg = 1; 185 arrange_windows(parent->parent, -1, -1);
308 if (strcasecmp(argv[1], "px") == 0) { 186}
309 height_arg = 2;
310 }
311 187
312 int height = (int)strtol(argv[height_arg], NULL, 10); 188static void resize(int amount, enum resize_axis axis, enum resize_unit unit) {
313 if (errno == ERANGE || height == 0) { 189 struct sway_container *current = config->handler_context.current_container;
314 errno = 0; 190 if (unit == RESIZE_UNIT_DEFAULT) {
315 return cmd_results_new(CMD_INVALID, "resize set", 191 // Default for tiling; TODO floating should be px
316 "Expected 'resize set <width> [px] <height> [px]'"); 192 unit = RESIZE_UNIT_PPT;
317 } 193 }
318 194
319 set_size(width, true); 195 if (unit == RESIZE_UNIT_PPT) {
320 set_size(height, false); 196 float pct = amount / 100.0f;
197 switch (axis) {
198 case RESIZE_AXIS_HORIZONTAL:
199 amount = (float)current->width * pct;
200 break;
201 case RESIZE_AXIS_VERTICAL:
202 amount = (float)current->height * pct;
203 break;
204 default:
205 sway_assert(0, "invalid resize axis");
206 return;
207 }
321 } 208 }
322 209
323 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 210 return resize_tiled(amount, axis);
324} 211}
325 212
326struct cmd_results *cmd_resize(int argc, char **argv) { 213struct cmd_results *cmd_resize(int argc, char **argv) {
327 struct cmd_results *error = NULL; 214 struct sway_container *current = config->handler_context.current_container;
328 if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file."); 215 if (!current) {
329 if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running."); 216 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
330 217 }
218 if (current->type != C_VIEW && current->type != C_CONTAINER) {
219 return cmd_results_new(CMD_INVALID, "resize",
220 "Can only resize views/containers");
221 }
331 if (strcasecmp(argv[0], "set") == 0) { 222 if (strcasecmp(argv[0], "set") == 0) {
332 return cmd_resize_set(argc - 1, &argv[1]); 223 // TODO
224 //return cmd_resize_set(argc - 1, &argv[1]);
225 return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented");
333 } 226 }
334 227 struct cmd_results *error;
335 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 228 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
336 return error; 229 return error;
337 } 230 }
338 231
339 int dim_arg = argc - 1; 232 // TODO: resize grow|shrink left|right|up|down
340 233
341 enum resize_dim_types dim_type = RESIZE_DIM_DEFAULT; 234 const char *usage = "Expected 'resize <shrink|grow> "
342 if (strcasecmp(argv[dim_arg], "ppt") == 0) { 235 "<width|height> [<amount>] [px|ppt]'";
343 dim_type = RESIZE_DIM_PPT; 236
344 dim_arg--; 237 int multiplier = 0;
345 } else if (strcasecmp(argv[dim_arg], "px") == 0) { 238 if (strcasecmp(*argv, "grow") == 0) {
346 dim_type = RESIZE_DIM_PX; 239 multiplier = 1;
347 dim_arg--; 240 } else if (strcasecmp(*argv, "shrink") == 0) {
241 multiplier = -1;
242 } else {
243 return cmd_results_new(CMD_INVALID, "resize", usage);
348 } 244 }
245 --argc; ++argv;
349 246
350 int amount = (int)strtol(argv[dim_arg], NULL, 10); 247 enum resize_axis axis = parse_resize_axis(*argv);
351 if (errno == ERANGE || amount == 0) { 248 if (axis == RESIZE_AXIS_INVALID) {
352 errno = 0; 249 return cmd_results_new(CMD_INVALID, "resize", usage);
353 amount = 10; // this is the default resize dimension used by i3 for both px and ppt 250 }
354 sway_log(L_DEBUG, "Tried to get resize dimension out of '%s' but failed; setting dimension to default %d", 251 --argc; ++argv;
355 argv[dim_arg], amount); 252
253 int amount = 10; // Default amount
254 enum resize_unit unit = RESIZE_UNIT_DEFAULT;
255
256 if (argc) {
257 char *err;
258 amount = (int)strtol(*argv, &err, 10);
259 if (*err) {
260 // e.g. `resize grow width 10px`
261 unit = parse_resize_unit(err);
262 if (unit == RESIZE_UNIT_INVALID) {
263 return cmd_results_new(CMD_INVALID, "resize", usage);
264 }
265 }
266 --argc; ++argv;
356 } 267 }
357 268
358 bool use_width = false; 269 if (argc) {
359 if (strcasecmp(argv[1], "width") == 0) { 270 unit = parse_resize_unit(*argv);
360 use_width = true; 271 if (unit == RESIZE_UNIT_INVALID) {
361 } else if (strcasecmp(argv[1], "height") != 0) { 272 return cmd_results_new(CMD_INVALID, "resize", usage);
362 return cmd_results_new(CMD_INVALID, "resize", 273 }
363 "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'"); 274 --argc; ++argv;
364 } 275 }
365 276
366 if (strcasecmp(argv[0], "shrink") == 0) { 277 if (argc) {
367 amount *= -1; 278 // Provied too many args, the bastard
368 } else if (strcasecmp(argv[0], "grow") != 0) { 279 return cmd_results_new(CMD_INVALID, "resize", usage);
369 return cmd_results_new(CMD_INVALID, "resize",
370 "Expected 'resize <shrink|grow> <width|height> [<amount>] [px|ppt]'");
371 } 280 }
372 281
373 resize(amount, use_width, dim_type); 282 resize(amount * multiplier, axis, unit);
374 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 283 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
375} 284}