summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/commands.h20
-rw-r--r--sway/commands.c506
-rw-r--r--sway/config.c6
-rw-r--r--sway/handlers.c15
-rw-r--r--sway/ipc.c8
5 files changed, 333 insertions, 222 deletions
diff --git a/include/commands.h b/include/commands.h
index 1b4cd9ca..1e0a1452 100644
--- a/include/commands.h
+++ b/include/commands.h
@@ -1,22 +1,34 @@
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
21 const char *input;
22 char *error;
23};
24
25struct cmd_results *handle_command(char *command);
18// Handles commands during config 26// Handles commands during config
19enum cmd_status config_command(char *command); 27struct cmd_results *config_command(char *command);
28
29struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *error, ...);
30void free_cmd_results(struct cmd_results *results);
31const char *cmd_results_to_json(struct cmd_results *results);
20 32
21void remove_view_from_scratchpad(); 33void remove_view_from_scratchpad();
22 34
diff --git a/sway/commands.c b/sway/commands.c
index a9c20e51..4b11886e 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,14 @@ 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 (!config->reading) return cmd_results_new(CMD_FAILURE, "floating_modifier", "Can only be used in config file.");
323 return CMD_FAILURE; 327 if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) {
328 return error;
324 } 329 }
325 int i, j; 330 int i, j;
326 list_t *split = split_string(argv[0], "+"); 331 list_t *split = split_string(argv[0], "+");
@@ -336,19 +341,20 @@ static enum cmd_status cmd_floating_mod(int argc, char **argv) {
336 } 341 }
337 free_flat_list(split); 342 free_flat_list(split);
338 if (!config->floating_mod) { 343 if (!config->floating_mod) {
339 sway_log(L_ERROR, "bindsym - unknown keys %s", argv[0]); 344 error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
340 return CMD_FAILURE; 345 return error;
341 } 346 }
342 return CMD_SUCCESS; 347 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
343} 348}
344 349
345static enum cmd_status cmd_focus(int argc, char **argv) { 350static struct cmd_results *cmd_focus(int argc, char **argv) {
346 if (config->reading) return CMD_INVALID; 351 if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
352 struct cmd_results *error = NULL;
353 if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
354 return error;
355 }
347 static int floating_toggled_index = 0; 356 static int floating_toggled_index = 0;
348 static int tiled_toggled_index = 0; 357 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) { 358 if (strcasecmp(argv[0], "left") == 0) {
353 move_focus(MOVE_LEFT); 359 move_focus(MOVE_LEFT);
354 } else if (strcasecmp(argv[0], "right") == 0) { 360 } else if (strcasecmp(argv[0], "right") == 0) {
@@ -397,26 +403,28 @@ static enum cmd_status cmd_focus(int argc, char **argv) {
397 } 403 }
398 } 404 }
399 } 405 }
400 return CMD_SUCCESS; 406 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
401} 407}
402 408
403static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) { 409static struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
404 if (!config->reading) return CMD_INVALID; 410 struct cmd_results *error = NULL;
405 if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) { 411 if (!config->reading) return cmd_results_new(CMD_FAILURE, "focus_follows_mouse", "Can only be used in config file.");
406 return CMD_FAILURE; 412 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
413 return error;
407 } 414 }
408 415
409 config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); 416 config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
410 return CMD_SUCCESS; 417 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
411} 418}
412 419
413static enum cmd_status cmd_seamless_mouse(int argc, char **argv) { 420static struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
414 if (!checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1)) { 421 struct cmd_results *error = NULL;
415 return CMD_FAILURE; 422 if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
423 return error;
416 } 424 }
417 425
418 config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0); 426 config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
419 return CMD_SUCCESS; 427 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
420} 428}
421 429
422static void hide_view_in_scratchpad(swayc_t *sp_view) { 430static void hide_view_in_scratchpad(swayc_t *sp_view) {
@@ -435,15 +443,17 @@ static void hide_view_in_scratchpad(swayc_t *sp_view) {
435 set_focused_container(container_under_pointer()); 443 set_focused_container(container_under_pointer());
436} 444}
437 445
438static enum cmd_status cmd_mode(int argc, char **argv) { 446static struct cmd_results *cmd_mode(int argc, char **argv) {
439 if (!checkarg(argc, "mode", EXPECTED_AT_LEAST, 1)) { 447 struct cmd_results *error = NULL;
440 return CMD_FAILURE; 448 if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
449 return error;
441 } 450 }
442 char *mode_name = join_args(argv, argc); 451 char *mode_name = join_args(argv, argc);
443 int mode_len = strlen(mode_name); 452 int mode_len = strlen(mode_name);
444 bool mode_make = mode_name[mode_len-1] == '{'; 453 bool mode_make = mode_name[mode_len-1] == '{';
445 if (mode_make) { 454 if (mode_make) {
446 if (!config->reading) return CMD_INVALID; 455 if (!config->reading)
456 return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
447 // Trim trailing spaces 457 // Trim trailing spaces
448 do { 458 do {
449 mode_name[--mode_len] = 0; 459 mode_name[--mode_len] = 0;
@@ -467,9 +477,9 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
467 list_add(config->modes, mode); 477 list_add(config->modes, mode);
468 } 478 }
469 if (!mode) { 479 if (!mode) {
470 sway_log(L_ERROR, "Unknown mode `%s'", mode_name); 480 error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
471 free(mode_name); 481 free(mode_name);
472 return CMD_FAILURE; 482 return error;
473 } 483 }
474 if ((config->reading && mode_make) || (!config->reading && !mode_make)) { 484 if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
475 sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); 485 sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
@@ -477,15 +487,15 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
477 free(mode_name); 487 free(mode_name);
478 // Set current mode 488 // Set current mode
479 config->current_mode = mode; 489 config->current_mode = mode;
480 return mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS; 490 return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
481} 491}
482 492
483static enum cmd_status cmd_move(int argc, char **argv) { 493static struct cmd_results *cmd_move(int argc, char **argv) {
484 if (config->reading) return CMD_FAILURE; 494 struct cmd_results *error = NULL;
485 if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { 495 if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
486 return CMD_FAILURE; 496 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
497 return error;
487 } 498 }
488
489 swayc_t *view = get_focused_container(&root_container); 499 swayc_t *view = get_focused_container(&root_container);
490 500
491 if (strcasecmp(argv[0], "left") == 0) { 501 if (strcasecmp(argv[0], "left") == 0) {
@@ -498,14 +508,14 @@ static enum cmd_status cmd_move(int argc, char **argv) {
498 move_container(view, MOVE_DOWN); 508 move_container(view, MOVE_DOWN);
499 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { 509 } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
500 // "move container to workspace x" 510 // "move container to workspace x"
501 if (!checkarg(argc, "move container/window", EXPECTED_EQUAL_TO, 4) 511 if ((error = checkarg(argc, "move container/window", EXPECTED_EQUAL_TO, 4))) {
502 || strcasecmp(argv[1], "to") != 0 512 return error;
503 || strcasecmp(argv[2], "workspace") != 0) { 513 } else if ( strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "workspace") != 0) {
504 return CMD_FAILURE; 514 return cmd_results_new(CMD_INVALID, "move", "Expected 'move %s to workspace <name>'", argv[0]);
505 } 515 }
506 516
507 if (view->type != C_CONTAINER && view->type != C_VIEW) { 517 if (view->type != C_CONTAINER && view->type != C_VIEW) {
508 return CMD_FAILURE; 518 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
509 } 519 }
510 520
511 const char *ws_name = argv[3]; 521 const char *ws_name = argv[3];
@@ -521,7 +531,7 @@ static enum cmd_status cmd_move(int argc, char **argv) {
521 move_container_to(view, get_focused_container(ws)); 531 move_container_to(view, get_focused_container(ws));
522 } else if (strcasecmp(argv[0], "scratchpad") == 0) { 532 } else if (strcasecmp(argv[0], "scratchpad") == 0) {
523 if (view->type != C_CONTAINER && view->type != C_VIEW) { 533 if (view->type != C_CONTAINER && view->type != C_VIEW) {
524 return CMD_FAILURE; 534 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
525 } 535 }
526 swayc_t *view = get_focused_container(&root_container); 536 swayc_t *view = get_focused_container(&root_container);
527 int i; 537 int i;
@@ -529,7 +539,7 @@ static enum cmd_status cmd_move(int argc, char **argv) {
529 if (scratchpad->items[i] == view) { 539 if (scratchpad->items[i] == view) {
530 hide_view_in_scratchpad(view); 540 hide_view_in_scratchpad(view);
531 sp_view = NULL; 541 sp_view = NULL;
532 return CMD_SUCCESS; 542 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
533 } 543 }
534 } 544 }
535 list_add(scratchpad, view); 545 list_add(scratchpad, view);
@@ -546,17 +556,18 @@ static enum cmd_status cmd_move(int argc, char **argv) {
546 } 556 }
547 set_focused_container(focused); 557 set_focused_container(focused);
548 } else { 558 } else {
549 return CMD_FAILURE; 559 return cmd_results_new(CMD_INVALID, "move",
560 "Expected 'move <left|right|up|down>' or 'move <container|window> to workspace <name>'");
550 } 561 }
551 return CMD_SUCCESS; 562 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
552} 563}
553 564
554static enum cmd_status cmd_orientation(int argc, char **argv) { 565static struct cmd_results *cmd_orientation(int argc, char **argv) {
555 if (!config->reading) return CMD_FAILURE; 566 struct cmd_results *error = NULL;
556 if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1)) { 567 if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
557 return CMD_FAILURE; 568 if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
569 return error;
558 } 570 }
559
560 if (strcasecmp(argv[0], "horizontal") == 0) { 571 if (strcasecmp(argv[0], "horizontal") == 0) {
561 config->default_orientation = L_HORIZ; 572 config->default_orientation = L_HORIZ;
562 } else if (strcasecmp(argv[0], "vertical") == 0) { 573 } else if (strcasecmp(argv[0], "vertical") == 0) {
@@ -564,16 +575,16 @@ static enum cmd_status cmd_orientation(int argc, char **argv) {
564 } else if (strcasecmp(argv[0], "auto") == 0) { 575 } else if (strcasecmp(argv[0], "auto") == 0) {
565 // Do nothing 576 // Do nothing
566 } else { 577 } else {
567 return CMD_FAILURE; 578 return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
568 } 579 }
569 return CMD_SUCCESS; 580 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
570} 581}
571 582
572static enum cmd_status cmd_output(int argc, char **argv) { 583static struct cmd_results *cmd_output(int argc, char **argv) {
573 if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { 584 struct cmd_results *error = NULL;
574 return CMD_FAILURE; 585 if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
586 return error;
575 } 587 }
576
577 struct output_config *output = calloc(1, sizeof(struct output_config)); 588 struct output_config *output = calloc(1, sizeof(struct output_config));
578 output->x = output->y = output->width = output->height = -1; 589 output->x = output->y = output->width = output->height = -1;
579 output->name = strdup(argv[0]); 590 output->name = strdup(argv[0]);
@@ -644,12 +655,13 @@ static enum cmd_status cmd_output(int argc, char **argv) {
644 } 655 }
645 } 656 }
646 657
647 return CMD_SUCCESS; 658 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
648} 659}
649 660
650static enum cmd_status cmd_gaps(int argc, char **argv) { 661static struct cmd_results *cmd_gaps(int argc, char **argv) {
651 if (!checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1)) { 662 struct cmd_results *error = NULL;
652 return CMD_FAILURE; 663 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
664 return error;
653 } 665 }
654 const char *amount_str = argv[0]; 666 const char *amount_str = argv[0];
655 // gaps amount 667 // gaps amount
@@ -657,7 +669,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
657 int amount = (int)strtol(amount_str, NULL, 10); 669 int amount = (int)strtol(amount_str, NULL, 10);
658 if (errno == ERANGE || amount == 0) { 670 if (errno == ERANGE || amount == 0) {
659 errno = 0; 671 errno = 0;
660 return CMD_FAILURE; 672 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
661 } 673 }
662 if (config->gaps_inner == 0) { 674 if (config->gaps_inner == 0) {
663 config->gaps_inner = amount; 675 config->gaps_inner = amount;
@@ -665,14 +677,14 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
665 if (config->gaps_outer == 0) { 677 if (config->gaps_outer == 0) {
666 config->gaps_outer = amount; 678 config->gaps_outer = amount;
667 } 679 }
668 return CMD_SUCCESS; 680 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
669 } 681 }
670 // gaps inner|outer n 682 // gaps inner|outer n
671 else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) { 683 else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
672 int amount = (int)strtol(amount_str, NULL, 10); 684 int amount = (int)strtol(amount_str, NULL, 10);
673 if (errno == ERANGE || amount == 0) { 685 if (errno == ERANGE || amount == 0) {
674 errno = 0; 686 errno = 0;
675 return CMD_FAILURE; 687 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
676 } 688 }
677 const char *target_str = argv[0]; 689 const char *target_str = argv[0];
678 if (strcasecmp(target_str, "inner") == 0) { 690 if (strcasecmp(target_str, "inner") == 0) {
@@ -680,11 +692,11 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
680 } else if (strcasecmp(target_str, "outer") == 0) { 692 } else if (strcasecmp(target_str, "outer") == 0) {
681 config->gaps_outer = amount; 693 config->gaps_outer = amount;
682 } 694 }
683 return CMD_SUCCESS; 695 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
684 } 696 }
685 // gaps inner|outer current|all set|plus|minus n 697 // gaps inner|outer current|all set|plus|minus n
686 if (argc < 4 || config->reading) { 698 if (argc < 4 || config->reading) {
687 return CMD_FAILURE; 699 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
688 } 700 }
689 // gaps inner|outer ... 701 // gaps inner|outer ...
690 const char *inout_str = argv[0]; 702 const char *inout_str = argv[0];
@@ -694,7 +706,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
694 } else if (strcasecmp(inout_str, "outer") == 0) { 706 } else if (strcasecmp(inout_str, "outer") == 0) {
695 inout = OUTER; 707 inout = OUTER;
696 } else { 708 } else {
697 return CMD_FAILURE; 709 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
698 } 710 }
699 711
700 // gaps ... current|all ... 712 // gaps ... current|all ...
@@ -712,7 +724,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
712 target = WORKSPACE; 724 target = WORKSPACE;
713 } 725 }
714 } else { 726 } else {
715 return CMD_FAILURE; 727 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
716 } 728 }
717 729
718 // gaps ... n 730 // gaps ... n
@@ -720,7 +732,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
720 int amount = (int)strtol(amount_str, NULL, 10); 732 int amount = (int)strtol(amount_str, NULL, 10);
721 if (errno == ERANGE || amount == 0) { 733 if (errno == ERANGE || amount == 0) {
722 errno = 0; 734 errno = 0;
723 return CMD_FAILURE; 735 return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
724 } 736 }
725 737
726 // gaps ... set|plus|minus ... 738 // gaps ... set|plus|minus ...
@@ -734,18 +746,18 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
734 method = ADD; 746 method = ADD;
735 amount *= -1; 747 amount *= -1;
736 } else { 748 } else {
737 return CMD_FAILURE; 749 return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all> <set|plus|minus n>'");
738 } 750 }
739 751
740 if (target == CURRENT) { 752 if (target == CURRENT) {
741 swayc_t *cont; 753 swayc_t *cont;
742 if (inout == OUTER) { 754 if (inout == OUTER) {
743 if ((cont = swayc_active_workspace()) == NULL) { 755 if ((cont = swayc_active_workspace()) == NULL) {
744 return CMD_FAILURE; 756 return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
745 } 757 }
746 } else { 758 } else {
747 if ((cont = get_focused_view(&root_container))->type != C_VIEW) { 759 if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
748 return CMD_FAILURE; 760 return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
749 } 761 }
750 } 762 }
751 cont->gaps = swayc_gap(cont); 763 cont->gaps = swayc_gap(cont);
@@ -775,7 +787,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
775 swayc_t *top; 787 swayc_t *top;
776 if (target == WORKSPACE) { 788 if (target == WORKSPACE) {
777 if ((top = swayc_active_workspace()) == NULL) { 789 if ((top = swayc_active_workspace()) == NULL) {
778 return CMD_FAILURE; 790 return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
779 } 791 }
780 } else { 792 } else {
781 top = &root_container; 793 top = &root_container;
@@ -786,22 +798,24 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
786 arrange_windows(top, -1, -1); 798 arrange_windows(top, -1, -1);
787 } 799 }
788 800
789 return CMD_SUCCESS; 801 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
790} 802}
791 803
792static enum cmd_status cmd_kill(int argc, char **argv) { 804static struct cmd_results *cmd_kill(int argc, char **argv) {
793 if (config->reading || !config->active) { 805 if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
794 return CMD_FAILURE; 806 if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
795 } 807
796 swayc_t *view = get_focused_container(&root_container); 808 swayc_t *view = get_focused_container(&root_container);
797 wlc_view_close(view->handle); 809 wlc_view_close(view->handle);
798 return CMD_SUCCESS; 810 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
799} 811}
800 812
801static enum cmd_status cmd_layout(int argc, char **argv) { 813static struct cmd_results *cmd_layout(int argc, char **argv) {
802 if (!checkarg(argc, "layout", EXPECTED_MORE_THAN, 0) 814 struct cmd_results *error = NULL;
803 || config->reading || !config->active) { 815 if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
804 return CMD_FAILURE; 816 if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
817 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
818 return error;
805 } 819 }
806 swayc_t *parent = get_focused_container(&root_container); 820 swayc_t *parent = get_focused_container(&root_container);
807 while (parent->type == C_VIEW) { 821 while (parent->type == C_VIEW) {
@@ -821,33 +835,38 @@ static enum cmd_status cmd_layout(int argc, char **argv) {
821 } 835 }
822 arrange_windows(parent, parent->width, parent->height); 836 arrange_windows(parent, parent->width, parent->height);
823 837
824 return CMD_SUCCESS; 838 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
825} 839}
826 840
827static enum cmd_status cmd_reload(int argc, char **argv) { 841static struct cmd_results *cmd_reload(int argc, char **argv) {
828 if (!checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0) 842 struct cmd_results *error = NULL;
829 || config->reading 843 if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
830 || !load_config(NULL)) { 844 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
831 return CMD_FAILURE; 845 return error;
832 } 846 }
847 if (!load_config(NULL)) return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
848
833 arrange_windows(&root_container, -1, -1); 849 arrange_windows(&root_container, -1, -1);
834 return CMD_SUCCESS; 850 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
835} 851}
836 852
837static enum cmd_status cmd_resize(int argc, char **argv) { 853static struct cmd_results *cmd_resize(int argc, char **argv) {
838 if (!checkarg(argc, "resize", EXPECTED_AT_LEAST, 3) 854 struct cmd_results *error = NULL;
839 || config->reading || !config->active) { 855 if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
840 return CMD_FAILURE; 856 if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
857 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 3))) {
858 return error;
841 } 859 }
842 char *end; 860 char *end;
843 int amount = (int)strtol(argv[2], &end, 10); 861 int amount = (int)strtol(argv[2], &end, 10);
844 if (errno == ERANGE || amount == 0) { 862 if (errno == ERANGE || amount == 0) {
845 errno = 0; 863 errno = 0;
846 return CMD_FAILURE; 864 return cmd_results_new(CMD_INVALID, "resize", "Number is out of range.");
847 } 865 }
848 866
849 if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) { 867 if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) {
850 return CMD_FAILURE; 868 return cmd_results_new(CMD_INVALID, "resize",
869 "Expected 'resize <shrink|grow> <width|height> <amount>'");
851 } 870 }
852 871
853 if (strcmp(argv[0], "shrink") == 0) { 872 if (strcmp(argv[0], "shrink") == 0) {
@@ -859,9 +878,10 @@ static enum cmd_status cmd_resize(int argc, char **argv) {
859 } else if (strcmp(argv[1], "height") == 0) { 878 } else if (strcmp(argv[1], "height") == 0) {
860 resize_tiled(amount, false); 879 resize_tiled(amount, false);
861 } else { 880 } else {
862 return CMD_FAILURE; 881 return cmd_results_new(CMD_INVALID, "resize",
882 "Expected 'resize <shrink|grow> <width|height> <amount>'");
863 } 883 }
864 return CMD_SUCCESS; 884 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
865} 885}
866 886
867static swayc_t *fetch_view_from_scratchpad() { 887static swayc_t *fetch_view_from_scratchpad() {
@@ -909,10 +929,12 @@ void remove_view_from_scratchpad(swayc_t *view) {
909 } 929 }
910} 930}
911 931
912static enum cmd_status cmd_scratchpad(int argc, char **argv) { 932static struct cmd_results *cmd_scratchpad(int argc, char **argv) {
913 if (!checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1) 933 struct cmd_results *error = NULL;
914 || config->reading || !config->active) { 934 if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
915 return CMD_FAILURE; 935 if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
936 if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
937 return error;
916 } 938 }
917 939
918 if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) { 940 if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
@@ -931,9 +953,9 @@ static enum cmd_status cmd_scratchpad(int argc, char **argv) {
931 sp_view = NULL; 953 sp_view = NULL;
932 } 954 }
933 } 955 }
934 return CMD_SUCCESS; 956 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
935 } 957 }
936 return CMD_FAILURE; 958 return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
937} 959}
938 960
939// sort in order of longest->shortest 961// sort in order of longest->shortest
@@ -943,11 +965,13 @@ static int compare_set(const void *_l, const void *_r) {
943 return strlen((*r)->name) - strlen((*l)->name); 965 return strlen((*r)->name) - strlen((*l)->name);
944} 966}
945 967
946static enum cmd_status cmd_set(int argc, char **argv) { 968static struct cmd_results *cmd_set(int argc, char **argv) {
947 if (!checkarg(argc, "set", EXPECTED_AT_LEAST, 2) 969 struct cmd_results *error = NULL;
948 || !config->reading) { 970 if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
949 return CMD_FAILURE; 971 if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
972 return error;
950 } 973 }
974
951 struct sway_variable *var = NULL; 975 struct sway_variable *var = NULL;
952 // Find old variable if it exists 976 // Find old variable if it exists
953 int i; 977 int i;
@@ -967,21 +991,23 @@ static enum cmd_status cmd_set(int argc, char **argv) {
967 list_sort(config->symbols, compare_set); 991 list_sort(config->symbols, compare_set);
968 } 992 }
969 var->value = join_args(argv + 1, argc - 1); 993 var->value = join_args(argv + 1, argc - 1);
970 return CMD_SUCCESS; 994 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
971} 995}
972 996
973static enum cmd_status _do_split(int argc, char **argv, int layout) { 997static struct cmd_results *_do_split(int argc, char **argv, int layout) {
974 char *name = layout == L_VERT ? "splitv" : 998 char *name = layout == L_VERT ? "splitv" :
975 layout == L_HORIZ ? "splith" : "split"; 999 layout == L_HORIZ ? "splith" : "split";
976 if (!checkarg(argc, name, EXPECTED_EQUAL_TO, 0) 1000 struct cmd_results *error = NULL;
977 || config->reading || !config->active) { 1001 if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
978 return CMD_FAILURE; 1002 if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
1003 if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
1004 return error;
979 } 1005 }
980 swayc_t *focused = get_focused_container(&root_container); 1006 swayc_t *focused = get_focused_container(&root_container);
981 1007
982 // Case of floating window, dont split 1008 // Case of floating window, dont split
983 if (focused->is_floating) { 1009 if (focused->is_floating) {
984 return CMD_SUCCESS; 1010 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
985 } 1011 }
986 /* Case that focus is on an workspace with 0/1 children.change its layout */ 1012 /* Case that focus is on an workspace with 0/1 children.change its layout */
987 if (focused->type == C_WORKSPACE && focused->children->length <= 1) { 1013 if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
@@ -999,56 +1025,61 @@ static enum cmd_status _do_split(int argc, char **argv, int layout) {
999 set_focused_container(focused); 1025 set_focused_container(focused);
1000 arrange_windows(parent, -1, -1); 1026 arrange_windows(parent, -1, -1);
1001 } 1027 }
1002 return CMD_SUCCESS; 1028 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1003} 1029}
1004 1030
1005static enum cmd_status cmd_split(int argc, char **argv) { 1031static struct cmd_results *cmd_split(int argc, char **argv) {
1006 if (!checkarg(argc, "split", EXPECTED_EQUAL_TO, 1) 1032 struct cmd_results *error = NULL;
1007 || config->reading || !config->active) { 1033 if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
1008 return CMD_FAILURE; 1034 if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
1035 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
1036 return error;
1009 } 1037 }
1010
1011 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { 1038 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
1012 _do_split(argc - 1, argv + 1, L_VERT); 1039 _do_split(argc - 1, argv + 1, L_VERT);
1013 } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { 1040 } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
1014 _do_split(argc - 1, argv + 1, L_HORIZ); 1041 _do_split(argc - 1, argv + 1, L_HORIZ);
1015 } else { 1042 } else {
1016 sway_log(L_ERROR, "Invalid split command (expected either horiziontal or vertical)."); 1043 error = cmd_results_new(CMD_FAILURE, "split",
1017 return CMD_FAILURE; 1044 "Invalid split command (expected either horiziontal or vertical).");
1045 return error;
1018 } 1046 }
1019 return CMD_SUCCESS; 1047 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1020} 1048}
1021 1049
1022static enum cmd_status cmd_splitv(int argc, char **argv) { 1050static struct cmd_results *cmd_splitv(int argc, char **argv) {
1023 return _do_split(argc, argv, L_VERT); 1051 return _do_split(argc, argv, L_VERT);
1024} 1052}
1025 1053
1026static enum cmd_status cmd_splith(int argc, char **argv) { 1054static struct cmd_results *cmd_splith(int argc, char **argv) {
1027 return _do_split(argc, argv, L_HORIZ); 1055 return _do_split(argc, argv, L_HORIZ);
1028} 1056}
1029 1057
1030static enum cmd_status cmd_log_colors(int argc, char **argv) { 1058static struct cmd_results *cmd_log_colors(int argc, char **argv) {
1031 if (!checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1) 1059 struct cmd_results *error = NULL;
1032 || !config->reading) { 1060 if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
1033 return CMD_FAILURE; 1061 if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
1062 return error;
1034 } 1063 }
1035 if (strcasecmp(argv[0], "no") == 0) { 1064 if (strcasecmp(argv[0], "no") == 0) {
1036 sway_log_colors(0); 1065 sway_log_colors(0);
1037 } else if (strcasecmp(argv[0], "yes") == 0) { 1066 } else if (strcasecmp(argv[0], "yes") == 0) {
1038 sway_log_colors(1); 1067 sway_log_colors(1);
1039 } else { 1068 } else {
1040 sway_log(L_ERROR, "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]); 1069 error = cmd_results_new(CMD_FAILURE, "log_colors",
1041 return CMD_FAILURE; 1070 "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
1071 return error;
1042 } 1072 }
1043 return CMD_SUCCESS; 1073 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1044} 1074}
1045 1075
1046static enum cmd_status cmd_fullscreen(int argc, char **argv) { 1076static struct cmd_results *cmd_fullscreen(int argc, char **argv) {
1047 if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0) 1077 struct cmd_results *error = NULL;
1048 || config->reading || !config->active) { 1078 if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
1049 return CMD_FAILURE; 1079 if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
1080 if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
1081 return error;
1050 } 1082 }
1051
1052 swayc_t *container = get_focused_view(&root_container); 1083 swayc_t *container = get_focused_view(&root_container);
1053 bool current = swayc_is_fullscreen(container); 1084 bool current = swayc_is_fullscreen(container);
1054 wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current); 1085 wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
@@ -1060,17 +1091,17 @@ static enum cmd_status cmd_fullscreen(int argc, char **argv) {
1060 // Only resize container when going into fullscreen 1091 // Only resize container when going into fullscreen
1061 arrange_windows(container, -1, -1); 1092 arrange_windows(container, -1, -1);
1062 1093
1063 return CMD_SUCCESS; 1094 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1064} 1095}
1065 1096
1066static enum cmd_status cmd_workspace(int argc, char **argv) { 1097static struct cmd_results *cmd_workspace(int argc, char **argv) {
1067 if (!checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1)) { 1098 struct cmd_results *error = NULL;
1068 return CMD_FAILURE; 1099 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
1100 return error;
1069 } 1101 }
1070
1071 if (argc == 1) { 1102 if (argc == 1) {
1072 if (config->reading || !config->active) { 1103 if (config->reading || !config->active) {
1073 return CMD_DEFER; 1104 return cmd_results_new(CMD_DEFER, "workspace", NULL);
1074 } 1105 }
1075 // Handle workspace next/prev 1106 // Handle workspace next/prev
1076 swayc_t *ws = NULL; 1107 swayc_t *ws = NULL;
@@ -1096,8 +1127,8 @@ static enum cmd_status cmd_workspace(int argc, char **argv) {
1096 workspace_switch(ws); 1127 workspace_switch(ws);
1097 } else { 1128 } else {
1098 if (strcasecmp(argv[1], "output") == 0) { 1129 if (strcasecmp(argv[1], "output") == 0) {
1099 if (!checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3)) { 1130 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3))) {
1100 return CMD_FAILURE; 1131 return error;
1101 } 1132 }
1102 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); 1133 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]); 1134 sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]);
@@ -1109,21 +1140,22 @@ static enum cmd_status cmd_workspace(int argc, char **argv) {
1109 } 1140 }
1110 } 1141 }
1111 } 1142 }
1112 return CMD_SUCCESS; 1143 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1113} 1144}
1114 1145
1115static enum cmd_status cmd_ws_auto_back_and_forth(int argc, char **argv) { 1146static 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)) { 1147 struct cmd_results *error = NULL;
1117 return CMD_FAILURE; 1148 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
1149 return error;
1118 } 1150 }
1119 if (strcasecmp(argv[0], "yes") == 0) { 1151 if (strcasecmp(argv[0], "yes") == 0) {
1120 config->auto_back_and_forth = true; 1152 config->auto_back_and_forth = true;
1121 } else if (strcasecmp(argv[0], "no") == 0) { 1153 } else if (strcasecmp(argv[0], "no") == 0) {
1122 config->auto_back_and_forth = false; 1154 config->auto_back_and_forth = false;
1123 } else { 1155 } else {
1124 return CMD_FAILURE; 1156 return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
1125 } 1157 }
1126 return CMD_SUCCESS; 1158 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
1127} 1159}
1128 1160
1129/* Keep alphabetized */ 1161/* Keep alphabetized */
@@ -1171,8 +1203,11 @@ static struct cmd_handler *find_handler(char *line) {
1171 return res; 1203 return res;
1172} 1204}
1173 1205
1174enum cmd_status handle_command(char *_exec) { 1206struct cmd_results *handle_command(char *_exec) {
1175 enum cmd_status status = CMD_SUCCESS; 1207 // Even though this function will process multiple commands we will only
1208 // return the last error, if any (for now). (Since we have access to an
1209 // error string we could e.g. concatonate all errors there.)
1210 struct cmd_results *results = NULL;
1176 char *exec = strdup(_exec); 1211 char *exec = strdup(_exec);
1177 char *head = exec; 1212 char *head = exec;
1178 char *cmdlist; 1213 char *cmdlist;
@@ -1188,8 +1223,7 @@ enum cmd_status handle_command(char *_exec) {
1188 ++head; 1223 ++head;
1189 // TODO handle criteria 1224 // TODO handle criteria
1190 } else { 1225 } else {
1191 sway_log(L_ERROR, "Unmatched ["); 1226 results = cmd_results_new(CMD_INVALID, NULL, "Unmatched [");
1192 status = CMD_INVALID;
1193 } 1227 }
1194 // Skip leading whitespace 1228 // Skip leading whitespace
1195 head += strspn(head, whitespace); 1229 head += strspn(head, whitespace);
@@ -1205,7 +1239,6 @@ enum cmd_status handle_command(char *_exec) {
1205 sway_log(L_INFO, "Ignoring empty command."); 1239 sway_log(L_INFO, "Ignoring empty command.");
1206 continue; 1240 continue;
1207 } 1241 }
1208
1209 sway_log(L_INFO, "Handling command '%s'", cmd); 1242 sway_log(L_INFO, "Handling command '%s'", cmd);
1210 //TODO better handling of argv 1243 //TODO better handling of argv
1211 int argc; 1244 int argc;
@@ -1214,37 +1247,54 @@ enum cmd_status handle_command(char *_exec) {
1214 strip_quotes(argv[1]); 1247 strip_quotes(argv[1]);
1215 } 1248 }
1216 struct cmd_handler *handler = find_handler(argv[0]); 1249 struct cmd_handler *handler = find_handler(argv[0]);
1217 enum cmd_status res = CMD_INVALID; 1250 if (!handler) {
1218 if (!handler 1251 if (results) {
1219 || (res = handler->handle(argc-1, argv+1)) != CMD_SUCCESS) { 1252 free_cmd_results(results);
1220 sway_log(L_ERROR, "Command '%s' failed", cmd); 1253 }
1254 results = cmd_results_new(CMD_INVALID, strdup(cmd), "Unknown/invalid command");
1255 free_argv(argc, argv);
1256 goto cleanup;
1257 }
1258 struct cmd_results *res = handler->handle(argc-1, argv+1);
1259 if (res->status != CMD_SUCCESS) {
1221 free_argv(argc, argv); 1260 free_argv(argc, argv);
1222 status = res; 1261 if (results) {
1262 free_cmd_results(results);
1263 }
1264 results = res;
1223 goto cleanup; 1265 goto cleanup;
1224 } 1266 }
1225 free_argv(argc, argv); 1267 free_argv(argc, argv);
1268 free_cmd_results(res);
1226 } while(cmdlist); 1269 } while(cmdlist);
1227 } while(head); 1270 } while(head);
1228 cleanup: 1271 cleanup:
1229 free(exec); 1272 free(exec);
1230 return status; 1273 if (!results) {
1274 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
1275 }
1276 return results;
1231} 1277}
1232 1278
1233enum cmd_status config_command(char *exec) { 1279struct cmd_results *config_command(char *exec) {
1234 enum cmd_status status = CMD_SUCCESS; 1280 struct cmd_results *results = NULL;
1235 int argc; 1281 int argc;
1236 char **argv = split_args(exec, &argc); 1282 char **argv = split_args(exec, &argc);
1237 if (!argc) goto cleanup; 1283 if (!argc) {
1284 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
1285 goto cleanup;
1286 }
1238 1287
1239 sway_log(L_INFO, "handling config command '%s'", exec); 1288 sway_log(L_INFO, "handling config command '%s'", exec);
1240 // Endblock 1289 // Endblock
1241 if (**argv == '}') { 1290 if (**argv == '}') {
1242 status = CMD_BLOCK_END; 1291 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
1243 goto cleanup; 1292 goto cleanup;
1244 } 1293 }
1245 struct cmd_handler *handler = find_handler(argv[0]); 1294 struct cmd_handler *handler = find_handler(argv[0]);
1246 if (!handler) { 1295 if (!handler) {
1247 status = CMD_INVALID; 1296 char *input = argv[0] ? strdup(argv[0]) : "(empty)";
1297 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
1248 goto cleanup; 1298 goto cleanup;
1249 } 1299 }
1250 int i; 1300 int i;
@@ -1257,8 +1307,44 @@ enum cmd_status config_command(char *exec) {
1257 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { 1307 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
1258 strip_quotes(argv[1]); 1308 strip_quotes(argv[1]);
1259 } 1309 }
1260 status = handler->handle(argc-1, argv+1); 1310 results = handler->handle(argc-1, argv+1);
1261 cleanup: 1311 cleanup:
1262 free_argv(argc, argv); 1312 free_argv(argc, argv);
1263 return status; 1313 return results;
1314}
1315
1316struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
1317 struct cmd_results *results = malloc(sizeof(struct cmd_results));
1318 results->status = status;
1319 results->input = input; // input is the command name
1320 if (format) {
1321 char *error = malloc(256);
1322 va_list args;
1323 va_start(args, format);
1324 vsnprintf(error, 256, format, args);
1325 va_end(args);
1326 results->error = error;
1327 } else {
1328 results->error = NULL;
1329 }
1330 return results;
1331}
1332
1333void free_cmd_results(struct cmd_results *results) {
1334 if (results->error)
1335 free(results->error);
1336 free(results);
1337}
1338
1339const char *cmd_results_to_json(struct cmd_results *results) {
1340 json_object *root = json_object_new_object();
1341 json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
1342 if (results->input)
1343 json_object_object_add(root, "input", json_object_new_string(results->input));
1344 if (results->error)
1345 json_object_object_add(root, "error", json_object_new_string(results->error));
1346
1347 const char *json = json_object_to_json_string(root);
1348 free(root);
1349 return json;
1264} 1350}
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 1521e5cd..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) == CMD_SUCCESS); 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: