Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this quote from src/commands_parser.c: We use a hand-written parser instead of lex/yacc because our commands are easy for humans, not for computers. Thus, it’s quite hard to specify a context-free grammar for the commands. A PEG grammar would be easier, but there’s downsides to every PEG parser generator I have come accross so far. This parser is basically a state machine which looks for literals or strings and can push either on a stack. After identifying a literal or string, it will either transition to the current state, to a different state, or call a function (like cmd_move()). Special care has been taken that error messages are useful and the code is well testable (when compiled with -DTEST_PARSER it will output to stdout instead of actually calling any function). During the migration phase (I plan to completely switch to this parser before 4.2 will be released), the new parser will parse every command you send to i3 and save the resulting call stack. Then, the old parser will parse your input and actually execute the commands. Afterwards, both call stacks will be compared and any differences will be logged. The new parser works with 100% of the test suite and produces identical call stacks.
This commit is contained in:
329
src/commands.c
329
src/commands.c
@ -8,6 +8,7 @@
|
||||
*
|
||||
*/
|
||||
#include <float.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "all.h"
|
||||
#include "cmdparse.tab.h"
|
||||
@ -60,6 +61,272 @@ static Output *get_output_from_string(Output *current_output, const char *output
|
||||
return output;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Helper functions for the migration testing. We let the new parser call every
|
||||
* function here and save the stack (current_match plus all parameters. Then we
|
||||
* let the old parser call every function and actually execute the code. When
|
||||
* there are differences between the first and the second invocation (or if
|
||||
* there has not been a first invocation at all), we generate an error.
|
||||
******************************************************************************/
|
||||
|
||||
static bool migration_test = false;
|
||||
typedef struct stackframe {
|
||||
Match match;
|
||||
int n_args;
|
||||
char *args[10];
|
||||
TAILQ_ENTRY(stackframe) stackframes;
|
||||
} stackframe;
|
||||
static TAILQ_HEAD(stackframes_head, stackframe) old_stackframes =
|
||||
TAILQ_HEAD_INITIALIZER(old_stackframes);
|
||||
static struct stackframes_head new_stackframes =
|
||||
TAILQ_HEAD_INITIALIZER(new_stackframes);
|
||||
/* We use this char* to uniquely terminate the list of parameters to save. */
|
||||
static char *last_parameter = "0";
|
||||
|
||||
void cmd_MIGRATION_enable() {
|
||||
migration_test = true;
|
||||
/* clear the current stack */
|
||||
while (!TAILQ_EMPTY(&old_stackframes)) {
|
||||
stackframe *current = TAILQ_FIRST(&old_stackframes);
|
||||
for (int c = 0; c < current->n_args; c++)
|
||||
if (current->args[c])
|
||||
free(current->args[c]);
|
||||
TAILQ_REMOVE(&old_stackframes, current, stackframes);
|
||||
free(current);
|
||||
}
|
||||
while (!TAILQ_EMPTY(&new_stackframes)) {
|
||||
stackframe *current = TAILQ_FIRST(&new_stackframes);
|
||||
for (int c = 0; c < current->n_args; c++)
|
||||
if (current->args[c])
|
||||
free(current->args[c]);
|
||||
TAILQ_REMOVE(&new_stackframes, current, stackframes);
|
||||
free(current);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_MIGRATION_disable() {
|
||||
migration_test = false;
|
||||
}
|
||||
|
||||
void cmd_MIGRATION_save_new_parameters(Match *current_match, ...) {
|
||||
va_list args;
|
||||
|
||||
DLOG("saving parameters.\n");
|
||||
stackframe *frame = scalloc(sizeof(stackframe));
|
||||
match_copy(&(frame->match), current_match);
|
||||
|
||||
/* All parameters are char*s */
|
||||
va_start(args, current_match);
|
||||
while (true) {
|
||||
char *parameter = va_arg(args, char*);
|
||||
if (parameter == last_parameter)
|
||||
break;
|
||||
DLOG("parameter = %s\n", parameter);
|
||||
if (parameter)
|
||||
frame->args[frame->n_args] = sstrdup(parameter);
|
||||
frame->n_args++;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
TAILQ_INSERT_TAIL(&new_stackframes, frame, stackframes);
|
||||
}
|
||||
|
||||
void cmd_MIGRATION_save_old_parameters(Match *current_match, ...) {
|
||||
va_list args;
|
||||
|
||||
DLOG("saving new parameters.\n");
|
||||
stackframe *frame = scalloc(sizeof(stackframe));
|
||||
match_copy(&(frame->match), current_match);
|
||||
|
||||
/* All parameters are char*s */
|
||||
va_start(args, current_match);
|
||||
while (true) {
|
||||
char *parameter = va_arg(args, char*);
|
||||
if (parameter == last_parameter)
|
||||
break;
|
||||
DLOG("parameter = %s\n", parameter);
|
||||
if (parameter)
|
||||
frame->args[frame->n_args] = sstrdup(parameter);
|
||||
frame->n_args++;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
TAILQ_INSERT_TAIL(&old_stackframes, frame, stackframes);
|
||||
}
|
||||
|
||||
static bool re_differ(struct regex *new, struct regex *old) {
|
||||
return ((new == NULL && old != NULL) ||
|
||||
(new != NULL && old == NULL) ||
|
||||
(new != NULL && old != NULL &&
|
||||
strcmp(new->pattern, old->pattern) != 0));
|
||||
}
|
||||
|
||||
static bool str_differ(char *new, char *old) {
|
||||
return ((new == NULL && old != NULL) ||
|
||||
(new != NULL && old == NULL) ||
|
||||
(new != NULL && old != NULL &&
|
||||
strcmp(new, old) != 0));
|
||||
}
|
||||
|
||||
static pid_t migration_pid = -1;
|
||||
|
||||
/*
|
||||
* Handler which will be called when we get a SIGCHLD for the nagbar, meaning
|
||||
* it exited (or could not be started, depending on the exit code).
|
||||
*
|
||||
*/
|
||||
static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
|
||||
ev_child_stop(EV_A_ watcher);
|
||||
if (!WIFEXITED(watcher->rstatus)) {
|
||||
fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int exitcode = WEXITSTATUS(watcher->rstatus);
|
||||
printf("i3-nagbar process exited with status %d\n", exitcode);
|
||||
if (exitcode == 2) {
|
||||
fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
|
||||
}
|
||||
|
||||
migration_pid = -1;
|
||||
}
|
||||
|
||||
/* We need ev >= 4 for the following code. Since it is not *that* important (it
|
||||
* only makes sure that there are no i3-nagbar instances left behind) we still
|
||||
* support old systems with libev 3. */
|
||||
#if EV_VERSION_MAJOR >= 4
|
||||
/*
|
||||
* Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
|
||||
* SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
|
||||
*
|
||||
*/
|
||||
static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
|
||||
if (migration_pid != -1) {
|
||||
LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", migration_pid);
|
||||
kill(migration_pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void cmd_MIGRATION_start_nagbar() {
|
||||
if (migration_pid != -1) {
|
||||
fprintf(stderr, "i3-nagbar already running.\n");
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Starting i3-nagbar, command parsing differs from expected output.\n");
|
||||
ELOG("Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile:\n");
|
||||
ELOG("i3-dump-log | gzip -9c > /tmp/i3.log.gz\n");
|
||||
ELOG("FYI: Your i3 version is " I3_VERSION "\n");
|
||||
migration_pid = fork();
|
||||
if (migration_pid == -1) {
|
||||
warn("Could not fork()");
|
||||
return;
|
||||
}
|
||||
|
||||
/* child */
|
||||
if (migration_pid == 0) {
|
||||
char *pageraction;
|
||||
sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
|
||||
char *argv[] = {
|
||||
NULL, /* will be replaced by the executable path */
|
||||
"-t",
|
||||
"error",
|
||||
"-m",
|
||||
"You found a parsing error. Please, please, please, report it!",
|
||||
"-b",
|
||||
"show errors",
|
||||
pageraction,
|
||||
NULL
|
||||
};
|
||||
exec_i3_utility("i3-nagbar", argv);
|
||||
}
|
||||
|
||||
/* parent */
|
||||
/* install a child watcher */
|
||||
ev_child *child = smalloc(sizeof(ev_child));
|
||||
ev_child_init(child, &nagbar_exited, migration_pid, 0);
|
||||
ev_child_start(main_loop, child);
|
||||
|
||||
/* We need ev >= 4 for the following code. Since it is not *that* important (it
|
||||
* only makes sure that there are no i3-nagbar instances left behind) we still
|
||||
* support old systems with libev 3. */
|
||||
#if EV_VERSION_MAJOR >= 4
|
||||
/* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
|
||||
* still running) */
|
||||
ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
|
||||
ev_cleanup_init(cleanup, nagbar_cleanup);
|
||||
ev_cleanup_start(main_loop, cleanup);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_MIGRATION_validate() {
|
||||
DLOG("validating the different stacks now\n");
|
||||
int old_count = 0;
|
||||
int new_count = 0;
|
||||
stackframe *current;
|
||||
TAILQ_FOREACH(current, &new_stackframes, stackframes)
|
||||
new_count++;
|
||||
TAILQ_FOREACH(current, &old_stackframes, stackframes)
|
||||
old_count++;
|
||||
if (new_count != old_count) {
|
||||
ELOG("FAILED, new_count == %d != old_count == %d\n", new_count, old_count);
|
||||
cmd_MIGRATION_start_nagbar();
|
||||
return;
|
||||
}
|
||||
DLOG("parameter count matching, comparing one by one...\n");
|
||||
|
||||
stackframe *new_frame = TAILQ_FIRST(&new_stackframes),
|
||||
*old_frame = TAILQ_FIRST(&old_stackframes);
|
||||
for (int i = 0; i < new_count; i++) {
|
||||
if (new_frame->match.dock != old_frame->match.dock ||
|
||||
new_frame->match.id != old_frame->match.id ||
|
||||
new_frame->match.con_id != old_frame->match.con_id ||
|
||||
new_frame->match.floating != old_frame->match.floating ||
|
||||
new_frame->match.insert_where != old_frame->match.insert_where ||
|
||||
re_differ(new_frame->match.title, old_frame->match.title) ||
|
||||
re_differ(new_frame->match.application, old_frame->match.application) ||
|
||||
re_differ(new_frame->match.class, old_frame->match.class) ||
|
||||
re_differ(new_frame->match.instance, old_frame->match.instance) ||
|
||||
re_differ(new_frame->match.mark, old_frame->match.mark) ||
|
||||
re_differ(new_frame->match.role, old_frame->match.role) ) {
|
||||
ELOG("FAILED, new_frame->match != old_frame->match (frame %d)\n", i);
|
||||
cmd_MIGRATION_start_nagbar();
|
||||
return;
|
||||
}
|
||||
if (new_frame->n_args != old_frame->n_args) {
|
||||
ELOG("FAILED, new_frame->n_args == %d != old_frame->n_args == %d (frame %d)\n",
|
||||
new_frame->n_args, old_frame->n_args, i);
|
||||
cmd_MIGRATION_start_nagbar();
|
||||
return;
|
||||
}
|
||||
for (int j = 0; j < new_frame->n_args; j++) {
|
||||
if (str_differ(new_frame->args[j], old_frame->args[j])) {
|
||||
ELOG("FAILED, new_frame->args[%d] == %s != old_frame->args[%d] == %s (frame %d)\n",
|
||||
j, new_frame->args[j], j, old_frame->args[j], i);
|
||||
cmd_MIGRATION_start_nagbar();
|
||||
return;
|
||||
}
|
||||
}
|
||||
new_frame = TAILQ_NEXT(new_frame, stackframes);
|
||||
old_frame = TAILQ_NEXT(old_frame, stackframes);
|
||||
}
|
||||
DLOG("OK\n");
|
||||
}
|
||||
|
||||
#define MIGRATION_init(x, ...) do { \
|
||||
if (migration_test) { \
|
||||
cmd_MIGRATION_save_new_parameters(current_match, __FUNCTION__, ##__VA_ARGS__ , last_parameter); \
|
||||
return NULL; \
|
||||
} else { \
|
||||
cmd_MIGRATION_save_old_parameters(current_match, __FUNCTION__, ##__VA_ARGS__ , last_parameter); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Criteria functions.
|
||||
******************************************************************************/
|
||||
|
||||
char *cmd_criteria_init(Match *current_match) {
|
||||
DLOG("Initializing criteria, current_match = %p\n", current_match);
|
||||
match_init(current_match);
|
||||
@ -79,6 +346,13 @@ char *cmd_criteria_init(Match *current_match) {
|
||||
char *cmd_criteria_match_windows(Match *current_match) {
|
||||
owindow *next, *current;
|
||||
|
||||
/* The same as MIGRATION_init, but doesn’t return */
|
||||
if (migration_test) {
|
||||
cmd_MIGRATION_save_new_parameters(current_match, __FUNCTION__, last_parameter);
|
||||
} else {
|
||||
cmd_MIGRATION_save_old_parameters(current_match, __FUNCTION__, last_parameter);
|
||||
}
|
||||
|
||||
DLOG("match specification finished, matching...\n");
|
||||
/* copy the old list head to iterate through it and start with a fresh
|
||||
* list which will contain only matching windows */
|
||||
@ -123,6 +397,13 @@ char *cmd_criteria_match_windows(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_criteria_add(Match *current_match, char *ctype, char *cvalue) {
|
||||
/* The same as MIGRATION_init, but doesn’t return */
|
||||
if (migration_test) {
|
||||
cmd_MIGRATION_save_new_parameters(current_match, __FUNCTION__, last_parameter);
|
||||
} else {
|
||||
cmd_MIGRATION_save_old_parameters(current_match, __FUNCTION__, last_parameter);
|
||||
}
|
||||
|
||||
DLOG("ctype=*%s*, cvalue=*%s*\n", ctype, cvalue);
|
||||
|
||||
if (strcmp(ctype, "class") == 0) {
|
||||
@ -189,6 +470,8 @@ char *cmd_criteria_add(Match *current_match, char *ctype, char *cvalue) {
|
||||
char *cmd_move_con_to_workspace(Match *current_match, char *which) {
|
||||
owindow *current;
|
||||
|
||||
MIGRATION_init(x, which);
|
||||
|
||||
DLOG("which=%s\n", which);
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
@ -220,6 +503,8 @@ char *cmd_move_con_to_workspace(Match *current_match, char *which) {
|
||||
}
|
||||
|
||||
char *cmd_move_con_to_workspace_name(Match *current_match, char *name) {
|
||||
MIGRATION_init(x, name);
|
||||
|
||||
if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
|
||||
LOG("You cannot switch to the i3 internal workspaces.\n");
|
||||
return sstrdup("{\"sucess\": false}");
|
||||
@ -250,6 +535,7 @@ char *cmd_move_con_to_workspace_name(Match *current_match, char *name) {
|
||||
}
|
||||
|
||||
char *cmd_resize(Match *current_match, char *way, char *direction, char *resize_px, char *resize_ppt) {
|
||||
MIGRATION_init(x, way, direction, resize_px, resize_ppt);
|
||||
/* resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt] */
|
||||
DLOG("resizing in way %s, direction %s, px %s or ppt %s\n", way, direction, resize_px, resize_ppt);
|
||||
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
|
||||
@ -348,6 +634,7 @@ char *cmd_resize(Match *current_match, char *way, char *direction, char *resize_
|
||||
}
|
||||
|
||||
char *cmd_border(Match *current_match, char *border_style_str) {
|
||||
MIGRATION_init(x, border_style_str);
|
||||
DLOG("border style should be changed to %s\n", border_style_str);
|
||||
owindow *current;
|
||||
|
||||
@ -381,6 +668,7 @@ char *cmd_border(Match *current_match, char *border_style_str) {
|
||||
}
|
||||
|
||||
char *cmd_nop(Match *current_match, char *comment) {
|
||||
MIGRATION_init(x, comment);
|
||||
LOG("-------------------------------------------------\n");
|
||||
LOG(" NOP: %s\n", comment);
|
||||
LOG("-------------------------------------------------\n");
|
||||
@ -389,6 +677,7 @@ char *cmd_nop(Match *current_match, char *comment) {
|
||||
}
|
||||
|
||||
char *cmd_append_layout(Match *current_match, char *path) {
|
||||
MIGRATION_init(x, path);
|
||||
LOG("Appending layout \"%s\"\n", path);
|
||||
tree_append_json(path);
|
||||
tree_render();
|
||||
@ -398,6 +687,7 @@ char *cmd_append_layout(Match *current_match, char *path) {
|
||||
}
|
||||
|
||||
char *cmd_workspace(Match *current_match, char *which) {
|
||||
MIGRATION_init(x, which);
|
||||
Con *ws;
|
||||
|
||||
DLOG("which=%s\n", which);
|
||||
@ -423,6 +713,7 @@ char *cmd_workspace(Match *current_match, char *which) {
|
||||
}
|
||||
|
||||
char *cmd_workspace_back_and_forth(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
workspace_back_and_forth();
|
||||
tree_render();
|
||||
|
||||
@ -431,6 +722,7 @@ char *cmd_workspace_back_and_forth(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_workspace_name(Match *current_match, char *name) {
|
||||
MIGRATION_init(x, name);
|
||||
if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
|
||||
LOG("You cannot switch to the i3 internal workspaces.\n");
|
||||
return sstrdup("{\"sucess\": false}");
|
||||
@ -459,6 +751,7 @@ char *cmd_workspace_name(Match *current_match, char *name) {
|
||||
}
|
||||
|
||||
char *cmd_mark(Match *current_match, char *mark) {
|
||||
MIGRATION_init(x, mark);
|
||||
DLOG("Clearing all windows which have that mark first\n");
|
||||
|
||||
Con *con;
|
||||
@ -484,6 +777,7 @@ char *cmd_mark(Match *current_match, char *mark) {
|
||||
}
|
||||
|
||||
char *cmd_mode(Match *current_match, char *mode) {
|
||||
MIGRATION_init(x, mode);
|
||||
DLOG("mode=%s\n", mode);
|
||||
switch_mode(mode);
|
||||
|
||||
@ -492,6 +786,7 @@ char *cmd_mode(Match *current_match, char *mode) {
|
||||
}
|
||||
|
||||
char *cmd_move_con_to_output(Match *current_match, char *name) {
|
||||
MIGRATION_init(x, name);
|
||||
owindow *current;
|
||||
|
||||
DLOG("should move window to output %s\n", name);
|
||||
@ -543,6 +838,7 @@ char *cmd_move_con_to_output(Match *current_match, char *name) {
|
||||
}
|
||||
|
||||
char *cmd_floating(Match *current_match, char *floating_mode) {
|
||||
MIGRATION_init(x, floating_mode);
|
||||
owindow *current;
|
||||
|
||||
DLOG("floating_mode=%s\n", floating_mode);
|
||||
@ -571,6 +867,7 @@ char *cmd_floating(Match *current_match, char *floating_mode) {
|
||||
}
|
||||
|
||||
char *cmd_move_workspace_to_output(Match *current_match, char *name) {
|
||||
MIGRATION_init(x, name);
|
||||
DLOG("should move workspace to output %s\n", name);
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
@ -619,6 +916,7 @@ char *cmd_move_workspace_to_output(Match *current_match, char *name) {
|
||||
}
|
||||
|
||||
char *cmd_split(Match *current_match, char *direction) {
|
||||
MIGRATION_init(x, direction);
|
||||
/* TODO: use matches */
|
||||
LOG("splitting in direction %c\n", direction[0]);
|
||||
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
|
||||
@ -630,12 +928,15 @@ char *cmd_split(Match *current_match, char *direction) {
|
||||
}
|
||||
|
||||
char *cmd_kill(Match *current_match, char *kill_mode_str) {
|
||||
if (kill_mode_str == NULL)
|
||||
kill_mode_str = "window";
|
||||
MIGRATION_init(x, kill_mode_str);
|
||||
owindow *current;
|
||||
|
||||
DLOG("kill_mode=%s\n", kill_mode_str);
|
||||
|
||||
int kill_mode;
|
||||
if (kill_mode_str == NULL || strcmp(kill_mode_str, "window") == 0)
|
||||
if (strcmp(kill_mode_str, "window") == 0)
|
||||
kill_mode = KILL_WINDOW;
|
||||
else if (strcmp(kill_mode_str, "client") == 0)
|
||||
kill_mode = KILL_CLIENT;
|
||||
@ -661,6 +962,7 @@ char *cmd_kill(Match *current_match, char *kill_mode_str) {
|
||||
}
|
||||
|
||||
char *cmd_exec(Match *current_match, char *nosn, char *command) {
|
||||
MIGRATION_init(x, nosn, command);
|
||||
bool no_startup_id = (nosn != NULL);
|
||||
|
||||
DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
|
||||
@ -671,6 +973,7 @@ char *cmd_exec(Match *current_match, char *nosn, char *command) {
|
||||
}
|
||||
|
||||
char *cmd_focus_direction(Match *current_match, char *direction) {
|
||||
MIGRATION_init(x, direction);
|
||||
if (focused &&
|
||||
focused->type != CT_WORKSPACE &&
|
||||
focused->fullscreen_mode != CF_NONE) {
|
||||
@ -700,6 +1003,7 @@ char *cmd_focus_direction(Match *current_match, char *direction) {
|
||||
}
|
||||
|
||||
char *cmd_focus_window_mode(Match *current_match, char *window_mode) {
|
||||
MIGRATION_init(x, window_mode);
|
||||
if (focused &&
|
||||
focused->type != CT_WORKSPACE &&
|
||||
focused->fullscreen_mode != CF_NONE) {
|
||||
@ -735,6 +1039,7 @@ char *cmd_focus_window_mode(Match *current_match, char *window_mode) {
|
||||
}
|
||||
|
||||
char *cmd_focus_level(Match *current_match, char *level) {
|
||||
MIGRATION_init(x, level);
|
||||
if (focused &&
|
||||
focused->type != CT_WORKSPACE &&
|
||||
focused->fullscreen_mode != CF_NONE) {
|
||||
@ -755,6 +1060,7 @@ char *cmd_focus_level(Match *current_match, char *level) {
|
||||
}
|
||||
|
||||
char *cmd_focus(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
DLOG("current_match = %p\n", current_match);
|
||||
if (focused &&
|
||||
focused->type != CT_WORKSPACE &&
|
||||
@ -769,14 +1075,12 @@ char *cmd_focus(Match *current_match) {
|
||||
ELOG("You have to specify which window/container should be focused.\n");
|
||||
ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
|
||||
|
||||
// TODO: json output
|
||||
char *json_output;
|
||||
sasprintf(&json_output, "{\"success\":false, \"error\":\"You have to "
|
||||
"specify which window/container should be focused\"}");
|
||||
return json_output;
|
||||
}
|
||||
|
||||
LOG("here");
|
||||
int count = 0;
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
Con *ws = con_get_workspace(current->con);
|
||||
@ -784,7 +1088,6 @@ char *cmd_focus(Match *current_match) {
|
||||
* Just skip it, you cannot focus dock windows. */
|
||||
if (!ws)
|
||||
continue;
|
||||
LOG("there");
|
||||
|
||||
/* If the container is not on the current workspace,
|
||||
* workspace_show() will switch to a different workspace and (if
|
||||
@ -820,6 +1123,9 @@ char *cmd_focus(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_fullscreen(Match *current_match, char *fullscreen_mode) {
|
||||
if (fullscreen_mode == NULL)
|
||||
fullscreen_mode = "output";
|
||||
MIGRATION_init(x, fullscreen_mode);
|
||||
DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
|
||||
owindow *current;
|
||||
|
||||
@ -827,7 +1133,7 @@ char *cmd_fullscreen(Match *current_match, char *fullscreen_mode) {
|
||||
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
printf("matching: %p / %s\n", current->con, current->con->name);
|
||||
con_toggle_fullscreen(current->con, (fullscreen_mode && strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
|
||||
con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
|
||||
}
|
||||
|
||||
tree_render();
|
||||
@ -837,6 +1143,7 @@ char *cmd_fullscreen(Match *current_match, char *fullscreen_mode) {
|
||||
}
|
||||
|
||||
char *cmd_move_direction(Match *current_match, char *direction, char *move_px) {
|
||||
MIGRATION_init(x, direction, move_px);
|
||||
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
|
||||
int px = atoi(move_px);
|
||||
|
||||
@ -869,10 +1176,13 @@ char *cmd_move_direction(Match *current_match, char *direction, char *move_px) {
|
||||
}
|
||||
|
||||
char *cmd_layout(Match *current_match, char *layout_str) {
|
||||
if (strcmp(layout_str, "stacking") == 0)
|
||||
layout_str = "stacked";
|
||||
MIGRATION_init(x, layout_str);
|
||||
DLOG("changing layout to %s\n", layout_str);
|
||||
owindow *current;
|
||||
int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
|
||||
(strcmp(layout_str, "stacked") == 0 || strcmp(layout_str, "stacking") == 0 ? L_STACKED :
|
||||
(strcmp(layout_str, "stacked") == 0 ? L_STACKED :
|
||||
L_TABBED));
|
||||
|
||||
/* check if the match is empty, not if the result is empty */
|
||||
@ -892,6 +1202,7 @@ char *cmd_layout(Match *current_match, char *layout_str) {
|
||||
}
|
||||
|
||||
char *cmd_exit(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
LOG("Exiting due to user command.\n");
|
||||
exit(0);
|
||||
|
||||
@ -899,6 +1210,7 @@ char *cmd_exit(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_reload(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
LOG("reloading\n");
|
||||
kill_configerror_nagbar(false);
|
||||
load_configuration(conn, NULL, true);
|
||||
@ -911,6 +1223,7 @@ char *cmd_reload(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_restart(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
LOG("restarting i3\n");
|
||||
i3_restart(false);
|
||||
|
||||
@ -919,6 +1232,7 @@ char *cmd_restart(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_open(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
LOG("opening new container\n");
|
||||
Con *con = tree_open_con(NULL, NULL);
|
||||
con_focus(con);
|
||||
@ -931,6 +1245,7 @@ char *cmd_open(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_focus_output(Match *current_match, char *name) {
|
||||
MIGRATION_init(x, name);
|
||||
owindow *current;
|
||||
|
||||
DLOG("name = %s\n", name);
|
||||
@ -966,6 +1281,7 @@ char *cmd_focus_output(Match *current_match, char *name) {
|
||||
}
|
||||
|
||||
char *cmd_move_scratchpad(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
DLOG("should move window to scratchpad\n");
|
||||
owindow *current;
|
||||
|
||||
@ -983,6 +1299,7 @@ char *cmd_move_scratchpad(Match *current_match) {
|
||||
}
|
||||
|
||||
char *cmd_scratchpad_show(Match *current_match) {
|
||||
MIGRATION_init(x);
|
||||
DLOG("should show scratchpad window\n");
|
||||
owindow *current;
|
||||
|
||||
|
Reference in New Issue
Block a user