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."
This commit is contained in:
S. Christoffer Eliesen 2015-10-22 14:14:13 +02:00
parent 544c6c412a
commit af30a1b67c
5 changed files with 333 additions and 222 deletions

View File

@ -1,22 +1,34 @@
#ifndef _SWAY_COMMANDS_H #ifndef _SWAY_COMMANDS_H
#define _SWAY_COMMANDS_H #define _SWAY_COMMANDS_H
#include <stdbool.h> #include <stdbool.h>
#include <json-c/json.h>
#include "config.h" #include "config.h"
enum cmd_status { enum cmd_status {
CMD_SUCCESS, CMD_SUCCESS,
CMD_FAILURE, CMD_FAILURE, // was or at least could be executed
CMD_INVALID, CMD_INVALID, // unknown or parse error
CMD_DEFER, CMD_DEFER,
// Config Blocks // Config Blocks
CMD_BLOCK_END, CMD_BLOCK_END,
CMD_BLOCK_MODE, CMD_BLOCK_MODE,
}; };
enum cmd_status handle_command(char *command); struct cmd_results {
enum cmd_status status;
const char *input;
char *error;
};
struct cmd_results *handle_command(char *command);
// Handles commands during config // Handles commands during config
enum cmd_status config_command(char *command); struct cmd_results *config_command(char *command);
struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *error, ...);
void free_cmd_results(struct cmd_results *results);
const char *cmd_results_to_json(struct cmd_results *results);
void remove_view_from_scratchpad(); void remove_view_from_scratchpad();

View File

@ -21,7 +21,7 @@
#include "sway.h" #include "sway.h"
#include "resize.h" #include "resize.h"
typedef enum cmd_status sway_cmd(int argc, char **argv); typedef struct cmd_results *sway_cmd(int argc, char **argv);
struct cmd_handler { struct cmd_handler {
char *command; char *command;
@ -81,41 +81,43 @@ enum expected_args {
EXPECTED_EQUAL_TO EXPECTED_EQUAL_TO
}; };
static bool checkarg(int argc, const char *name, enum expected_args type, int val) { // Returns error object, or NULL if check succeeds.
static struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
struct cmd_results *error = NULL;
switch (type) { switch (type) {
case EXPECTED_MORE_THAN: case EXPECTED_MORE_THAN:
if (argc > val) { if (argc > val) {
return true; return NULL;
} }
sway_log(L_ERROR, "Invalid %s command." error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
"(expected more than %d argument%s, got %d)", "(expected more than %d argument%s, got %d)",
name, val, (char*[2]){"s", ""}[argc==1], argc); name, val, (char*[2]){"s", ""}[argc==1], argc);
break; break;
case EXPECTED_AT_LEAST: case EXPECTED_AT_LEAST:
if (argc >= val) { if (argc >= val) {
return true; return NULL;
} }
sway_log(L_ERROR, "Invalid %s command." error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
"(expected at least %d argument%s, got %d)", "(expected at least %d argument%s, got %d)",
name, val, (char*[2]){"s", ""}[argc==1], argc); name, val, (char*[2]){"s", ""}[argc==1], argc);
break; break;
case EXPECTED_LESS_THAN: case EXPECTED_LESS_THAN:
if (argc < val) { if (argc < val) {
return true; return NULL;
}; };
sway_log(L_ERROR, "Invalid %s command." error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
"(expected less than %d argument%s, got %d)", "(expected less than %d argument%s, got %d)",
name, val, (char*[2]){"s", ""}[argc==1], argc); name, val, (char*[2]){"s", ""}[argc==1], argc);
break; break;
case EXPECTED_EQUAL_TO: case EXPECTED_EQUAL_TO:
if (argc == val) { if (argc == val) {
return true; return NULL;
}; };
sway_log(L_ERROR, "Invalid %s command." error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
"(expected %d arguments, got %d)", name, val, argc); "(expected %d arguments, got %d)", name, val, argc);
break; break;
} }
return false; return error;
} }
static int bindsym_sort(const void *_lbind, const void *_rbind) { static int bindsym_sort(const void *_lbind, const void *_rbind) {
@ -131,11 +133,13 @@ static int bindsym_sort(const void *_lbind, const void *_rbind) {
return (rbind->keys->length + rmod) - (lbind->keys->length + lmod); return (rbind->keys->length + rmod) - (lbind->keys->length + lmod);
} }
static enum cmd_status cmd_bindsym(int argc, char **argv) { static struct cmd_results *cmd_bindsym(int argc, char **argv) {
if (!checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1) struct cmd_results *error = NULL;
|| !config->reading) { if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
return CMD_FAILURE; return error;
}; } else if (!config->reading) {
return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
}
struct sway_binding *binding = malloc(sizeof(struct sway_binding)); struct sway_binding *binding = malloc(sizeof(struct sway_binding));
binding->keys = create_list(); binding->keys = create_list();
@ -159,12 +163,12 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) {
// Check for xkb key // Check for xkb key
xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE); xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE);
if (!sym) { if (!sym) {
sway_log(L_ERROR, "bindsym - unknown key %s", (char *)split->items[i]); error = cmd_results_new(CMD_INVALID, "bindsym", "Unknown key '%s'", (char *)split->items[i]);
list_free(binding->keys); list_free(binding->keys);
free(binding->command); free(binding->command);
free(binding); free(binding);
list_free(split); list_free(split);
return CMD_FAILURE; return error;
} }
xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
*key = sym; *key = sym;
@ -178,21 +182,21 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) {
list_sort(mode->bindings, bindsym_sort); list_sort(mode->bindings, bindsym_sort);
sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command); sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_exec_always(int argc, char **argv) { static struct cmd_results *cmd_exec_always(int argc, char **argv) {
if (!config->active) return CMD_DEFER; struct cmd_results *error = NULL;
if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) { if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL);
return CMD_FAILURE; if ((error = checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0))) {
return error;
} }
char *tmp = NULL; char *tmp = NULL;
if (strcmp((char*)*argv, "--no-startup-id") == 0) { if (strcmp((char*)*argv, "--no-startup-id") == 0) {
sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
if (!checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0)) { return error;
return CMD_FAILURE;
} }
tmp = join_args(argv + 1, argc - 1); tmp = join_args(argv + 1, argc - 1);
@ -218,22 +222,20 @@ static enum cmd_status cmd_exec_always(int argc, char **argv) {
// Close child process // Close child process
_exit(0); _exit(0);
} else if (pid < 0) { } else if (pid < 0) {
sway_log(L_ERROR, "exec command failed, sway could not fork"); return cmd_results_new(CMD_FAILURE, "exec_always", "Command failed (sway could not fork).");
return CMD_FAILURE;
} }
// cleanup child process // cleanup child process
wait(0); wait(0);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_exec(int argc, char **argv) { static struct cmd_results *cmd_exec(int argc, char **argv) {
if (!config->active) return CMD_DEFER; if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
if (config->reloading) { if (config->reloading) {
char *args = join_args(argv, argc); char *args = join_args(argv, argc);
sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
free(args); free(args);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
return cmd_exec_always(argc, argv); return cmd_exec_always(argc, argv);
} }
@ -244,28 +246,30 @@ static void kill_views(swayc_t *container, void *data) {
} }
} }
static enum cmd_status cmd_exit(int argc, char **argv) { static struct cmd_results *cmd_exit(int argc, char **argv) {
if (config->reading) return CMD_INVALID; struct cmd_results *error = NULL;
if (!checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0)) { if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
return error;
} }
// Close all views // Close all views
container_map(&root_container, kill_views, NULL); container_map(&root_container, kill_views, NULL);
sway_terminate(); sway_terminate();
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_floating(int argc, char **argv) { static struct cmd_results *cmd_floating(int argc, char **argv) {
if (config->reading) return CMD_INVALID; struct cmd_results *error = NULL;
if (!checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1)) { if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
if (strcasecmp(argv[0], "toggle") == 0) { if (strcasecmp(argv[0], "toggle") == 0) {
swayc_t *view = get_focused_container(&root_container); swayc_t *view = get_focused_container(&root_container);
// Prevent running floating commands on things like workspaces // Prevent running floating commands on things like workspaces
if (view->type != C_VIEW) { if (view->type != C_VIEW) {
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
// Change from nonfloating to floating // Change from nonfloating to floating
if (!view->is_floating) { if (!view->is_floating) {
@ -314,13 +318,14 @@ static enum cmd_status cmd_floating(int argc, char **argv) {
} }
set_focused_container(view); set_focused_container(view);
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_floating_mod(int argc, char **argv) { static struct cmd_results *cmd_floating_mod(int argc, char **argv) {
if (!config->reading) return CMD_INVALID; struct cmd_results *error = NULL;
if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1)) { if (!config->reading) return cmd_results_new(CMD_FAILURE, "floating_modifier", "Can only be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
int i, j; int i, j;
list_t *split = split_string(argv[0], "+"); list_t *split = split_string(argv[0], "+");
@ -336,19 +341,20 @@ static enum cmd_status cmd_floating_mod(int argc, char **argv) {
} }
free_flat_list(split); free_flat_list(split);
if (!config->floating_mod) { if (!config->floating_mod) {
sway_log(L_ERROR, "bindsym - unknown keys %s", argv[0]); error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
return CMD_FAILURE; return error;
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_focus(int argc, char **argv) { static struct cmd_results *cmd_focus(int argc, char **argv) {
if (config->reading) return CMD_INVALID; if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
return error;
}
static int floating_toggled_index = 0; static int floating_toggled_index = 0;
static int tiled_toggled_index = 0; static int tiled_toggled_index = 0;
if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1)) {
return CMD_FAILURE;
}
if (strcasecmp(argv[0], "left") == 0) { if (strcasecmp(argv[0], "left") == 0) {
move_focus(MOVE_LEFT); move_focus(MOVE_LEFT);
} else if (strcasecmp(argv[0], "right") == 0) { } else if (strcasecmp(argv[0], "right") == 0) {
@ -397,26 +403,28 @@ static enum cmd_status cmd_focus(int argc, char **argv) {
} }
} }
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) { static struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
if (!config->reading) return CMD_INVALID; struct cmd_results *error = NULL;
if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) { if (!config->reading) return cmd_results_new(CMD_FAILURE, "focus_follows_mouse", "Can only be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_seamless_mouse(int argc, char **argv) { static struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
if (!checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1)) { struct cmd_results *error = NULL;
return CMD_FAILURE; if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0); config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static void hide_view_in_scratchpad(swayc_t *sp_view) { static void hide_view_in_scratchpad(swayc_t *sp_view) {
@ -435,15 +443,17 @@ static void hide_view_in_scratchpad(swayc_t *sp_view) {
set_focused_container(container_under_pointer()); set_focused_container(container_under_pointer());
} }
static enum cmd_status cmd_mode(int argc, char **argv) { static struct cmd_results *cmd_mode(int argc, char **argv) {
if (!checkarg(argc, "mode", EXPECTED_AT_LEAST, 1)) { struct cmd_results *error = NULL;
return CMD_FAILURE; if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
return error;
} }
char *mode_name = join_args(argv, argc); char *mode_name = join_args(argv, argc);
int mode_len = strlen(mode_name); int mode_len = strlen(mode_name);
bool mode_make = mode_name[mode_len-1] == '{'; bool mode_make = mode_name[mode_len-1] == '{';
if (mode_make) { if (mode_make) {
if (!config->reading) return CMD_INVALID; if (!config->reading)
return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
// Trim trailing spaces // Trim trailing spaces
do { do {
mode_name[--mode_len] = 0; mode_name[--mode_len] = 0;
@ -467,9 +477,9 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
list_add(config->modes, mode); list_add(config->modes, mode);
} }
if (!mode) { if (!mode) {
sway_log(L_ERROR, "Unknown mode `%s'", mode_name); error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
free(mode_name); free(mode_name);
return CMD_FAILURE; return error;
} }
if ((config->reading && mode_make) || (!config->reading && !mode_make)) { if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
@ -477,15 +487,15 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
free(mode_name); free(mode_name);
// Set current mode // Set current mode
config->current_mode = mode; config->current_mode = mode;
return mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS; return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_move(int argc, char **argv) { static struct cmd_results *cmd_move(int argc, char **argv) {
if (config->reading) return CMD_FAILURE; struct cmd_results *error = NULL;
if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
return error;
} }
swayc_t *view = get_focused_container(&root_container); swayc_t *view = get_focused_container(&root_container);
if (strcasecmp(argv[0], "left") == 0) { if (strcasecmp(argv[0], "left") == 0) {
@ -498,14 +508,14 @@ static enum cmd_status cmd_move(int argc, char **argv) {
move_container(view, MOVE_DOWN); move_container(view, MOVE_DOWN);
} else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
// "move container to workspace x" // "move container to workspace x"
if (!checkarg(argc, "move container/window", EXPECTED_EQUAL_TO, 4) if ((error = checkarg(argc, "move container/window", EXPECTED_EQUAL_TO, 4))) {
|| strcasecmp(argv[1], "to") != 0 return error;
|| strcasecmp(argv[2], "workspace") != 0) { } else if ( strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "workspace") != 0) {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "move", "Expected 'move %s to workspace <name>'", argv[0]);
} }
if (view->type != C_CONTAINER && view->type != C_VIEW) { if (view->type != C_CONTAINER && view->type != C_VIEW) {
return CMD_FAILURE; return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
} }
const char *ws_name = argv[3]; const char *ws_name = argv[3];
@ -521,7 +531,7 @@ static enum cmd_status cmd_move(int argc, char **argv) {
move_container_to(view, get_focused_container(ws)); move_container_to(view, get_focused_container(ws));
} else if (strcasecmp(argv[0], "scratchpad") == 0) { } else if (strcasecmp(argv[0], "scratchpad") == 0) {
if (view->type != C_CONTAINER && view->type != C_VIEW) { if (view->type != C_CONTAINER && view->type != C_VIEW) {
return CMD_FAILURE; return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
} }
swayc_t *view = get_focused_container(&root_container); swayc_t *view = get_focused_container(&root_container);
int i; int i;
@ -529,7 +539,7 @@ static enum cmd_status cmd_move(int argc, char **argv) {
if (scratchpad->items[i] == view) { if (scratchpad->items[i] == view) {
hide_view_in_scratchpad(view); hide_view_in_scratchpad(view);
sp_view = NULL; sp_view = NULL;
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
} }
list_add(scratchpad, view); list_add(scratchpad, view);
@ -546,17 +556,18 @@ static enum cmd_status cmd_move(int argc, char **argv) {
} }
set_focused_container(focused); set_focused_container(focused);
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "move",
"Expected 'move <left|right|up|down>' or 'move <container|window> to workspace <name>'");
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_orientation(int argc, char **argv) { static struct cmd_results *cmd_orientation(int argc, char **argv) {
if (!config->reading) return CMD_FAILURE; struct cmd_results *error = NULL;
if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1)) { if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
if (strcasecmp(argv[0], "horizontal") == 0) { if (strcasecmp(argv[0], "horizontal") == 0) {
config->default_orientation = L_HORIZ; config->default_orientation = L_HORIZ;
} else if (strcasecmp(argv[0], "vertical") == 0) { } else if (strcasecmp(argv[0], "vertical") == 0) {
@ -564,16 +575,16 @@ static enum cmd_status cmd_orientation(int argc, char **argv) {
} else if (strcasecmp(argv[0], "auto") == 0) { } else if (strcasecmp(argv[0], "auto") == 0) {
// Do nothing // Do nothing
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_output(int argc, char **argv) { static struct cmd_results *cmd_output(int argc, char **argv) {
if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { struct cmd_results *error = NULL;
return CMD_FAILURE; if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
return error;
} }
struct output_config *output = calloc(1, sizeof(struct output_config)); struct output_config *output = calloc(1, sizeof(struct output_config));
output->x = output->y = output->width = output->height = -1; output->x = output->y = output->width = output->height = -1;
output->name = strdup(argv[0]); output->name = strdup(argv[0]);
@ -644,12 +655,13 @@ static enum cmd_status cmd_output(int argc, char **argv) {
} }
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_gaps(int argc, char **argv) { static struct cmd_results *cmd_gaps(int argc, char **argv) {
if (!checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1)) { struct cmd_results *error = NULL;
return CMD_FAILURE; if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
return error;
} }
const char *amount_str = argv[0]; const char *amount_str = argv[0];
// gaps amount // gaps amount
@ -657,7 +669,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
int amount = (int)strtol(amount_str, NULL, 10); int amount = (int)strtol(amount_str, NULL, 10);
if (errno == ERANGE || amount == 0) { if (errno == ERANGE || amount == 0) {
errno = 0; errno = 0;
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
} }
if (config->gaps_inner == 0) { if (config->gaps_inner == 0) {
config->gaps_inner = amount; config->gaps_inner = amount;
@ -665,14 +677,14 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
if (config->gaps_outer == 0) { if (config->gaps_outer == 0) {
config->gaps_outer = amount; config->gaps_outer = amount;
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
// gaps inner|outer n // gaps inner|outer n
else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) { else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
int amount = (int)strtol(amount_str, NULL, 10); int amount = (int)strtol(amount_str, NULL, 10);
if (errno == ERANGE || amount == 0) { if (errno == ERANGE || amount == 0) {
errno = 0; errno = 0;
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
} }
const char *target_str = argv[0]; const char *target_str = argv[0];
if (strcasecmp(target_str, "inner") == 0) { if (strcasecmp(target_str, "inner") == 0) {
@ -680,11 +692,11 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
} else if (strcasecmp(target_str, "outer") == 0) { } else if (strcasecmp(target_str, "outer") == 0) {
config->gaps_outer = amount; config->gaps_outer = amount;
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
// gaps inner|outer current|all set|plus|minus n // gaps inner|outer current|all set|plus|minus n
if (argc < 4 || config->reading) { if (argc < 4 || config->reading) {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
} }
// gaps inner|outer ... // gaps inner|outer ...
const char *inout_str = argv[0]; const char *inout_str = argv[0];
@ -694,7 +706,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
} else if (strcasecmp(inout_str, "outer") == 0) { } else if (strcasecmp(inout_str, "outer") == 0) {
inout = OUTER; inout = OUTER;
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
} }
// gaps ... current|all ... // gaps ... current|all ...
@ -712,7 +724,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
target = WORKSPACE; target = WORKSPACE;
} }
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'");
} }
// gaps ... n // gaps ... n
@ -720,7 +732,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
int amount = (int)strtol(amount_str, NULL, 10); int amount = (int)strtol(amount_str, NULL, 10);
if (errno == ERANGE || amount == 0) { if (errno == ERANGE || amount == 0) {
errno = 0; errno = 0;
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
} }
// gaps ... set|plus|minus ... // gaps ... set|plus|minus ...
@ -734,18 +746,18 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
method = ADD; method = ADD;
amount *= -1; amount *= -1;
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "gaps", "Expected 'gaps <inner|outer> <current|all> <set|plus|minus n>'");
} }
if (target == CURRENT) { if (target == CURRENT) {
swayc_t *cont; swayc_t *cont;
if (inout == OUTER) { if (inout == OUTER) {
if ((cont = swayc_active_workspace()) == NULL) { if ((cont = swayc_active_workspace()) == NULL) {
return CMD_FAILURE; return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
} }
} else { } else {
if ((cont = get_focused_view(&root_container))->type != C_VIEW) { if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
return CMD_FAILURE; return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
} }
} }
cont->gaps = swayc_gap(cont); cont->gaps = swayc_gap(cont);
@ -775,7 +787,7 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
swayc_t *top; swayc_t *top;
if (target == WORKSPACE) { if (target == WORKSPACE) {
if ((top = swayc_active_workspace()) == NULL) { if ((top = swayc_active_workspace()) == NULL) {
return CMD_FAILURE; return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
} }
} else { } else {
top = &root_container; top = &root_container;
@ -786,22 +798,24 @@ static enum cmd_status cmd_gaps(int argc, char **argv) {
arrange_windows(top, -1, -1); arrange_windows(top, -1, -1);
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_kill(int argc, char **argv) { static struct cmd_results *cmd_kill(int argc, char **argv) {
if (config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
}
swayc_t *view = get_focused_container(&root_container); swayc_t *view = get_focused_container(&root_container);
wlc_view_close(view->handle); wlc_view_close(view->handle);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_layout(int argc, char **argv) { static struct cmd_results *cmd_layout(int argc, char **argv) {
if (!checkarg(argc, "layout", EXPECTED_MORE_THAN, 0) struct cmd_results *error = NULL;
|| config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error;
} }
swayc_t *parent = get_focused_container(&root_container); swayc_t *parent = get_focused_container(&root_container);
while (parent->type == C_VIEW) { while (parent->type == C_VIEW) {
@ -821,33 +835,38 @@ static enum cmd_status cmd_layout(int argc, char **argv) {
} }
arrange_windows(parent, parent->width, parent->height); arrange_windows(parent, parent->width, parent->height);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_reload(int argc, char **argv) { static struct cmd_results *cmd_reload(int argc, char **argv) {
if (!checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0) struct cmd_results *error = NULL;
|| config->reading if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
|| !load_config(NULL)) { if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
return CMD_FAILURE; return error;
} }
if (!load_config(NULL)) return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
arrange_windows(&root_container, -1, -1); arrange_windows(&root_container, -1, -1);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_resize(int argc, char **argv) { static struct cmd_results *cmd_resize(int argc, char **argv) {
if (!checkarg(argc, "resize", EXPECTED_AT_LEAST, 3) struct cmd_results *error = NULL;
|| config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 3))) {
return error;
} }
char *end; char *end;
int amount = (int)strtol(argv[2], &end, 10); int amount = (int)strtol(argv[2], &end, 10);
if (errno == ERANGE || amount == 0) { if (errno == ERANGE || amount == 0) {
errno = 0; errno = 0;
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "resize", "Number is out of range.");
} }
if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) { if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "resize",
"Expected 'resize <shrink|grow> <width|height> <amount>'");
} }
if (strcmp(argv[0], "shrink") == 0) { if (strcmp(argv[0], "shrink") == 0) {
@ -859,9 +878,10 @@ static enum cmd_status cmd_resize(int argc, char **argv) {
} else if (strcmp(argv[1], "height") == 0) { } else if (strcmp(argv[1], "height") == 0) {
resize_tiled(amount, false); resize_tiled(amount, false);
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "resize",
"Expected 'resize <shrink|grow> <width|height> <amount>'");
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static swayc_t *fetch_view_from_scratchpad() { static swayc_t *fetch_view_from_scratchpad() {
@ -909,10 +929,12 @@ void remove_view_from_scratchpad(swayc_t *view) {
} }
} }
static enum cmd_status cmd_scratchpad(int argc, char **argv) { static struct cmd_results *cmd_scratchpad(int argc, char **argv) {
if (!checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1) struct cmd_results *error = NULL;
|| config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) { if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
@ -931,9 +953,9 @@ static enum cmd_status cmd_scratchpad(int argc, char **argv) {
sp_view = NULL; sp_view = NULL;
} }
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
return CMD_FAILURE; return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
} }
// sort in order of longest->shortest // sort in order of longest->shortest
@ -943,11 +965,13 @@ static int compare_set(const void *_l, const void *_r) {
return strlen((*r)->name) - strlen((*l)->name); return strlen((*r)->name) - strlen((*l)->name);
} }
static enum cmd_status cmd_set(int argc, char **argv) { static struct cmd_results *cmd_set(int argc, char **argv) {
if (!checkarg(argc, "set", EXPECTED_AT_LEAST, 2) struct cmd_results *error = NULL;
|| !config->reading) { if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
return error;
} }
struct sway_variable *var = NULL; struct sway_variable *var = NULL;
// Find old variable if it exists // Find old variable if it exists
int i; int i;
@ -967,21 +991,23 @@ static enum cmd_status cmd_set(int argc, char **argv) {
list_sort(config->symbols, compare_set); list_sort(config->symbols, compare_set);
} }
var->value = join_args(argv + 1, argc - 1); var->value = join_args(argv + 1, argc - 1);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status _do_split(int argc, char **argv, int layout) { static struct cmd_results *_do_split(int argc, char **argv, int layout) {
char *name = layout == L_VERT ? "splitv" : char *name = layout == L_VERT ? "splitv" :
layout == L_HORIZ ? "splith" : "split"; layout == L_HORIZ ? "splith" : "split";
if (!checkarg(argc, name, EXPECTED_EQUAL_TO, 0) struct cmd_results *error = NULL;
|| config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
return error;
} }
swayc_t *focused = get_focused_container(&root_container); swayc_t *focused = get_focused_container(&root_container);
// Case of floating window, dont split // Case of floating window, dont split
if (focused->is_floating) { if (focused->is_floating) {
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
/* Case that focus is on an workspace with 0/1 children.change its layout */ /* Case that focus is on an workspace with 0/1 children.change its layout */
if (focused->type == C_WORKSPACE && focused->children->length <= 1) { 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) {
set_focused_container(focused); set_focused_container(focused);
arrange_windows(parent, -1, -1); arrange_windows(parent, -1, -1);
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_split(int argc, char **argv) { static struct cmd_results *cmd_split(int argc, char **argv) {
if (!checkarg(argc, "split", EXPECTED_EQUAL_TO, 1) struct cmd_results *error = NULL;
|| config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
_do_split(argc - 1, argv + 1, L_VERT); _do_split(argc - 1, argv + 1, L_VERT);
} else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
_do_split(argc - 1, argv + 1, L_HORIZ); _do_split(argc - 1, argv + 1, L_HORIZ);
} else { } else {
sway_log(L_ERROR, "Invalid split command (expected either horiziontal or vertical)."); error = cmd_results_new(CMD_FAILURE, "split",
return CMD_FAILURE; "Invalid split command (expected either horiziontal or vertical).");
return error;
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_splitv(int argc, char **argv) { static struct cmd_results *cmd_splitv(int argc, char **argv) {
return _do_split(argc, argv, L_VERT); return _do_split(argc, argv, L_VERT);
} }
static enum cmd_status cmd_splith(int argc, char **argv) { static struct cmd_results *cmd_splith(int argc, char **argv) {
return _do_split(argc, argv, L_HORIZ); return _do_split(argc, argv, L_HORIZ);
} }
static enum cmd_status cmd_log_colors(int argc, char **argv) { static struct cmd_results *cmd_log_colors(int argc, char **argv) {
if (!checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1) struct cmd_results *error = NULL;
|| !config->reading) { if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
return CMD_FAILURE; if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
if (strcasecmp(argv[0], "no") == 0) { if (strcasecmp(argv[0], "no") == 0) {
sway_log_colors(0); sway_log_colors(0);
} else if (strcasecmp(argv[0], "yes") == 0) { } else if (strcasecmp(argv[0], "yes") == 0) {
sway_log_colors(1); sway_log_colors(1);
} else { } else {
sway_log(L_ERROR, "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]); error = cmd_results_new(CMD_FAILURE, "log_colors",
return CMD_FAILURE; "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
return error;
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_fullscreen(int argc, char **argv) { static struct cmd_results *cmd_fullscreen(int argc, char **argv) {
if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0) struct cmd_results *error = NULL;
|| config->reading || !config->active) { if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
return CMD_FAILURE; if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
return error;
} }
swayc_t *container = get_focused_view(&root_container); swayc_t *container = get_focused_view(&root_container);
bool current = swayc_is_fullscreen(container); bool current = swayc_is_fullscreen(container);
wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current); wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
@ -1060,17 +1091,17 @@ static enum cmd_status cmd_fullscreen(int argc, char **argv) {
// Only resize container when going into fullscreen // Only resize container when going into fullscreen
arrange_windows(container, -1, -1); arrange_windows(container, -1, -1);
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_workspace(int argc, char **argv) { static struct cmd_results *cmd_workspace(int argc, char **argv) {
if (!checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1)) { struct cmd_results *error = NULL;
return CMD_FAILURE; if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
return error;
} }
if (argc == 1) { if (argc == 1) {
if (config->reading || !config->active) { if (config->reading || !config->active) {
return CMD_DEFER; return cmd_results_new(CMD_DEFER, "workspace", NULL);
} }
// Handle workspace next/prev // Handle workspace next/prev
swayc_t *ws = NULL; swayc_t *ws = NULL;
@ -1096,8 +1127,8 @@ static enum cmd_status cmd_workspace(int argc, char **argv) {
workspace_switch(ws); workspace_switch(ws);
} else { } else {
if (strcasecmp(argv[1], "output") == 0) { if (strcasecmp(argv[1], "output") == 0) {
if (!checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3)) { if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3))) {
return CMD_FAILURE; return error;
} }
struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); struct workspace_output *wso = calloc(1, sizeof(struct workspace_output));
sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]); 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) {
} }
} }
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static enum cmd_status cmd_ws_auto_back_and_forth(int argc, char **argv) { static struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {
if (!checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1)) { struct cmd_results *error = NULL;
return CMD_FAILURE; if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
return error;
} }
if (strcasecmp(argv[0], "yes") == 0) { if (strcasecmp(argv[0], "yes") == 0) {
config->auto_back_and_forth = true; config->auto_back_and_forth = true;
} else if (strcasecmp(argv[0], "no") == 0) { } else if (strcasecmp(argv[0], "no") == 0) {
config->auto_back_and_forth = false; config->auto_back_and_forth = false;
} else { } else {
return CMD_FAILURE; return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
} }
return CMD_SUCCESS; return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
/* Keep alphabetized */ /* Keep alphabetized */
@ -1171,8 +1203,11 @@ static struct cmd_handler *find_handler(char *line) {
return res; return res;
} }
enum cmd_status handle_command(char *_exec) { struct cmd_results *handle_command(char *_exec) {
enum cmd_status status = CMD_SUCCESS; // Even though this function will process multiple commands we will only
// return the last error, if any (for now). (Since we have access to an
// error string we could e.g. concatonate all errors there.)
struct cmd_results *results = NULL;
char *exec = strdup(_exec); char *exec = strdup(_exec);
char *head = exec; char *head = exec;
char *cmdlist; char *cmdlist;
@ -1188,8 +1223,7 @@ enum cmd_status handle_command(char *_exec) {
++head; ++head;
// TODO handle criteria // TODO handle criteria
} else { } else {
sway_log(L_ERROR, "Unmatched ["); results = cmd_results_new(CMD_INVALID, NULL, "Unmatched [");
status = CMD_INVALID;
} }
// Skip leading whitespace // Skip leading whitespace
head += strspn(head, whitespace); head += strspn(head, whitespace);
@ -1205,7 +1239,6 @@ enum cmd_status handle_command(char *_exec) {
sway_log(L_INFO, "Ignoring empty command."); sway_log(L_INFO, "Ignoring empty command.");
continue; continue;
} }
sway_log(L_INFO, "Handling command '%s'", cmd); sway_log(L_INFO, "Handling command '%s'", cmd);
//TODO better handling of argv //TODO better handling of argv
int argc; int argc;
@ -1214,37 +1247,54 @@ enum cmd_status handle_command(char *_exec) {
strip_quotes(argv[1]); strip_quotes(argv[1]);
} }
struct cmd_handler *handler = find_handler(argv[0]); struct cmd_handler *handler = find_handler(argv[0]);
enum cmd_status res = CMD_INVALID; if (!handler) {
if (!handler if (results) {
|| (res = handler->handle(argc-1, argv+1)) != CMD_SUCCESS) { free_cmd_results(results);
sway_log(L_ERROR, "Command '%s' failed", cmd); }
results = cmd_results_new(CMD_INVALID, strdup(cmd), "Unknown/invalid command");
free_argv(argc, argv); free_argv(argc, argv);
status = res; goto cleanup;
}
struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
if (results) {
free_cmd_results(results);
}
results = res;
goto cleanup; goto cleanup;
} }
free_argv(argc, argv); free_argv(argc, argv);
free_cmd_results(res);
} while(cmdlist); } while(cmdlist);
} while(head); } while(head);
cleanup: cleanup:
free(exec); free(exec);
return status; if (!results) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
return results;
} }
enum cmd_status config_command(char *exec) { struct cmd_results *config_command(char *exec) {
enum cmd_status status = CMD_SUCCESS; struct cmd_results *results = NULL;
int argc; int argc;
char **argv = split_args(exec, &argc); char **argv = split_args(exec, &argc);
if (!argc) goto cleanup; if (!argc) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
goto cleanup;
}
sway_log(L_INFO, "handling config command '%s'", exec); sway_log(L_INFO, "handling config command '%s'", exec);
// Endblock // Endblock
if (**argv == '}') { if (**argv == '}') {
status = CMD_BLOCK_END; results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
goto cleanup; goto cleanup;
} }
struct cmd_handler *handler = find_handler(argv[0]); struct cmd_handler *handler = find_handler(argv[0]);
if (!handler) { if (!handler) {
status = CMD_INVALID; char *input = argv[0] ? strdup(argv[0]) : "(empty)";
results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
goto cleanup; goto cleanup;
} }
int i; int i;
@ -1257,8 +1307,44 @@ enum cmd_status config_command(char *exec) {
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
strip_quotes(argv[1]); strip_quotes(argv[1]);
} }
status = handler->handle(argc-1, argv+1); results = handler->handle(argc-1, argv+1);
cleanup: cleanup:
free_argv(argc, argv); free_argv(argc, argv);
return status; return results;
}
struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
struct cmd_results *results = malloc(sizeof(struct cmd_results));
results->status = status;
results->input = input; // input is the command name
if (format) {
char *error = malloc(256);
va_list args;
va_start(args, format);
vsnprintf(error, 256, format, args);
va_end(args);
results->error = error;
} else {
results->error = NULL;
}
return results;
}
void free_cmd_results(struct cmd_results *results) {
if (results->error)
free(results->error);
free(results);
}
const char *cmd_results_to_json(struct cmd_results *results) {
json_object *root = json_object_new_object();
json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
if (results->input)
json_object_object_add(root, "input", json_object_new_string(results->input));
if (results->error)
json_object_object_add(root, "error", json_object_new_string(results->error));
const char *json = json_object_to_json_string(root);
free(root);
return json;
} }

View File

@ -234,10 +234,11 @@ bool read_config(FILE *file, bool is_active) {
while (!feof(file)) { while (!feof(file)) {
line = read_line(file); line = read_line(file);
line = strip_comments(line); line = strip_comments(line);
switch(config_command(line)) { struct cmd_results *res = config_command(line);
switch(res->status) {
case CMD_FAILURE: case CMD_FAILURE:
case CMD_INVALID: case CMD_INVALID:
sway_log(L_ERROR, "Error on line '%s'", line); sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
success = false; success = false;
break; break;
@ -270,6 +271,7 @@ bool read_config(FILE *file, bool is_active) {
default:; default:;
} }
free(line); free(line);
free(res);
} }
if (is_active) { if (is_active) {

View File

@ -341,7 +341,11 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
} }
if (match) { if (match) {
if (state == WLC_KEY_STATE_PRESSED) { if (state == WLC_KEY_STATE_PRESSED) {
handle_command(binding->command); struct cmd_results *res = handle_command(binding->command);
if (res->status != CMD_SUCCESS) {
sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error);
}
free_cmd_results(res);
return EVENT_HANDLED; return EVENT_HANDLED;
} else if (state == WLC_KEY_STATE_RELEASED) { } else if (state == WLC_KEY_STATE_RELEASED) {
// TODO: --released // TODO: --released
@ -544,8 +548,13 @@ static void handle_wlc_ready(void) {
// Execute commands until there are none left // Execute commands until there are none left
config->active = true; config->active = true;
while (config->cmd_queue->length) { while (config->cmd_queue->length) {
handle_command(config->cmd_queue->items[0]); char *line = config->cmd_queue->items[0];
free(config->cmd_queue->items[0]); struct cmd_results *res = handle_command(line);
if (res->status != CMD_SUCCESS) {
sway_log(L_ERROR, "Error on line '%s': %s", line, res->error);
}
free_cmd_results(res);
free(line);
list_del(config->cmd_queue, 0); list_del(config->cmd_queue, 0);
} }
} }

View File

@ -222,10 +222,12 @@ void ipc_client_handle_command(struct ipc_client *client) {
case IPC_COMMAND: case IPC_COMMAND:
{ {
buf[client->payload_length] = '\0'; buf[client->payload_length] = '\0';
bool success = (handle_command(buf) == CMD_SUCCESS); struct cmd_results *results = handle_command(buf);
char reply[64]; const char *json = cmd_results_to_json(results);
int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); char reply[256];
int length = snprintf(reply, sizeof(reply), "%s", json);
ipc_send_reply(client, reply, (uint32_t) length); ipc_send_reply(client, reply, (uint32_t) length);
free_cmd_results(results);
break; break;
} }
case IPC_GET_WORKSPACES: case IPC_GET_WORKSPACES: