]> git.sur5r.net Git - i3/i3/blob - src/commands.c
Merge branch 'master' into next
[i3/i3] / src / commands.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * commands.c: all command functions (see commands_parser.c)
8  *
9  */
10 #include <float.h>
11 #include <stdarg.h>
12
13 #include "all.h"
14 #include "cmdparse.tab.h"
15
16 /** When the command did not include match criteria (!), we use the currently
17  * focused command. Do not confuse this case with a command which included
18  * criteria but which did not match any windows. This macro has to be called in
19  * every command.
20  */
21 #define HANDLE_EMPTY_MATCH do { \
22     if (match_is_empty(current_match)) { \
23         owindow *ow = smalloc(sizeof(owindow)); \
24         ow->con = focused; \
25         TAILQ_INIT(&owindows); \
26         TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
27     } \
28 } while (0)
29
30 static owindows_head owindows;
31
32 /*
33  * Returns true if a is definitely greater than b (using the given epsilon)
34  *
35  */
36 static bool definitelyGreaterThan(float a, float b, float epsilon) {
37     return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
38 }
39
40 static Output *get_output_from_string(Output *current_output, const char *output_str) {
41     Output *output;
42
43     if (strcasecmp(output_str, "left") == 0) {
44         output = get_output_next(D_LEFT, current_output);
45         if (!output)
46             output = get_output_most(D_RIGHT, current_output);
47     } else if (strcasecmp(output_str, "right") == 0) {
48         output = get_output_next(D_RIGHT, current_output);
49         if (!output)
50             output = get_output_most(D_LEFT, current_output);
51     } else if (strcasecmp(output_str, "up") == 0) {
52         output = get_output_next(D_UP, current_output);
53         if (!output)
54             output = get_output_most(D_DOWN, current_output);
55     } else if (strcasecmp(output_str, "down") == 0) {
56         output = get_output_next(D_DOWN, current_output);
57         if (!output)
58             output = get_output_most(D_UP, current_output);
59     } else output = get_output_by_name(output_str);
60
61     return output;
62 }
63
64 /*******************************************************************************
65  * Helper functions for the migration testing. We let the new parser call every
66  * function here and save the stack (current_match plus all parameters. Then we
67  * let the old parser call every function and actually execute the code. When
68  * there are differences between the first and the second invocation (or if
69  * there has not been a first invocation at all), we generate an error.
70  ******************************************************************************/
71
72 static bool migration_test = false;
73 typedef struct stackframe {
74     Match match;
75     int n_args;
76     char *args[10];
77     TAILQ_ENTRY(stackframe) stackframes;
78 } stackframe;
79 static TAILQ_HEAD(stackframes_head, stackframe) old_stackframes =
80   TAILQ_HEAD_INITIALIZER(old_stackframes);
81 static struct stackframes_head new_stackframes =
82   TAILQ_HEAD_INITIALIZER(new_stackframes);
83 /* We use this char* to uniquely terminate the list of parameters to save. */
84 static char *last_parameter = "0";
85
86 void cmd_MIGRATION_enable() {
87     migration_test = true;
88     /* clear the current stack */
89     while (!TAILQ_EMPTY(&old_stackframes)) {
90         stackframe *current = TAILQ_FIRST(&old_stackframes);
91         for (int c = 0; c < current->n_args; c++)
92             if (current->args[c])
93                 free(current->args[c]);
94         TAILQ_REMOVE(&old_stackframes, current, stackframes);
95         free(current);
96     }
97     while (!TAILQ_EMPTY(&new_stackframes)) {
98         stackframe *current = TAILQ_FIRST(&new_stackframes);
99         for (int c = 0; c < current->n_args; c++)
100             if (current->args[c])
101                 free(current->args[c]);
102         TAILQ_REMOVE(&new_stackframes, current, stackframes);
103         free(current);
104     }
105 }
106
107 void cmd_MIGRATION_disable() {
108     migration_test = false;
109 }
110
111 void cmd_MIGRATION_save_new_parameters(Match *current_match, ...) {
112     va_list args;
113
114     DLOG("saving parameters.\n");
115     stackframe *frame = scalloc(sizeof(stackframe));
116     match_copy(&(frame->match), current_match);
117
118     /* All parameters are char*s */
119     va_start(args, current_match);
120     while (true) {
121         char *parameter = va_arg(args, char*);
122         if (parameter == last_parameter)
123             break;
124         DLOG("parameter = %s\n", parameter);
125         if (parameter)
126             frame->args[frame->n_args] = sstrdup(parameter);
127         frame->n_args++;
128     }
129     va_end(args);
130
131     TAILQ_INSERT_TAIL(&new_stackframes, frame, stackframes);
132 }
133
134 void cmd_MIGRATION_save_old_parameters(Match *current_match, ...) {
135     va_list args;
136
137     DLOG("saving new parameters.\n");
138     stackframe *frame = scalloc(sizeof(stackframe));
139     match_copy(&(frame->match), current_match);
140
141     /* All parameters are char*s */
142     va_start(args, current_match);
143     while (true) {
144         char *parameter = va_arg(args, char*);
145         if (parameter == last_parameter)
146             break;
147         DLOG("parameter = %s\n", parameter);
148         if (parameter)
149             frame->args[frame->n_args] = sstrdup(parameter);
150         frame->n_args++;
151     }
152     va_end(args);
153
154     TAILQ_INSERT_TAIL(&old_stackframes, frame, stackframes);
155 }
156
157 static bool re_differ(struct regex *new, struct regex *old) {
158     return ((new == NULL && old != NULL) ||
159         (new != NULL && old == NULL) ||
160         (new != NULL && old != NULL &&
161          strcmp(new->pattern, old->pattern) != 0));
162 }
163
164 static bool str_differ(char *new, char *old) {
165     return ((new == NULL && old != NULL) ||
166         (new != NULL && old == NULL) ||
167         (new != NULL && old != NULL &&
168          strcmp(new, old) != 0));
169 }
170
171 static pid_t migration_pid = -1;
172
173 /*
174  * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
175  * it exited (or could not be started, depending on the exit code).
176  *
177  */
178 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
179     ev_child_stop(EV_A_ watcher);
180     if (!WIFEXITED(watcher->rstatus)) {
181         fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
182         return;
183     }
184
185     int exitcode = WEXITSTATUS(watcher->rstatus);
186     printf("i3-nagbar process exited with status %d\n", exitcode);
187     if (exitcode == 2) {
188         fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
189     }
190
191     migration_pid = -1;
192 }
193
194 /* We need ev >= 4 for the following code. Since it is not *that* important (it
195  * only makes sure that there are no i3-nagbar instances left behind) we still
196  * support old systems with libev 3. */
197 #if EV_VERSION_MAJOR >= 4
198 /*
199  * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
200  * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
201  *
202  */
203 static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
204     if (migration_pid != -1) {
205         LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", migration_pid);
206         kill(migration_pid, SIGKILL);
207     }
208 }
209 #endif
210
211 void cmd_MIGRATION_start_nagbar() {
212     if (migration_pid != -1) {
213         fprintf(stderr, "i3-nagbar already running.\n");
214         return;
215     }
216     fprintf(stderr, "Starting i3-nagbar, command parsing differs from expected output.\n");
217     ELOG("Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile:\n");
218     ELOG("i3-dump-log | gzip -9c > /tmp/i3.log.gz\n");
219     ELOG("FYI: Your i3 version is " I3_VERSION "\n");
220     migration_pid = fork();
221     if (migration_pid == -1) {
222         warn("Could not fork()");
223         return;
224     }
225
226     /* child */
227     if (migration_pid == 0) {
228         char *pageraction;
229         sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
230         char *argv[] = {
231             NULL, /* will be replaced by the executable path */
232             "-t",
233             "error",
234             "-m",
235             "You found a parsing error. Please, please, please, report it!",
236             "-b",
237             "show errors",
238             pageraction,
239             NULL
240         };
241         exec_i3_utility("i3-nagbar", argv);
242     }
243
244     /* parent */
245     /* install a child watcher */
246     ev_child *child = smalloc(sizeof(ev_child));
247     ev_child_init(child, &nagbar_exited, migration_pid, 0);
248     ev_child_start(main_loop, child);
249
250 /* We need ev >= 4 for the following code. Since it is not *that* important (it
251  * only makes sure that there are no i3-nagbar instances left behind) we still
252  * support old systems with libev 3. */
253 #if EV_VERSION_MAJOR >= 4
254     /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
255      * still running) */
256     ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
257     ev_cleanup_init(cleanup, nagbar_cleanup);
258     ev_cleanup_start(main_loop, cleanup);
259 #endif
260 }
261
262 void cmd_MIGRATION_validate() {
263     DLOG("validating the different stacks now\n");
264     int old_count = 0;
265     int new_count = 0;
266     stackframe *current;
267     TAILQ_FOREACH(current, &new_stackframes, stackframes)
268         new_count++;
269     TAILQ_FOREACH(current, &old_stackframes, stackframes)
270         old_count++;
271     if (new_count != old_count) {
272         ELOG("FAILED, new_count == %d != old_count == %d\n", new_count, old_count);
273         cmd_MIGRATION_start_nagbar();
274         return;
275     }
276     DLOG("parameter count matching, comparing one by one...\n");
277
278     stackframe *new_frame = TAILQ_FIRST(&new_stackframes),
279                *old_frame = TAILQ_FIRST(&old_stackframes);
280     for (int i = 0; i < new_count; i++) {
281         if (new_frame->match.dock != old_frame->match.dock ||
282             new_frame->match.id != old_frame->match.id ||
283             new_frame->match.con_id != old_frame->match.con_id ||
284             new_frame->match.floating != old_frame->match.floating ||
285             new_frame->match.insert_where != old_frame->match.insert_where ||
286             re_differ(new_frame->match.title, old_frame->match.title)  ||
287             re_differ(new_frame->match.application, old_frame->match.application)  ||
288             re_differ(new_frame->match.class, old_frame->match.class)  ||
289             re_differ(new_frame->match.instance, old_frame->match.instance)  ||
290             re_differ(new_frame->match.mark, old_frame->match.mark)  ||
291             re_differ(new_frame->match.role, old_frame->match.role) ) {
292             ELOG("FAILED, new_frame->match != old_frame->match (frame %d)\n", i);
293             cmd_MIGRATION_start_nagbar();
294             return;
295         }
296         if (new_frame->n_args != old_frame->n_args) {
297             ELOG("FAILED, new_frame->n_args == %d != old_frame->n_args == %d (frame %d)\n",
298                  new_frame->n_args, old_frame->n_args, i);
299             cmd_MIGRATION_start_nagbar();
300             return;
301         }
302         for (int j = 0; j < new_frame->n_args; j++) {
303             if (str_differ(new_frame->args[j], old_frame->args[j])) {
304                 ELOG("FAILED, new_frame->args[%d] == %s != old_frame->args[%d] == %s (frame %d)\n",
305                      j, new_frame->args[j], j, old_frame->args[j], i);
306                 cmd_MIGRATION_start_nagbar();
307                 return;
308             }
309         }
310         new_frame = TAILQ_NEXT(new_frame, stackframes);
311         old_frame = TAILQ_NEXT(old_frame, stackframes);
312     }
313     DLOG("OK\n");
314 }
315
316 #define MIGRATION_init(x, ...) do { \
317     if (migration_test) { \
318         cmd_MIGRATION_save_new_parameters(current_match, __FUNCTION__, ##__VA_ARGS__ , last_parameter); \
319         return NULL; \
320     } else { \
321         cmd_MIGRATION_save_old_parameters(current_match, __FUNCTION__, ##__VA_ARGS__ , last_parameter); \
322     } \
323 } while (0)
324
325
326 /*******************************************************************************
327  * Criteria functions.
328  ******************************************************************************/
329
330 char *cmd_criteria_init(Match *current_match) {
331     DLOG("Initializing criteria, current_match = %p\n", current_match);
332     match_init(current_match);
333     TAILQ_INIT(&owindows);
334     /* copy all_cons */
335     Con *con;
336     TAILQ_FOREACH(con, &all_cons, all_cons) {
337         owindow *ow = smalloc(sizeof(owindow));
338         ow->con = con;
339         TAILQ_INSERT_TAIL(&owindows, ow, owindows);
340     }
341
342     /* This command is internal and does not generate a JSON reply. */
343     return NULL;
344 }
345
346 char *cmd_criteria_match_windows(Match *current_match) {
347     owindow *next, *current;
348
349     /* The same as MIGRATION_init, but doesn’t return */
350     if (migration_test) {
351         cmd_MIGRATION_save_new_parameters(current_match, __FUNCTION__, last_parameter);
352     } else {
353         cmd_MIGRATION_save_old_parameters(current_match, __FUNCTION__, last_parameter);
354     }
355
356     DLOG("match specification finished, matching...\n");
357     /* copy the old list head to iterate through it and start with a fresh
358      * list which will contain only matching windows */
359     struct owindows_head old = owindows;
360     TAILQ_INIT(&owindows);
361     for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
362         /* make a copy of the next pointer and advance the pointer to the
363          * next element as we are going to invalidate the element’s
364          * next/prev pointers by calling TAILQ_INSERT_TAIL later */
365         current = next;
366         next = TAILQ_NEXT(next, owindows);
367
368         DLOG("checking if con %p / %s matches\n", current->con, current->con->name);
369         if (current_match->con_id != NULL) {
370             if (current_match->con_id == current->con) {
371                 DLOG("matches container!\n");
372                 TAILQ_INSERT_TAIL(&owindows, current, owindows);
373             }
374         } else if (current_match->mark != NULL && current->con->mark != NULL &&
375                    regex_matches(current_match->mark, current->con->mark)) {
376             DLOG("match by mark\n");
377             TAILQ_INSERT_TAIL(&owindows, current, owindows);
378         } else {
379             if (current->con->window == NULL)
380                 continue;
381             if (match_matches_window(current_match, current->con->window)) {
382                 DLOG("matches window!\n");
383                 TAILQ_INSERT_TAIL(&owindows, current, owindows);
384             } else {
385                 DLOG("doesnt match\n");
386                 free(current);
387             }
388         }
389     }
390
391     TAILQ_FOREACH(current, &owindows, owindows) {
392         DLOG("matching: %p / %s\n", current->con, current->con->name);
393     }
394
395     /* This command is internal and does not generate a JSON reply. */
396     return NULL;
397 }
398
399 char *cmd_criteria_add(Match *current_match, char *ctype, char *cvalue) {
400     /* The same as MIGRATION_init, but doesn’t return */
401     if (migration_test) {
402         cmd_MIGRATION_save_new_parameters(current_match, __FUNCTION__, last_parameter);
403     } else {
404         cmd_MIGRATION_save_old_parameters(current_match, __FUNCTION__, last_parameter);
405     }
406
407     DLOG("ctype=*%s*, cvalue=*%s*\n", ctype, cvalue);
408
409     if (strcmp(ctype, "class") == 0) {
410         current_match->class = regex_new(cvalue);
411         return NULL;
412     }
413
414     if (strcmp(ctype, "instance") == 0) {
415         current_match->instance = regex_new(cvalue);
416         return NULL;
417     }
418
419     if (strcmp(ctype, "window_role") == 0) {
420         current_match->role = regex_new(cvalue);
421         return NULL;
422     }
423
424     if (strcmp(ctype, "con_id") == 0) {
425         char *end;
426         long parsed = strtol(cvalue, &end, 10);
427         if (parsed == LONG_MIN ||
428             parsed == LONG_MAX ||
429             parsed < 0 ||
430             (end && *end != '\0')) {
431             ELOG("Could not parse con id \"%s\"\n", cvalue);
432         } else {
433             current_match->con_id = (Con*)parsed;
434             printf("id as int = %p\n", current_match->con_id);
435         }
436         return NULL;
437     }
438
439     if (strcmp(ctype, "id") == 0) {
440         char *end;
441         long parsed = strtol(cvalue, &end, 10);
442         if (parsed == LONG_MIN ||
443             parsed == LONG_MAX ||
444             parsed < 0 ||
445             (end && *end != '\0')) {
446             ELOG("Could not parse window id \"%s\"\n", cvalue);
447         } else {
448             current_match->id = parsed;
449             printf("window id as int = %d\n", current_match->id);
450         }
451         return NULL;
452     }
453
454     if (strcmp(ctype, "con_mark") == 0) {
455         current_match->mark = regex_new(cvalue);
456         return NULL;
457     }
458
459     if (strcmp(ctype, "title") == 0) {
460         current_match->title = regex_new(cvalue);
461         return NULL;
462     }
463
464     ELOG("Unknown criterion: %s\n", ctype);
465
466     /* This command is internal and does not generate a JSON reply. */
467     return NULL;
468 }
469
470 char *cmd_move_con_to_workspace(Match *current_match, char *which) {
471     owindow *current;
472
473     MIGRATION_init(x, which);
474
475     DLOG("which=%s\n", which);
476
477     HANDLE_EMPTY_MATCH;
478
479     /* get the workspace */
480     Con *ws;
481     if (strcmp(which, "next") == 0)
482         ws = workspace_next();
483     else if (strcmp(which, "prev") == 0)
484         ws = workspace_prev();
485     else if (strcmp(which, "next_on_output") == 0)
486         ws = workspace_next_on_output();
487     else if (strcmp(which, "prev_on_output") == 0)
488         ws = workspace_prev_on_output();
489     else {
490         ELOG("BUG: called with which=%s\n", which);
491         return sstrdup("{\"sucess\": false}");
492     }
493
494     TAILQ_FOREACH(current, &owindows, owindows) {
495         DLOG("matching: %p / %s\n", current->con, current->con->name);
496         con_move_to_workspace(current->con, ws, true, false);
497     }
498
499     tree_render();
500
501     // XXX: default reply for now, make this a better reply
502     return sstrdup("{\"success\": true}");
503 }
504
505 char *cmd_move_con_to_workspace_name(Match *current_match, char *name) {
506     MIGRATION_init(x, name);
507
508     if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
509         LOG("You cannot switch to the i3 internal workspaces.\n");
510         return sstrdup("{\"sucess\": false}");
511     }
512
513     owindow *current;
514
515     /* Error out early to not create a non-existing workspace (in
516      * workspace_get()) if we are not actually able to move anything. */
517     if (match_is_empty(current_match) && focused->type == CT_WORKSPACE)
518         return sstrdup("{\"sucess\": false}");
519
520     LOG("should move window to workspace %s\n", name);
521     /* get the workspace */
522     Con *ws = workspace_get(name, NULL);
523
524     HANDLE_EMPTY_MATCH;
525
526     TAILQ_FOREACH(current, &owindows, owindows) {
527         DLOG("matching: %p / %s\n", current->con, current->con->name);
528         con_move_to_workspace(current->con, ws, true, false);
529     }
530
531     tree_render();
532
533     // XXX: default reply for now, make this a better reply
534     return sstrdup("{\"success\": true}");
535 }
536
537 char *cmd_resize(Match *current_match, char *way, char *direction, char *resize_px, char *resize_ppt) {
538     MIGRATION_init(x, way, direction, resize_px, resize_ppt);
539     /* resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt] */
540     DLOG("resizing in way %s, direction %s, px %s or ppt %s\n", way, direction, resize_px, resize_ppt);
541     // 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
542     int px = atoi(resize_px);
543     int ppt = atoi(resize_ppt);
544     if (strcmp(way, "shrink") == 0) {
545         px *= -1;
546         ppt *= -1;
547     }
548
549     Con *floating_con;
550     if ((floating_con = con_inside_floating(focused))) {
551         printf("floating resize\n");
552         if (strcmp(direction, "up") == 0) {
553             floating_con->rect.y -= px;
554             floating_con->rect.height += px;
555         } else if (strcmp(direction, "down") == 0) {
556             floating_con->rect.height += px;
557         } else if (strcmp(direction, "left") == 0) {
558             floating_con->rect.x -= px;
559             floating_con->rect.width += px;
560         } else {
561             floating_con->rect.width += px;
562         }
563     } else {
564         LOG("tiling resize\n");
565         /* get the appropriate current container (skip stacked/tabbed cons) */
566         Con *current = focused;
567         while (current->parent->layout == L_STACKED ||
568                current->parent->layout == L_TABBED)
569             current = current->parent;
570
571         /* Then further go up until we find one with the matching orientation. */
572         orientation_t search_orientation =
573             (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
574
575         while (current->type != CT_WORKSPACE &&
576                current->type != CT_FLOATING_CON &&
577                current->parent->orientation != search_orientation)
578             current = current->parent;
579
580         /* get the default percentage */
581         int children = con_num_children(current->parent);
582         Con *other;
583         LOG("ins. %d children\n", children);
584         double percentage = 1.0 / children;
585         LOG("default percentage = %f\n", percentage);
586
587         orientation_t orientation = current->parent->orientation;
588
589         if ((orientation == HORIZ &&
590              (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
591             (orientation == VERT &&
592              (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
593             LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
594                 (orientation == HORIZ ? "horizontal" : "vertical"));
595             return sstrdup("{\"sucess\": false}");
596         }
597
598         if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
599             other = TAILQ_PREV(current, nodes_head, nodes);
600         } else {
601             other = TAILQ_NEXT(current, nodes);
602         }
603         if (other == TAILQ_END(workspaces)) {
604             LOG("No other container in this direction found, cannot resize.\n");
605             return sstrdup("{\"sucess\": false}");
606         }
607         LOG("other->percent = %f\n", other->percent);
608         LOG("current->percent before = %f\n", current->percent);
609         if (current->percent == 0.0)
610             current->percent = percentage;
611         if (other->percent == 0.0)
612             other->percent = percentage;
613         double new_current_percent = current->percent + ((double)ppt / 100.0);
614         double new_other_percent = other->percent - ((double)ppt / 100.0);
615         LOG("new_current_percent = %f\n", new_current_percent);
616         LOG("new_other_percent = %f\n", new_other_percent);
617         /* Ensure that the new percentages are positive and greater than
618          * 0.05 to have a reasonable minimum size. */
619         if (definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON) &&
620             definitelyGreaterThan(new_other_percent, 0.05, DBL_EPSILON)) {
621             current->percent += ((double)ppt / 100.0);
622             other->percent -= ((double)ppt / 100.0);
623             LOG("current->percent after = %f\n", current->percent);
624             LOG("other->percent after = %f\n", other->percent);
625         } else {
626             LOG("Not resizing, already at minimum size\n");
627         }
628     }
629
630     tree_render();
631
632     // XXX: default reply for now, make this a better reply
633     return sstrdup("{\"success\": true}");
634 }
635
636 char *cmd_border(Match *current_match, char *border_style_str) {
637     MIGRATION_init(x, border_style_str);
638     DLOG("border style should be changed to %s\n", border_style_str);
639     owindow *current;
640
641     HANDLE_EMPTY_MATCH;
642
643     TAILQ_FOREACH(current, &owindows, owindows) {
644         DLOG("matching: %p / %s\n", current->con, current->con->name);
645         int border_style = current->con->border_style;
646         if (strcmp(border_style_str, "toggle") == 0) {
647             border_style++;
648             border_style %= 3;
649         } else {
650             if (strcmp(border_style_str, "normal") == 0)
651                 border_style = BS_NORMAL;
652             else if (strcmp(border_style_str, "none") == 0)
653                 border_style = BS_NONE;
654             else if (strcmp(border_style_str, "1pixel") == 0)
655                 border_style = BS_1PIXEL;
656             else {
657                 ELOG("BUG: called with border_style=%s\n", border_style_str);
658                 return sstrdup("{\"sucess\": false}");
659             }
660         }
661         con_set_border_style(current->con, border_style);
662     }
663
664     tree_render();
665
666     // XXX: default reply for now, make this a better reply
667     return sstrdup("{\"success\": true}");
668 }
669
670 char *cmd_nop(Match *current_match, char *comment) {
671     MIGRATION_init(x, comment);
672     LOG("-------------------------------------------------\n");
673     LOG("  NOP: %s\n", comment);
674     LOG("-------------------------------------------------\n");
675
676     return NULL;
677 }
678
679 char *cmd_append_layout(Match *current_match, char *path) {
680     MIGRATION_init(x, path);
681     LOG("Appending layout \"%s\"\n", path);
682     tree_append_json(path);
683     tree_render();
684
685     // XXX: default reply for now, make this a better reply
686     return sstrdup("{\"success\": true}");
687 }
688
689 char *cmd_workspace(Match *current_match, char *which) {
690     MIGRATION_init(x, which);
691     Con *ws;
692
693     DLOG("which=%s\n", which);
694
695     if (strcmp(which, "next") == 0)
696         ws = workspace_next();
697     else if (strcmp(which, "prev") == 0)
698         ws = workspace_prev();
699     else if (strcmp(which, "next_on_output") == 0)
700         ws = workspace_next_on_output();
701     else if (strcmp(which, "prev_on_output") == 0)
702         ws = workspace_prev_on_output();
703     else {
704         ELOG("BUG: called with which=%s\n", which);
705         return sstrdup("{\"sucess\": false}");
706     }
707
708     workspace_show(ws);
709     tree_render();
710
711     // XXX: default reply for now, make this a better reply
712     return sstrdup("{\"success\": true}");
713 }
714
715 char *cmd_workspace_back_and_forth(Match *current_match) {
716     MIGRATION_init(x);
717     workspace_back_and_forth();
718     tree_render();
719
720     // XXX: default reply for now, make this a better reply
721     return sstrdup("{\"success\": true}");
722 }
723
724 char *cmd_workspace_name(Match *current_match, char *name) {
725     MIGRATION_init(x, name);
726     if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
727         LOG("You cannot switch to the i3 internal workspaces.\n");
728         return sstrdup("{\"sucess\": false}");
729     }
730
731     DLOG("should switch to workspace %s\n", name);
732
733     Con *ws = con_get_workspace(focused);
734
735     /* Check if the command wants to switch to the current workspace */
736     if (strcmp(ws->name, name) == 0) {
737         DLOG("This workspace is already focused.\n");
738         if (config.workspace_auto_back_and_forth) {
739             workspace_back_and_forth();
740             tree_render();
741         }
742         return sstrdup("{\"sucess\": false}");
743     }
744
745     workspace_show_by_name(name);
746
747     tree_render();
748
749     // XXX: default reply for now, make this a better reply
750     return sstrdup("{\"success\": true}");
751 }
752
753 char *cmd_mark(Match *current_match, char *mark) {
754     MIGRATION_init(x, mark);
755     DLOG("Clearing all windows which have that mark first\n");
756
757     Con *con;
758     TAILQ_FOREACH(con, &all_cons, all_cons) {
759         if (con->mark && strcmp(con->mark, mark) == 0)
760             FREE(con->mark);
761     }
762
763     DLOG("marking window with str %s\n", mark);
764     owindow *current;
765
766     HANDLE_EMPTY_MATCH;
767
768     TAILQ_FOREACH(current, &owindows, owindows) {
769         DLOG("matching: %p / %s\n", current->con, current->con->name);
770         current->con->mark = sstrdup(mark);
771     }
772
773     tree_render();
774
775     // XXX: default reply for now, make this a better reply
776     return sstrdup("{\"success\": true}");
777 }
778
779 char *cmd_mode(Match *current_match, char *mode) {
780     MIGRATION_init(x, mode);
781     DLOG("mode=%s\n", mode);
782     switch_mode(mode);
783
784     // XXX: default reply for now, make this a better reply
785     return sstrdup("{\"success\": true}");
786 }
787
788 char *cmd_move_con_to_output(Match *current_match, char *name) {
789     MIGRATION_init(x, name);
790     owindow *current;
791
792     DLOG("should move window to output %s\n", name);
793
794     HANDLE_EMPTY_MATCH;
795
796     /* get the output */
797     Output *current_output = NULL;
798     Output *output;
799
800     // TODO: fix the handling of criteria
801     TAILQ_FOREACH(current, &owindows, owindows)
802         current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
803
804     assert(current_output != NULL);
805
806     // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
807     if (strcasecmp(name, "up") == 0)
808         output = get_output_next(D_UP, current_output);
809     else if (strcasecmp(name, "down") == 0)
810         output = get_output_next(D_DOWN, current_output);
811     else if (strcasecmp(name, "left") == 0)
812         output = get_output_next(D_LEFT, current_output);
813     else if (strcasecmp(name, "right") == 0)
814         output = get_output_next(D_RIGHT, current_output);
815     else
816         output = get_output_by_name(name);
817
818     if (!output) {
819         LOG("No such output found.\n");
820         return sstrdup("{\"sucess\": false}");
821     }
822
823     /* get visible workspace on output */
824     Con *ws = NULL;
825     GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
826     if (!ws)
827         return sstrdup("{\"sucess\": false}");
828
829     TAILQ_FOREACH(current, &owindows, owindows) {
830         DLOG("matching: %p / %s\n", current->con, current->con->name);
831         con_move_to_workspace(current->con, ws, true, false);
832     }
833
834     tree_render();
835
836     // XXX: default reply for now, make this a better reply
837     return sstrdup("{\"success\": true}");
838 }
839
840 char *cmd_floating(Match *current_match, char *floating_mode) {
841     MIGRATION_init(x, floating_mode);
842     owindow *current;
843
844     DLOG("floating_mode=%s\n", floating_mode);
845
846     HANDLE_EMPTY_MATCH;
847
848     TAILQ_FOREACH(current, &owindows, owindows) {
849         DLOG("matching: %p / %s\n", current->con, current->con->name);
850         if (strcmp(floating_mode, "toggle") == 0) {
851             DLOG("should toggle mode\n");
852             toggle_floating_mode(current->con, false);
853         } else {
854             DLOG("should switch mode to %s\n", floating_mode);
855             if (strcmp(floating_mode, "enable") == 0) {
856                 floating_enable(current->con, false);
857             } else {
858                 floating_disable(current->con, false);
859             }
860         }
861     }
862
863     tree_render();
864
865     // XXX: default reply for now, make this a better reply
866     return sstrdup("{\"success\": true}");
867 }
868
869 char *cmd_move_workspace_to_output(Match *current_match, char *name) {
870     MIGRATION_init(x, name);
871     DLOG("should move workspace to output %s\n", name);
872
873     HANDLE_EMPTY_MATCH;
874
875     owindow *current;
876     TAILQ_FOREACH(current, &owindows, owindows) {
877         Output *current_output = get_output_containing(current->con->rect.x,
878                                                        current->con->rect.y);
879         Output *output = get_output_from_string(current_output, name);
880         if (!output) {
881             LOG("No such output\n");
882             return sstrdup("{\"sucess\": false}");
883         }
884
885         Con *content = output_get_content(output->con);
886         LOG("got output %p with content %p\n", output, content);
887
888         Con *ws = con_get_workspace(current->con);
889         LOG("should move workspace %p / %s\n", ws, ws->name);
890         if (con_num_children(ws->parent) == 1) {
891             LOG("Not moving workspace \"%s\", it is the only workspace on its output.\n", ws->name);
892             continue;
893         }
894         bool workspace_was_visible = workspace_is_visible(ws);
895         Con *old_content = ws->parent;
896         con_detach(ws);
897         if (workspace_was_visible) {
898             /* The workspace which we just detached was visible, so focus
899              * the next one in the focus-stack. */
900             Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
901             LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
902             workspace_show(focus_ws);
903         }
904         con_attach(ws, content, false);
905         ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
906         if (workspace_was_visible) {
907             /* Focus the moved workspace on the destination output. */
908             workspace_show(ws);
909         }
910     }
911
912     tree_render();
913
914     // XXX: default reply for now, make this a better reply
915     return sstrdup("{\"success\": true}");
916 }
917
918 char *cmd_split(Match *current_match, char *direction) {
919     MIGRATION_init(x, direction);
920     /* TODO: use matches */
921     LOG("splitting in direction %c\n", direction[0]);
922     tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
923
924     tree_render();
925
926     // XXX: default reply for now, make this a better reply
927     return sstrdup("{\"success\": true}");
928 }
929
930 char *cmd_kill(Match *current_match, char *kill_mode_str) {
931     if (kill_mode_str == NULL)
932         kill_mode_str = "window";
933     MIGRATION_init(x, kill_mode_str);
934     owindow *current;
935
936     DLOG("kill_mode=%s\n", kill_mode_str);
937
938     int kill_mode;
939     if (strcmp(kill_mode_str, "window") == 0)
940         kill_mode = KILL_WINDOW;
941     else if (strcmp(kill_mode_str, "client") == 0)
942         kill_mode = KILL_CLIENT;
943     else {
944         ELOG("BUG: called with kill_mode=%s\n", kill_mode_str);
945         return sstrdup("{\"sucess\": false}");
946     }
947
948     /* check if the match is empty, not if the result is empty */
949     if (match_is_empty(current_match))
950         tree_close_con(kill_mode);
951     else {
952         TAILQ_FOREACH(current, &owindows, owindows) {
953             DLOG("matching: %p / %s\n", current->con, current->con->name);
954             tree_close(current->con, kill_mode, false, false);
955         }
956     }
957
958     tree_render();
959
960     // XXX: default reply for now, make this a better reply
961     return sstrdup("{\"success\": true}");
962 }
963
964 char *cmd_exec(Match *current_match, char *nosn, char *command) {
965     MIGRATION_init(x, nosn, command);
966     bool no_startup_id = (nosn != NULL);
967
968     DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
969     start_application(command, no_startup_id);
970
971     // XXX: default reply for now, make this a better reply
972     return sstrdup("{\"success\": true}");
973 }
974
975 char *cmd_focus_direction(Match *current_match, char *direction) {
976     MIGRATION_init(x, direction);
977     if (focused &&
978         focused->type != CT_WORKSPACE &&
979         focused->fullscreen_mode != CF_NONE) {
980         LOG("Cannot change focus while in fullscreen mode.\n");
981         return sstrdup("{\"sucess\": false}");
982     }
983
984     DLOG("direction = *%s*\n", direction);
985
986     if (strcmp(direction, "left") == 0)
987         tree_next('p', HORIZ);
988     else if (strcmp(direction, "right") == 0)
989         tree_next('n', HORIZ);
990     else if (strcmp(direction, "up") == 0)
991         tree_next('p', VERT);
992     else if (strcmp(direction, "down") == 0)
993         tree_next('n', VERT);
994     else {
995         ELOG("Invalid focus direction (%s)\n", direction);
996         return sstrdup("{\"sucess\": false}");
997     }
998
999     tree_render();
1000
1001     // XXX: default reply for now, make this a better reply
1002     return sstrdup("{\"success\": true}");
1003 }
1004
1005 char *cmd_focus_window_mode(Match *current_match, char *window_mode) {
1006     MIGRATION_init(x, window_mode);
1007     if (focused &&
1008         focused->type != CT_WORKSPACE &&
1009         focused->fullscreen_mode != CF_NONE) {
1010         LOG("Cannot change focus while in fullscreen mode.\n");
1011         return sstrdup("{\"sucess\": false}");
1012     }
1013
1014     DLOG("window_mode = %s\n", window_mode);
1015
1016     Con *ws = con_get_workspace(focused);
1017     Con *current;
1018     if (ws != NULL) {
1019         if (strcmp(window_mode, "mode_toggle") == 0) {
1020             current = TAILQ_FIRST(&(ws->focus_head));
1021             if (current != NULL && current->type == CT_FLOATING_CON)
1022                 window_mode = "tiling";
1023             else window_mode = "floating";
1024         }
1025         TAILQ_FOREACH(current, &(ws->focus_head), focused) {
1026             if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
1027                 (strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
1028                 continue;
1029
1030             con_focus(con_descend_focused(current));
1031             break;
1032         }
1033     }
1034
1035     tree_render();
1036
1037     // XXX: default reply for now, make this a better reply
1038     return sstrdup("{\"success\": true}");
1039 }
1040
1041 char *cmd_focus_level(Match *current_match, char *level) {
1042     MIGRATION_init(x, level);
1043     if (focused &&
1044         focused->type != CT_WORKSPACE &&
1045         focused->fullscreen_mode != CF_NONE) {
1046         LOG("Cannot change focus while in fullscreen mode.\n");
1047         return sstrdup("{\"sucess\": false}");
1048     }
1049
1050     DLOG("level = %s\n", level);
1051
1052     if (strcmp(level, "parent") == 0)
1053         level_up();
1054     else level_down();
1055
1056     tree_render();
1057
1058     // XXX: default reply for now, make this a better reply
1059     return sstrdup("{\"success\": true}");
1060 }
1061
1062 char *cmd_focus(Match *current_match) {
1063     MIGRATION_init(x);
1064     DLOG("current_match = %p\n", current_match);
1065     if (focused &&
1066         focused->type != CT_WORKSPACE &&
1067         focused->fullscreen_mode != CF_NONE) {
1068         LOG("Cannot change focus while in fullscreen mode.\n");
1069         return sstrdup("{\"sucess\": false}");
1070     }
1071
1072     owindow *current;
1073
1074     if (match_is_empty(current_match)) {
1075         ELOG("You have to specify which window/container should be focused.\n");
1076         ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
1077
1078         char *json_output;
1079         sasprintf(&json_output, "{\"success\":false, \"error\":\"You have to "
1080                   "specify which window/container should be focused\"}");
1081         return json_output;
1082     }
1083
1084     int count = 0;
1085     TAILQ_FOREACH(current, &owindows, owindows) {
1086         Con *ws = con_get_workspace(current->con);
1087         /* If no workspace could be found, this was a dock window.
1088          * Just skip it, you cannot focus dock windows. */
1089         if (!ws)
1090             continue;
1091
1092         /* If the container is not on the current workspace,
1093          * workspace_show() will switch to a different workspace and (if
1094          * enabled) trigger a mouse pointer warp to the currently focused
1095          * container (!) on the target workspace.
1096          *
1097          * Therefore, before calling workspace_show(), we make sure that
1098          * 'current' will be focused on the workspace. However, we cannot
1099          * just con_focus(current) because then the pointer will not be
1100          * warped at all (the code thinks we are already there).
1101          *
1102          * So we focus 'current' to make it the currently focused window of
1103          * the target workspace, then revert focus. */
1104         Con *currently_focused = focused;
1105         con_focus(current->con);
1106         con_focus(currently_focused);
1107
1108         /* Now switch to the workspace, then focus */
1109         workspace_show(ws);
1110         LOG("focusing %p / %s\n", current->con, current->con->name);
1111         con_focus(current->con);
1112         count++;
1113     }
1114
1115     if (count > 1)
1116         LOG("WARNING: Your criteria for the focus command matches %d containers, "
1117             "while only exactly one container can be focused at a time.\n", count);
1118
1119     tree_render();
1120
1121     // XXX: default reply for now, make this a better reply
1122     return sstrdup("{\"success\": true}");
1123 }
1124
1125 char *cmd_fullscreen(Match *current_match, char *fullscreen_mode) {
1126     if (fullscreen_mode == NULL)
1127         fullscreen_mode = "output";
1128     MIGRATION_init(x, fullscreen_mode);
1129     DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
1130     owindow *current;
1131
1132     HANDLE_EMPTY_MATCH;
1133
1134     TAILQ_FOREACH(current, &owindows, owindows) {
1135         printf("matching: %p / %s\n", current->con, current->con->name);
1136         con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
1137     }
1138
1139     tree_render();
1140
1141     // XXX: default reply for now, make this a better reply
1142     return sstrdup("{\"success\": true}");
1143 }
1144
1145 char *cmd_move_direction(Match *current_match, char *direction, char *move_px) {
1146     MIGRATION_init(x, direction, move_px);
1147     // 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
1148     int px = atoi(move_px);
1149
1150     /* TODO: make 'move' work with criteria. */
1151     DLOG("moving in direction %s, px %s\n", direction, move_px);
1152     if (con_is_floating(focused)) {
1153         DLOG("floating move with %d pixels\n", px);
1154         Rect newrect = focused->parent->rect;
1155         if (strcmp(direction, "left") == 0) {
1156             newrect.x -= px;
1157         } else if (strcmp(direction, "right") == 0) {
1158             newrect.x += px;
1159         } else if (strcmp(direction, "up") == 0) {
1160             newrect.y -= px;
1161         } else if (strcmp(direction, "down") == 0) {
1162             newrect.y += px;
1163         }
1164         floating_reposition(focused->parent, newrect);
1165     } else {
1166         tree_move((strcmp(direction, "right") == 0 ? TOK_RIGHT :
1167                    (strcmp(direction, "left") == 0 ? TOK_LEFT :
1168                     (strcmp(direction, "up") == 0 ? TOK_UP :
1169                      TOK_DOWN))));
1170         tree_render();
1171     }
1172
1173
1174     // XXX: default reply for now, make this a better reply
1175     return sstrdup("{\"success\": true}");
1176 }
1177
1178 char *cmd_layout(Match *current_match, char *layout_str) {
1179     if (strcmp(layout_str, "stacking") == 0)
1180         layout_str = "stacked";
1181     MIGRATION_init(x, layout_str);
1182     DLOG("changing layout to %s\n", layout_str);
1183     owindow *current;
1184     int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
1185                   (strcmp(layout_str, "stacked") == 0 ? L_STACKED :
1186                    L_TABBED));
1187
1188     /* check if the match is empty, not if the result is empty */
1189     if (match_is_empty(current_match))
1190         con_set_layout(focused->parent, layout);
1191     else {
1192         TAILQ_FOREACH(current, &owindows, owindows) {
1193             DLOG("matching: %p / %s\n", current->con, current->con->name);
1194             con_set_layout(current->con, layout);
1195         }
1196     }
1197
1198     tree_render();
1199
1200     // XXX: default reply for now, make this a better reply
1201     return sstrdup("{\"success\": true}");
1202 }
1203
1204 char *cmd_exit(Match *current_match) {
1205     MIGRATION_init(x);
1206     LOG("Exiting due to user command.\n");
1207     exit(0);
1208
1209     /* unreached */
1210 }
1211
1212 char *cmd_reload(Match *current_match) {
1213     MIGRATION_init(x);
1214     LOG("reloading\n");
1215     kill_configerror_nagbar(false);
1216     load_configuration(conn, NULL, true);
1217     x_set_i3_atoms();
1218     /* Send an IPC event just in case the ws names have changed */
1219     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
1220
1221     // XXX: default reply for now, make this a better reply
1222     return sstrdup("{\"success\": true}");
1223 }
1224
1225 char *cmd_restart(Match *current_match) {
1226     MIGRATION_init(x);
1227     LOG("restarting i3\n");
1228     i3_restart(false);
1229
1230     // XXX: default reply for now, make this a better reply
1231     return sstrdup("{\"success\": true}");
1232 }
1233
1234 char *cmd_open(Match *current_match) {
1235     MIGRATION_init(x);
1236     LOG("opening new container\n");
1237     Con *con = tree_open_con(NULL, NULL);
1238     con_focus(con);
1239     char *json_output;
1240     sasprintf(&json_output, "{\"success\":true, \"id\":%ld}", (long int)con);
1241
1242     tree_render();
1243
1244     return json_output;
1245 }
1246
1247 char *cmd_focus_output(Match *current_match, char *name) {
1248     MIGRATION_init(x, name);
1249     owindow *current;
1250
1251     DLOG("name = %s\n", name);
1252
1253     HANDLE_EMPTY_MATCH;
1254
1255     /* get the output */
1256     Output *current_output = NULL;
1257     Output *output;
1258
1259     TAILQ_FOREACH(current, &owindows, owindows)
1260         current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
1261     assert(current_output != NULL);
1262
1263     output = get_output_from_string(current_output, name);
1264
1265     if (!output) {
1266         LOG("No such output found.\n");
1267         return sstrdup("{\"sucess\": false}");
1268     }
1269
1270     /* get visible workspace on output */
1271     Con *ws = NULL;
1272     GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1273     if (!ws)
1274         return sstrdup("{\"sucess\": false}");
1275
1276     workspace_show(ws);
1277     tree_render();
1278
1279     // XXX: default reply for now, make this a better reply
1280     return sstrdup("{\"success\": true}");
1281 }
1282
1283 char *cmd_move_scratchpad(Match *current_match) {
1284     MIGRATION_init(x);
1285     DLOG("should move window to scratchpad\n");
1286     owindow *current;
1287
1288     HANDLE_EMPTY_MATCH;
1289
1290     TAILQ_FOREACH(current, &owindows, owindows) {
1291         DLOG("matching: %p / %s\n", current->con, current->con->name);
1292         scratchpad_move(current->con);
1293     }
1294
1295     tree_render();
1296
1297     // XXX: default reply for now, make this a better reply
1298     return sstrdup("{\"success\": true}");
1299 }
1300
1301 char *cmd_scratchpad_show(Match *current_match) {
1302     MIGRATION_init(x);
1303     DLOG("should show scratchpad window\n");
1304     owindow *current;
1305
1306     if (match_is_empty(current_match)) {
1307         scratchpad_show(NULL);
1308     } else {
1309         TAILQ_FOREACH(current, &owindows, owindows) {
1310             DLOG("matching: %p / %s\n", current->con, current->con->name);
1311             scratchpad_show(current->con);
1312         }
1313     }
1314
1315     tree_render();
1316
1317     // XXX: default reply for now, make this a better reply
1318     return sstrdup("{\"success\": true}");
1319 }