summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2015-10-23 10:34:38 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2015-10-23 10:34:38 -0400
commit988a0bd67e02755308b478d369a3825c712a842e (patch)
treec29db67ec97907448084a36909c1e51d74cd8eaf
parentMerge pull request #200 from sce/fix_seamless_transitions (diff)
parentcommands: cmd_results->input is duplicated/freed. (diff)
downloadsway-988a0bd67e02755308b478d369a3825c712a842e.tar.gz
sway-988a0bd67e02755308b478d369a3825c712a842e.tar.zst
sway-988a0bd67e02755308b478d369a3825c712a842e.zip
Merge pull request #201 from sce/cmd_results
Replace cmd_status with cmd_results struct
-rw-r--r--include/commands.h19
-rw-r--r--sway/commands.c513
-rw-r--r--sway/config.c6
-rw-r--r--sway/handlers.c15
-rw-r--r--sway/ipc.c8
5 files changed, 339 insertions, 222 deletions
diff --git a/include/commands.h b/include/commands.h
index 1b4cd9ca..8e53c74d 100644
--- a/include/commands.h
+++ b/include/commands.h
@@ -1,22 +1,33 @@
1#ifndef _SWAY_COMMANDS_H 1#ifndef _SWAY_COMMANDS_H
2#define _SWAY_COMMANDS_H 2#define _SWAY_COMMANDS_H
3#include <stdbool.h> 3#include <stdbool.h>
4#include <json-c/json.h>
4#include "config.h" 5#include "config.h"
5 6
6 7
7enum cmd_status { 8enum cmd_status {
8 CMD_SUCCESS, 9 CMD_SUCCESS,
9 CMD_FAILURE, 10 CMD_FAILURE, // was or at least could be executed
10 CMD_INVALID, 11 CMD_INVALID, // unknown or parse error
11 CMD_DEFER, 12 CMD_DEFER,
12 // Config Blocks 13 // Config Blocks
13 CMD_BLOCK_END, 14 CMD_BLOCK_END,
14 CMD_BLOCK_MODE, 15 CMD_BLOCK_MODE,
15}; 16};
16 17
17enum cmd_status handle_command(char *command); 18struct cmd_results {
19 enum cmd_status status;
20 char *input;
21 char *error;
22};
23
24struct cmd_results *handle_command(char *command);
18// Handles commands during config 25// Handles commands during config
19enum cmd_status config_command(char *command); 26struct cmd_results *config_command(char *command);
27
28struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *error, ...);
29void free_cmd_results(struct cmd_results *results);
30const char *cmd_results_to_json(struct cmd_results *results);
20 31
21void remove_view_from_scratchpad(); 32void remove_view_from_scratchpad();
22 33
diff --git a/sway/commands.c b/sway/commands.c
index a9c20e51..8c45dabe 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -21,7 +21,7 @@
21#include "sway.h" 21#include "sway.h"
22#include "resize.h" 22#include "resize.h"
23 23
24typedef enum cmd_status sway_cmd(int argc, char **argv); 24typedef struct cmd_results *sway_cmd(int argc, char **argv);
25 25
26struct cmd_handler { 26struct cmd_handler {
27 char *command; 27 char *command;
@@ -81,41 +81,43 @@ enum expected_args {
81 EXPECTED_EQUAL_TO 81 EXPECTED_EQUAL_TO
82}; 82};
83 83
84static bool checkarg(int argc, const char *name, enum expected_args type, int val) { 84// Returns error object, or NULL if check succeeds.
85static struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
86 struct cmd_results *error = NULL;
85 switch (type) { 87 switch (type) {
86 case EXPECTED_MORE_THAN: 88 case EXPECTED_MORE_THAN:
87 if (argc > val) { 89 if (argc > val) {
88 return true; 90 return NULL;
89 } 91 }
90 sway_log(L_ERROR, "Invalid %s command." 92 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
91 "(expected more than %d argument%s, got %d)", 93 "(expected more than %d argument%s, got %d)",
92 name, val, (char*[2]){"s", ""}[argc==1], argc); 94 name, val, (char*[2]){"s", ""}[argc==1], argc);
93 break; 95 break;
94 case EXPECTED_AT_LEAST: 96 case EXPECTED_AT_LEAST:
95 if (argc >= val) { 97 if (argc >= val) {
96 return true; 98 return NULL;
97 } 99 }
98 sway_log(L_ERROR, "Invalid %s command." 100 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
99 "(expected at least %d argument%s, got %d)", 101 "(expected at least %d argument%s, got %d)",
100 name, val, (char*[2]){"s", ""}[argc==1], argc); 102 name, val, (char*[2]){"s", ""}[argc==1], argc);
101 break; 103 break;
102 case EXPECTED_LESS_THAN: 104 case EXPECTED_LESS_THAN:
103 if (argc < val) { 105 if (argc < val) {
104 return true; 106 return NULL;
105 }; 107 };
106 sway_log(L_ERROR, "Invalid %s command." 108 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
107 "(expected less than %d argument%s, got %d)", 109 "(expected less than %d argument%s, got %d)",
108 name, val, (char*[2]){"s", ""}[argc==1], argc); 110 name, val, (char*[2]){"s", ""}[argc==1], argc);
109 break; 111 break;
110 case EXPECTED_EQUAL_TO: 112 case EXPECTED_EQUAL_TO:
111 if (argc == val) { 113 if (argc == val) {
112 return true; 114 return NULL;
113 }; 115 };
114 sway_log(L_ERROR, "Invalid %s command." 116 error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
115 "(expected %d arguments, got %d)", name, val, argc); 117 "(expected %d arguments, got %d)", name, val, argc);
116 break; 118 break;
117 } 119 }
118 return false; 120 return error;
119} 121}
120 122
121static int bindsym_sort(const void *_lbind, const void *_rbind) { 123static int bindsym_sort(const void *_lbind, const void *_rbind) {
@@ -131,11 +133,13 @@ static int bindsym_sort(const void *_lbind, const void *_rbind) {
131 return (rbind->keys->length + rmod) - (lbind->keys->length + lmod); 133 return (rbind->keys->length + rmod) - (lbind->keys->length + lmod);
132} 134}
133 135
134static enum cmd_status cmd_bindsym(int argc, char **argv) { 136static struct cmd_results *cmd_bindsym(int argc, char **argv) {
135 if (!checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1) 137 struct cmd_results *error = NULL;
136 || !config->reading) { 138 if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
137 return CMD_FAILURE; 139 return error;
138 }; 140 } else if (!config->reading) {
141 return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
142 }
139 143
140 struct sway_binding *binding = malloc(sizeof(struct sway_binding)); 144 struct sway_binding *binding = malloc(sizeof(struct sway_binding));
141 binding->keys = create_list(); 145 binding->keys = create_list();
@@ -159,12 +163,12 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) {
159 // Check for xkb key 163 // Check for xkb key
160 xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE); 164 xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE);
161 if (!sym) { 165 if (!sym) {
162 sway_log(L_ERROR, "bindsym - unknown key %s", (char *)split->items[i]); 166 error = cmd_results_new(CMD_INVALID, "bindsym", "Unknown key '%s'", (char *)split->items[i]);
163 list_free(binding->keys); 167 list_free(binding->keys);
164 free(binding->command); 168 free(binding->command);
165 free(binding); 169 free(binding);
166 list_free(split); 170 list_free(split);
167 return CMD_FAILURE; 171 return error;
168 } 172 }
169 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); 173 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
170 *key = sym; 174 *key = sym;
@@ -178,21 +182,21 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) {
178 list_sort(mode->bindings, bindsym_sort); 182 list_sort(mode->bindings, bindsym_sort);
179 183
180 sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command); 184 sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
181 return CMD_SUCCESS; 185 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
182} 186}
183 187
184static enum cmd_status cmd_exec_always(int argc, char **argv) { 188static struct cmd_results *cmd_exec_always(int argc, char **argv) {
185 if (!config->active) return CMD_DEFER; 189 struct cmd_results *error = NULL;
186 if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) { 190 if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL);
187 return CMD_FAILURE; 191 if ((error = checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0))) {
192 return error;
188 } 193 }
189 194
190 char *tmp = NULL; 195 char *tmp = NULL;
191 if (strcmp((char*)*argv, "--no-startup-id") == 0) { 196 if (strcmp((char*)*argv, "--no-startup-id") == 0) {
192 sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); 197 sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
193 198 if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
194 if (!checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0)) { 199 return error;
195 return CMD_FAILURE;
196 } 200 }
197 201
198 tmp = join_args(argv + 1, argc - 1); 202 tmp = join_args(argv + 1, argc - 1);
@@ -218,22 +222,20 @@ static enum cmd_status cmd_exec_always(int argc, char **argv) {
218 // Close child process 222 // Close child process
219 _exit(0); 223 _exit(0);
220 } else if (pid < 0) { 224 } else if (pid < 0) {
221 sway_log(L_ERROR, "exec command failed, sway could not fork"); 225 return cmd_results_new(CMD_FAILURE, "exec_always", "Command failed (sway could not fork).");
222 return CMD_FAILURE;
223 } 226 }
224 // cleanup child process 227 // cleanup child process
225 wait(0); 228 wait(0);
226 return CMD_SUCCESS; 229 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
227} 230}
228 231
229static enum cmd_status cmd_exec(int argc, char **argv) { 232static struct cmd_results *cmd_exec(int argc, char **argv) {
230 if (!config->active) return CMD_DEFER; 233 if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
231
232 if (config->reloading) { 234 if (config->reloading) {
233 char *args = join_args(argv, argc); 235 char *args = join_args(argv, argc);
234 sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); 236 sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
235 free(args); 237 free(args);
236 return CMD_SUCCESS; 238 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
237 } 239 }
238 return cmd_exec_always(argc, argv); 240 return cmd_exec_always(argc, argv);
239} 241}
@@ -244,28 +246,30 @@ static void kill_views(swayc_t *container, void *data) {
244 } 246 }
245} 247}
246 248
247static enum cmd_status cmd_exit(int argc, char **argv) { 249static struct cmd_results *cmd_exit(int argc, char **argv) {
248 if (config->reading) return CMD_INVALID; 250 struct cmd_results *error = NULL;
249 if (!checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0)) { 251 if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
250 return CMD_FAILURE; 252 if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
253 return error;
251 } 254 }
252 // Close all views 255 // Close all views
253 container_map(&root_container, kill_views, NULL); 256 container_map(&root_container, kill_views, NULL);
254 sway_terminate(); 257 sway_terminate();
255 return CMD_SUCCESS; 258 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
256} 259}
257 260
258static enum cmd_status cmd_floating(int argc, char **argv) { 261static struct cmd_results *cmd_floating(int argc, char **argv) {
259 if (config->reading) return CMD_INVALID; 262 struct cmd_results *error = NULL;
260 if (!checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1)) { 263 if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
261 return CMD_FAILURE; 264 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
265 return error;
262 } 266 }
263 267
264 if (strcasecmp(argv[0], "toggle") == 0) { 268 if (strcasecmp(argv[0], "toggle") == 0) {
265 swayc_t *view = get_focused_container(&root_container); 269 swayc_t *view = get_focused_container(&root_container);
266 // Prevent running floating commands on things like workspaces 270 // Prevent running floating commands on things like workspaces
267 if (view->type != C_VIEW) { 271 if (view->type != C_VIEW) {
268 return CMD_SUCCESS; 272 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
269 } 273 }
270 // Change from nonfloating to floating 274 // Change from nonfloating to floating
271 if (!view->is_floating) { 275 if (!view->is_floating) {
@@ -314,13 +318,13 @@ static enum cmd_status cmd_floating(int argc, char **argv) {
314 } 318 }
315 set_focused_container(view); 319 set_focused_container(view);
316 } 320 }
317 return CMD_SUCCESS; 321 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
318} 322}
319 323
320static enum cmd_status cmd_floating_mod(int argc, char **argv) { 324static struct cmd_results *cmd_floating_mod(int argc, char **argv) {
321 if (!config->reading) return CMD_INVALID; 325 struct cmd_results *error = NULL;
322 if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1)) { 326 if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) {
323 return CMD_FAILURE; 327 return error;
324 } 328 }
325 int i, j; 329 int i, j;
326 list_t *split = split_string(argv[0], "+"); 330 list_t *split = split_string(argv[0], "+");
@@ -336,19 +340,20 @@ static enum cmd_status cmd_floating_mod(int argc, char **argv) {
336 } 340 }
337 free_flat_list(split); 341 free_flat_list(split);
338 if (!config->floating_mod) { 342 if (!config->floating_mod) {
339 sway_log(L_ERROR, "bindsym - unknown keys %s", argv[0]); 343 error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
340 return CMD_FAILURE; 344 return error;
341 } 345 }
342 return CMD_SUCCESS; 346 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
343} 347}
344 348
345static enum cmd_status cmd_focus(int argc, char **argv) { 349static struct cmd_results *cmd_focus(int argc, char **argv) {
346 if (config->reading) return CMD_INVALID; 350 if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
351 struct cmd_results *error = NULL;
352 if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
353 return error;
354 }
347 static int floating_toggled_index = 0; 355 static int floating_toggled_index = 0;
348 static int tiled_toggled_index = 0; 356 static int tiled_toggled_index = 0;
349 if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1)) {
350 return CMD_FAILURE;
351 }
352 if (strcasecmp(argv[0], "left") == 0) { 357 if (strcasecmp(argv[0], "left") == 0) {
353 move_focus(MOVE_LEFT); 358 move_focus(MOVE_LEFT);
354 } else if (strcasecmp(argv[0], "right") == 0) { 359 } else if (strcasecmp(argv[0], "right") == 0) {
@@ -397,26 +402,27 @@ static enum cmd_status cmd_focus(int argc, char **argv) {
397 } 402 }
398 } 403 }
399 } 404 }
400 return CMD_SUCCESS; 405 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
401} 406}
402 407
403static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) { 408static struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
404 if (!config->reading) return CMD_INVALID; 409 struct cmd_results *error = NULL;
405 if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) { 410 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
406 return CMD_FAILURE; 411 return error;
407 } 412 }
408 413
409 config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); 414 config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
410 return CMD_SUCCESS; 415 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
411} 416}
412 417
413static enum cmd_status cmd_seamless_mouse(int argc, char **argv) { 418static struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
414 if (!checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1)) { 419 struct cmd_results *error = NULL;
415 return CMD_FAILURE; 420 if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
421 return error;
416 } 422 }
417 423
418 config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0); 424 config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
419 return CMD_SUCCESS; 425 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
420} 426}
421 427
422static void hide_view_in_scratchpad(swayc_t *sp_view) { 428static void hide_view_in_scratchpad(swayc_t *sp_view) {
@@ -435,15 +441,17 @@ static void hide_view_in_scratchpad(swayc_t *sp_view) {
435 set_focused_container(container_under_pointer()); 441 set_focused_container(container_under_pointer());
436} 442}
437 443
438static enum cmd_status cmd_mode(int argc, char **argv) { 444static struct cmd_results *cmd_mode(int argc, char **argv) {
439 if (!checkarg(argc, "mode", EXPECTED_AT_LEAST, 1)) { 445 struct cmd_results *error = NULL;
440 return CMD_FAILURE; 446 if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
447 return error;
441 } 448 }
442 char *mode_name = join_args(argv, argc); 449 char *mode_name = join_args(argv, argc);
443 int mode_len = strlen(mode_name); 450 int mode_len = strlen(mode_name);
444 bool mode_make = mode_name[mode_len-1] == '{'; 451 bool mode_make = mode_name[mode_len-1] == '{';
445 if (mode_make) { 452 if (mode_make) {
446 if (!config->reading) return CMD_INVALID; 453 if (!config->reading)
454 return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
447 // Trim trailing spaces 455 // Trim trailing spaces
448 do { 456 do {
449 mode_name[--mode_len] = 0; 457 mode_name[--mode_len] = 0;
@@ -467,9 +475,9 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
467 list_add(config->modes, mode); 475 list_add(config->modes, mode);
468 } 476 }
469 if (!mode) { 477 if (!mode) {
470 sway_log(L_ERROR, "Unknown mode `%s'", mode_name); 478 error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
471 free(mode_name); 479 free(mode_name);
472 return CMD_FAILURE; 480 return error;
473 } 481 }
474 if ((config->reading && mode_make) || (!config->reading && !mode_make)) { 482 if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
475 sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); 483 sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
@@ -477,15 +485,15 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
477 free(mode_name); 485 free(mode_name);
478 // Set current mode 486 // Set current mode
479 config->current_mode = mode; 487 config->current_mode = mode;
480 return mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS; 488 return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
481} 489}
482 490
483static enum cmd_status cmd_move(int argc, char **argv) { 491static struct cmd_results *cmd_move(int argc, char **argv) {
484 if (config->reading) return CMD_FAILURE; 492 struct cmd_results *error = NULL;
485 if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { 493 if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
486 return CMD_FAILURE; 494 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
495 return error;
487 } 496 }
488
489 swayc_t *view = get_focused_container(&root_container); 497 swayc_t *view = get_focused_container(&root_container);
490 498
491 if (strcasecmp(argv[0], "left") == 0) { 499 if (strcasecmp(argv[0], "left") == 0) {
@@ -498,14 +506,14 @@ static enum cmd_status cmd_move(int argc, char **argv) {
498 move_container(view, MOVE_DOWN); 506 move_container(view, MOVE_DOWN);
499 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { 507 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
500 // "move container to workspace x" 508 // "move container to workspace x"
501 if (!checkarg(argc, "move container/window", EXPECTED_EQUAL_TO, 4) 509 if ((error = checkarg(argc, "move container/window", EXPECTED_EQUAL_TO, 4))) {
502 || strcasecmp(argv[1], "to") != 0 510 return error;
503 || strcasecmp(argv[2], "workspace") != 0) { 511 } else if ( strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "workspace") != 0) {
504 return CMD_FAILURE; 512 return cmd_results_new(CMD_INVALID, "move", "Expected 'move %s to workspace <name>'", argv[0]);
505 } 513 }
506 514
507 if (view->type != C_CONTAINER && view->type != C_VIEW) { 515 if (view->type != C_CONTAINER && view->type != C_VIEW) {
508 return CMD_FAILURE; 516 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
509 } 517 }
510 518
511 const char *ws_name = argv[3]; 519 const char *ws_name = argv[3];
@@ -521,7 +529,7 @@ static enum cmd_status cmd_move(int argc, char **argv) {
521 move_container_to(view, get_focused_container(ws)); 529 move_container_to(view, get_focused_container(ws));
522 } else if (strcasecmp(argv[0], "scratchpad") == 0) { 530 } else if (strcasecmp(argv[0], "scratchpad") == 0) {
523 if (view->type != C_CONTAINER && view->type != C_VIEW) { 531 if (view->type != C_CONTAINER && view->type != C_VIEW) {
524 return CMD_FAILURE; 532 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
525 } 533 }
526 swayc_t *view = get_focused_container(&root_container); 534 swayc_t *view = get_focused_container(&root_container);
527 int i; 535 int i;
@@ -529,7 +537,7 @@ static enum cmd_status cmd_move(int argc, char **argv) {
529 if (scratchpad->items[i] == view) { 537 if (scratchpad->items[i] == view) {
530 hide_view_in_scratchpad(view); 538 hide_view_in_scratchpad(view);
531 sp_view = NULL; 539 sp_view = NULL;
532 return CMD_SUCCESS; 540 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
533 } 541 }
534 } 542 }
535 list_add(scratchpad, view); 543 list_add(scratchpad, view);
@@ -546,17 +554,18 @@ static enum cmd_status cmd_move(int argc, char **argv) {
546 } 554 }
547 set_focused_container(focused); 555 set_focused_container(focused);
548 } else { 556 } else {
549 return CMD_FAILURE; 557 return cmd_results_new(CMD_INVALID, "move",
558 "Expected 'move <left|right|up|down>' or 'move <container|window> to workspace <name>'");
550 } 559 }
551 return CMD_SUCCESS; 560 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
552} 561}
553 562
554static enum cmd_status cmd_orientation(int argc, char **argv) { 563static struct cmd_results *cmd_orientation(int argc, char **argv) {
555 if (!config->reading) return CMD_FAILURE; 564 struct cmd_results *error = NULL;
556 if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1)) { 565 if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
557 return CMD_FAILURE; 566 if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
567 return error;
558 } 568 }
559
560 if (strcasecmp(argv[0], "horizontal") == 0) { 569 if (strcasecmp(argv[0], "horizontal") == 0) {
561 config->default_orientation = L_HORIZ; 570 config->default_orientation = L_HORIZ;
562 } else if (strcasecmp(argv[0], "vertical") == 0) { 571 } else if (strcasecmp(argv[0], "vertical") == 0) {
@@ -564,16 +573,16 @@ static enum cmd_status cmd_orientation(int argc, char **argv) {
564 } else if (strcasecmp(argv[0], "auto") == 0) { 573 } else if (strcasecmp(argv[0], "auto") == 0) {
565 // Do nothing 574 // Do nothing
566 } else { 575 } else {
567 return CMD_FAILURE; 576 return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
568 } 577 }
569 return CMD_SUCCESS; 578 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
570} 579}
571 580
572static enum cmd_status cmd_output(int argc, char **argv) { 581static struct cmd_results *cmd_output(int argc, char **argv) {
573 if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { 582 struct cmd_results *error = NULL;
574 return CMD_FAILURE; 583 if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
584 return error;
575 } 585 }
576
577 struct output_config *output = calloc(1, sizeof(struct output_config)); 586 struct output_config *output = calloc(1, sizeof(struct output_config));
578 output->x = output->y = output->width = output->height = -1; 587 output->x = output->y = output->width = output->height = -1;
579 output->name = strdup(argv[0]); 588 output->name = strdup(argv[0]);
@@ -644,12 +653,13 @@ static enum cmd_status cmd_output(int argc, char **argv) {
644 } 653 }
645 } 654 }
646 655
647 return CMD_SUCCESS; 656 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
648} 657}
649 658
650static enum cmd_status cmd_gaps(int argc, char **argv) { 659static struct cmd_results *cmd_gaps(int argc, char **argv) {
651 if (!checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1)) { 660 struct cmd_results *error = NULL;
652 return CMD_FAILURE; 661 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
662 return error;
653 } 663 }
654 const char *amount_str = argv[0]; 664 const char *amount_str = argv[0];
655 // gaps amount 665 // gaps amount
@@ -657,7 +667,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
657 int amount = (int)strtol(amount_str, NULL, 10); 667 int amount = (int)strtol(amount_str, NULL, 10);
658 if (errno == ERANGE || amount == 0) { 668 if (errno == ERANGE || amount == 0) {
659 errno = 0; 669 errno = 0;
660 return CMD_FAILURE; 670 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
661 } 671 }
662 if (config->gaps_inner == 0) { 672 if (config->gaps_inner == 0) {
663 config->gaps_inner = amount; 673 config->gaps_inner = amount;
@@ -665,14 +675,14 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
665 if (config->gaps_outer == 0) { 675 if (config->gaps_outer == 0) {
666 config->gaps_outer = amount; 676 config->gaps_outer = amount;
667 } 677 }
668 return CMD_SUCCESS; 678 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
669 } 679 }
670 // gaps inner|outer n 680 // gaps inner|outer n
671 else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) { 681 else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
672 int amount = (int)strtol(amount_str, NULL, 10); 682 int amount = (int)strtol(amount_str, NULL, 10);
673 if (errno == ERANGE || amount == 0) { 683 if (errno == ERANGE || amount == 0) {
674 errno = 0; 684 errno = 0;
675 return CMD_FAILURE; 685 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
676 } 686 }
677 const char *target_str = argv[0]; 687 const char *target_str = argv[0];
678 if (strcasecmp(target_str, "inner") == 0) { 688 if (strcasecmp(target_str, "inner") == 0) {
@@ -680,11 +690,11 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
680 } else if (strcasecmp(target_str, "outer") == 0) { 690 } else if (strcasecmp(target_str, "outer") == 0) {
681 config->gaps_outer = amount; 691 config->gaps_outer = amount;
682 } 692 }
683 return CMD_SUCCESS; 693 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
684 } 694 }
685 // gaps inner|outer current|all set|plus|minus n 695 // gaps inner|outer current|all set|plus|minus n
686 if (argc < 4 || config->reading) { 696 if (argc < 4 || config->reading) {
687 return CMD_FAILURE; 697 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
688 } 698 }
689 // gaps inner|outer ... 699 // gaps inner|outer ...
690 const char *inout_str = argv[0]; 700 const char *inout_str = argv[0];
@@ -694,7 +704,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
694 } else if (strcasecmp(inout_str, "outer") == 0) { 704 } else if (strcasecmp(inout_str, "outer") == 0) {
695 inout = OUTER; 705 inout = OUTER;
696 } else { 706 } else {
697 return CMD_FAILURE; 707 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
698 } 708 }
699 709
700 // gaps ... current|all ... 710 // gaps ... current|all ...
@@ -712,7 +722,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
712 target = WORKSPACE; 722 target = WORKSPACE;
713 } 723 }
714 } else { 724 } else {
715 return CMD_FAILURE; 725 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
716 } 726 }
717 727
718 // gaps ... n 728 // gaps ... n
@@ -720,7 +730,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
720 int amount = (int)strtol(amount_str, NULL, 10); 730 int amount = (int)strtol(amount_str, NULL, 10);
721 if (errno == ERANGE || amount == 0) { 731 if (errno == ERANGE || amount == 0) {
722 errno = 0; 732 errno = 0;
723 return CMD_FAILURE; 733 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
724 } 734 }
725 735
726 // gaps ... set|plus|minus ... 736 // gaps ... set|plus|minus ...
@@ -734,18 +744,18 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
734 method = ADD; 744 method = ADD;
735 amount *= -1; 745 amount *= -1;
736 } else { 746 } else {
737 return CMD_FAILURE; 747 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all> <set|plus|minus n>'");
738 } 748 }
739 749
740 if (target == CURRENT) { 750 if (target == CURRENT) {
741 swayc_t *cont; 751 swayc_t *cont;
742 if (inout == OUTER) { 752 if (inout == OUTER) {
743 if ((cont = swayc_active_workspace()) == NULL) { 753 if ((cont = swayc_active_workspace()) == NULL) {
744 return CMD_FAILURE; 754 return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
745 } 755 }
746 } else { 756 } else {
747 if ((cont = get_focused_view(&root_container))->type != C_VIEW) { 757 if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
748 return CMD_FAILURE; 758 return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
749 } 759 }
750 } 760 }
751 cont->gaps = swayc_gap(cont); 761 cont->gaps = swayc_gap(cont);
@@ -775,7 +785,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
775 swayc_t *top; 785 swayc_t *top;
776 if (target == WORKSPACE) { 786 if (target == WORKSPACE) {
777 if ((top = swayc_active_workspace()) == NULL) { 787 if ((top = swayc_active_workspace()) == NULL) {
778 return CMD_FAILURE; 788 return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
779 } 789 }
780 } else { 790 } else {
781 top = &root_container; 791 top = &root_container;
@@ -786,22 +796,24 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
786 arrange_windows(top, -1, -1); 796 arrange_windows(top, -1, -1);
787 } 797 }
788 798
789 return CMD_SUCCESS; 799 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
790} 800}
791 801
792static enum cmd_status cmd_kill(int argc, char **argv) { 802static struct cmd_results *cmd_kill(int argc, char **argv) {
793 if (config->reading || !config->active) { 803 if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
794 return CMD_FAILURE; 804 if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
795 } 805
796 swayc_t *view = get_focused_container(&root_container); 806 swayc_t *view = get_focused_container(&root_container);
797 wlc_view_close(view->handle); 807 wlc_view_close(view->handle);
798 return CMD_SUCCESS; 808 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
799} 809}
800 810
801static enum cmd_status cmd_layout(int argc, char **argv) { 811static struct cmd_results *cmd_layout(int argc, char **argv) {
802 if (!checkarg(argc, "layout", EXPECTED_MORE_THAN, 0) 812 struct cmd_results *error = NULL;
803 || config->reading || !config->active) { 813 if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
804 return CMD_FAILURE; 814 if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
815 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
816 return error;
805 } 817 }
806 swayc_t *parent = get_focused_container(&root_container); 818 swayc_t *parent = get_focused_container(&root_container);
807 while (parent->type == C_VIEW) { 819 while (parent->type == C_VIEW) {
@@ -821,33 +833,38 @@ static enum cmd_status cmd_layout(int argc, char **argv) {
821 } 833 }
822 arrange_windows(parent, parent->width, parent->height); 834 arrange_windows(parent, parent->width, parent->height);
823 835
824 return CMD_SUCCESS; 836 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
825} 837}
826 838
827static enum cmd_status cmd_reload(int argc, char **argv) { 839static struct cmd_results *cmd_reload(int argc, char **argv) {
828 if (!checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0) 840 struct cmd_results *error = NULL;
829 || config->reading 841 if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
830 || !load_config(NULL)) { 842 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
831 return CMD_FAILURE; 843 return error;
832 } 844 }
845 if (!load_config(NULL)) return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
846
833 arrange_windows(&root_container, -1, -1); 847 arrange_windows(&root_container, -1, -1);
834 return CMD_SUCCESS; 848 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
835} 849}
836 850
837static enum cmd_status cmd_resize(int argc, char **argv) { 851static struct cmd_results *cmd_resize(int argc, char **argv) {
838 if (!checkarg(argc, "resize", EXPECTED_AT_LEAST, 3) 852 struct cmd_results *error = NULL;
839 || config->reading || !config->active) { 853 if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
840 return CMD_FAILURE; 854 if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
855 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 3))) {
856 return error;
841 } 857 }
842 char *end; 858 char *end;
843 int amount = (int)strtol(argv[2], &end, 10); 859 int amount = (int)strtol(argv[2], &end, 10);
844 if (errno == ERANGE || amount == 0) { 860 if (errno == ERANGE || amount == 0) {
845 errno = 0; 861 errno = 0;
846 return CMD_FAILURE; 862 return cmd_results_new(CMD_INVALID, "resize", "Number is out of range.");
847 } 863 }
848 864
849 if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) { 865 if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) {
850 return CMD_FAILURE; 866 return cmd_results_new(CMD_INVALID, "resize",
867 "Expected 'resize <shrink|grow> <width|height> <amount>'");
851 } 868 }
852 869
853 if (strcmp(argv[0], "shrink") == 0) { 870 if (strcmp(argv[0], "shrink") == 0) {
@@ -859,9 +876,10 @@ static enum cmd_status cmd_resize(int argc, char **argv) {
859 } else if (strcmp(argv[1], "height") == 0) { 876 } else if (strcmp(argv[1], "height") == 0) {
860 resize_tiled(amount, false); 877 resize_tiled(amount, false);
861 } else { 878 } else {
862 return CMD_FAILURE; 879 return cmd_results_new(CMD_INVALID, "resize",
880 "Expected 'resize <shrink|grow> <width|height> <amount>'");
863 } 881 }
864 return CMD_SUCCESS; 882 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
865} 883}
866 884
867static swayc_t *fetch_view_from_scratchpad() { 885static swayc_t *fetch_view_from_scratchpad() {
@@ -909,10 +927,12 @@ void remove_view_from_scratchpad(swayc_t *view) {
909 } 927 }
910} 928}
911 929
912static enum cmd_status cmd_scratchpad(int argc, char **argv) { 930static struct cmd_results *cmd_scratchpad(int argc, char **argv) {
913 if (!checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1) 931 struct cmd_results *error = NULL;
914 || config->reading || !config->active) { 932 if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
915 return CMD_FAILURE; 933 if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
934 if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
935 return error;
916 } 936 }
917 937
918 if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) { 938 if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
@@ -931,9 +951,9 @@ static enum cmd_status cmd_scratchpad(int argc, char **argv) {
931 sp_view = NULL; 951 sp_view = NULL;
932 } 952 }
933 } 953 }
934 return CMD_SUCCESS; 954 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
935 } 955 }
936 return CMD_FAILURE; 956 return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
937} 957}
938 958
939// sort in order of longest->shortest 959// sort in order of longest->shortest
@@ -943,11 +963,13 @@ static int compare_set(const void *_l, const void *_r) {
943 return strlen((*r)->name) - strlen((*l)->name); 963 return strlen((*r)->name) - strlen((*l)->name);
944} 964}
945 965
946static enum cmd_status cmd_set(int argc, char **argv) { 966static struct cmd_results *cmd_set(int argc, char **argv) {
947 if (!checkarg(argc, "set", EXPECTED_AT_LEAST, 2) 967 struct cmd_results *error = NULL;
948 || !config->reading) { 968 if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
949 return CMD_FAILURE; 969 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
970 return error;
950 } 971 }
972
951 struct sway_variable *var = NULL; 973 struct sway_variable *var = NULL;
952 // Find old variable if it exists 974 // Find old variable if it exists
953 int i; 975 int i;
@@ -967,21 +989,23 @@ static enum cmd_status cmd_set(int argc, char **argv) {
967 list_sort(config->symbols, compare_set); 989 list_sort(config->symbols, compare_set);
968 } 990 }
969 var->value = join_args(argv + 1, argc - 1); 991 var->value = join_args(argv + 1, argc - 1);
970 return CMD_SUCCESS; 992 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
971} 993}
972 994
973static enum cmd_status _do_split(int argc, char **argv, int layout) { 995static struct cmd_results *_do_split(int argc, char **argv, int layout) {
974 char *name = layout == L_VERT ? "splitv" : 996 char *name = layout == L_VERT ? "splitv" :
975 layout == L_HORIZ ? "splith" : "split"; 997 layout == L_HORIZ ? "splith" : "split";
976 if (!checkarg(argc, name, EXPECTED_EQUAL_TO, 0) 998 struct cmd_results *error = NULL;
977 || config->reading || !config->active) { 999 if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
978 return CMD_FAILURE; 1000 if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
1001 if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
1002 return error;
979 } 1003 }
980 swayc_t *focused = get_focused_container(&root_container); 1004 swayc_t *focused = get_focused_container(&root_container);
981 1005
982 // Case of floating window, dont split 1006 // Case of floating window, dont split
983 if (focused->is_floating) { 1007 if (focused->is_floating) {
984 return CMD_SUCCESS; 1008 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
985 } 1009 }
986 /* Case that focus is on an workspace with 0/1 children.change its layout */ 1010 /* Case that focus is on an workspace with 0/1 children.change its layout */
987 if (focused->type == C_WORKSPACE && focused->children->length <= 1) { 1011 if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
@@ -999,56 +1023,61 @@ static enum cmd_status _do_split(int argc, char **argv, int layout) {
999 set_focused_container(focused); 1023 set_focused_container(focused);
1000 arrange_windows(parent, -1, -1); 1024 arrange_windows(parent, -1, -1);
1001 } 1025 }
1002 return CMD_SUCCESS; 1026 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1003} 1027}
1004 1028
1005static enum cmd_status cmd_split(int argc, char **argv) { 1029static struct cmd_results *cmd_split(int argc, char **argv) {
1006 if (!checkarg(argc, "split", EXPECTED_EQUAL_TO, 1) 1030 struct cmd_results *error = NULL;
1007 || config->reading || !config->active) { 1031 if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
1008 return CMD_FAILURE; 1032 if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
1033 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
1034 return error;
1009 } 1035 }
1010
1011 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { 1036 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
1012 _do_split(argc - 1, argv + 1, L_VERT); 1037 _do_split(argc - 1, argv + 1, L_VERT);
1013 } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { 1038 } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
1014 _do_split(argc - 1, argv + 1, L_HORIZ); 1039 _do_split(argc - 1, argv + 1, L_HORIZ);
1015 } else { 1040 } else {
1016 sway_log(L_ERROR, "Invalid split command (expected either horiziontal or vertical)."); 1041 error = cmd_results_new(CMD_FAILURE, "split",
1017 return CMD_FAILURE; 1042 "Invalid split command (expected either horiziontal or vertical).");
1043 return error;
1018 } 1044 }
1019 return CMD_SUCCESS; 1045 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1020} 1046}
1021 1047
1022static enum cmd_status cmd_splitv(int argc, char **argv) { 1048static struct cmd_results *cmd_splitv(int argc, char **argv) {
1023 return _do_split(argc, argv, L_VERT); 1049 return _do_split(argc, argv, L_VERT);
1024} 1050}
1025 1051
1026static enum cmd_status cmd_splith(int argc, char **argv) { 1052static struct cmd_results *cmd_splith(int argc, char **argv) {
1027 return _do_split(argc, argv, L_HORIZ); 1053 return _do_split(argc, argv, L_HORIZ);
1028} 1054}
1029 1055
1030static enum cmd_status cmd_log_colors(int argc, char **argv) { 1056static struct cmd_results *cmd_log_colors(int argc, char **argv) {
1031 if (!checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1) 1057 struct cmd_results *error = NULL;
1032 || !config->reading) { 1058 if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
1033 return CMD_FAILURE; 1059 if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
1060 return error;
1034 } 1061 }
1035 if (strcasecmp(argv[0], "no") == 0) { 1062 if (strcasecmp(argv[0], "no") == 0) {
1036 sway_log_colors(0); 1063 sway_log_colors(0);
1037 } else if (strcasecmp(argv[0], "yes") == 0) { 1064 } else if (strcasecmp(argv[0], "yes") == 0) {
1038 sway_log_colors(1); 1065 sway_log_colors(1);
1039 } else { 1066 } else {
1040 sway_log(L_ERROR, "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]); 1067 error = cmd_results_new(CMD_FAILURE, "log_colors",
1041 return CMD_FAILURE; 1068 "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
1069 return error;
1042 } 1070 }
1043 return CMD_SUCCESS; 1071 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1044} 1072}
1045 1073
1046static enum cmd_status cmd_fullscreen(int argc, char **argv) { 1074static struct cmd_results *cmd_fullscreen(int argc, char **argv) {
1047 if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0) 1075 struct cmd_results *error = NULL;
1048 || config->reading || !config->active) { 1076 if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
1049 return CMD_FAILURE; 1077 if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
1078 if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
1079 return error;
1050 } 1080 }
1051
1052 swayc_t *container = get_focused_view(&root_container); 1081 swayc_t *container = get_focused_view(&root_container);
1053 bool current = swayc_is_fullscreen(container); 1082 bool current = swayc_is_fullscreen(container);
1054 wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current); 1083 wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
@@ -1060,17 +1089,17 @@ static enum cmd_status cmd_fullscreen(int argc, char **argv) {
1060 // Only resize container when going into fullscreen 1089 // Only resize container when going into fullscreen
1061 arrange_windows(container, -1, -1); 1090 arrange_windows(container, -1, -1);
1062 1091
1063 return CMD_SUCCESS; 1092 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1064} 1093}
1065 1094
1066static enum cmd_status cmd_workspace(int argc, char **argv) { 1095static struct cmd_results *cmd_workspace(int argc, char **argv) {
1067 if (!checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1)) { 1096 struct cmd_results *error = NULL;
1068 return CMD_FAILURE; 1097 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
1098 return error;
1069 } 1099 }
1070
1071 if (argc == 1) { 1100 if (argc == 1) {
1072 if (config->reading || !config->active) { 1101 if (config->reading || !config->active) {
1073 return CMD_DEFER; 1102 return cmd_results_new(CMD_DEFER, "workspace", NULL);
1074 } 1103 }
1075 // Handle workspace next/prev 1104 // Handle workspace next/prev
1076 swayc_t *ws = NULL; 1105 swayc_t *ws = NULL;
@@ -1096,8 +1125,8 @@ static enum cmd_status cmd_workspace(int argc, char **argv) {
1096 workspace_switch(ws); 1125 workspace_switch(ws);
1097 } else { 1126 } else {
1098 if (strcasecmp(argv[1], "output") == 0) { 1127 if (strcasecmp(argv[1], "output") == 0) {
1099 if (!checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3)) { 1128 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3))) {
1100 return CMD_FAILURE; 1129 return error;
1101 } 1130 }
1102 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); 1131 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output));
1103 sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]); 1132 sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]);
@@ -1109,21 +1138,22 @@ static enum cmd_status cmd_workspace(int argc, char **argv) {
1109 } 1138 }
1110 } 1139 }
1111 } 1140 }
1112 return CMD_SUCCESS; 1141 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1113} 1142}
1114 1143
1115static enum cmd_status cmd_ws_auto_back_and_forth(int argc, char **argv) { 1144static struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {
1116 if (!checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1)) { 1145 struct cmd_results *error = NULL;
1117 return CMD_FAILURE; 1146 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
1147 return error;
1118 } 1148 }
1119 if (strcasecmp(argv[0], "yes") == 0) { 1149 if (strcasecmp(argv[0], "yes") == 0) {
1120 config->auto_back_and_forth = true; 1150 config->auto_back_and_forth = true;
1121 } else if (strcasecmp(argv[0], "no") == 0) { 1151 } else if (strcasecmp(argv[0], "no") == 0) {
1122 config->auto_back_and_forth = false; 1152 config->auto_back_and_forth = false;
1123 } else { 1153 } else {
1124 return CMD_FAILURE; 1154 return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
1125 } 1155 }
1126 return CMD_SUCCESS; 1156 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1127} 1157}
1128 1158
1129/* Keep alphabetized */ 1159/* Keep alphabetized */
@@ -1171,8 +1201,11 @@ static struct cmd_handler *find_handler(char *line) {
1171 return res; 1201 return res;
1172} 1202}
1173 1203
1174enum cmd_status handle_command(char *_exec) { 1204struct cmd_results *handle_command(char *_exec) {
1175 enum cmd_status status = CMD_SUCCESS; 1205 // Even though this function will process multiple commands we will only
1206 // return the last error, if any (for now). (Since we have access to an
1207 // error string we could e.g. concatonate all errors there.)
1208 struct cmd_results *results = NULL;
1176 char *exec = strdup(_exec); 1209 char *exec = strdup(_exec);
1177 char *head = exec; 1210 char *head = exec;
1178 char *cmdlist; 1211 char *cmdlist;
@@ -1188,8 +1221,7 @@ enum cmd_status handle_command(char *_exec) {
1188 ++head; 1221 ++head;
1189 // TODO handle criteria 1222 // TODO handle criteria
1190 } else { 1223 } else {
1191 sway_log(L_ERROR, "Unmatched ["); 1224 results = cmd_results_new(CMD_INVALID, NULL, "Unmatched [");
1192 status = CMD_INVALID;
1193 } 1225 }
1194 // Skip leading whitespace 1226 // Skip leading whitespace
1195 head += strspn(head, whitespace); 1227 head += strspn(head, whitespace);
@@ -1205,7 +1237,6 @@ enum cmd_status handle_command(char *_exec) {
1205 sway_log(L_INFO, "Ignoring empty command."); 1237 sway_log(L_INFO, "Ignoring empty command.");
1206 continue; 1238 continue;
1207 } 1239 }
1208
1209 sway_log(L_INFO, "Handling command '%s'", cmd); 1240 sway_log(L_INFO, "Handling command '%s'", cmd);
1210 //TODO better handling of argv 1241 //TODO better handling of argv
1211 int argc; 1242 int argc;
@@ -1214,37 +1245,54 @@ enum cmd_status handle_command(char *_exec) {
1214 strip_quotes(argv[1]); 1245 strip_quotes(argv[1]);
1215 } 1246 }
1216 struct cmd_handler *handler = find_handler(argv[0]); 1247 struct cmd_handler *handler = find_handler(argv[0]);
1217 enum cmd_status res = CMD_INVALID; 1248 if (!handler) {
1218 if (!handler 1249 if (results) {
1219 || (res = handler->handle(argc-1, argv+1)) != CMD_SUCCESS) { 1250 free_cmd_results(results);
1220 sway_log(L_ERROR, "Command '%s' failed", cmd); 1251 }
1252 results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
1221 free_argv(argc, argv); 1253 free_argv(argc, argv);
1222 status = res; 1254 goto cleanup;
1255 }
1256 struct cmd_results *res = handler->handle(argc-1, argv+1);
1257 if (res->status != CMD_SUCCESS) {
1258 free_argv(argc, argv);
1259 if (results) {
1260 free_cmd_results(results);
1261 }
1262 results = res;
1223 goto cleanup; 1263 goto cleanup;
1224 } 1264 }
1225 free_argv(argc, argv); 1265 free_argv(argc, argv);
1266 free_cmd_results(res);
1226 } while(cmdlist); 1267 } while(cmdlist);
1227 } while(head); 1268 } while(head);
1228 cleanup: 1269 cleanup:
1229 free(exec); 1270 free(exec);
1230 return status; 1271 if (!results) {
1272 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
1273 }
1274 return results;
1231} 1275}
1232 1276
1233enum cmd_status config_command(char *exec) { 1277struct cmd_results *config_command(char *exec) {
1234 enum cmd_status status = CMD_SUCCESS; 1278 struct cmd_results *results = NULL;
1235 int argc; 1279 int argc;
1236 char **argv = split_args(exec, &argc); 1280 char **argv = split_args(exec, &argc);
1237 if (!argc) goto cleanup; 1281 if (!argc) {
1282 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
1283 goto cleanup;
1284 }
1238 1285
1239 sway_log(L_INFO, "handling config command '%s'", exec); 1286 sway_log(L_INFO, "handling config command '%s'", exec);
1240 // Endblock 1287 // Endblock
1241 if (**argv == '}') { 1288 if (**argv == '}') {
1242 status = CMD_BLOCK_END; 1289 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
1243 goto cleanup; 1290 goto cleanup;
1244 } 1291 }
1245 struct cmd_handler *handler = find_handler(argv[0]); 1292 struct cmd_handler *handler = find_handler(argv[0]);
1246 if (!handler) { 1293 if (!handler) {
1247 status = CMD_INVALID; 1294 char *input = argv[0] ? argv[0] : "(empty)";
1295 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
1248 goto cleanup; 1296 goto cleanup;
1249 } 1297 }
1250 int i; 1298 int i;
@@ -1257,8 +1305,53 @@ enum cmd_status config_command(char *exec) {
1257 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { 1305 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
1258 strip_quotes(argv[1]); 1306 strip_quotes(argv[1]);
1259 } 1307 }
1260 status = handler->handle(argc-1, argv+1); 1308 results = handler->handle(argc-1, argv+1);
1261 cleanup: 1309 cleanup:
1262 free_argv(argc, argv); 1310 free_argv(argc, argv);
1263 return status; 1311 return results;
1312}
1313
1314struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
1315 struct cmd_results *results = malloc(sizeof(struct cmd_results));
1316 results->status = status;
1317 if (input) {
1318 results->input = strdup(input); // input is the command name
1319 } else {
1320 results->input = NULL;
1321 }
1322 if (format) {
1323 char *error = malloc(256);
1324 va_list args;
1325 va_start(args, format);
1326 vsnprintf(error, 256, format, args);
1327 va_end(args);
1328 results->error = error;
1329 } else {
1330 results->error = NULL;
1331 }
1332 return results;
1333}
1334
1335void free_cmd_results(struct cmd_results *results) {
1336 if (results->input) {
1337 free(results->input);
1338 }
1339 if (results->error) {
1340 free(results->error);
1341 }
1342 free(results);
1343}
1344
1345const char *cmd_results_to_json(struct cmd_results *results) {
1346 json_object *root = json_object_new_object();
1347 json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
1348 if (results->input) {
1349 json_object_object_add(root, "input", json_object_new_string(results->input));
1350 }
1351 if (results->error) {
1352 json_object_object_add(root, "error", json_object_new_string(results->error));
1353 }
1354 const char *json = json_object_to_json_string(root);
1355 free(root);
1356 return json;
1264} 1357}
diff --git a/sway/config.c b/sway/config.c
index 67f8284c..7e0b22f9 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -234,10 +234,11 @@ bool read_config(FILE *file, bool is_active) {
234 while (!feof(file)) { 234 while (!feof(file)) {
235 line = read_line(file); 235 line = read_line(file);
236 line = strip_comments(line); 236 line = strip_comments(line);
237 switch(config_command(line)) { 237 struct cmd_results *res = config_command(line);
238 switch(res->status) {
238 case CMD_FAILURE: 239 case CMD_FAILURE:
239 case CMD_INVALID: 240 case CMD_INVALID:
240 sway_log(L_ERROR, "Error on line '%s'", line); 241 sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
241 success = false; 242 success = false;
242 break; 243 break;
243 244
@@ -270,6 +271,7 @@ bool read_config(FILE *file, bool is_active) {
270 default:; 271 default:;
271 } 272 }
272 free(line); 273 free(line);
274 free(res);
273 } 275 }
274 276
275 if (is_active) { 277 if (is_active) {
diff --git a/sway/handlers.c b/sway/handlers.c
index 6120e663..24105130 100644
--- a/sway/handlers.c
+++ b/sway/handlers.c
@@ -341,7 +341,11 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
341 } 341 }
342 if (match) { 342 if (match) {
343 if (state == WLC_KEY_STATE_PRESSED) { 343 if (state == WLC_KEY_STATE_PRESSED) {
344 handle_command(binding->command); 344 struct cmd_results *res = handle_command(binding->command);
345 if (res->status != CMD_SUCCESS) {
346 sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
347 }
348 free_cmd_results(res);
345 return EVENT_HANDLED; 349 return EVENT_HANDLED;
346 } else if (state == WLC_KEY_STATE_RELEASED) { 350 } else if (state == WLC_KEY_STATE_RELEASED) {
347 // TODO: --released 351 // TODO: --released
@@ -544,8 +548,13 @@ static void handle_wlc_ready(void) {
544 // Execute commands until there are none left 548 // Execute commands until there are none left
545 config->active = true; 549 config->active = true;
546 while (config->cmd_queue->length) { 550 while (config->cmd_queue->length) {
547 handle_command(config->cmd_queue->items[0]); 551 char *line = config->cmd_queue->items[0];
548 free(config->cmd_queue->items[0]); 552 struct cmd_results *res = handle_command(line);
553 if (res->status != CMD_SUCCESS) {
554 sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
555 }
556 free_cmd_results(res);
557 free(line);
549 list_del(config->cmd_queue, 0); 558 list_del(config->cmd_queue, 0);
550 } 559 }
551} 560}
diff --git a/sway/ipc.c b/sway/ipc.c
index e4deb4d4..1134f1a2 100644
--- a/sway/ipc.c
+++ b/sway/ipc.c
@@ -222,10 +222,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
222 case IPC_COMMAND: 222 case IPC_COMMAND:
223 { 223 {
224 buf[client->payload_length] = '\0'; 224 buf[client->payload_length] = '\0';
225 bool success = handle_command(buf); 225 struct cmd_results *results = handle_command(buf);
226 char reply[64]; 226 const char *json = cmd_results_to_json(results);
227 int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); 227 char reply[256];
228 int length = snprintf(reply, sizeof(reply), "%s", json);
228 ipc_send_reply(client, reply, (uint32_t) length); 229 ipc_send_reply(client, reply, (uint32_t) length);
230 free_cmd_results(results);
229 break; 231 break;
230 } 232 }
231 case IPC_GET_WORKSPACES: 233 case IPC_GET_WORKSPACES: