summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar S. Christoffer Eliesen <christoffer@eliesen.no>2015-10-22 14:14:13 +0200
committerLibravatar S. Christoffer Eliesen <christoffer@eliesen.no>2015-10-22 23:36:24 +0200
commitaf30a1b67c22aa54dad4e1a0ee3aacb537c4ab92 (patch)
tree013c1860550bb3c190095fd0633c08aca16838d1
parentipc: Return correct status in ipc reply. (diff)
downloadsway-af30a1b67c22aa54dad4e1a0ee3aacb537c4ab92.tar.gz
sway-af30a1b67c22aa54dad4e1a0ee3aacb537c4ab92.tar.zst
sway-af30a1b67c22aa54dad4e1a0ee3aacb537c4ab92.zip
ipc,commands,config: Replace cmd_status enum with cmd_results struct.
In i3 the ipc reply will contain a human readable error message, and this patch replicates that behaviour. However, that error message is also useful for logging, which this patch takes advantage of. E.g. instead of logging errors directly in commands.c/checkargs, it is fed back to the caller which eventually ends up logging everything with maximum context available (config.c/read_config). So instead of logging e.g. "Error on line 'exit'" it will now log: "Error on line 'exit': Can't execute from config."
-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: