aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/list.c2
-rw-r--r--include/list.h2
-rw-r--r--include/sway/container.h20
-rw-r--r--include/sway/focus.h6
-rw-r--r--include/sway/layout.h7
-rw-r--r--sway/commands/focus.c4
-rw-r--r--sway/commands/layout.c127
-rw-r--r--sway/commands/move.c8
-rw-r--r--sway/commands/resize.c313
-rw-r--r--sway/commands/workspace_layout.c10
-rw-r--r--sway/container.c26
-rw-r--r--sway/debug_log.c4
-rw-r--r--sway/layout.c690
-rw-r--r--sway/sway.5.txt41
14 files changed, 911 insertions, 349 deletions
diff --git a/common/list.c b/common/list.c
index dd864a9b..39cc10e1 100644
--- a/common/list.c
+++ b/common/list.c
@@ -76,7 +76,7 @@ int list_seq_find(list_t *list, int compare(const void *item, const void *data),
76 return -1; 76 return -1;
77} 77}
78 78
79static void list_swap(list_t *list, int src, int dest) { 79void list_swap(list_t *list, int src, int dest) {
80 void *tmp = list->items[src]; 80 void *tmp = list->items[src];
81 list->items[src] = list->items[dest]; 81 list->items[src] = list->items[dest];
82 list->items[dest] = tmp; 82 list->items[dest] = tmp;
diff --git a/include/list.h b/include/list.h
index f478b6bb..7eead4ac 100644
--- a/include/list.h
+++ b/include/list.h
@@ -22,4 +22,6 @@ void list_qsort(list_t *list, int compare(const void *left, const void *right));
22int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to); 22int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to);
23// stable sort since qsort is not guaranteed to be stable 23// stable sort since qsort is not guaranteed to be stable
24void list_stable_sort(list_t *list, int compare(const void *a, const void *b)); 24void list_stable_sort(list_t *list, int compare(const void *a, const void *b));
25// swap two elements in a list
26void list_swap(list_t *list, int src, int dest);
25#endif 27#endif
diff --git a/include/sway/container.h b/include/sway/container.h
index 2bedd136..ff65628c 100644
--- a/include/sway/container.h
+++ b/include/sway/container.h
@@ -37,6 +37,16 @@ enum swayc_layouts {
37 L_STACKED, 37 L_STACKED,
38 L_TABBED, 38 L_TABBED,
39 L_FLOATING, /**< A psuedo-container, removed from the tree, to hold floating windows */ 39 L_FLOATING, /**< A psuedo-container, removed from the tree, to hold floating windows */
40
41 /* Awesome/Monad style auto layouts */
42 L_AUTO_LEFT,
43 L_AUTO_RIGHT,
44 L_AUTO_TOP,
45 L_AUTO_BOTTOM,
46
47 L_AUTO_FIRST = L_AUTO_LEFT,
48 L_AUTO_LAST = L_AUTO_BOTTOM,
49
40 // Keep last 50 // Keep last
41 L_LAYOUTS, 51 L_LAYOUTS,
42}; 52};
@@ -144,6 +154,16 @@ struct sway_container {
144 struct wlc_geometry title_bar_geometry; 154 struct wlc_geometry title_bar_geometry;
145 struct wlc_geometry actual_geometry; 155 struct wlc_geometry actual_geometry;
146 int border_thickness; 156 int border_thickness;
157
158 /**
159 * Number of master views in auto layouts.
160 */
161 size_t nb_master;
162
163 /**
164 * Number of slave groups (e.g. columns) in auto layouts.
165 */
166 size_t nb_slave_groups;
147}; 167};
148 168
149enum visibility_mask { 169enum visibility_mask {
diff --git a/include/sway/focus.h b/include/sway/focus.h
index b532edc2..652cdccc 100644
--- a/include/sway/focus.h
+++ b/include/sway/focus.h
@@ -6,7 +6,10 @@ enum movement_direction {
6 MOVE_UP, 6 MOVE_UP,
7 MOVE_DOWN, 7 MOVE_DOWN,
8 MOVE_PARENT, 8 MOVE_PARENT,
9 MOVE_CHILD 9 MOVE_CHILD,
10 MOVE_NEXT,
11 MOVE_PREV,
12 MOVE_FIRST
10}; 13};
11 14
12#include "container.h" 15#include "container.h"
@@ -40,4 +43,3 @@ extern bool suspend_workspace_cleanup;
40bool move_focus(enum movement_direction direction); 43bool move_focus(enum movement_direction direction);
41 44
42#endif 45#endif
43
diff --git a/include/sway/layout.h b/include/sway/layout.h
index b982365c..fbedcdb3 100644
--- a/include/sway/layout.h
+++ b/include/sway/layout.h
@@ -75,4 +75,11 @@ void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ..
75 */ 75 */
76enum swayc_layouts default_layout(swayc_t *output); 76enum swayc_layouts default_layout(swayc_t *output);
77 77
78bool is_auto_layout(enum swayc_layouts layout);
79int auto_group_start_index(const swayc_t *container, int index);
80int auto_group_end_index(const swayc_t *container, int index);
81size_t auto_group_count(const swayc_t *container);
82size_t auto_group_index(const swayc_t *container, int index);
83bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end);
84
78#endif 85#endif
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 8442305f..0be442ca 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -46,6 +46,10 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
46 move_focus(MOVE_PARENT); 46 move_focus(MOVE_PARENT);
47 } else if (strcasecmp(argv[0], "child") == 0) { 47 } else if (strcasecmp(argv[0], "child") == 0) {
48 move_focus(MOVE_CHILD); 48 move_focus(MOVE_CHILD);
49 } else if (strcasecmp(argv[0], "next") == 0) {
50 move_focus(MOVE_NEXT);
51 } else if (strcasecmp(argv[0], "prev") == 0) {
52 move_focus(MOVE_PREV);
49 } else if (strcasecmp(argv[0], "mode_toggle") == 0) { 53 } else if (strcasecmp(argv[0], "mode_toggle") == 0) {
50 int i; 54 int i;
51 swayc_t *workspace = swayc_active_workspace(); 55 swayc_t *workspace = swayc_active_workspace();
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 08336150..ff097fef 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -3,6 +3,11 @@
3#include "sway/container.h" 3#include "sway/container.h"
4#include "sway/layout.h" 4#include "sway/layout.h"
5 5
6/**
7 * handle "layout auto" command group
8 */
9static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv);
10
6struct cmd_results *cmd_layout(int argc, char **argv) { 11struct cmd_results *cmd_layout(int argc, char **argv) {
7 struct cmd_results *error = NULL; 12 struct cmd_results *error = NULL;
8 if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file."); 13 if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
@@ -49,11 +54,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
49 } else if (strcasecmp(argv[0], "splitv") == 0) { 54 } else if (strcasecmp(argv[0], "splitv") == 0) {
50 swayc_change_layout(parent, L_VERT); 55 swayc_change_layout(parent, L_VERT);
51 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 56 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
52 if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE || parent->workspace_layout == L_HORIZ)) { 57 if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
58 || parent->workspace_layout == L_HORIZ)) {
53 swayc_change_layout(parent, L_VERT); 59 swayc_change_layout(parent, L_VERT);
54 } else { 60 } else {
55 swayc_change_layout(parent, L_HORIZ); 61 swayc_change_layout(parent, L_HORIZ);
56 } 62 }
63 } else if (strcasecmp(argv[0], "auto") == 0) {
64 return cmd_layout_auto(parent, argc, argv);
57 } 65 }
58 } 66 }
59 67
@@ -64,3 +72,120 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
64 72
65 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 73 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
66} 74}
75
76static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv) {
77 // called after checking that argv[0] is auto, so just continue parsing from there
78 struct cmd_results *error = NULL;
79 const char *cmd_name = "layout auto";
80 const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
81 const char *err_msg = "Allowed arguments are <right|left|top|bot|next|prev|master|ncol>";
82
83 bool need_layout_update = false;
84 enum swayc_layouts old_layout = container->layout;
85 enum swayc_layouts layout = old_layout;
86
87 if (strcasecmp(argv[1], "left") == 0) {
88 layout = L_AUTO_LEFT;
89 } else if (strcasecmp(argv[1], "right") == 0) {
90 layout = L_AUTO_RIGHT;
91 } else if (strcasecmp(argv[1], "top") == 0) {
92 layout = L_AUTO_TOP;
93 } else if (strcasecmp(argv[1], "bot") == 0) {
94 layout = L_AUTO_BOTTOM;
95 } else if (strcasecmp(argv[1], "next") == 0) {
96 if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
97 layout = container->layout + 1;
98 } else {
99 layout = L_AUTO_FIRST;
100 }
101 } else if (strcasecmp(argv[1], "prev") == 0) {
102 if (is_auto_layout(container->layout) && container->layout > L_AUTO_FIRST) {
103 layout = container->layout - 1;
104 } else {
105 layout = L_AUTO_LAST;
106 }
107 } else {
108 bool is_nmaster;
109 bool is_set;
110 if (strcasecmp(argv[1], "master") == 0) {
111 is_nmaster = true;
112 } else if (strcasecmp(argv[1], "ncol") == 0) {
113 is_nmaster = false;
114 } else {
115 return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command. %s",
116 cmd_name, err_msg);
117 }
118 if ((error = checkarg(argc, "auto <master|ncol>", EXPECTED_EQUAL_TO, 4))) {
119 return error;
120 }
121 if (strcasecmp(argv[2], "set") == 0) {
122 is_set = true;
123 } else if (strcasecmp(argv[2], "inc") == 0) {
124 is_set = false;
125 } else {
126 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command. %s, "
127 "Argument must be on of <set|inc>",
128 set_inc_cmd_name);
129 }
130 char *end;
131 int n = (int)strtol(argv[3], &end, 10);
132 if (*end) {
133 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
134 "(argument must be an integer)", set_inc_cmd_name);
135 }
136 if (is_auto_layout(container->layout)) {
137 int inc = 0; /* difference between current master/ncol and requested value */
138 if (is_nmaster) {
139 if (is_set) {
140 if (n < 0) {
141 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
142 "(master must be >= 0)", set_inc_cmd_name);
143 }
144 inc = n - (int)container->nb_master;
145 } else { /* inc command */
146 if ((int)container->nb_master + n >= 0) {
147 inc = n;
148 }
149 }
150 if (inc) {
151 for (int i = container->nb_master;
152 i >= 0 && i < container->children->length
153 && i != (int)container->nb_master + inc;) {
154 ((swayc_t *)container->children->items[i])->height = -1;
155 ((swayc_t *)container->children->items[i])->width = -1;
156 i += inc > 0 ? 1 : -1;
157 }
158 container->nb_master += inc;
159 need_layout_update = true;
160 }
161 } else { /* ncol modification */
162 if (is_set) {
163 if (n <= 0) {
164 return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
165 "(ncol must be > 0)", set_inc_cmd_name);
166 }
167 inc = n - (int)container->nb_slave_groups;
168 } else { /* inc command */
169 if ((int)container->nb_slave_groups + n > 0) {
170 inc = n;
171 }
172 }
173 if (inc) {
174 container->nb_slave_groups += inc;
175 need_layout_update = true;
176 }
177 }
178 }
179 }
180
181 if (layout != old_layout) {
182 swayc_change_layout(container, layout);
183 update_layout_geometry(container, old_layout);
184 need_layout_update = true;
185 }
186 if (need_layout_update) {
187 update_geometry(container);
188 arrange_windows(container, container->width, container->height);
189 }
190 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
191}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 4819d9ef..0b134494 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -13,7 +13,7 @@ struct cmd_results *cmd_move(int argc, char **argv) {
13 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 13 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
14 return error; 14 return error;
15 } 15 }
16 const char* expected_syntax = "Expected 'move <left|right|up|down>' or " 16 const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first>' or "
17 "'move <container|window> to workspace <name>' or " 17 "'move <container|window> to workspace <name>' or "
18 "'move <container|window|workspace> to output <name|direction>' or " 18 "'move <container|window|workspace> to output <name|direction>' or "
19 "'move position mouse'"; 19 "'move position mouse'";
@@ -27,6 +27,12 @@ struct cmd_results *cmd_move(int argc, char **argv) {
27 move_container(view, MOVE_UP); 27 move_container(view, MOVE_UP);
28 } else if (strcasecmp(argv[0], "down") == 0) { 28 } else if (strcasecmp(argv[0], "down") == 0) {
29 move_container(view, MOVE_DOWN); 29 move_container(view, MOVE_DOWN);
30 } else if (strcasecmp(argv[0], "next") == 0) {
31 move_container(view, MOVE_NEXT);
32 } else if (strcasecmp(argv[0], "prev") == 0) {
33 move_container(view, MOVE_PREV);
34 } else if (strcasecmp(argv[0], "first") == 0) {
35 move_container(view, MOVE_FIRST);
30 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { 36 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
31 // "move container ... 37 // "move container ...
32 if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) { 38 if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 2c5b3f6b..28b20dc4 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -63,224 +63,135 @@ static bool resize_floating(int amount, bool use_width) {
63} 63}
64 64
65static bool resize_tiled(int amount, bool use_width) { 65static bool resize_tiled(int amount, bool use_width) {
66 swayc_t *parent = get_focused_view(swayc_active_workspace()); 66 swayc_t *container = get_focused_view(swayc_active_workspace());
67 swayc_t *focused = parent; 67 swayc_t *parent = container->parent;
68 swayc_t *sibling; 68 int idx_focused = 0;
69 if (!parent) { 69 bool use_major = false;
70 return true; 70 size_t nb_before = 0;
71 } 71 size_t nb_after = 0;
72 // Find the closest parent container which has siblings of the proper layout. 72
73 // Then apply the resize to all of them. 73 // 1. Identify a container ancestor that will allow the focused child to grow in the requested
74 int i; 74 // direction.
75 if (use_width) { 75 while (container->parent) {
76 int lnumber = 0; 76 parent = container->parent;
77 int rnumber = 0; 77 if ((parent->children && parent->children->length > 1)
78 while (parent->parent) { 78 && (is_auto_layout(parent->layout)
79 if (parent->parent->layout == L_HORIZ && parent->parent->children) { 79 || (use_width ? parent->layout == L_HORIZ : parent->layout == L_VERT))) {
80 for (i = 0; i < parent->parent->children->length; i++) { 80 // check if container has siblings that can provide/absorb the space needed for
81 sibling = parent->parent->children->items[i]; 81 // the resize operation.
82 if (sibling->x != focused->x) { 82 use_major = use_width
83 if (sibling->x < parent->x) { 83 ? parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
84 lnumber++; 84 : parent->layout == L_AUTO_TOP || parent->layout == L_AUTO_BOTTOM;
85 } else if (sibling->x > parent->x) { 85 // Note: use_major will be false for L_HORIZ and L_VERT
86 rnumber++; 86
87 } 87 idx_focused = index_child(container);
88 } 88 if (idx_focused < 0) {
89 } 89 sway_log(L_ERROR, "Something weird is happening, child container not "
90 if (rnumber || lnumber) { 90 "present in its parent's children list.");
91 break; 91 continue;
92 }
93 } 92 }
94 parent = parent->parent; 93 if (use_major) {
95 } 94 nb_before = auto_group_index(parent, idx_focused);
96 if (parent == &root_container) { 95 nb_after = auto_group_count(parent) - nb_before - 1;
97 return true;
98 }
99 sway_log(L_DEBUG, "Found the proper parent: %p. It has %d l conts, and %d r conts", parent->parent, lnumber, rnumber);
100 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
101 bool valid = true;
102 for (i = 0; i < parent->parent->children->length; i++) {
103 sibling = parent->parent->children->items[i];
104 if (sibling->x != focused->x) {
105 if (sibling->x < parent->x) {
106 double pixels = -1 * amount;
107 pixels /= lnumber;
108 if (rnumber) {
109 if ((sibling->width + pixels/2) < min_sane_w) {
110 valid = false;
111 break;
112 }
113 } else {
114 if ((sibling->width + pixels) < min_sane_w) {
115 valid = false;
116 break;
117 }
118 }
119 } else if (sibling->x > parent->x) {
120 double pixels = -1 * amount;
121 pixels /= rnumber;
122 if (lnumber) {
123 if ((sibling->width + pixels/2) < min_sane_w) {
124 valid = false;
125 break;
126 }
127 } else {
128 if ((sibling->width + pixels) < min_sane_w) {
129 valid = false;
130 break;
131 }
132 }
133 }
134 } else { 96 } else {
135 double pixels = amount; 97 nb_before = idx_focused - auto_group_start_index(parent, idx_focused);
136 if (parent->width + pixels < min_sane_w) { 98 nb_after = auto_group_end_index(parent, idx_focused) - idx_focused - 1;
137 valid = false; 99 sway_log(L_DEBUG, "+++ focused: %d, start: %d, end: %d, before: %d, after: %d",
138 break; 100 idx_focused,
139 } 101 (int)auto_group_start_index(parent, idx_focused),
102 (int)auto_group_end_index(parent, idx_focused),
103 (int)nb_before, (int)nb_after);
104
140 } 105 }
141 } 106 if (nb_before || nb_after) {
142 if (valid) { 107 break;
143 for (i = 0; i < parent->parent->children->length; i++) {
144 sibling = parent->parent->children->items[i];
145 if (sibling->x != focused->x) {
146 if (sibling->x < parent->x) {
147 double pixels = -1 * amount;
148 pixels /= lnumber;
149 if (rnumber) {
150 recursive_resize(sibling, pixels/2, WLC_RESIZE_EDGE_RIGHT);
151 } else {
152 recursive_resize(sibling, pixels, WLC_RESIZE_EDGE_RIGHT);
153 }
154 } else if (sibling->x > parent->x) {
155 double pixels = -1 * amount;
156 pixels /= rnumber;
157 if (lnumber) {
158 recursive_resize(sibling, pixels/2, WLC_RESIZE_EDGE_LEFT);
159 } else {
160 recursive_resize(sibling, pixels, WLC_RESIZE_EDGE_LEFT);
161 }
162 }
163 } else {
164 if (rnumber != 0 && lnumber != 0) {
165 double pixels = amount;
166 pixels /= 2;
167 recursive_resize(parent, pixels, WLC_RESIZE_EDGE_LEFT);
168 recursive_resize(parent, pixels, WLC_RESIZE_EDGE_RIGHT);
169 } else if (rnumber) {
170 recursive_resize(parent, amount, WLC_RESIZE_EDGE_RIGHT);
171 } else if (lnumber) {
172 recursive_resize(parent, amount, WLC_RESIZE_EDGE_LEFT);
173 }
174 }
175 } 108 }
176 // Recursive resize does not handle positions, let arrange_windows
177 // take care of that.
178 arrange_windows(swayc_active_workspace(), -1, -1);
179 } 109 }
110 container = parent; /* continue up the tree to the next ancestor */
111 }
112 if (parent == &root_container) {
180 return true; 113 return true;
181 } else { 114 }
182 int tnumber = 0; 115 sway_log(L_DEBUG, "Found the proper parent: %p. It has %zu before conts, "
183 int bnumber = 0; 116 "and %zu after conts", parent, nb_before, nb_after);
184 while (parent->parent) { 117 // 2. Ensure that the resize operation will not make one of the resized containers drop
185 if (parent->parent->layout == L_VERT) { 118 // below the "sane" size threshold.
186 for (i = 0; i < parent->parent->children->length; i++) { 119 bool valid = true;
187 sibling = parent->parent->children->items[i]; 120 swayc_t *focused = parent->children->items[idx_focused];
188 if (sibling->y != focused->y) { 121 int start = use_major ? 0 : auto_group_start_index(parent, idx_focused);
189 if (sibling->y < parent->y) { 122 int end = use_major ? parent->children->length : auto_group_end_index(parent, idx_focused);
190 bnumber++; 123 sway_log(L_DEBUG, "Check children of container %p [%d,%d[", container, start, end);
191 } else if (sibling->y > parent->y) { 124 for (int i = start; i < end; ) {
192 tnumber++; 125 swayc_t *sibling = parent->children->items[i];
193 } 126 double pixels = amount;
194 } 127 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
195 } 128 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
196 if (bnumber || tnumber) { 129 if (is_before || is_after) {
197 break; 130 pixels = -pixels;
198 } 131 pixels /= is_before ? nb_before : nb_after;
132 if (nb_after != 0 && nb_before != 0) {
133 pixels /= 2;
199 } 134 }
200 parent = parent->parent;
201 } 135 }
202 if (parent->parent == NULL || parent->parent->children == NULL) { 136 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);
203 return true; 137 if (use_width ?
138 sibling->width + pixels < min_sane_w :
139 sibling->height + pixels < min_sane_h) {
140 valid = false;
141 sway_log(L_DEBUG, "Container size no longer sane");
142 break;
204 } 143 }
205 sway_log(L_DEBUG, "Found the proper parent: %p. It has %d b conts, and %d t conts", parent->parent, bnumber, tnumber); 144 i = use_major ? auto_group_end_index(parent, i) : (i + 1);
206 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks 145 sway_log(L_DEBUG, "+++++ check %i", i);
207 bool valid = true; 146 }
208 for (i = 0; i < parent->parent->children->length; i++) { 147 // 3. Apply the size change
209 sibling = parent->parent->children->items[i]; 148 if (valid) {
210 if (sibling->y != focused->y) { 149 for (int i = start; i < end; ) {
211 if (sibling->y < parent->y) { 150 int next_i = use_major ? auto_group_end_index(parent, i) : (i + 1);
212 double pixels = -1 * amount; 151 swayc_t *sibling = parent->children->items[i];
213 pixels /= bnumber; 152 double pixels = amount;
214 if (tnumber) { 153 bool is_before = use_width ? sibling->x < focused->x : sibling->y < focused->y;
215 if ((sibling->height + pixels/2) < min_sane_h) { 154 bool is_after = use_width ? sibling->x > focused->x : sibling->y > focused->y;
216 valid = false; 155 if (is_before || is_after) {
217 break; 156 pixels = -pixels;
218 } 157 pixels /= is_before ? nb_before : nb_after;
219 } else { 158 if (nb_after != 0 && nb_before != 0) {
220 if ((sibling->height + pixels) < min_sane_h) { 159 pixels /= 2;
221 valid = false; 160 }
222 break; 161 sway_log(L_DEBUG, "%p: %s", sibling, is_before ? "before" : "after");
223 } 162 if (use_major) {
224 } 163 for (int j = i; j < next_i; ++j) {
225 } else if (sibling->y > parent->y) { 164 recursive_resize(parent->children->items[j], pixels,
226 double pixels = -1 * amount; 165 use_width ?
227 pixels /= tnumber; 166 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
228 if (bnumber) { 167 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
229 if ((sibling->height + pixels/2) < min_sane_h) {
230 valid = false;
231 break;
232 }
233 } else {
234 if ((sibling->height + pixels) < min_sane_h) {
235 valid = false;
236 break;
237 }
238 } 168 }
169 } else {
170 recursive_resize(sibling, pixels,
171 use_width ?
172 (is_before ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_LEFT) :
173 (is_before ? WLC_RESIZE_EDGE_BOTTOM : WLC_RESIZE_EDGE_TOP));
239 } 174 }
240 } else { 175 } else {
241 double pixels = amount; 176 if (use_major) {
242 if (parent->height + pixels < min_sane_h) { 177 for (int j = i; j < next_i; ++j) {
243 valid = false; 178 recursive_resize(parent->children->items[j], pixels / 2,
244 break; 179 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
245 } 180 recursive_resize(parent->children->items[j], pixels / 2,
246 } 181 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
247 }
248 if (valid) {
249 for (i = 0; i < parent->parent->children->length; i++) {
250 sibling = parent->parent->children->items[i];
251 if (sibling->y != focused->y) {
252 if (sibling->y < parent->y) {
253 double pixels = -1 * amount;
254 pixels /= bnumber;
255 if (tnumber) {
256 recursive_resize(sibling, pixels/2, WLC_RESIZE_EDGE_BOTTOM);
257 } else {
258 recursive_resize(sibling, pixels, WLC_RESIZE_EDGE_BOTTOM);
259 }
260 } else if (sibling->x > parent->x) {
261 double pixels = -1 * amount;
262 pixels /= tnumber;
263 if (bnumber) {
264 recursive_resize(sibling, pixels/2, WLC_RESIZE_EDGE_TOP);
265 } else {
266 recursive_resize(sibling, pixels, WLC_RESIZE_EDGE_TOP);
267 }
268 } 182 }
269 } else { 183 } else {
270 if (bnumber != 0 && tnumber != 0) { 184 recursive_resize(sibling, pixels / 2,
271 double pixels = amount/2; 185 use_width ? WLC_RESIZE_EDGE_LEFT : WLC_RESIZE_EDGE_TOP);
272 recursive_resize(parent, pixels, WLC_RESIZE_EDGE_TOP); 186 recursive_resize(sibling, pixels / 2,
273 recursive_resize(parent, pixels, WLC_RESIZE_EDGE_BOTTOM); 187 use_width ? WLC_RESIZE_EDGE_RIGHT : WLC_RESIZE_EDGE_BOTTOM);
274 } else if (tnumber) {
275 recursive_resize(parent, amount, WLC_RESIZE_EDGE_TOP);
276 } else if (bnumber) {
277 recursive_resize(parent, amount, WLC_RESIZE_EDGE_BOTTOM);
278 }
279 } 188 }
280 } 189 }
281 arrange_windows(swayc_active_workspace(), -1, -1); 190 i = next_i;
282 } 191 }
283 return true; 192 // Recursive resize does not handle positions, let arrange_windows
193 // take care of that.
194 arrange_windows(swayc_active_workspace(), -1, -1);
284 } 195 }
285 return true; 196 return true;
286} 197}
diff --git a/sway/commands/workspace_layout.c b/sway/commands/workspace_layout.c
index b7b4b033..3e0a12ce 100644
--- a/sway/commands/workspace_layout.c
+++ b/sway/commands/workspace_layout.c
@@ -13,8 +13,16 @@ struct cmd_results *cmd_workspace_layout(int argc, char **argv) {
13 config->default_layout = L_STACKED; 13 config->default_layout = L_STACKED;
14 } else if (strcasecmp(argv[0], "tabbed") == 0) { 14 } else if (strcasecmp(argv[0], "tabbed") == 0) {
15 config->default_layout = L_TABBED; 15 config->default_layout = L_TABBED;
16 } else if (strcasecmp(argv[0], "auto_left") == 0) {
17 config->default_layout = L_AUTO_LEFT;
18 } else if (strcasecmp(argv[0], "auto_right") == 0) {
19 config->default_layout = L_AUTO_RIGHT;
20 } else if (strcasecmp(argv[0], "auto_top") == 0) {
21 config->default_layout = L_AUTO_TOP;
22 } else if (strcasecmp(argv[0], "auto_bottom") == 0) {
23 config->default_layout = L_AUTO_BOTTOM;
16 } else { 24 } else {
17 return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed>'"); 25 return cmd_results_new(CMD_INVALID, "workspace_layout", "Expected 'workspace_layout <default|stacking|tabbed|auto_left|auto_right|auto_top|auto_bottom>'");
18 } 26 }
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20} 28}
diff --git a/sway/container.c b/sway/container.c
index e557f450..cf7d7dda 100644
--- a/sway/container.c
+++ b/sway/container.c
@@ -32,6 +32,8 @@ static swayc_t *new_swayc(enum swayc_types type) {
32 c->layout = L_NONE; 32 c->layout = L_NONE;
33 c->workspace_layout = L_NONE; 33 c->workspace_layout = L_NONE;
34 c->type = type; 34 c->type = type;
35 c->nb_master = 1;
36 c->nb_slave_groups = 1;
35 if (type != C_VIEW) { 37 if (type != C_VIEW) {
36 c->children = create_list(); 38 c->children = create_list();
37 } 39 }
@@ -960,11 +962,29 @@ swayc_t *swayc_tabbed_stacked_parent(swayc_t *con) {
960} 962}
961 963
962swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) { 964swayc_t *swayc_change_layout(swayc_t *container, enum swayc_layouts layout) {
965 // if layout change modifies the auto layout's major axis, swap width and height
966 // to preserve current ratios.
967 if (is_auto_layout(layout) && is_auto_layout(container->layout)) {
968 enum swayc_layouts prev_major =
969 container->layout == L_AUTO_LEFT || container->layout == L_AUTO_RIGHT
970 ? L_HORIZ : L_VERT;
971 enum swayc_layouts new_major =
972 layout == L_AUTO_LEFT || layout == L_AUTO_RIGHT
973 ? L_HORIZ : L_VERT;
974 if (new_major != prev_major) {
975 for (int i = 0; i < container->children->length; ++i) {
976 swayc_t *child = container->children->items[i];
977 double h = child->height;
978 child->height = child->width;
979 child->width = h;
980 }
981 }
982 }
963 if (container->type == C_WORKSPACE) { 983 if (container->type == C_WORKSPACE) {
964 container->workspace_layout = layout; 984 container->workspace_layout = layout;
965 if (layout == L_HORIZ || layout == L_VERT) { 985 if (layout == L_HORIZ || layout == L_VERT || is_auto_layout(layout)) {
966 container->layout = layout; 986 container->layout = layout;
967 } 987 }
968 } else { 988 } else {
969 container->layout = layout; 989 container->layout = layout;
970 } 990 }
diff --git a/sway/debug_log.c b/sway/debug_log.c
index 8d891a44..d1eafae8 100644
--- a/sway/debug_log.c
+++ b/sway/debug_log.c
@@ -38,6 +38,10 @@ static void container_log(const swayc_t *c, int depth) {
38 c->layout == L_STACKED ? "Stack": 38 c->layout == L_STACKED ? "Stack":
39 c->layout == L_TABBED ? "Tab": 39 c->layout == L_TABBED ? "Tab":
40 c->layout == L_FLOATING ? "Float": 40 c->layout == L_FLOATING ? "Float":
41 c->layout == L_AUTO_LEFT ? "A_lft":
42 c->layout == L_AUTO_RIGHT ? "A_rgt":
43 c->layout == L_AUTO_TOP ? "A_top":
44 c->layout == L_AUTO_BOTTOM ? "A_bot":
41 "Unknown"); 45 "Unknown");
42 fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height); 46 fprintf(stderr, "w:%4.f|h:%4.f|", c->width, c->height);
43 fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y); 47 fprintf(stderr, "x:%4.f|y:%4.f|", c->x, c->y);
diff --git a/sway/layout.c b/sway/layout.c
index bb37a360..4b30f729 100644
--- a/sway/layout.c
+++ b/sway/layout.c
@@ -118,7 +118,11 @@ swayc_t *add_sibling(swayc_t *fixed, swayc_t *active) {
118 list_add(parent->floating, active); 118 list_add(parent->floating, active);
119 } else { 119 } else {
120 int i = index_child(fixed); 120 int i = index_child(fixed);
121 list_insert(parent->children, i + 1, active); 121 if (is_auto_layout(parent->layout)) {
122 list_add(parent->children, active);
123 } else {
124 list_insert(parent->children, i + 1, active);
125 }
122 } 126 }
123 } 127 }
124 active->parent = parent; 128 active->parent = parent;
@@ -247,19 +251,32 @@ void swap_geometry(swayc_t *a, swayc_t *b) {
247} 251}
248 252
249void move_container(swayc_t *container, enum movement_direction dir) { 253void move_container(swayc_t *container, enum movement_direction dir) {
250 enum swayc_layouts layout; 254 enum swayc_layouts layout = L_NONE;
251 if (container->is_floating 255 swayc_t *parent = container->parent;
252 || (container->type != C_VIEW && container->type != C_CONTAINER)) { 256 if (container->is_floating || (container->type != C_VIEW && container->type != C_CONTAINER)) {
253 return; 257 return;
254 } 258 }
255 if (dir == MOVE_UP || dir == MOVE_DOWN) { 259 if (dir == MOVE_UP || dir == MOVE_DOWN) {
256 layout = L_VERT; 260 layout = L_VERT;
257 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 261 } else if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
258 layout = L_HORIZ; 262 layout = L_HORIZ;
259 } else { 263 } else if (dir == MOVE_FIRST) {
264 // swap first child in auto layout with currently focused child
265 if (is_auto_layout(parent->layout)) {
266 int focused_idx = index_child(container);
267 swayc_t *first = parent->children->items[0];
268 if (focused_idx > 0) {
269 list_swap(parent->children, 0, focused_idx);
270 swap_geometry(first, container);
271 }
272 arrange_windows(parent->parent, -1, -1);
273 ipc_event_window(container, "move");
274 set_focused_container_for(parent->parent, container);
275 }
276 return;
277 } else if (! (dir == MOVE_NEXT || dir == MOVE_PREV)) {
260 return; 278 return;
261 } 279 }
262 swayc_t *parent = container->parent;
263 swayc_t *child = container; 280 swayc_t *child = container;
264 bool ascended = false; 281 bool ascended = false;
265 282
@@ -279,19 +296,53 @@ void move_container(swayc_t *container, enum movement_direction dir) {
279 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,", 296 sway_log(L_DEBUG, "container:%p, parent:%p, child %p,",
280 container,parent,child); 297 container,parent,child);
281 if (parent->layout == layout 298 if (parent->layout == layout
299 || (layout == L_NONE && parent->type == C_CONTAINER) /* accept any layout for next/prev direction */
282 || (parent->layout == L_TABBED && layout == L_HORIZ) 300 || (parent->layout == L_TABBED && layout == L_HORIZ)
283 || (parent->layout == L_STACKED && layout == L_VERT)) { 301 || (parent->layout == L_STACKED && layout == L_VERT)
302 || is_auto_layout(parent->layout)) {
284 int diff; 303 int diff;
285 // If it has ascended (parent has moved up), no container is removed 304 // If it has ascended (parent has moved up), no container is removed
286 // so insert it at index, or index+1. 305 // so insert it at index, or index+1.
287 // if it has not, the moved container is removed, so it needs to be 306 // if it has not, the moved container is removed, so it needs to be
288 // inserted at index-1, or index+1 307 // inserted at index-1, or index+1
289 if (ascended) { 308 if (ascended) {
290 diff = dir == MOVE_LEFT || dir == MOVE_UP ? 0 : 1; 309 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? 0 : 1;
291 } else { 310 } else {
292 diff = dir == MOVE_LEFT || dir == MOVE_UP ? -1 : 1; 311 diff = dir == MOVE_LEFT || dir == MOVE_UP || dir == MOVE_PREV ? -1 : 1;
312 }
313 int idx = index_child(child);
314 int desired = idx + diff;
315 if (dir == MOVE_NEXT || dir == MOVE_PREV) {
316 // Next/Prev always wrap.
317 if (desired < 0) {
318 desired += parent->children->length;
319 } else if (desired >= parent->children->length) {
320 desired = 0;
321 }
322 // if move command makes container change from master to slave
323 // (or the contrary), reset its geometry an the one of the replaced item.
324 if (parent->nb_master
325 && (size_t)parent->children->length > parent->nb_master) {
326 swayc_t *swap_geom = NULL;
327 // if child is being promoted/demoted, it will swap geometry
328 // with the sibling being demoted/promoted.
329 if ((dir == MOVE_NEXT && desired == 0)
330 || (dir == MOVE_PREV && (size_t)desired == parent->nb_master - 1)) {
331 swap_geom = parent->children->items[parent->nb_master - 1];
332 } else if ((dir == MOVE_NEXT && (size_t)desired == parent->nb_master)
333 || (dir == MOVE_PREV && desired == parent->children->length - 1)) {
334 swap_geom = parent->children->items[parent->nb_master];
335 }
336 if (swap_geom) {
337 double h = child->height;
338 double w = child->width;
339 child->width = swap_geom->width;
340 child->height = swap_geom->height;
341 swap_geom->width = w;
342 swap_geom->height = h;
343 }
344 }
293 } 345 }
294 int desired = index_child(child) + diff;
295 // when it has ascended, legal insertion position is 0:len 346 // when it has ascended, legal insertion position is 0:len
296 // when it has not, legal insertion position is 0:len-1 347 // when it has not, legal insertion position is 0:len-1
297 if (desired >= 0 && desired - ascended < parent->children->length) { 348 if (desired >= 0 && desired - ascended < parent->children->length) {
@@ -304,7 +355,8 @@ void move_container(swayc_t *container, enum movement_direction dir) {
304 // insert it next to focused container 355 // insert it next to focused container
305 if (parent->layout == layout 356 if (parent->layout == layout
306 || (parent->layout == L_TABBED && layout == L_HORIZ) 357 || (parent->layout == L_TABBED && layout == L_HORIZ)
307 || (parent->layout == L_STACKED && layout == L_VERT)) { 358 || (parent->layout == L_STACKED && layout == L_VERT)
359 || is_auto_layout(parent->layout)) {
308 desired = (diff < 0) * parent->children->length; 360 desired = (diff < 0) * parent->children->length;
309 } else { 361 } else {
310 desired = index_child(child->focused) + 1; 362 desired = index_child(child->focused) + 1;
@@ -321,7 +373,7 @@ void move_container(swayc_t *container, enum movement_direction dir) {
321 } 373 }
322 } 374 }
323 // Change parent layout if we need to 375 // Change parent layout if we need to
324 if (parent->children->length == 1 && parent->layout != layout) { 376 if (parent->children->length == 1 && parent->layout != layout && layout != L_NONE) {
325 /* swayc_change_layout(parent, layout); */ 377 /* swayc_change_layout(parent, layout); */
326 parent->layout = layout; 378 parent->layout = layout;
327 continue; 379 continue;
@@ -776,6 +828,26 @@ void update_geometry(swayc_t *container) {
776 } 828 }
777} 829}
778 830
831/**
832 * Layout application prototypes
833 */
834static void apply_horiz_layout(swayc_t *container, const double x,
835 const double y, const double width,
836 const double height, const int start,
837 const int end);
838static void apply_vert_layout(swayc_t *container, const double x,
839 const double y, const double width,
840 const double height, const int start,
841 const int end);
842static void apply_tabbed_or_stacked_layout(swayc_t *container, double x,
843 double y, double width,
844 double height);
845
846static void apply_auto_layout(swayc_t *container, const double x, const double y,
847 const double width, const double height,
848 enum swayc_layouts group_layout,
849 bool master_first);
850
779static void arrange_windows_r(swayc_t *container, double width, double height) { 851static void arrange_windows_r(swayc_t *container, double width, double height) {
780 int i; 852 int i;
781 if (width == -1 || height == -1) { 853 if (width == -1 || height == -1) {
@@ -783,14 +855,15 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
783 width = container->width; 855 width = container->width;
784 height = container->height; 856 height = container->height;
785 } 857 }
786 // pixels are indivisable. if we don't round the pixels, then the view 858 // pixels are indivisible. if we don't round the pixels, then the view
787 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's 859 // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's
788 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y. 860 // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y.
789 width = floor(width); 861 width = floor(width);
790 height = floor(height); 862 height = floor(height);
791 863
792 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, 864 sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container,
793 container->name, container->width, container->height, container->x, container->y); 865 container->name, container->width, container->height, container->x,
866 container->y);
794 867
795 double x = 0, y = 0; 868 double x = 0, y = 0;
796 switch (container->type) { 869 switch (container->type) {
@@ -902,135 +975,298 @@ static void arrange_windows_r(swayc_t *container, double width, double height) {
902 break; 975 break;
903 } 976 }
904 977
905 double scale = 0;
906 switch (container->layout) { 978 switch (container->layout) {
907 case L_HORIZ: 979 case L_HORIZ:
908 default: 980 default:
909 // Calculate total width 981 apply_horiz_layout(container, x, y, width, height, 0,
910 for (i = 0; i < container->children->length; ++i) { 982 container->children->length);
911 double *old_width = &((swayc_t *)container->children->items[i])->width; 983 break;
912 if (*old_width <= 0) { 984 case L_VERT:
913 if (container->children->length > 1) { 985 apply_vert_layout(container, x, y, width, height, 0,
914 *old_width = width / (container->children->length - 1); 986 container->children->length);
915 } else { 987 break;
916 *old_width = width; 988 case L_TABBED:
917 } 989 case L_STACKED:
918 } 990 apply_tabbed_or_stacked_layout(container, x, y, width, height);
919 scale += *old_width; 991 break;
920 } 992 case L_AUTO_LEFT:
921 993 apply_auto_layout(container, x, y, width, height, L_VERT, true);
922 // Resize windows 994 break;
923 if (scale > 0.1) { 995 case L_AUTO_RIGHT:
924 scale = width / scale; 996 apply_auto_layout(container, x, y, width, height, L_VERT, false);
925 sway_log(L_DEBUG, "Arranging %p horizontally", container); 997 break;
926 swayc_t *focused = NULL; 998 case L_AUTO_TOP:
927 for (i = 0; i < container->children->length; ++i) { 999 apply_auto_layout(container, x, y, width, height, L_HORIZ, true);
928 swayc_t *child = container->children->items[i]; 1000 break;
929 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, width, scale); 1001 case L_AUTO_BOTTOM:
930 child->x = x; 1002 apply_auto_layout(container, x, y, width, height, L_HORIZ, false);
931 child->y = y; 1003 break;
932 1004 }
933 if (child == container->focused) {
934 focused = child;
935 }
936 1005
937 if (i == container->children->length - 1) { 1006 // Arrage floating layouts for workspaces last
938 double remaining_width = container->x + width - x; 1007 if (container->type == C_WORKSPACE) {
939 arrange_windows_r(child, remaining_width, height); 1008 for (int i = 0; i < container->floating->length; ++i) {
940 } else { 1009 swayc_t *view = container->floating->items[i];
941 arrange_windows_r(child, child->width * scale, height); 1010 if (view->type == C_VIEW) {
1011 update_geometry(view);
1012 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f",
1013 view->width, view->height, view->x, view->y);
1014 if (swayc_is_fullscreen(view)) {
1015 wlc_view_bring_to_front(view->handle);
1016 } else if (!container->focused ||
1017 !swayc_is_fullscreen(container->focused)) {
1018 wlc_view_bring_to_front(view->handle);
942 } 1019 }
943 x += child->width;
944 } 1020 }
1021 }
1022 }
1023}
945 1024
946 // update focused view border last because it may 1025void apply_horiz_layout(swayc_t *container, const double x, const double y,
947 // depend on the title bar geometry of its siblings. 1026 const double width, const double height,
948 if (focused && container->children->length > 1) { 1027 const int start, const int end) {
949 update_container_border(focused); 1028 double scale = 0;
1029 // Calculate total width
1030 for (int i = start; i < end; ++i) {
1031 double *old_width = &((swayc_t *)container->children->items[i])->width;
1032 if (*old_width <= 0) {
1033 if (end - start > 1) {
1034 *old_width = width / (end - start - 1);
1035 } else {
1036 *old_width = width;
950 } 1037 }
951 } 1038 }
952 break; 1039 scale += *old_width;
953 case L_VERT: 1040 }
954 // Calculate total height 1041 scale = width / scale;
955 for (i = 0; i < container->children->length; ++i) { 1042
956 double *old_height = &((swayc_t *)container->children->items[i])->height; 1043 // Resize windows
957 if (*old_height <= 0) { 1044 double child_x = x;
958 if (container->children->length > 1) { 1045 if (scale > 0.1) {
959 *old_height = height / (container->children->length - 1); 1046 sway_log(L_DEBUG, "Arranging %p horizontally", container);
960 } else { 1047 swayc_t *focused = NULL;
961 *old_height = height; 1048 for (int i = start; i < end; ++i) {
962 } 1049 swayc_t *child = container->children->items[i];
1050 sway_log(L_DEBUG,
1051 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1052 child->type, width, scale);
1053 child->x = child_x;
1054 child->y = y;
1055
1056 if (child == container->focused) {
1057 focused = child;
963 } 1058 }
964 scale += *old_height;
965 }
966 // Resize
967 if (scale > 0.1) {
968 scale = height / scale;
969 sway_log(L_DEBUG, "Arranging %p vertically", container);
970 swayc_t *focused = NULL;
971 for (i = 0; i < container->children->length; ++i) {
972 swayc_t *child = container->children->items[i];
973 sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, height, scale);
974 child->x = x;
975 child->y = y;
976
977 if (child == container->focused) {
978 focused = child;
979 }
980 1059
981 if (i == container->children->length - 1) { 1060 if (i == end - 1) {
982 double remaining_height = container->y + height - y; 1061 double remaining_width = x + width - child_x;
983 arrange_windows_r(child, width, remaining_height); 1062 arrange_windows_r(child, remaining_width, height);
984 } else { 1063 } else {
985 arrange_windows_r(child, width, child->height * scale); 1064 arrange_windows_r(child, child->width * scale, height);
986 }
987 y += child->height;
988 } 1065 }
1066 child_x += child->width;
1067 }
989 1068
990 // update focused view border last because it may 1069 // update focused view border last because it may
991 // depend on the title bar geometry of its siblings. 1070 // depend on the title bar geometry of its siblings.
992 if (focused && container->children->length > 1) { 1071 if (focused && container->children->length > 1) {
993 update_container_border(focused); 1072 update_container_border(focused);
1073 }
1074 }
1075}
1076
1077void apply_vert_layout(swayc_t *container, const double x, const double y,
1078 const double width, const double height, const int start,
1079 const int end) {
1080 int i;
1081 double scale = 0;
1082 // Calculate total height
1083 for (i = start; i < end; ++i) {
1084 double *old_height = &((swayc_t *)container->children->items[i])->height;
1085 if (*old_height <= 0) {
1086 if (end - start > 1) {
1087 *old_height = height / (end - start - 1);
1088 } else {
1089 *old_height = height;
994 } 1090 }
995 } 1091 }
996 break; 1092 scale += *old_height;
997 case L_TABBED: 1093 }
998 case L_STACKED: 1094 scale = height / scale;
999 { 1095
1000 swayc_t *focused = NULL; 1096 // Resize
1001 for (i = 0; i < container->children->length; ++i) { 1097 double child_y = y;
1002 swayc_t *child = container->children->items[i]; 1098 if (scale > 0.1) {
1003 child->x = x; 1099 sway_log(L_DEBUG, "Arranging %p vertically", container);
1004 child->y = y; 1100 swayc_t *focused = NULL;
1005 if (child == container->focused) { 1101 for (i = start; i < end; ++i) {
1006 focused = child; 1102 swayc_t *child = container->children->items[i];
1007 } else { 1103 sway_log(L_DEBUG,
1008 arrange_windows_r(child, width, height); 1104 "Calculating arrangement for %p:%d (will scale %f by %f)", child,
1009 } 1105 child->type, height, scale);
1106 child->x = x;
1107 child->y = child_y;
1108
1109 if (child == container->focused) {
1110 focused = child;
1010 } 1111 }
1011 1112
1012 if (focused) { 1113 if (i == end - 1) {
1013 arrange_windows_r(focused, width, height); 1114 double remaining_height = y + height - child_y;
1115 arrange_windows_r(child, width, remaining_height);
1116 } else {
1117 arrange_windows_r(child, width, child->height * scale);
1014 } 1118 }
1015 break; 1119 child_y += child->height;
1120 }
1121
1122 // update focused view border last because it may
1123 // depend on the title bar geometry of its siblings.
1124 if (focused && container->children->length > 1) {
1125 update_container_border(focused);
1016 } 1126 }
1017 } 1127 }
1128}
1018 1129
1019 // Arrage floating layouts for workspaces last 1130void apply_tabbed_or_stacked_layout(swayc_t *container, double x, double y,
1020 if (container->type == C_WORKSPACE) { 1131 double width, double height) {
1021 for (i = 0; i < container->floating->length; ++i) { 1132 int i;
1022 swayc_t *view = container->floating->items[i]; 1133 swayc_t *focused = NULL;
1023 if (view->type == C_VIEW) { 1134 for (i = 0; i < container->children->length; ++i) {
1024 update_geometry(view); 1135 swayc_t *child = container->children->items[i];
1025 sway_log(L_DEBUG, "Set floating view to %.f x %.f @ %.f, %.f", view->width, 1136 child->x = x;
1026 view->height, view->x, view->y); 1137 child->y = y;
1027 if (swayc_is_fullscreen(view)) { 1138 if (child == container->focused) {
1028 wlc_view_bring_to_front(view->handle); 1139 focused = child;
1029 } else if (!container->focused 1140 } else {
1030 || !swayc_is_fullscreen(container->focused)) { 1141 arrange_windows_r(child, width, height);
1031 wlc_view_bring_to_front(view->handle); 1142 }
1143 }
1144
1145 if (focused) {
1146 arrange_windows_r(focused, width, height);
1147 }
1148}
1149
1150void apply_auto_layout(swayc_t *container, const double x, const double y,
1151 const double width, const double height,
1152 enum swayc_layouts group_layout,
1153 bool master_first) {
1154 // Auto layout "container" in width x height @ x, y
1155 // using "group_layout" for each of the groups in the container.
1156 // There is one "master" group, plus container->nb_slave_groups.
1157 // Each group is layed out side by side following the "major" axis.
1158 // The direction of the layout used for groups is the "minor" axis.
1159 // Example:
1160 //
1161 // ---- major axis -->
1162 // +---------+-----------+
1163 // | | | |
1164 // | master | slave 1 | |
1165 // | +-----------+ | minor axis (direction of group_layout)
1166 // | | | |
1167 // | | slave 2 | V
1168 // +---------+-----------+
1169 //
1170 // container with three children (one master and two slaves) and
1171 // a single slave group (containing slave 1 and 2). The master
1172 // group and slave group are layed out using L_VERT.
1173
1174 size_t nb_groups = auto_group_count(container);
1175
1176 // the target dimension of the container along the "major" axis, each
1177 // group in the container will be layed out using "group_layout" along
1178 // the "minor" axis.
1179 double dim_maj;
1180 double pos_maj;
1181
1182 // x and y coords for the next group to be laid out.
1183 const double *group_x, *group_y;
1184
1185 // pos of the next group to layout along the major axis
1186 double pos;
1187
1188 // size of the next group along the major axis.
1189 double group_dim;
1190
1191 // height and width of next group to be laid out.
1192 const double *group_h, *group_w;
1193
1194 switch (group_layout) {
1195 default:
1196 sway_log(L_DEBUG, "Unknown layout type (%d) used in %s()",
1197 group_layout, __func__);
1198 /* fall through */
1199 case L_VERT:
1200 dim_maj = width;
1201 pos_maj = x;
1202
1203 group_x = &pos;
1204 group_y = &y;
1205 group_w = &group_dim;
1206 group_h = &height;
1207 break;
1208 case L_HORIZ:
1209 dim_maj = height;
1210 pos_maj = y;
1211
1212 group_x = &x;
1213 group_y = &pos;
1214 group_w = &width;
1215 group_h = &group_dim;
1216 break;
1217 }
1218
1219 /* Determine the dimension of each of the groups in the layout.
1220 * Dimension will be width for a VERT layout and height for a HORIZ
1221 * layout. */
1222 double old_group_dim[nb_groups];
1223 double old_dim = 0;
1224 for (size_t group = 0; group < nb_groups; ++group) {
1225 int idx;
1226 if (auto_group_bounds(container, group, &idx, NULL)) {
1227 swayc_t *child = container->children->items[idx];
1228 double *dim = group_layout == L_HORIZ ? &child->height : &child->width;
1229 if (*dim <= 0) {
1230 // New child with uninitialized dimension
1231 *dim = dim_maj;
1232 if (nb_groups > 1) {
1233 // child gets a dimension proportional to existing groups,
1234 // it will be later scaled based on to the available size
1235 // in the major axis.
1236 *dim /= (nb_groups - 1);
1032 } 1237 }
1033 } 1238 }
1239 old_dim += *dim;
1240 old_group_dim[group] = *dim;
1241 }
1242 }
1243 double scale = dim_maj / old_dim;
1244
1245 /* Apply layout to each group */
1246 pos = pos_maj;
1247
1248 for (size_t group = 0; group < nb_groups; ++group) {
1249 int start, end; // index of first (inclusive) and last (exclusive) child in the group
1250 if (auto_group_bounds(container, group, &start, &end)) {
1251 // adjusted size of the group
1252 group_dim = old_group_dim[group] * scale;
1253 if (group == nb_groups - 1) {
1254 group_dim = pos_maj + dim_maj - pos; // remaining width
1255 }
1256 sway_log(L_DEBUG, "Arranging container %p column %zu, children [%d,%d[ (%fx%f+%f,%f)",
1257 container, group, start, end, *group_w, *group_h, *group_x, *group_y);
1258 switch (group_layout) {
1259 default:
1260 case L_VERT:
1261 apply_vert_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1262 break;
1263 case L_HORIZ:
1264 apply_horiz_layout(container, *group_x, *group_y, *group_w, *group_h, start, end);
1265 break;
1266 }
1267
1268 /* update position for next group */
1269 pos += group_dim;
1034 } 1270 }
1035 } 1271 }
1036} 1272}
@@ -1106,6 +1342,21 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1106 return parent; 1342 return parent;
1107 } 1343 }
1108 } 1344 }
1345
1346 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
1347 int focused_idx = index_child(container);
1348 if (focused_idx == -1) {
1349 return NULL;
1350 } else {
1351 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
1352 parent->children->length;
1353 if (desired < 0) {
1354 desired += parent->children->length;
1355 }
1356 return parent->children->items[desired];
1357 }
1358 }
1359
1109 // If moving to an adjacent output we need a starting position (since this 1360 // If moving to an adjacent output we need a starting position (since this
1110 // output might border to multiple outputs). 1361 // output might border to multiple outputs).
1111 struct wlc_point abs_pos; 1362 struct wlc_point abs_pos;
@@ -1128,7 +1379,8 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1128 while (true) { 1379 while (true) {
1129 // Test if we can even make a difference here 1380 // Test if we can even make a difference here
1130 bool can_move = false; 1381 bool can_move = false;
1131 int diff = 0; 1382 int desired;
1383 int idx = index_child(container);
1132 if (parent->type == C_ROOT) { 1384 if (parent->type == C_ROOT) {
1133 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true); 1385 swayc_t *output = swayc_adjacent_output(container, dir, &abs_pos, true);
1134 if (!output || output == container) { 1386 if (!output || output == container) {
@@ -1137,21 +1389,36 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1137 sway_log(L_DEBUG, "Moving between outputs"); 1389 sway_log(L_DEBUG, "Moving between outputs");
1138 return get_swayc_in_output_direction(output, dir); 1390 return get_swayc_in_output_direction(output, dir);
1139 } else { 1391 } else {
1140 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 1392 if (is_auto_layout(parent->layout)) {
1141 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { 1393 bool is_major = parent->layout == L_AUTO_LEFT || parent->layout == L_AUTO_RIGHT
1142 can_move = true; 1394 ? dir == MOVE_LEFT || dir == MOVE_RIGHT
1143 diff = dir == MOVE_LEFT ? -1 : 1; 1395 : dir == MOVE_DOWN || dir == MOVE_UP;
1396 size_t gidx = auto_group_index(parent, idx);
1397 if (is_major) {
1398 size_t desired_grp = gidx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1399 can_move = auto_group_bounds(parent, desired_grp, &desired, NULL);
1400 } else {
1401 desired = idx + (dir == MOVE_RIGHT || dir == MOVE_DOWN ? 1 : -1);
1402 int start, end;
1403 can_move = auto_group_bounds(parent, gidx, &start, &end)
1404 && desired >= start && desired < end;
1144 } 1405 }
1145 } else { 1406 } else {
1146 if (parent->layout == L_VERT || parent->layout == L_STACKED) { 1407 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
1147 can_move = true; 1408 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) {
1148 diff = dir == MOVE_UP ? -1 : 1; 1409 can_move = true;
1410 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
1411 }
1412 } else {
1413 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
1414 can_move = true;
1415 desired = idx + (dir == MOVE_UP ? -1 : 1);
1416 }
1149 } 1417 }
1150 } 1418 }
1151 } 1419 }
1152 1420
1153 if (can_move) { 1421 if (can_move) {
1154 int desired = index_child(container) + diff;
1155 if (container->is_floating) { 1422 if (container->is_floating) {
1156 if (desired < 0) { 1423 if (desired < 0) {
1157 wrap_candidate = parent->floating->items[parent->floating->length-1]; 1424 wrap_candidate = parent->floating->items[parent->floating->length-1];
@@ -1178,6 +1445,8 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio
1178 } 1445 }
1179 } 1446 }
1180 } else { 1447 } else {
1448 sway_log(L_DEBUG, "%s cont %d-%p dir %i sibling %d: %p", __func__,
1449 idx, container, dir, desired, parent->children->items[desired]);
1181 return parent->children->items[desired]; 1450 return parent->children->items[desired];
1182 } 1451 }
1183 } 1452 }
@@ -1233,3 +1502,166 @@ enum swayc_layouts default_layout(swayc_t *output) {
1233 return L_VERT; 1502 return L_VERT;
1234 } 1503 }
1235} 1504}
1505
1506bool is_auto_layout(enum swayc_layouts layout) {
1507 return (layout >= L_AUTO_FIRST) && (layout <= L_AUTO_LAST);
1508}
1509
1510/**
1511 * Return the number of master elements in a container
1512 */
1513static inline size_t auto_master_count(const swayc_t *container) {
1514 sway_assert(container->children->length >= 0, "Container %p has (negative) children %d",
1515 container, container->children->length);
1516 return MIN(container->nb_master, (size_t)container->children->length);
1517}
1518
1519/**
1520 * Return the number of children in the slave groups. This corresponds to the children
1521 * that are not members of the master group.
1522 */
1523static inline size_t auto_slave_count(const swayc_t *container) {
1524 return container->children->length - auto_master_count(container);
1525}
1526
1527/**
1528 * Return the number of slave groups in the container.
1529 */
1530size_t auto_slave_group_count(const swayc_t *container) {
1531 return MIN(container->nb_slave_groups, auto_slave_count(container));
1532}
1533
1534/**
1535 * Return the combined number of master and slave groups in the container.
1536 */
1537size_t auto_group_count(const swayc_t *container) {
1538 return auto_slave_group_count(container) + (container->nb_master ? 1 : 0);
1539}
1540
1541/**
1542 * given the index of a container's child, return the index of the first child of the group
1543 * which index is a member of.
1544 */
1545int auto_group_start_index(const swayc_t *container, int index) {
1546 if (index < 0 || ! is_auto_layout(container->layout)
1547 || (size_t)index < container->nb_master) {
1548 return 0;
1549 } else {
1550 size_t nb_slaves = auto_slave_count(container);
1551 size_t nb_slave_grp = auto_slave_group_count(container);
1552 size_t grp_sz = nb_slaves / nb_slave_grp;
1553 size_t remainder = nb_slaves % nb_slave_grp;
1554 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1555 int start_idx;
1556 if (index < idx2) {
1557 start_idx = ((index - container->nb_master) / grp_sz) * grp_sz + container->nb_master;
1558 } else {
1559 start_idx = idx2 + ((index - idx2) / (grp_sz + 1)) * (grp_sz + 1);
1560 }
1561 return MIN(start_idx, container->children->length);
1562 }
1563}
1564
1565/**
1566 * given the index of a container's child, return the index of the first child of the group
1567 * that follows the one which index is a member of.
1568 * This makes the function usable to walk through the groups in a container.
1569 */
1570int auto_group_end_index(const swayc_t *container, int index) {
1571 if (index < 0 || ! is_auto_layout(container->layout)) {
1572 return container->children->length;
1573 } else {
1574 int nxt_idx;
1575 if ((size_t)index < container->nb_master) {
1576 nxt_idx = auto_master_count(container);
1577 } else {
1578 size_t nb_slaves = auto_slave_count(container);
1579 size_t nb_slave_grp = auto_slave_group_count(container);
1580 size_t grp_sz = nb_slaves / nb_slave_grp;
1581 size_t remainder = nb_slaves % nb_slave_grp;
1582 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1583 if (index < idx2) {
1584 nxt_idx = ((index - container->nb_master) / grp_sz + 1) * grp_sz + container->nb_master;
1585 } else {
1586 nxt_idx = idx2 + ((index - idx2) / (grp_sz + 1) + 1) * (grp_sz + 1);
1587 }
1588 }
1589 return MIN(nxt_idx, container->children->length);
1590 }
1591}
1592
1593/**
1594 * return the index of the Group containing <index>th child of <container>.
1595 * The index is the order of the group along the container's major axis (starting at 0).
1596 */
1597size_t auto_group_index(const swayc_t *container, int index) {
1598 if (index < 0) {
1599 return 0;
1600 }
1601 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1602 size_t nb_slaves = auto_slave_count(container);
1603 if ((size_t)index < container->nb_master) {
1604 if (master_first || nb_slaves <= 0) {
1605 return 0;
1606 } else {
1607 return auto_slave_group_count(container);
1608 }
1609 } else {
1610 size_t nb_slave_grp = auto_slave_group_count(container);
1611 size_t grp_sz = nb_slaves / nb_slave_grp;
1612 size_t remainder = nb_slaves % nb_slave_grp;
1613 int idx2 = (nb_slave_grp - remainder) * grp_sz + container->nb_master;
1614 size_t grp_idx;
1615 if (index < idx2) {
1616 grp_idx = (index - container->nb_master) / grp_sz;
1617 } else {
1618 grp_idx = (nb_slave_grp - remainder) + (index - idx2) / (grp_sz + 1) ;
1619 }
1620 return grp_idx + (master_first && container-> nb_master ? 1 : 0);
1621 }
1622}
1623
1624/**
1625 * Return the first index (inclusive) and last index (exclusive) of the elements of a group in
1626 * an auto layout.
1627 * If the bounds of the given group can be calculated, they are returned in the start/end
1628 * parameters (int pointers) and the return value will be true.
1629 * The indexes are passed by reference and can be NULL.
1630 */
1631bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end) {
1632 size_t nb_grp = auto_group_count(container);
1633 if (group_index >= nb_grp) {
1634 return false;
1635 }
1636 bool master_first = (container->layout == L_AUTO_LEFT || container->layout == L_AUTO_TOP);
1637 size_t nb_master = auto_master_count(container);
1638 size_t nb_slave_grp = auto_slave_group_count(container);
1639 int g_start, g_end;
1640 if (nb_master && (master_first ? group_index == 0 : group_index == nb_grp - 1)) {
1641 g_start = 0;
1642 g_end = nb_master;
1643 } else {
1644 size_t nb_slaves = auto_slave_count(container);
1645 size_t grp_sz = nb_slaves / nb_slave_grp;
1646 size_t remainder = nb_slaves % nb_slave_grp;
1647 size_t g0 = master_first && container->nb_master ? 1 : 0;
1648 size_t g1 = g0 + nb_slave_grp - remainder;
1649 if (group_index < g1) {
1650 g_start = container->nb_master + (group_index - g0) * grp_sz;
1651 g_end = g_start + grp_sz;
1652 } else {
1653 size_t g2 = group_index - g1;
1654 g_start = container->nb_master
1655 + (nb_slave_grp - remainder) * grp_sz
1656 + g2 * (grp_sz + 1);
1657 g_end = g_start + grp_sz + 1;
1658 }
1659 }
1660 if (start) {
1661 *start = g_start;
1662 }
1663 if (end) {
1664 *end = g_end;
1665 }
1666 return true;
1667}
diff --git a/sway/sway.5.txt b/sway/sway.5.txt
index 7980ba82..5e0a07bd 100644
--- a/sway/sway.5.txt
+++ b/sway/sway.5.txt
@@ -39,7 +39,7 @@ The following commands may only be used in the configuration file.
39**set** <name> <value>:: 39**set** <name> <value>::
40 Sets variable $name to _value_. You can use the new variable in the arguments 40 Sets variable $name to _value_. You can use the new variable in the arguments
41 of future commands. 41 of future commands.
42 42
43**orientation** <horizontal|vertical|auto>:: 43**orientation** <horizontal|vertical|auto>::
44 Sets the default container layout for tiled containers. 44 Sets the default container layout for tiled containers.
45 45
@@ -62,11 +62,14 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
62 Make focused view floating, non-floating, or the opposite of what it is now. 62 Make focused view floating, non-floating, or the opposite of what it is now.
63 63
64**focus** <direction>:: 64**focus** <direction>::
65 Direction may be one of _up_, _down_, _left_, _right_, _parent_, or _child_. 65 Direction may be one of _up_, _down_, _left_, _right_, _next_, _prev_,
66 The directional focus commands will move the focus in that direction. The parent 66 _parent_, or _child_. The directional focus commands will move the focus
67 focus command will change the focus to the parent of the currently focused 67 in that direction. The auto_next and auto_prev will focus the next,
68 container, which is useful, for example, to open a sibling of the parent 68 respectively previous, element in the current container if it is using
69 container, or to move the entire container around. 69 one of the _auto_ layouts. The parent focus command will change the
70 focus to the parent of the currently focused container, which is useful,
71 for example, to open a sibling of the parent container, or to move the
72 entire container around.
70 73
71**focus** output <direction|name>:: 74**focus** output <direction|name>::
72 Direction may be one of _up_, _down_, _left_, _right_. The directional focus 75 Direction may be one of _up_, _down_, _left_, _right_. The directional focus
@@ -81,10 +84,28 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
81 84
82**layout** <mode>:: 85**layout** <mode>::
83 Sets the layout mode of the focused container. _mode_ can be one of _splith_, 86 Sets the layout mode of the focused container. _mode_ can be one of _splith_,
84 _splitv_, _toggle split_, _stacking_ or _tabbed_. 87 _splitv_, _toggle split_, _stacking_, _tabbed_.
88
89**layout** auto <mode>::
90 Sets layout to one of the auto modes, i.e. one of _left_, right_, _top_,
91 or _bot_.
92
93**layout** auto <next|prev>::
94 Cycles between available auto layouts.
95
96**layout** auto [master|ncol] [inc|set] <n>::
97 Modify the number of master elements, respectively slave columns, in the
98 focused container. <n> can be a positive or negative integer. These commands
99 only have an effect if the focused container uses one of the "auto" layouts.
100
101**layout** toggle split::
102 Cycles between available split layouts.
85 103
86**move** <left|right|up|down>:: 104**move** <left|right|up|down|next|prev|first>::
87 Moves the focused container _left_, _right_, _up_, or _down_. 105 Moves the focused container _left_, _right_, _up_, or _down_. Moving to _prev_
106 or _next_ swaps the container with its sibling in the same container. Move
107 _first_ exchanges the focused element in an auto layout with the first
108 element, i.e. promotes the focused element to master position.
88 109
89**move** <container|window> to workspace <name>:: 110**move** <container|window> to workspace <name>::
90 Moves the focused container to the workspace identified by _name_. 111 Moves the focused container to the workspace identified by _name_.
@@ -360,7 +381,7 @@ The default colors are:
360 switch to workspace 2, then invoke the "workspace 2" command again, you 381 switch to workspace 2, then invoke the "workspace 2" command again, you
361 will be returned to workspace 1. Defaults to _no_. 382 will be returned to workspace 1. Defaults to _no_.
362 383
363**workspace_layout** <default|stacking|tabbed>:: 384**workspace_layout** <default|stacking|tabbed|auto_left|auto_right|auto_top|auto_bottom>::
364 Specifies the start layout for new workspaces. 385 Specifies the start layout for new workspaces.
365 386
366**include** <path>:: 387**include** <path>::