Send proper error messages upon parser failures, use yajl for generating command replies

Fixes: #693
This commit is contained in:
Michael Stapelberg 2012-05-02 22:01:50 +02:00
parent 87cb664236
commit bbe607899c
8 changed files with 192 additions and 108 deletions

View File

@ -35,6 +35,9 @@
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
#include <yajl/yajl_gen.h>
#include <yajl/yajl_version.h>
/* Contains compatibility definitions for old libxcb versions */ /* Contains compatibility definitions for old libxcb versions */
#ifdef XCB_COMPAT #ifdef XCB_COMPAT
#include "xcb_compat.h" #include "xcb_compat.h"

View File

@ -10,6 +10,8 @@
#ifndef _COMMANDS_PARSER_H #ifndef _COMMANDS_PARSER_H
#define _COMMANDS_PARSER_H #define _COMMANDS_PARSER_H
#include <yajl/yajl_gen.h>
/* /*
* Holds the result of a call to any command. When calling * Holds the result of a call to any command. When calling
* parse_command("floating enable, border none"), the parser will internally * parse_command("floating enable, border none"), the parser will internally
@ -20,8 +22,8 @@
* *
*/ */
struct CommandResult { struct CommandResult {
/* The JSON-serialized output of this command. */ /* The JSON generator to append a reply to. */
char *json_output; yajl_gen json_gen;
/* Whether the command requires calling tree_render. */ /* Whether the command requires calling tree_render. */
bool needs_tree_render; bool needs_tree_render;

View File

@ -49,7 +49,7 @@ void run_assignments(i3Window *window) {
if (command_output->needs_tree_render) if (command_output->needs_tree_render)
needs_tree_render = true; needs_tree_render = true;
free(command_output->json_output); yajl_gen_free(command_output->json_gen);
} }
/* Store that we ran this assignment to not execute it again */ /* Store that we ran this assignment to not execute it again */

View File

@ -12,6 +12,16 @@
#include "all.h" #include "all.h"
// Macros to make the YAJL API a bit easier to use.
#define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__)
#define ystr(str) yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str))
#define ysuccess(success) do { \
y(map_open); \
ystr("success"); \
y(bool, success); \
y(map_close); \
} while (0)
/** When the command did not include match criteria (!), we use the currently /** When the command did not include match criteria (!), we use the currently
* focused command. Do not confuse this case with a command which included * focused command. Do not confuse this case with a command which included
* criteria but which did not match any windows. This macro has to be called in * criteria but which did not match any windows. This macro has to be called in
@ -341,7 +351,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
ws = workspace_prev_on_output(); ws = workspace_prev_on_output();
else { else {
ELOG("BUG: called with which=%s\n", which); ELOG("BUG: called with which=%s\n", which);
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -352,7 +362,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -362,7 +372,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
void cmd_move_con_to_workspace_name(I3_CMD, char *name) { void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) { if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
LOG("You cannot switch to the i3 internal workspaces.\n"); LOG("You cannot switch to the i3 internal workspaces.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -371,7 +381,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
/* Error out early to not create a non-existing workspace (in /* Error out early to not create a non-existing workspace (in
* workspace_get()) if we are not actually able to move anything. */ * workspace_get()) if we are not actually able to move anything. */
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) { if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -388,7 +398,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -401,7 +411,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
/* Error out early to not create a non-existing workspace (in /* Error out early to not create a non-existing workspace (in
* workspace_get()) if we are not actually able to move anything. */ * workspace_get()) if we are not actually able to move anything. */
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) { if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -416,8 +426,13 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
parsed_num < 0 || parsed_num < 0 ||
*endptr != '\0') { *endptr != '\0') {
LOG("Could not parse \"%s\" as a number.\n", which); LOG("Could not parse \"%s\" as a number.\n", which);
cmd_output->json_output = sstrdup("{\"success\": false, " y(map_open);
"\"error\": \"Could not parse number\"}"); ystr("success");
y(bool, false);
ystr("error");
// TODO: better error message
ystr("Could not parse number");
y(map_close);
return; return;
} }
@ -426,8 +441,13 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
child->num == parsed_num); child->num == parsed_num);
if (!workspace) { if (!workspace) {
cmd_output->json_output = sstrdup("{\"success\": false, " y(map_open);
"\"error\": \"No such workspace\"}"); ystr("success");
y(bool, false);
ystr("error");
// TODO: better error message
ystr("No such workspace");
y(map_close);
return; return;
} }
@ -440,7 +460,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) { static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
@ -490,7 +510,7 @@ static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
(strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
(orientation == HORIZ ? "horizontal" : "vertical")); (orientation == HORIZ ? "horizontal" : "vertical"));
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -501,7 +521,7 @@ static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
} }
if (other == TAILQ_END(workspaces)) { if (other == TAILQ_END(workspaces)) {
LOG("No other container in this direction found, cannot resize.\n"); LOG("No other container in this direction found, cannot resize.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
LOG("other->percent = %f\n", other->percent); LOG("other->percent = %f\n", other->percent);
@ -558,13 +578,13 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
strcmp(direction, "width") == 0)) { strcmp(direction, "width") == 0)) {
LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
(orientation == HORIZ ? "horizontal" : "vertical")); (orientation == HORIZ ? "horizontal" : "vertical"));
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
if (children == 1) { if (children == 1) {
LOG("This is the only container, cannot resize.\n"); LOG("This is the only container, cannot resize.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -587,13 +607,13 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i
continue; continue;
if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) { if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent); LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent);
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
} }
if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) { if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) {
LOG("Not resizing, already at minimum size\n"); LOG("Not resizing, already at minimum size\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -635,7 +655,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -663,7 +683,7 @@ void cmd_border(I3_CMD, char *border_style_str) {
border_style = BS_1PIXEL; border_style = BS_1PIXEL;
else { else {
ELOG("BUG: called with border_style=%s\n", border_style_str); ELOG("BUG: called with border_style=%s\n", border_style_str);
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
} }
@ -672,7 +692,7 @@ void cmd_border(I3_CMD, char *border_style_str) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -695,7 +715,7 @@ void cmd_append_layout(I3_CMD, char *path) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -717,7 +737,7 @@ void cmd_workspace(I3_CMD, char *which) {
ws = workspace_prev_on_output(); ws = workspace_prev_on_output();
else { else {
ELOG("BUG: called with which=%s\n", which); ELOG("BUG: called with which=%s\n", which);
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -725,7 +745,7 @@ void cmd_workspace(I3_CMD, char *which) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -742,8 +762,14 @@ void cmd_workspace_number(I3_CMD, char *which) {
parsed_num < 0 || parsed_num < 0 ||
*endptr != '\0') { *endptr != '\0') {
LOG("Could not parse \"%s\" as a number.\n", which); LOG("Could not parse \"%s\" as a number.\n", which);
cmd_output->json_output = sstrdup("{\"success\": false, " y(map_open);
"\"error\": \"Could not parse number\"}"); ystr("success");
y(bool, false);
ystr("error");
// TODO: better error message
ystr("Could not parse number");
y(map_close);
return; return;
} }
@ -753,8 +779,12 @@ void cmd_workspace_number(I3_CMD, char *which) {
if (!workspace) { if (!workspace) {
LOG("There is no workspace with number %d.\n", parsed_num); LOG("There is no workspace with number %d.\n", parsed_num);
cmd_output->json_output = sstrdup("{\"success\": false, " y(map_open);
"\"error\": \"No such workspace\"}"); ystr("success");
y(bool, false);
ystr("error");
ystr("No such workspace");
y(map_close);
return; return;
} }
@ -762,7 +792,7 @@ void cmd_workspace_number(I3_CMD, char *which) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -774,7 +804,7 @@ void cmd_workspace_back_and_forth(I3_CMD) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -784,7 +814,7 @@ void cmd_workspace_back_and_forth(I3_CMD) {
void cmd_workspace_name(I3_CMD, char *name) { void cmd_workspace_name(I3_CMD, char *name) {
if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) { if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
LOG("You cannot switch to the i3 internal workspaces.\n"); LOG("You cannot switch to the i3 internal workspaces.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -799,7 +829,7 @@ void cmd_workspace_name(I3_CMD, char *name) {
workspace_back_and_forth(); workspace_back_and_forth();
tree_render(); tree_render();
} }
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -807,7 +837,7 @@ void cmd_workspace_name(I3_CMD, char *name) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -835,7 +865,7 @@ void cmd_mark(I3_CMD, char *mark) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -847,7 +877,7 @@ void cmd_mode(I3_CMD, char *mode) {
switch_mode(mode); switch_mode(mode);
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -885,7 +915,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
if (!output) { if (!output) {
LOG("No such output found.\n"); LOG("No such output found.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -893,7 +923,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
Con *ws = NULL; Con *ws = NULL;
GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
if (!ws) { if (!ws) {
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -904,7 +934,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -935,7 +965,7 @@ void cmd_floating(I3_CMD, char *floating_mode) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -954,7 +984,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
Output *output = get_output_from_string(current_output, name); Output *output = get_output_from_string(current_output, name);
if (!output) { if (!output) {
LOG("No such output\n"); LOG("No such output\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1025,7 +1055,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1039,7 +1069,7 @@ void cmd_split(I3_CMD, char *direction) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1060,7 +1090,7 @@ void cmd_kill(I3_CMD, char *kill_mode_str) {
kill_mode = KILL_CLIENT; kill_mode = KILL_CLIENT;
else { else {
ELOG("BUG: called with kill_mode=%s\n", kill_mode_str); ELOG("BUG: called with kill_mode=%s\n", kill_mode_str);
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1076,7 +1106,7 @@ void cmd_kill(I3_CMD, char *kill_mode_str) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1090,7 +1120,7 @@ void cmd_exec(I3_CMD, char *nosn, char *command) {
start_application(command, no_startup_id); start_application(command, no_startup_id);
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1102,7 +1132,7 @@ void cmd_focus_direction(I3_CMD, char *direction) {
focused->type != CT_WORKSPACE && focused->type != CT_WORKSPACE &&
focused->fullscreen_mode != CF_NONE) { focused->fullscreen_mode != CF_NONE) {
LOG("Cannot change focus while in fullscreen mode.\n"); LOG("Cannot change focus while in fullscreen mode.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1118,13 +1148,13 @@ void cmd_focus_direction(I3_CMD, char *direction) {
tree_next('n', VERT); tree_next('n', VERT);
else { else {
ELOG("Invalid focus direction (%s)\n", direction); ELOG("Invalid focus direction (%s)\n", direction);
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1136,7 +1166,7 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
focused->type != CT_WORKSPACE && focused->type != CT_WORKSPACE &&
focused->fullscreen_mode != CF_NONE) { focused->fullscreen_mode != CF_NONE) {
LOG("Cannot change focus while in fullscreen mode.\n"); LOG("Cannot change focus while in fullscreen mode.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1163,7 +1193,7 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1175,7 +1205,7 @@ void cmd_focus_level(I3_CMD, char *level) {
focused->type != CT_WORKSPACE && focused->type != CT_WORKSPACE &&
focused->fullscreen_mode != CF_NONE) { focused->fullscreen_mode != CF_NONE) {
LOG("Cannot change focus while in fullscreen mode.\n"); LOG("Cannot change focus while in fullscreen mode.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1187,7 +1217,7 @@ void cmd_focus_level(I3_CMD, char *level) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1200,7 +1230,7 @@ void cmd_focus(I3_CMD) {
focused->type != CT_WORKSPACE && focused->type != CT_WORKSPACE &&
focused->fullscreen_mode != CF_NONE) { focused->fullscreen_mode != CF_NONE) {
LOG("Cannot change focus while in fullscreen mode.\n"); LOG("Cannot change focus while in fullscreen mode.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1210,9 +1240,13 @@ void cmd_focus(I3_CMD) {
ELOG("You have to specify which window/container should be focused.\n"); ELOG("You have to specify which window/container should be focused.\n");
ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n"); ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
sasprintf(&(cmd_output->json_output), y(map_open);
"{\"success\":false, \"error\":\"You have to " ystr("success");
"specify which window/container should be focused\"}"); y(bool, false);
ystr("error");
ystr("You have to specify which window/container should be focused");
y(map_close);
return; return;
} }
@ -1253,7 +1287,7 @@ void cmd_focus(I3_CMD) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1275,7 +1309,7 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1310,7 +1344,7 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
} }
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1338,7 +1372,7 @@ void cmd_layout(I3_CMD, char *layout_str) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1365,7 +1399,7 @@ void cmd_reload(I3_CMD) {
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1377,7 +1411,7 @@ void cmd_restart(I3_CMD) {
i3_restart(false); i3_restart(false);
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1388,8 +1422,13 @@ void cmd_open(I3_CMD) {
LOG("opening new container\n"); LOG("opening new container\n");
Con *con = tree_open_con(NULL, NULL); Con *con = tree_open_con(NULL, NULL);
con_focus(con); con_focus(con);
sasprintf(&(cmd_output->json_output),
"{\"success\":true, \"id\":%ld}", (long int)con); y(map_open);
ystr("success");
y(bool, true);
ystr("id");
y(integer, (long int)con);
y(map_close);
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
} }
@ -1417,7 +1456,7 @@ void cmd_focus_output(I3_CMD, char *name) {
if (!output) { if (!output) {
LOG("No such output found.\n"); LOG("No such output found.\n");
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1425,7 +1464,7 @@ void cmd_focus_output(I3_CMD, char *name) {
Con *ws = NULL; Con *ws = NULL;
GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child)); GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
if (!ws) { if (!ws) {
cmd_output->json_output = sstrdup("{\"success\": false}"); ysuccess(false);
return; return;
} }
@ -1433,7 +1472,7 @@ void cmd_focus_output(I3_CMD, char *name) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1447,9 +1486,12 @@ void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
if (!con_is_floating(focused)) { if (!con_is_floating(focused)) {
ELOG("Cannot change position. The window/container is not floating\n"); ELOG("Cannot change position. The window/container is not floating\n");
sasprintf(&(cmd_output->json_output), y(map_open);
"{\"success\":false, \"error\":\"Cannot change position. " ystr("success");
"The window/container is not floating.\"}"); y(bool, false);
ystr("error");
ystr("Cannot change position. The window/container is not floating.");
y(map_close);
return; return;
} }
@ -1473,7 +1515,7 @@ void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
} }
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1484,9 +1526,12 @@ void cmd_move_window_to_center(I3_CMD, char *method) {
if (!con_is_floating(focused)) { if (!con_is_floating(focused)) {
ELOG("Cannot change position. The window/container is not floating\n"); ELOG("Cannot change position. The window/container is not floating\n");
sasprintf(&(cmd_output->json_output), y(map_open);
"{\"success\":false, \"error\":\"Cannot change position. " ystr("success");
"The window/container is not floating.\"}"); y(bool, false);
ystr("error");
ystr("Cannot change position. The window/container is not floating.");
y(map_close);
} }
if (strcmp(method, "absolute") == 0) { if (strcmp(method, "absolute") == 0) {
@ -1512,7 +1557,7 @@ void cmd_move_window_to_center(I3_CMD, char *method) {
} }
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1532,7 +1577,7 @@ void cmd_move_scratchpad(I3_CMD) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1554,7 +1599,7 @@ void cmd_scratchpad_show(I3_CMD) {
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply // XXX: default reply for now, make this a better reply
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
} }
/* /*
@ -1572,8 +1617,13 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
if (!workspace) { if (!workspace) {
// TODO: we should include the old workspace name here and use yajl for // TODO: we should include the old workspace name here and use yajl for
// generating the reply. // generating the reply.
cmd_output->json_output = sstrdup("{\"success\": false, " y(map_open);
"\"error\":\"Old workspace not found\"}"); ystr("success");
y(bool, false);
ystr("error");
// TODO: better error message
ystr("Old workspace not found");
y(map_close);
return; return;
} }
@ -1585,8 +1635,13 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
if (check_dest != NULL) { if (check_dest != NULL) {
// TODO: we should include the new workspace name here and use yajl for // TODO: we should include the new workspace name here and use yajl for
// generating the reply. // generating the reply.
cmd_output->json_output = sstrdup("{\"success\": false, " y(map_open);
"\"error\":\"New workspace already exists\"}"); ystr("success");
y(bool, false);
ystr("error");
// TODO: better error message
ystr("New workspace already exists");
y(map_close);
return; return;
} }
@ -1612,7 +1667,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
con_focus(previously_focused); con_focus(previously_focused);
cmd_output->needs_tree_render = true; cmd_output->needs_tree_render = true;
cmd_output->json_output = sstrdup("{\"success\": true}"); ysuccess(true);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
} }

View File

@ -32,6 +32,10 @@
#include "all.h" #include "all.h"
// Macros to make the YAJL API a bit easier to use.
#define y(x, ...) yajl_gen_ ## x (command_output.json_gen, ##__VA_ARGS__)
#define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char*)str, strlen(str))
/******************************************************************************* /*******************************************************************************
* The data structures used for parsing. Essentially the current state and a * The data structures used for parsing. Essentially the current state and a
* list of tokens for that state. * list of tokens for that state.
@ -185,20 +189,9 @@ static void next_state(const cmdp_token *token) {
if (token->next_state == __CALL) { if (token->next_state == __CALL) {
DLOG("should call stuff, yay. call_id = %d\n", DLOG("should call stuff, yay. call_id = %d\n",
token->extra.call_identifier); token->extra.call_identifier);
subcommand_output.json_output = NULL; subcommand_output.json_gen = command_output.json_gen;
subcommand_output.needs_tree_render = false; subcommand_output.needs_tree_render = false;
GENERATED_call(token->extra.call_identifier, &subcommand_output); GENERATED_call(token->extra.call_identifier, &subcommand_output);
if (subcommand_output.json_output) {
DLOG("Subcommand JSON output: %s\n", subcommand_output.json_output);
char *buffer;
/* In the beginning, the contents of json_output are "[\0". */
if (command_output.json_output[1] == '\0')
sasprintf(&buffer, "%s%s", command_output.json_output, subcommand_output.json_output);
else sasprintf(&buffer, "%s, %s", command_output.json_output, subcommand_output.json_output);
free(command_output.json_output);
command_output.json_output = buffer;
DLOG("merged command JSON output: %s\n", command_output.json_output);
}
/* If any subcommand requires a tree_render(), we need to make the /* If any subcommand requires a tree_render(), we need to make the
* whole parser result request a tree_render(). */ * whole parser result request a tree_render(). */
if (subcommand_output.needs_tree_render) if (subcommand_output.needs_tree_render)
@ -217,7 +210,15 @@ static void next_state(const cmdp_token *token) {
struct CommandResult *parse_command(const char *input) { struct CommandResult *parse_command(const char *input) {
DLOG("new parser handling: %s\n", input); DLOG("new parser handling: %s\n", input);
state = INITIAL; state = INITIAL;
command_output.json_output = sstrdup("[");
/* A YAJL JSON generator used for formatting replies. */
#if YAJL_MAJOR >= 2
command_output.json_gen = yajl_gen_alloc(NULL);
#else
command_output.json_gen = yajl_gen_alloc(NULL, NULL);
#endif
y(array_open);
command_output.needs_tree_render = false; command_output.needs_tree_render = false;
const char *walk = input; const char *walk = input;
@ -392,6 +393,18 @@ struct CommandResult *parse_command(const char *input) {
printf("Your command: %s\n", input); printf("Your command: %s\n", input);
printf(" %s\n", position); printf(" %s\n", position);
/* Format this error message as a JSON reply. */
y(map_open);
ystr("success");
y(bool, false);
ystr("error");
ystr(errormessage);
ystr("input");
ystr(input);
ystr("errorposition");
ystr(position);
y(map_close);
free(position); free(position);
free(errormessage); free(errormessage);
clear_stack(); clear_stack();
@ -399,11 +412,8 @@ struct CommandResult *parse_command(const char *input) {
} }
} }
char *buffer; y(array_close);
sasprintf(&buffer, "%s]", command_output.json_output);
free(command_output.json_output);
command_output.json_output = buffer;
DLOG("command_output.json_output = %s\n", command_output.json_output);
DLOG("command_output.needs_tree_render = %d\n", command_output.needs_tree_render); DLOG("command_output.needs_tree_render = %d\n", command_output.needs_tree_render);
return &command_output; return &command_output;
} }

View File

@ -124,7 +124,7 @@ static void handle_key_press(xcb_key_press_event_t *event) {
if (command_output->needs_tree_render) if (command_output->needs_tree_render)
tree_render(); tree_render();
free(command_output->json_output); yajl_gen_free(command_output->json_gen);
} }
/* /*

View File

@ -125,12 +125,18 @@ IPC_HANDLER(command) {
if (command_output->needs_tree_render) if (command_output->needs_tree_render)
tree_render(); tree_render();
/* If no reply was provided, we just use the default success message */ const unsigned char *reply;
ipc_send_message(fd, strlen(command_output->json_output), #if YAJL_MAJOR >= 2
I3_IPC_REPLY_TYPE_COMMAND, size_t length;
(const uint8_t*)command_output->json_output); #else
unsigned int length;
#endif
yajl_gen_get_buf(command_output->json_gen, &reply, &length);
free(command_output->json_output); ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
(const uint8_t*)reply);
yajl_gen_free(command_output->json_gen);
} }
static void dump_rect(yajl_gen gen, const char *name, Rect r) { static void dump_rect(yajl_gen gen, const char *name, Rect r) {

View File

@ -63,4 +63,12 @@ cmd 'move gibberish' for (0 .. 10);
does_i3_live; does_i3_live;
################################################################################
# regression test: an invalid command should come back with an error.
################################################################################
my $reply = cmd 'bullshit-command-which-we-never-implement meh';
is(scalar @$reply, 1, 'got one command reply');
ok(!$reply->[0]->{success}, 'reply has success == false');
done_testing; done_testing;