aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-11-07 22:44:11 -0500
committerLibravatar Brian Ashworth <bosrsf04@gmail.com>2018-11-07 22:44:11 -0500
commit9e8aa3953098adb6175c26aebd984a32a2beccb0 (patch)
treeac64adf9f2720ddbb2476810f6ec05ec2a85a4ae /sway/commands
parentAdd focus_follows_mouse always. (#3081) (diff)
downloadsway-9e8aa3953098adb6175c26aebd984a32a2beccb0.tar.gz
sway-9e8aa3953098adb6175c26aebd984a32a2beccb0.tar.zst
sway-9e8aa3953098adb6175c26aebd984a32a2beccb0.zip
Implement per side and per direction outer gaps
This introduces the following command extensions from `i3-gaps`: * `gaps horizontal|vertical|top|right|bottom|left <amount>` * `gaps horizontal|vertical|top|right|bottom|left all|current set|plus|minus <amount>` * `workspace <ws> gaps horizontal|vertical|top|right|bottom|left <amount>` `inner` and `outer` are also still available as options for all three of the above commands. `outer` now acts as a shorthand to set/alter all sides. Additionally, this fixes two bugs with the prevention of invalid gap configurations for workspace configs: 1. If outer gaps were not set and inner gaps were, the outer gaps would be snapped to the negation of the inner gaps due to `INT_MIN` being less than the negation. This took precedence over the default outer gaps. 2. Similarly, if inner gaps were not set and outer gaps were, inner gaps would be set to zero, which would take precedence over the default inner gaps. Fixing both of the above items also requires checking the gaps again when creating a workspace since the default outer gaps can be smaller than the negation of the workspace specific inner gaps.
Diffstat (limited to 'sway/commands')
-rw-r--r--sway/commands/gaps.c151
-rw-r--r--sway/commands/workspace.c124
2 files changed, 196 insertions, 79 deletions
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 3f0ef155..faaeab37 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -16,73 +16,128 @@ enum gaps_op {
16 16
17struct gaps_data { 17struct gaps_data {
18 bool inner; 18 bool inner;
19 struct {
20 bool top;
21 bool right;
22 bool bottom;
23 bool left;
24 } outer;
19 enum gaps_op operation; 25 enum gaps_op operation;
20 int amount; 26 int amount;
21}; 27};
22 28
23// gaps inner|outer <px> 29// Prevent negative outer gaps from moving windows out of the workspace.
30static void prevent_invalid_outer_gaps(void) {
31 if (config->gaps_outer.top < -config->gaps_inner) {
32 config->gaps_outer.top = -config->gaps_inner;
33 }
34 if (config->gaps_outer.right < -config->gaps_inner) {
35 config->gaps_outer.right = -config->gaps_inner;
36 }
37 if (config->gaps_outer.bottom < -config->gaps_inner) {
38 config->gaps_outer.bottom = -config->gaps_inner;
39 }
40 if (config->gaps_outer.left < -config->gaps_inner) {
41 config->gaps_outer.left = -config->gaps_inner;
42 }
43}
44
45// gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>
46static const char *expected_defaults =
47 "'gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
24static struct cmd_results *gaps_set_defaults(int argc, char **argv) { 48static struct cmd_results *gaps_set_defaults(int argc, char **argv) {
25 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); 49 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
26 if (error) { 50 if (error) {
27 return error; 51 return error;
28 } 52 }
29 53
30 bool inner;
31 if (strcasecmp(argv[0], "inner") == 0) {
32 inner = true;
33 } else if (strcasecmp(argv[0], "outer") == 0) {
34 inner = false;
35 } else {
36 return cmd_results_new(CMD_INVALID, "gaps",
37 "Expected 'gaps inner|outer <px>'");
38 }
39
40 char *end; 54 char *end;
41 int amount = strtol(argv[1], &end, 10); 55 int amount = strtol(argv[1], &end, 10);
42 if (strlen(end) && strcasecmp(end, "px") != 0) { 56 if (strlen(end) && strcasecmp(end, "px") != 0) {
43 return cmd_results_new(CMD_INVALID, "gaps", 57 return cmd_results_new(CMD_INVALID, "gaps",
44 "Expected 'gaps inner|outer <px>'"); 58 "Expected %s", expected_defaults);
45 } 59 }
46 if (inner) { 60
61 bool valid = false;
62 if (!strcasecmp(argv[0], "inner")) {
63 valid = true;
47 config->gaps_inner = (amount >= 0) ? amount : 0; 64 config->gaps_inner = (amount >= 0) ? amount : 0;
48 } else { 65 } else {
49 config->gaps_outer = amount; 66 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical")
50 } 67 || !strcasecmp(argv[0], "top")) {
51 68 valid = true;
52 // Prevent negative outer gaps from moving windows out of the workspace. 69 config->gaps_outer.top = amount;
53 if (config->gaps_outer < -config->gaps_inner) { 70 }
54 config->gaps_outer = -config->gaps_inner; 71 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal")
72 || !strcasecmp(argv[0], "right")) {
73 valid = true;
74 config->gaps_outer.right = amount;
75 }
76 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical")
77 || !strcasecmp(argv[0], "bottom")) {
78 valid = true;
79 config->gaps_outer.bottom = amount;
80 }
81 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal")
82 || !strcasecmp(argv[0], "left")) {
83 valid = true;
84 config->gaps_outer.left = amount;
85 }
86 }
87 if (!valid) {
88 return cmd_results_new(CMD_INVALID, "gaps",
89 "Expected %s", expected_defaults);
55 } 90 }
56 91
92 prevent_invalid_outer_gaps();
57 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 93 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
58} 94}
59 95
60static void configure_gaps(struct sway_workspace *ws, void *_data) { 96static void apply_gaps_op(int *prop, enum gaps_op op, int amount) {
61 struct gaps_data *data = _data; 97 switch (op) {
62 int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer;
63
64 switch (data->operation) {
65 case GAPS_OP_SET: 98 case GAPS_OP_SET:
66 *prop = data->amount; 99 *prop = amount;
67 break; 100 break;
68 case GAPS_OP_ADD: 101 case GAPS_OP_ADD:
69 *prop += data->amount; 102 *prop += amount;
70 break; 103 break;
71 case GAPS_OP_SUBTRACT: 104 case GAPS_OP_SUBTRACT:
72 *prop -= data->amount; 105 *prop -= amount;
73 break; 106 break;
74 } 107 }
108}
109
110static void configure_gaps(struct sway_workspace *ws, void *_data) {
111 // Apply operation to gaps
112 struct gaps_data *data = _data;
113 if (data->inner) {
114 apply_gaps_op(&ws->gaps_inner, data->operation, data->amount);
115 }
116 if (data->outer.top) {
117 apply_gaps_op(&(ws->gaps_outer.top), data->operation, data->amount);
118 }
119 if (data->outer.right) {
120 apply_gaps_op(&(ws->gaps_outer.right), data->operation, data->amount);
121 }
122 if (data->outer.bottom) {
123 apply_gaps_op(&(ws->gaps_outer.bottom), data->operation, data->amount);
124 }
125 if (data->outer.left) {
126 apply_gaps_op(&(ws->gaps_outer.left), data->operation, data->amount);
127 }
128
75 // Prevent invalid gaps configurations. 129 // Prevent invalid gaps configurations.
76 if (ws->gaps_inner < 0) { 130 if (ws->gaps_inner < 0) {
77 ws->gaps_inner = 0; 131 ws->gaps_inner = 0;
78 } 132 }
79 if (ws->gaps_outer < -ws->gaps_inner) { 133 prevent_invalid_outer_gaps();
80 ws->gaps_outer = -ws->gaps_inner;
81 }
82 arrange_workspace(ws); 134 arrange_workspace(ws);
83} 135}
84 136
85// gaps inner|outer current|all set|plus|minus <px> 137// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all
138// set|plus|minus <px>
139static const char *expected_runtime = "'gaps inner|outer|horizontal|vertical|"
140 "top|right|bottom|left current|all set|plus|minus <px>'";
86static struct cmd_results *gaps_set_runtime(int argc, char **argv) { 141static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
87 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 142 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
88 if (error) { 143 if (error) {
@@ -93,15 +148,24 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
93 "Can't run this command while there's no outputs connected."); 148 "Can't run this command while there's no outputs connected.");
94 } 149 }
95 150
96 struct gaps_data data; 151 struct gaps_data data = {0};
97 152
98 if (strcasecmp(argv[0], "inner") == 0) { 153 if (strcasecmp(argv[0], "inner") == 0) {
99 data.inner = true; 154 data.inner = true;
100 } else if (strcasecmp(argv[0], "outer") == 0) {
101 data.inner = false;
102 } else { 155 } else {
156 data.outer.top = !strcasecmp(argv[0], "outer") ||
157 !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "top");
158 data.outer.right = !strcasecmp(argv[0], "outer") ||
159 !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "right");
160 data.outer.bottom = !strcasecmp(argv[0], "outer") ||
161 !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "bottom");
162 data.outer.left = !strcasecmp(argv[0], "outer") ||
163 !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "left");
164 }
165 if (!data.inner && !data.outer.top && !data.outer.right &&
166 !data.outer.bottom && !data.outer.left) {
103 return cmd_results_new(CMD_INVALID, "gaps", 167 return cmd_results_new(CMD_INVALID, "gaps",
104 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 168 "Expected %s", expected_runtime);
105 } 169 }
106 170
107 bool all; 171 bool all;
@@ -111,7 +175,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
111 all = true; 175 all = true;
112 } else { 176 } else {
113 return cmd_results_new(CMD_INVALID, "gaps", 177 return cmd_results_new(CMD_INVALID, "gaps",
114 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 178 "Expected %s", expected_runtime);
115 } 179 }
116 180
117 if (strcasecmp(argv[2], "set") == 0) { 181 if (strcasecmp(argv[2], "set") == 0) {
@@ -122,14 +186,14 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
122 data.operation = GAPS_OP_SUBTRACT; 186 data.operation = GAPS_OP_SUBTRACT;
123 } else { 187 } else {
124 return cmd_results_new(CMD_INVALID, "gaps", 188 return cmd_results_new(CMD_INVALID, "gaps",
125 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 189 "Expected %s", expected_runtime);
126 } 190 }
127 191
128 char *end; 192 char *end;
129 data.amount = strtol(argv[3], &end, 10); 193 data.amount = strtol(argv[3], &end, 10);
130 if (strlen(end) && strcasecmp(end, "px") != 0) { 194 if (strlen(end) && strcasecmp(end, "px") != 0) {
131 return cmd_results_new(CMD_INVALID, "gaps", 195 return cmd_results_new(CMD_INVALID, "gaps",
132 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 196 "Expected %s", expected_runtime);
133 } 197 }
134 198
135 if (all) { 199 if (all) {
@@ -141,8 +205,10 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
141 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 205 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
142} 206}
143 207
144// gaps inner|outer <px> - sets defaults for workspaces 208// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces
145// gaps inner|outer current|all set|plus|minus <px> - runtime only 209// gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only
210// <dir> = horizontal|vertical
211// <side> = top|right|bottom|left
146struct cmd_results *cmd_gaps(int argc, char **argv) { 212struct cmd_results *cmd_gaps(int argc, char **argv) {
147 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); 213 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2);
148 if (error) { 214 if (error) {
@@ -159,9 +225,8 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
159 } 225 }
160 if (config_loading) { 226 if (config_loading) {
161 return cmd_results_new(CMD_INVALID, "gaps", 227 return cmd_results_new(CMD_INVALID, "gaps",
162 "Expected 'gaps inner|outer <px>'"); 228 "Expected %s", expected_defaults);
163 } 229 }
164 return cmd_results_new(CMD_INVALID, "gaps", 230 return cmd_results_new(CMD_INVALID, "gaps",
165 "Expected 'gaps inner|outer <px>' or " 231 "Expected %s or %s", expected_runtime, expected_defaults);
166 "'gaps inner|outer current|all set|plus|minus <px>'");
167} 232}
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 5abbb676..168494d2 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -22,7 +22,10 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
22 } 22 }
23 wsc->workspace = strdup(ws_name); 23 wsc->workspace = strdup(ws_name);
24 wsc->gaps_inner = INT_MIN; 24 wsc->gaps_inner = INT_MIN;
25 wsc->gaps_outer = INT_MIN; 25 wsc->gaps_outer.top = INT_MIN;
26 wsc->gaps_outer.right = INT_MIN;
27 wsc->gaps_outer.bottom = INT_MIN;
28 wsc->gaps_outer.left = INT_MIN;
26 list_add(config->workspace_configs, wsc); 29 list_add(config->workspace_configs, wsc);
27 return wsc; 30 return wsc;
28} 31}
@@ -33,6 +36,89 @@ void free_workspace_config(struct workspace_config *wsc) {
33 free(wsc); 36 free(wsc);
34} 37}
35 38
39static void prevent_invalid_outer_gaps(struct workspace_config *wsc) {
40 if (wsc->gaps_outer.top != INT_MIN &&
41 wsc->gaps_outer.top < -wsc->gaps_inner) {
42 wsc->gaps_outer.top = -wsc->gaps_inner;
43 }
44 if (wsc->gaps_outer.right != INT_MIN &&
45 wsc->gaps_outer.right < -wsc->gaps_inner) {
46 wsc->gaps_outer.right = -wsc->gaps_inner;
47 }
48 if (wsc->gaps_outer.bottom != INT_MIN &&
49 wsc->gaps_outer.bottom < -wsc->gaps_inner) {
50 wsc->gaps_outer.bottom = -wsc->gaps_inner;
51 }
52 if (wsc->gaps_outer.left != INT_MIN &&
53 wsc->gaps_outer.left < -wsc->gaps_inner) {
54 wsc->gaps_outer.left = -wsc->gaps_inner;
55 }
56}
57
58static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
59 int gaps_location) {
60 const char *expected = "Expected 'workspace <name> gaps "
61 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
62 struct cmd_results *error = NULL;
63 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO,
64 gaps_location + 3))) {
65 return error;
66 }
67 char *ws_name = join_args(argv, argc - 3);
68 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
69 free(ws_name);
70 if (!wsc) {
71 return cmd_results_new(CMD_FAILURE, "workspace gaps",
72 "Unable to allocate workspace output");
73 }
74
75 char *end;
76 int amount = strtol(argv[gaps_location + 2], &end, 10);
77 if (strlen(end)) {
78 free(end);
79 return cmd_results_new(CMD_FAILURE, "workspace gaps", expected);
80 }
81
82 bool valid = false;
83 char *type = argv[gaps_location + 1];
84 if (!strcasecmp(type, "inner")) {
85 valid = true;
86 wsc->gaps_inner = (amount >= 0) ? amount : 0;
87 } else {
88 if (!strcasecmp(type, "outer") || !strcasecmp(type, "vertical")
89 || !strcasecmp(type, "top")) {
90 valid = true;
91 wsc->gaps_outer.top = amount;
92 }
93 if (!strcasecmp(type, "outer") || !strcasecmp(type, "horizontal")
94 || !strcasecmp(type, "right")) {
95 valid = true;
96 wsc->gaps_outer.right = amount;
97 }
98 if (!strcasecmp(type, "outer") || !strcasecmp(type, "vertical")
99 || !strcasecmp(type, "bottom")) {
100 valid = true;
101 wsc->gaps_outer.bottom = amount;
102 }
103 if (!strcasecmp(type, "outer") || !strcasecmp(type, "horizontal")
104 || !strcasecmp(type, "left")) {
105 valid = true;
106 wsc->gaps_outer.left = amount;
107 }
108 }
109 if (!valid) {
110 return cmd_results_new(CMD_INVALID, "workspace gaps", expected);
111 }
112
113 // Prevent invalid gaps configurations.
114 if (wsc->gaps_inner != INT_MIN && wsc->gaps_inner < 0) {
115 wsc->gaps_inner = 0;
116 }
117 prevent_invalid_outer_gaps(wsc);
118
119 return error;
120}
121
36struct cmd_results *cmd_workspace(int argc, char **argv) { 122struct cmd_results *cmd_workspace(int argc, char **argv) {
37 struct cmd_results *error = NULL; 123 struct cmd_results *error = NULL;
38 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { 124 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
@@ -68,43 +154,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
68 free(wsc->output); 154 free(wsc->output);
69 wsc->output = strdup(argv[output_location + 1]); 155 wsc->output = strdup(argv[output_location + 1]);
70 } else if (gaps_location >= 0) { 156 } else if (gaps_location >= 0) {
71 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { 157 if ((error = cmd_workspace_gaps(argc, argv, gaps_location))) {
72 return error; 158 return error;
73 } 159 }
74 char *ws_name = join_args(argv, argc - 3);
75 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
76 free(ws_name);
77 if (!wsc) {
78 return cmd_results_new(CMD_FAILURE, "workspace gaps",
79 "Unable to allocate workspace output");
80 }
81 int *prop = NULL;
82 if (strcasecmp(argv[gaps_location + 1], "inner") == 0) {
83 prop = &wsc->gaps_inner;
84 } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) {
85 prop = &wsc->gaps_outer;
86 } else {
87 return cmd_results_new(CMD_FAILURE, "workspace gaps",
88 "Expected 'workspace <ws> gaps inner|outer <px>'");
89 }
90 char *end;
91 int val = strtol(argv[gaps_location + 2], &end, 10);
92
93 if (strlen(end)) {
94 free(end);
95 return cmd_results_new(CMD_FAILURE, "workspace gaps",
96 "Expected 'workspace <ws> gaps inner|outer <px>'");
97 }
98 *prop = val;
99
100 // Prevent invalid gaps configurations.
101 if (wsc->gaps_inner < 0) {
102 wsc->gaps_inner = 0;
103 }
104 if (wsc->gaps_outer < -wsc->gaps_inner) {
105 wsc->gaps_outer = -wsc->gaps_inner;
106 }
107
108 } else { 160 } else {
109 if (config->reading || !config->active) { 161 if (config->reading || !config->active) {
110 return cmd_results_new(CMD_DEFER, "workspace", NULL); 162 return cmd_results_new(CMD_DEFER, "workspace", NULL);