]> git.sur5r.net Git - i3/i3/blob - src/commands.c
Merge branch 'next'
[i3/i3] / src / commands.c
1 #undef I3__FILE__
2 #define I3__FILE__ "commands.c"
3 /*
4  * vim:ts=4:sw=4:expandtab
5  *
6  * i3 - an improved dynamic tiling window manager
7  * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
8  *
9  * commands.c: all command functions (see commands_parser.c)
10  *
11  */
12 #include <float.h>
13 #include <stdarg.h>
14
15 #include "all.h"
16
17 // Macros to make the YAJL API a bit easier to use.
18 #define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__)
19 #define ystr(str) yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str))
20 #define ysuccess(success) do { \
21     y(map_open); \
22     ystr("success"); \
23     y(bool, success); \
24     y(map_close); \
25 } while (0)
26
27 /** When the command did not include match criteria (!), we use the currently
28  * focused container. Do not confuse this case with a command which included
29  * criteria but which did not match any windows. This macro has to be called in
30  * every command.
31  */
32 #define HANDLE_EMPTY_MATCH do { \
33     if (match_is_empty(current_match)) { \
34         owindow *ow = smalloc(sizeof(owindow)); \
35         ow->con = focused; \
36         TAILQ_INIT(&owindows); \
37         TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
38     } \
39 } while (0)
40
41
42 /*
43  * Returns true if a is definitely greater than b (using the given epsilon)
44  *
45  */
46 static bool definitelyGreaterThan(float a, float b, float epsilon) {
47     return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
48 }
49
50 /*
51  * Returns an 'output' corresponding to one of left/right/down/up or a specific
52  * output name.
53  *
54  */
55 static Output *get_output_from_string(Output *current_output, const char *output_str) {
56     Output *output;
57
58     if (strcasecmp(output_str, "left") == 0)
59         output = get_output_next_wrap(D_LEFT, current_output);
60     else if (strcasecmp(output_str, "right") == 0)
61         output = get_output_next_wrap(D_RIGHT, current_output);
62     else if (strcasecmp(output_str, "up") == 0)
63         output = get_output_next_wrap(D_UP, current_output);
64     else if (strcasecmp(output_str, "down") == 0)
65         output = get_output_next_wrap(D_DOWN, current_output);
66     else output = get_output_by_name(output_str);
67
68     return output;
69 }
70
71 /*
72  * Checks whether we switched to a new workspace and returns false in that case,
73  * signaling that further workspace switching should be done by the calling function
74  * If not, calls workspace_back_and_forth() if workspace_auto_back_and_forth is set
75  * and return true, signaling that no further workspace switching should occur in the calling function.
76  *
77  */
78 static bool maybe_back_and_forth(struct CommandResult *cmd_output, char *name) {
79     Con *ws = con_get_workspace(focused);
80
81     /* If we switched to a different workspace, do nothing */
82     if (strcmp(ws->name, name) != 0)
83         return false;
84
85     DLOG("This workspace is already focused.\n");
86     if (config.workspace_auto_back_and_forth) {
87         workspace_back_and_forth();
88         cmd_output->needs_tree_render = true;
89     }
90     return true;
91 }
92
93 /*
94  * Return the passed workspace unless it is the current one and auto back and
95  * forth is enabled, in which case the back_and_forth workspace is returned.
96  */
97 static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
98     Con *current, *baf;
99
100     if (!config.workspace_auto_back_and_forth)
101         return workspace;
102
103     current = con_get_workspace(focused);
104
105     if (current == workspace) {
106         baf = workspace_back_and_forth_get();
107         if (baf != NULL) {
108             DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
109             return baf;
110         }
111     }
112
113     return workspace;
114 }
115
116 // This code is commented out because we might recycle it for popping up error
117 // messages on parser errors.
118 #if 0
119 static pid_t migration_pid = -1;
120
121 /*
122  * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
123  * it exited (or could not be started, depending on the exit code).
124  *
125  */
126 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
127     ev_child_stop(EV_A_ watcher);
128     if (!WIFEXITED(watcher->rstatus)) {
129         fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
130         return;
131     }
132
133     int exitcode = WEXITSTATUS(watcher->rstatus);
134     printf("i3-nagbar process exited with status %d\n", exitcode);
135     if (exitcode == 2) {
136         fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
137     }
138
139     migration_pid = -1;
140 }
141
142 /* We need ev >= 4 for the following code. Since it is not *that* important (it
143  * only makes sure that there are no i3-nagbar instances left behind) we still
144  * support old systems with libev 3. */
145 #if EV_VERSION_MAJOR >= 4
146 /*
147  * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
148  * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
149  *
150  */
151 static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
152     if (migration_pid != -1) {
153         LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", migration_pid);
154         kill(migration_pid, SIGKILL);
155     }
156 }
157 #endif
158
159 void cmd_MIGRATION_start_nagbar(void) {
160     if (migration_pid != -1) {
161         fprintf(stderr, "i3-nagbar already running.\n");
162         return;
163     }
164     fprintf(stderr, "Starting i3-nagbar, command parsing differs from expected output.\n");
165     ELOG("Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile:\n");
166     ELOG("i3-dump-log | gzip -9c > /tmp/i3.log.gz\n");
167     ELOG("FYI: Your i3 version is " I3_VERSION "\n");
168     migration_pid = fork();
169     if (migration_pid == -1) {
170         warn("Could not fork()");
171         return;
172     }
173
174     /* child */
175     if (migration_pid == 0) {
176         char *pageraction;
177         sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
178         char *argv[] = {
179             NULL, /* will be replaced by the executable path */
180             "-t",
181             "error",
182             "-m",
183             "You found a parsing error. Please, please, please, report it!",
184             "-b",
185             "show errors",
186             pageraction,
187             NULL
188         };
189         exec_i3_utility("i3-nagbar", argv);
190     }
191
192     /* parent */
193     /* install a child watcher */
194     ev_child *child = smalloc(sizeof(ev_child));
195     ev_child_init(child, &nagbar_exited, migration_pid, 0);
196     ev_child_start(main_loop, child);
197
198 /* We need ev >= 4 for the following code. Since it is not *that* important (it
199  * only makes sure that there are no i3-nagbar instances left behind) we still
200  * support old systems with libev 3. */
201 #if EV_VERSION_MAJOR >= 4
202     /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
203      * still running) */
204     ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
205     ev_cleanup_init(cleanup, nagbar_cleanup);
206     ev_cleanup_start(main_loop, cleanup);
207 #endif
208 }
209
210 #endif
211
212 /*******************************************************************************
213  * Criteria functions.
214  ******************************************************************************/
215
216 /*
217  * Helper data structure for an operation window (window on which the operation
218  * will be performed). Used to build the TAILQ owindows.
219  *
220  */
221 typedef struct owindow {
222     Con *con;
223     TAILQ_ENTRY(owindow) owindows;
224 } owindow;
225
226 typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
227
228 static owindows_head owindows;
229
230 /*
231  * Initializes the specified 'Match' data structure and the initial state of
232  * commands.c for matching target windows of a command.
233  *
234  */
235 void cmd_criteria_init(I3_CMD) {
236     Con *con;
237     owindow *ow;
238
239     DLOG("Initializing criteria, current_match = %p\n", current_match);
240     match_init(current_match);
241     while (!TAILQ_EMPTY(&owindows)) {
242         ow = TAILQ_FIRST(&owindows);
243         TAILQ_REMOVE(&owindows, ow, owindows);
244         free(ow);
245     }
246     TAILQ_INIT(&owindows);
247     /* copy all_cons */
248     TAILQ_FOREACH(con, &all_cons, all_cons) {
249         ow = smalloc(sizeof(owindow));
250         ow->con = con;
251         TAILQ_INSERT_TAIL(&owindows, ow, owindows);
252     }
253 }
254
255 /*
256  * A match specification just finished (the closing square bracket was found),
257  * so we filter the list of owindows.
258  *
259  */
260 void cmd_criteria_match_windows(I3_CMD) {
261     owindow *next, *current;
262
263     DLOG("match specification finished, matching...\n");
264     /* copy the old list head to iterate through it and start with a fresh
265      * list which will contain only matching windows */
266     struct owindows_head old = owindows;
267     TAILQ_INIT(&owindows);
268     for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
269         /* make a copy of the next pointer and advance the pointer to the
270          * next element as we are going to invalidate the element’s
271          * next/prev pointers by calling TAILQ_INSERT_TAIL later */
272         current = next;
273         next = TAILQ_NEXT(next, owindows);
274
275         DLOG("checking if con %p / %s matches\n", current->con, current->con->name);
276         if (current_match->con_id != NULL) {
277             if (current_match->con_id == current->con) {
278                 DLOG("matches container!\n");
279                 TAILQ_INSERT_TAIL(&owindows, current, owindows);
280             }
281         } else if (current_match->mark != NULL && current->con->mark != NULL &&
282                    regex_matches(current_match->mark, current->con->mark)) {
283             DLOG("match by mark\n");
284             TAILQ_INSERT_TAIL(&owindows, current, owindows);
285         } else {
286             if (current->con->window == NULL)
287                 continue;
288             if (match_matches_window(current_match, current->con->window)) {
289                 DLOG("matches window!\n");
290                 TAILQ_INSERT_TAIL(&owindows, current, owindows);
291             } else {
292                 DLOG("doesnt match\n");
293                 free(current);
294             }
295         }
296     }
297
298     TAILQ_FOREACH(current, &owindows, owindows) {
299         DLOG("matching: %p / %s\n", current->con, current->con->name);
300     }
301 }
302
303 /*
304  * Interprets a ctype=cvalue pair and adds it to the current match
305  * specification.
306  *
307  */
308 void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
309     DLOG("ctype=*%s*, cvalue=*%s*\n", ctype, cvalue);
310
311     if (strcmp(ctype, "class") == 0) {
312         current_match->class = regex_new(cvalue);
313         return;
314     }
315
316     if (strcmp(ctype, "instance") == 0) {
317         current_match->instance = regex_new(cvalue);
318         return;
319     }
320
321     if (strcmp(ctype, "window_role") == 0) {
322         current_match->role = regex_new(cvalue);
323         return;
324     }
325
326     if (strcmp(ctype, "con_id") == 0) {
327         char *end;
328         long parsed = strtol(cvalue, &end, 10);
329         if (parsed == LONG_MIN ||
330             parsed == LONG_MAX ||
331             parsed < 0 ||
332             (end && *end != '\0')) {
333             ELOG("Could not parse con id \"%s\"\n", cvalue);
334         } else {
335             current_match->con_id = (Con*)parsed;
336             printf("id as int = %p\n", current_match->con_id);
337         }
338         return;
339     }
340
341     if (strcmp(ctype, "id") == 0) {
342         char *end;
343         long parsed = strtol(cvalue, &end, 10);
344         if (parsed == LONG_MIN ||
345             parsed == LONG_MAX ||
346             parsed < 0 ||
347             (end && *end != '\0')) {
348             ELOG("Could not parse window id \"%s\"\n", cvalue);
349         } else {
350             current_match->id = parsed;
351             printf("window id as int = %d\n", current_match->id);
352         }
353         return;
354     }
355
356     if (strcmp(ctype, "con_mark") == 0) {
357         current_match->mark = regex_new(cvalue);
358         return;
359     }
360
361     if (strcmp(ctype, "title") == 0) {
362         current_match->title = regex_new(cvalue);
363         return;
364     }
365
366     if (strcmp(ctype, "urgent") == 0) {
367         if (strcasecmp(cvalue, "latest") == 0 ||
368             strcasecmp(cvalue, "newest") == 0 ||
369             strcasecmp(cvalue, "recent") == 0 ||
370             strcasecmp(cvalue, "last") == 0) {
371             current_match->urgent = U_LATEST;
372         } else if (strcasecmp(cvalue, "oldest") == 0 ||
373                    strcasecmp(cvalue, "first") == 0) {
374             current_match->urgent = U_OLDEST;
375         }
376         return;
377     }
378
379     ELOG("Unknown criterion: %s\n", ctype);
380 }
381
382 /*
383  * Implementation of 'move [window|container] [to] workspace
384  * next|prev|next_on_output|prev_on_output|current'.
385  *
386  */
387 void cmd_move_con_to_workspace(I3_CMD, char *which) {
388     owindow *current;
389
390     DLOG("which=%s\n", which);
391
392     /* We have nothing to move:
393      *  when criteria was specified but didn't match any window or
394      *  when criteria wasn't specified and we don't have any window focused. */
395     if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
396         (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
397         !con_has_children(focused))) {
398         ysuccess(false);
399         return;
400     }
401
402     HANDLE_EMPTY_MATCH;
403
404     /* get the workspace */
405     Con *ws;
406     if (strcmp(which, "next") == 0)
407         ws = workspace_next();
408     else if (strcmp(which, "prev") == 0)
409         ws = workspace_prev();
410     else if (strcmp(which, "next_on_output") == 0)
411         ws = workspace_next_on_output();
412     else if (strcmp(which, "prev_on_output") == 0)
413         ws = workspace_prev_on_output();
414     else if (strcmp(which, "current") == 0)
415         ws = con_get_workspace(focused);
416     else {
417         ELOG("BUG: called with which=%s\n", which);
418         ysuccess(false);
419         return;
420     }
421
422     TAILQ_FOREACH(current, &owindows, owindows) {
423         DLOG("matching: %p / %s\n", current->con, current->con->name);
424         con_move_to_workspace(current->con, ws, true, false);
425     }
426
427     cmd_output->needs_tree_render = true;
428     // XXX: default reply for now, make this a better reply
429     ysuccess(true);
430 }
431
432 /**
433  * Implementation of 'move [window|container] [to] workspace back_and_forth'.
434  *
435  */
436 void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
437     owindow *current;
438     Con *ws;
439
440     ws = workspace_back_and_forth_get();
441
442     if (ws == NULL) {
443         y(map_open);
444         ystr("success");
445         y(bool, false);
446         ystr("error");
447         ystr("No workspace was previously active.");
448         y(map_close);
449         return;
450     }
451
452     HANDLE_EMPTY_MATCH;
453
454     TAILQ_FOREACH(current, &owindows, owindows) {
455         DLOG("matching: %p / %s\n", current->con, current->con->name);
456         con_move_to_workspace(current->con, ws, true, false);
457     }
458
459     cmd_output->needs_tree_render = true;
460     // XXX: default reply for now, make this a better reply
461     ysuccess(true);
462 }
463
464 /*
465  * Implementation of 'move [window|container] [to] workspace <name>'.
466  *
467  */
468 void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
469     if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
470         LOG("You cannot switch to the i3 internal workspaces.\n");
471         ysuccess(false);
472         return;
473     }
474
475     owindow *current;
476
477     /* We have nothing to move:
478      *  when criteria was specified but didn't match any window or
479      *  when criteria wasn't specified and we don't have any window focused. */
480     if (!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) {
481         ELOG("No windows match your criteria, cannot move.\n");
482         ysuccess(false);
483         return;
484     }
485     else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
486         !con_has_children(focused)) {
487         ysuccess(false);
488         return;
489     }
490
491     LOG("should move window to workspace %s\n", name);
492     /* get the workspace */
493     Con *ws = workspace_get(name, NULL);
494
495     ws = maybe_auto_back_and_forth_workspace(ws);
496
497     HANDLE_EMPTY_MATCH;
498
499     TAILQ_FOREACH(current, &owindows, owindows) {
500         DLOG("matching: %p / %s\n", current->con, current->con->name);
501         con_move_to_workspace(current->con, ws, true, false);
502     }
503
504     cmd_output->needs_tree_render = true;
505     // XXX: default reply for now, make this a better reply
506     ysuccess(true);
507 }
508
509 /*
510  * Implementation of 'move [window|container] [to] workspace number <name>'.
511  *
512  */
513 void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
514     owindow *current;
515
516     /* We have nothing to move:
517      *  when criteria was specified but didn't match any window or
518      *  when criteria wasn't specified and we don't have any window focused. */
519     if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
520         (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
521         !con_has_children(focused))) {
522         ysuccess(false);
523         return;
524     }
525
526     LOG("should move window to workspace %s\n", which);
527     /* get the workspace */
528     Con *output, *workspace = NULL;
529
530     char *endptr = NULL;
531     long parsed_num = strtol(which, &endptr, 10);
532     if (parsed_num == LONG_MIN ||
533         parsed_num == LONG_MAX ||
534         parsed_num < 0 ||
535         endptr == which) {
536         LOG("Could not parse initial part of \"%s\" as a number.\n", which);
537         y(map_open);
538         ystr("success");
539         y(bool, false);
540         ystr("error");
541         // TODO: better error message
542         ystr("Could not parse number");
543         y(map_close);
544         return;
545     }
546
547     TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
548         GREP_FIRST(workspace, output_get_content(output),
549             child->num == parsed_num);
550
551     if (!workspace) {
552         workspace = workspace_get(which, NULL);
553     }
554
555     workspace = maybe_auto_back_and_forth_workspace(workspace);
556
557     HANDLE_EMPTY_MATCH;
558
559     TAILQ_FOREACH(current, &owindows, owindows) {
560         DLOG("matching: %p / %s\n", current->con, current->con->name);
561         con_move_to_workspace(current->con, workspace, true, false);
562     }
563
564     cmd_output->needs_tree_render = true;
565     // XXX: default reply for now, make this a better reply
566     ysuccess(true);
567 }
568
569 static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
570     LOG("floating resize\n");
571     Rect old_rect = floating_con->rect;
572
573     if (strcmp(direction, "up") == 0) {
574         floating_con->rect.height += px;
575     } else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
576         floating_con->rect.height += px;
577     } else if (strcmp(direction, "left") == 0) {
578         floating_con->rect.width += px;
579     } else {
580         floating_con->rect.width += px;
581     }
582
583     floating_check_size(floating_con);
584
585     /* Did we actually resize anything or did the size constraints prevent us?
586      * If we could not resize, exit now to not move the window. */
587     if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0)
588         return;
589
590     if (strcmp(direction, "up") == 0) {
591         floating_con->rect.y -= (floating_con->rect.height - old_rect.height);
592     } else if (strcmp(direction, "left") == 0) {
593         floating_con->rect.x -= (floating_con->rect.width - old_rect.width);
594     }
595
596     /* If this is a scratchpad window, don't auto center it from now on. */
597     if (floating_con->scratchpad_state == SCRATCHPAD_FRESH)
598         floating_con->scratchpad_state = SCRATCHPAD_CHANGED;
599 }
600
601 static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) {
602     LOG("tiling resize\n");
603     /* get the appropriate current container (skip stacked/tabbed cons) */
604     Con *other = NULL;
605     double percentage = 0;
606     while (current->parent->layout == L_STACKED ||
607            current->parent->layout == L_TABBED)
608         current = current->parent;
609
610     /* Then further go up until we find one with the matching orientation. */
611     orientation_t search_orientation =
612         (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
613
614     do {
615         if (con_orientation(current->parent) != search_orientation) {
616             current = current->parent;
617             continue;
618         }
619
620         /* get the default percentage */
621         int children = con_num_children(current->parent);
622         LOG("ins. %d children\n", children);
623         percentage = 1.0 / children;
624         LOG("default percentage = %f\n", percentage);
625
626         orientation_t orientation = con_orientation(current->parent);
627
628         if ((orientation == HORIZ &&
629              (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
630             (orientation == VERT &&
631              (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
632             LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
633                 (orientation == HORIZ ? "horizontal" : "vertical"));
634             ysuccess(false);
635             return false;
636         }
637
638         if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
639             other = TAILQ_PREV(current, nodes_head, nodes);
640         } else {
641             other = TAILQ_NEXT(current, nodes);
642         }
643         if (other == TAILQ_END(workspaces)) {
644             LOG("No other container in this direction found, trying to look further up in the tree...\n");
645             current = current->parent;
646             continue;
647         }
648         break;
649     } while (current->type != CT_WORKSPACE &&
650              current->type != CT_FLOATING_CON);
651
652     if (other == NULL) {
653         LOG("No other container in this direction found, trying to look further up in the tree...\n");
654         ysuccess(false);
655         return false;
656     }
657
658     LOG("other->percent = %f\n", other->percent);
659     LOG("current->percent before = %f\n", current->percent);
660     if (current->percent == 0.0)
661         current->percent = percentage;
662     if (other->percent == 0.0)
663         other->percent = percentage;
664     double new_current_percent = current->percent + ((double)ppt / 100.0);
665     double new_other_percent = other->percent - ((double)ppt / 100.0);
666     LOG("new_current_percent = %f\n", new_current_percent);
667     LOG("new_other_percent = %f\n", new_other_percent);
668     /* Ensure that the new percentages are positive and greater than
669      * 0.05 to have a reasonable minimum size. */
670     if (definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON) &&
671         definitelyGreaterThan(new_other_percent, 0.05, DBL_EPSILON)) {
672         current->percent += ((double)ppt / 100.0);
673         other->percent -= ((double)ppt / 100.0);
674         LOG("current->percent after = %f\n", current->percent);
675         LOG("other->percent after = %f\n", other->percent);
676     } else {
677         LOG("Not resizing, already at minimum size\n");
678     }
679
680     return true;
681 }
682
683 static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt) {
684     LOG("width/height resize\n");
685     /* get the appropriate current container (skip stacked/tabbed cons) */
686     while (current->parent->layout == L_STACKED ||
687            current->parent->layout == L_TABBED)
688         current = current->parent;
689
690     /* Then further go up until we find one with the matching orientation. */
691     orientation_t search_orientation =
692         (strcmp(direction, "width") == 0 ? HORIZ : VERT);
693
694     while (current->type != CT_WORKSPACE &&
695            current->type != CT_FLOATING_CON &&
696            con_orientation(current->parent) != search_orientation)
697         current = current->parent;
698
699     /* get the default percentage */
700     int children = con_num_children(current->parent);
701     LOG("ins. %d children\n", children);
702     double percentage = 1.0 / children;
703     LOG("default percentage = %f\n", percentage);
704
705     orientation_t orientation = con_orientation(current->parent);
706
707     if ((orientation == HORIZ &&
708          strcmp(direction, "height") == 0) ||
709         (orientation == VERT &&
710          strcmp(direction, "width") == 0)) {
711         LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
712             (orientation == HORIZ ? "horizontal" : "vertical"));
713         ysuccess(false);
714         return false;
715     }
716
717     if (children == 1) {
718         LOG("This is the only container, cannot resize.\n");
719         ysuccess(false);
720         return false;
721     }
722
723     /* Ensure all the other children have a percentage set. */
724     Con *child;
725     TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
726         LOG("child->percent = %f (child %p)\n", child->percent, child);
727         if (child->percent == 0.0)
728             child->percent = percentage;
729     }
730
731     double new_current_percent = current->percent + ((double)ppt / 100.0);
732     double subtract_percent = ((double)ppt / 100.0) / (children - 1);
733     LOG("new_current_percent = %f\n", new_current_percent);
734     LOG("subtract_percent = %f\n", subtract_percent);
735     /* Ensure that the new percentages are positive and greater than
736      * 0.05 to have a reasonable minimum size. */
737     TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
738         if (child == current)
739             continue;
740         if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
741             LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent);
742             ysuccess(false);
743             return false;
744         }
745     }
746     if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) {
747         LOG("Not resizing, already at minimum size\n");
748         ysuccess(false);
749         return false;
750     }
751
752     current->percent += ((double)ppt / 100.0);
753     LOG("current->percent after = %f\n", current->percent);
754
755     TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
756         if (child == current)
757             continue;
758         child->percent -= subtract_percent;
759         LOG("child->percent after (%p) = %f\n", child, child->percent);
760     }
761
762     return true;
763 }
764
765 /*
766  * Implementation of 'resize grow|shrink <direction> [<px> px] [or <ppt> ppt]'.
767  *
768  */
769 void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resize_ppt) {
770     /* resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt] */
771     DLOG("resizing in way %s, direction %s, px %s or ppt %s\n", way, direction, resize_px, resize_ppt);
772     // 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
773     int px = atoi(resize_px);
774     int ppt = atoi(resize_ppt);
775     if (strcmp(way, "shrink") == 0) {
776         px *= -1;
777         ppt *= -1;
778     }
779
780     HANDLE_EMPTY_MATCH;
781
782     owindow *current;
783     TAILQ_FOREACH(current, &owindows, owindows) {
784         Con *floating_con;
785         if ((floating_con = con_inside_floating(current->con))) {
786             cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
787         } else {
788             if (strcmp(direction, "width") == 0 ||
789                 strcmp(direction, "height") == 0) {
790                 if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt))
791                     return;
792             } else {
793                 if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt))
794                     return;
795             }
796         }
797     }
798
799     cmd_output->needs_tree_render = true;
800     // XXX: default reply for now, make this a better reply
801     ysuccess(true);
802 }
803
804 /*
805  * Implementation of 'border normal|none|1pixel|toggle|pixel'.
806  *
807  */
808 void cmd_border(I3_CMD, char *border_style_str, char *border_width ) {
809     DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width);
810     owindow *current;
811
812     HANDLE_EMPTY_MATCH;
813
814     TAILQ_FOREACH(current, &owindows, owindows) {
815         DLOG("matching: %p / %s\n", current->con, current->con->name);
816         int border_style = current->con->border_style;
817         char *end;
818         int tmp_border_width = -1;
819         tmp_border_width = strtol(border_width, &end, 10);
820         if (end == border_width) {
821             /* no valid digits found */
822             tmp_border_width = -1;
823         }
824         if (strcmp(border_style_str, "toggle") == 0) {
825             border_style++;
826             border_style %= 3;
827             if (border_style == BS_NORMAL)
828                 tmp_border_width = 2;
829             else if (border_style == BS_NONE)
830                 tmp_border_width = 0;
831             else if (border_style == BS_PIXEL)
832                 tmp_border_width = 1;
833         } else {
834             if (strcmp(border_style_str, "normal") == 0)
835                 border_style = BS_NORMAL;
836             else if (strcmp(border_style_str, "pixel") == 0)
837                 border_style = BS_PIXEL;
838             else if (strcmp(border_style_str, "1pixel") == 0){
839                 border_style = BS_PIXEL;
840                 tmp_border_width = 1;
841             } else if (strcmp(border_style_str, "none") == 0)
842                 border_style = BS_NONE;
843             else {
844                 ELOG("BUG: called with border_style=%s\n", border_style_str);
845                 ysuccess(false);
846                 return;
847             }
848         }
849         con_set_border_style(current->con, border_style, tmp_border_width);
850     }
851
852     cmd_output->needs_tree_render = true;
853     // XXX: default reply for now, make this a better reply
854     ysuccess(true);
855 }
856
857 /*
858  * Implementation of 'nop <comment>'.
859  *
860  */
861 void cmd_nop(I3_CMD, char *comment) {
862     LOG("-------------------------------------------------\n");
863     LOG("  NOP: %s\n", comment);
864     LOG("-------------------------------------------------\n");
865 }
866
867 /*
868  * Implementation of 'append_layout <path>'.
869  *
870  */
871 void cmd_append_layout(I3_CMD, char *path) {
872     LOG("Appending layout \"%s\"\n", path);
873     tree_append_json(path);
874
875     cmd_output->needs_tree_render = true;
876     // XXX: default reply for now, make this a better reply
877     ysuccess(true);
878 }
879
880 /*
881  * Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
882  *
883  */
884 void cmd_workspace(I3_CMD, char *which) {
885     Con *ws;
886
887     DLOG("which=%s\n", which);
888
889     if (strcmp(which, "next") == 0)
890         ws = workspace_next();
891     else if (strcmp(which, "prev") == 0)
892         ws = workspace_prev();
893     else if (strcmp(which, "next_on_output") == 0)
894         ws = workspace_next_on_output();
895     else if (strcmp(which, "prev_on_output") == 0)
896         ws = workspace_prev_on_output();
897     else {
898         ELOG("BUG: called with which=%s\n", which);
899         ysuccess(false);
900         return;
901     }
902
903     workspace_show(ws);
904
905     cmd_output->needs_tree_render = true;
906     // XXX: default reply for now, make this a better reply
907     ysuccess(true);
908 }
909
910 /*
911  * Implementation of 'workspace number <name>'
912  *
913  */
914 void cmd_workspace_number(I3_CMD, char *which) {
915     Con *output, *workspace = NULL;
916
917     char *endptr = NULL;
918     long parsed_num = strtol(which, &endptr, 10);
919     if (parsed_num == LONG_MIN ||
920         parsed_num == LONG_MAX ||
921         parsed_num < 0 ||
922         endptr == which) {
923         LOG("Could not parse initial part of \"%s\" as a number.\n", which);
924         y(map_open);
925         ystr("success");
926         y(bool, false);
927         ystr("error");
928         // TODO: better error message
929         ystr("Could not parse number");
930         y(map_close);
931
932         return;
933     }
934
935     TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
936         GREP_FIRST(workspace, output_get_content(output),
937             child->num == parsed_num);
938
939     if (!workspace) {
940         LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
941         ysuccess(true);
942         workspace_show_by_name(which);
943         cmd_output->needs_tree_render = true;
944         return;
945     }
946     if (maybe_back_and_forth(cmd_output, workspace->name))
947         return;
948     workspace_show(workspace);
949
950     cmd_output->needs_tree_render = true;
951     // XXX: default reply for now, make this a better reply
952     ysuccess(true);
953 }
954
955 /*
956  * Implementation of 'workspace back_and_forth'.
957  *
958  */
959 void cmd_workspace_back_and_forth(I3_CMD) {
960     workspace_back_and_forth();
961
962     cmd_output->needs_tree_render = true;
963     // XXX: default reply for now, make this a better reply
964     ysuccess(true);
965 }
966
967 /*
968  * Implementation of 'workspace <name>'
969  *
970  */
971 void cmd_workspace_name(I3_CMD, char *name) {
972     if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
973         LOG("You cannot switch to the i3 internal workspaces.\n");
974         ysuccess(false);
975         return;
976     }
977
978     DLOG("should switch to workspace %s\n", name);
979     if (maybe_back_and_forth(cmd_output, name))
980        return;
981     workspace_show_by_name(name);
982
983     cmd_output->needs_tree_render = true;
984     // XXX: default reply for now, make this a better reply
985     ysuccess(true);
986 }
987
988 /*
989  * Implementation of 'mark <mark>'
990  *
991  */
992 void cmd_mark(I3_CMD, char *mark) {
993     DLOG("Clearing all windows which have that mark first\n");
994
995     Con *con;
996     TAILQ_FOREACH(con, &all_cons, all_cons) {
997         if (con->mark && strcmp(con->mark, mark) == 0)
998             FREE(con->mark);
999     }
1000
1001     DLOG("marking window with str %s\n", mark);
1002     owindow *current;
1003
1004     HANDLE_EMPTY_MATCH;
1005
1006     TAILQ_FOREACH(current, &owindows, owindows) {
1007         DLOG("matching: %p / %s\n", current->con, current->con->name);
1008         current->con->mark = sstrdup(mark);
1009     }
1010
1011     cmd_output->needs_tree_render = true;
1012     // XXX: default reply for now, make this a better reply
1013     ysuccess(true);
1014 }
1015
1016 /*
1017  * Implementation of 'mode <string>'.
1018  *
1019  */
1020 void cmd_mode(I3_CMD, char *mode) {
1021     DLOG("mode=%s\n", mode);
1022     switch_mode(mode);
1023
1024     // XXX: default reply for now, make this a better reply
1025     ysuccess(true);
1026 }
1027
1028 /*
1029  * Implementation of 'move [window|container] [to] output <str>'.
1030  *
1031  */
1032 void cmd_move_con_to_output(I3_CMD, char *name) {
1033     owindow *current;
1034
1035     DLOG("should move window to output %s\n", name);
1036
1037     HANDLE_EMPTY_MATCH;
1038
1039     /* get the output */
1040     Output *current_output = NULL;
1041     Output *output;
1042
1043     // TODO: fix the handling of criteria
1044     TAILQ_FOREACH(current, &owindows, owindows)
1045         current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
1046
1047     assert(current_output != NULL);
1048
1049     // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
1050     if (strcasecmp(name, "up") == 0)
1051         output = get_output_next_wrap(D_UP, current_output);
1052     else if (strcasecmp(name, "down") == 0)
1053         output = get_output_next_wrap(D_DOWN, current_output);
1054     else if (strcasecmp(name, "left") == 0)
1055         output = get_output_next_wrap(D_LEFT, current_output);
1056     else if (strcasecmp(name, "right") == 0)
1057         output = get_output_next_wrap(D_RIGHT, current_output);
1058     else
1059         output = get_output_by_name(name);
1060
1061     if (!output) {
1062         LOG("No such output found.\n");
1063         ysuccess(false);
1064         return;
1065     }
1066
1067     /* get visible workspace on output */
1068     Con *ws = NULL;
1069     GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1070     if (!ws) {
1071         ysuccess(false);
1072         return;
1073     }
1074
1075     TAILQ_FOREACH(current, &owindows, owindows) {
1076         DLOG("matching: %p / %s\n", current->con, current->con->name);
1077         con_move_to_workspace(current->con, ws, true, false);
1078     }
1079
1080     cmd_output->needs_tree_render = true;
1081     // XXX: default reply for now, make this a better reply
1082     ysuccess(true);
1083 }
1084
1085 /*
1086  * Implementation of 'floating enable|disable|toggle'
1087  *
1088  */
1089 void cmd_floating(I3_CMD, char *floating_mode) {
1090     owindow *current;
1091
1092     DLOG("floating_mode=%s\n", floating_mode);
1093
1094     HANDLE_EMPTY_MATCH;
1095
1096     TAILQ_FOREACH(current, &owindows, owindows) {
1097         DLOG("matching: %p / %s\n", current->con, current->con->name);
1098         if (strcmp(floating_mode, "toggle") == 0) {
1099             DLOG("should toggle mode\n");
1100             toggle_floating_mode(current->con, false);
1101         } else {
1102             DLOG("should switch mode to %s\n", floating_mode);
1103             if (strcmp(floating_mode, "enable") == 0) {
1104                 floating_enable(current->con, false);
1105             } else {
1106                 floating_disable(current->con, false);
1107             }
1108         }
1109     }
1110
1111     cmd_output->needs_tree_render = true;
1112     // XXX: default reply for now, make this a better reply
1113     ysuccess(true);
1114 }
1115
1116 /*
1117  * Implementation of 'move workspace to [output] <str>'.
1118  *
1119  */
1120 void cmd_move_workspace_to_output(I3_CMD, char *name) {
1121     DLOG("should move workspace to output %s\n", name);
1122
1123     HANDLE_EMPTY_MATCH;
1124
1125     owindow *current;
1126     TAILQ_FOREACH(current, &owindows, owindows) {
1127         Output *current_output = get_output_containing(current->con->rect.x,
1128                                                        current->con->rect.y);
1129         if (!current_output) {
1130             ELOG("Cannot get current output. This is a bug in i3.\n");
1131             ysuccess(false);
1132             return;
1133         }
1134         Output *output = get_output_from_string(current_output, name);
1135         if (!output) {
1136             ELOG("Could not get output from string \"%s\"\n", name);
1137             ysuccess(false);
1138             return;
1139         }
1140
1141         Con *content = output_get_content(output->con);
1142         LOG("got output %p with content %p\n", output, content);
1143
1144         Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
1145         LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
1146
1147         Con *ws = con_get_workspace(current->con);
1148         LOG("should move workspace %p / %s\n", ws, ws->name);
1149         bool workspace_was_visible = workspace_is_visible(ws);
1150
1151         if (con_num_children(ws->parent) == 1) {
1152             LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
1153
1154             /* check if we can find a workspace assigned to this output */
1155             bool used_assignment = false;
1156             struct Workspace_Assignment *assignment;
1157             TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
1158                 if (strcmp(assignment->output, current_output->name) != 0)
1159                     continue;
1160
1161                 /* check if this workspace is already attached to the tree */
1162                 Con *workspace = NULL, *out;
1163                 TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
1164                     GREP_FIRST(workspace, output_get_content(out),
1165                                !strcasecmp(child->name, assignment->name));
1166                 if (workspace != NULL)
1167                     continue;
1168
1169                 /* so create the workspace referenced to by this assignment */
1170                 LOG("Creating workspace from assignment %s.\n", assignment->name);
1171                 workspace_get(assignment->name, NULL);
1172                 used_assignment = true;
1173                 break;
1174             }
1175
1176             /* if we couldn't create the workspace using an assignment, create
1177              * it on the output */
1178             if (!used_assignment)
1179                 create_workspace_on_output(current_output, ws->parent);
1180
1181             /* notify the IPC listeners */
1182             ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
1183         }
1184         DLOG("Detaching\n");
1185
1186         /* detach from the old output and attach to the new output */
1187         Con *old_content = ws->parent;
1188         con_detach(ws);
1189         if (workspace_was_visible) {
1190             /* The workspace which we just detached was visible, so focus
1191              * the next one in the focus-stack. */
1192             Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
1193             LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
1194             workspace_show(focus_ws);
1195         }
1196         con_attach(ws, content, false);
1197
1198         /* fix the coordinates of the floating containers */
1199         Con *floating_con;
1200         TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
1201             floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
1202
1203         ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
1204         if (workspace_was_visible) {
1205             /* Focus the moved workspace on the destination output. */
1206             workspace_show(ws);
1207         }
1208
1209         /* NB: We cannot simply work with previously_visible_ws since it might
1210          * have been cleaned up by workspace_show() already, depending on the
1211          * focus order/number of other workspaces on the output.
1212          * Instead, we loop through the available workspaces and only work with
1213          * previously_visible_ws if we still find it. */
1214         TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
1215             if (ws != previously_visible_ws)
1216                 continue;
1217
1218             /* Call the on_remove_child callback of the workspace which previously
1219              * was visible on the destination output. Since it is no longer
1220              * visible, it might need to get cleaned up. */
1221             CALL(previously_visible_ws, on_remove_child);
1222             break;
1223         }
1224     }
1225
1226     cmd_output->needs_tree_render = true;
1227     // XXX: default reply for now, make this a better reply
1228     ysuccess(true);
1229 }
1230
1231 /*
1232  * Implementation of 'split v|h|vertical|horizontal'.
1233  *
1234  */
1235 void cmd_split(I3_CMD, char *direction) {
1236     owindow *current;
1237     /* TODO: use matches */
1238     LOG("splitting in direction %c\n", direction[0]);
1239     if (match_is_empty(current_match))
1240         tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
1241     else {
1242         TAILQ_FOREACH(current, &owindows, owindows) {
1243             DLOG("matching: %p / %s\n", current->con, current->con->name);
1244             tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
1245         }
1246     }
1247
1248     cmd_output->needs_tree_render = true;
1249     // XXX: default reply for now, make this a better reply
1250     ysuccess(true);
1251 }
1252
1253 /*
1254  * Implementation of 'kill [window|client]'.
1255  *
1256  */
1257 void cmd_kill(I3_CMD, char *kill_mode_str) {
1258     if (kill_mode_str == NULL)
1259         kill_mode_str = "window";
1260     owindow *current;
1261
1262     DLOG("kill_mode=%s\n", kill_mode_str);
1263
1264     int kill_mode;
1265     if (strcmp(kill_mode_str, "window") == 0)
1266         kill_mode = KILL_WINDOW;
1267     else if (strcmp(kill_mode_str, "client") == 0)
1268         kill_mode = KILL_CLIENT;
1269     else {
1270         ELOG("BUG: called with kill_mode=%s\n", kill_mode_str);
1271         ysuccess(false);
1272         return;
1273     }
1274
1275     /* check if the match is empty, not if the result is empty */
1276     if (match_is_empty(current_match))
1277         tree_close_con(kill_mode);
1278     else {
1279         TAILQ_FOREACH(current, &owindows, owindows) {
1280             DLOG("matching: %p / %s\n", current->con, current->con->name);
1281             tree_close(current->con, kill_mode, false, false);
1282         }
1283     }
1284
1285     cmd_output->needs_tree_render = true;
1286     // XXX: default reply for now, make this a better reply
1287     ysuccess(true);
1288 }
1289
1290 /*
1291  * Implementation of 'exec [--no-startup-id] <command>'.
1292  *
1293  */
1294 void cmd_exec(I3_CMD, char *nosn, char *command) {
1295     bool no_startup_id = (nosn != NULL);
1296
1297     DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
1298     start_application(command, no_startup_id);
1299
1300     // XXX: default reply for now, make this a better reply
1301     ysuccess(true);
1302 }
1303
1304 /*
1305  * Implementation of 'focus left|right|up|down'.
1306  *
1307  */
1308 void cmd_focus_direction(I3_CMD, char *direction) {
1309     DLOG("direction = *%s*\n", direction);
1310
1311     if (strcmp(direction, "left") == 0)
1312         tree_next('p', HORIZ);
1313     else if (strcmp(direction, "right") == 0)
1314         tree_next('n', HORIZ);
1315     else if (strcmp(direction, "up") == 0)
1316         tree_next('p', VERT);
1317     else if (strcmp(direction, "down") == 0)
1318         tree_next('n', VERT);
1319     else {
1320         ELOG("Invalid focus direction (%s)\n", direction);
1321         ysuccess(false);
1322         return;
1323     }
1324
1325     cmd_output->needs_tree_render = true;
1326     // XXX: default reply for now, make this a better reply
1327     ysuccess(true);
1328 }
1329
1330 /*
1331  * Implementation of 'focus tiling|floating|mode_toggle'.
1332  *
1333  */
1334 void cmd_focus_window_mode(I3_CMD, char *window_mode) {
1335     DLOG("window_mode = %s\n", window_mode);
1336
1337     Con *ws = con_get_workspace(focused);
1338     Con *current;
1339     if (ws != NULL) {
1340         if (strcmp(window_mode, "mode_toggle") == 0) {
1341             current = TAILQ_FIRST(&(ws->focus_head));
1342             if (current != NULL && current->type == CT_FLOATING_CON)
1343                 window_mode = "tiling";
1344             else window_mode = "floating";
1345         }
1346         TAILQ_FOREACH(current, &(ws->focus_head), focused) {
1347             if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
1348                 (strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
1349                 continue;
1350
1351             con_focus(con_descend_focused(current));
1352             break;
1353         }
1354     }
1355
1356     cmd_output->needs_tree_render = true;
1357     // XXX: default reply for now, make this a better reply
1358     ysuccess(true);
1359 }
1360
1361 /*
1362  * Implementation of 'focus parent|child'.
1363  *
1364  */
1365 void cmd_focus_level(I3_CMD, char *level) {
1366     DLOG("level = %s\n", level);
1367     bool success = false;
1368
1369     /* Focusing the parent can only be allowed if the newly
1370      * focused container won't escape the fullscreen container. */
1371     if (strcmp(level, "parent") == 0) {
1372         if (focused && focused->parent) {
1373             if (con_fullscreen_permits_focusing(focused->parent))
1374                 success = level_up();
1375             else
1376                 ELOG("'focus parent': Currently in fullscreen, not going up\n");
1377         }
1378     }
1379
1380     /* Focusing a child should always be allowed. */
1381     else success = level_down();
1382
1383     cmd_output->needs_tree_render = success;
1384     // XXX: default reply for now, make this a better reply
1385     ysuccess(success);
1386 }
1387
1388 /*
1389  * Implementation of 'focus'.
1390  *
1391  */
1392 void cmd_focus(I3_CMD) {
1393     DLOG("current_match = %p\n", current_match);
1394
1395     if (match_is_empty(current_match)) {
1396         ELOG("You have to specify which window/container should be focused.\n");
1397         ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
1398
1399         y(map_open);
1400         ystr("success");
1401         y(bool, false);
1402         ystr("error");
1403         ystr("You have to specify which window/container should be focused");
1404         y(map_close);
1405
1406         return;
1407     }
1408
1409     Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
1410     int count = 0;
1411     owindow *current;
1412     TAILQ_FOREACH(current, &owindows, owindows) {
1413         Con *ws = con_get_workspace(current->con);
1414         /* If no workspace could be found, this was a dock window.
1415          * Just skip it, you cannot focus dock windows. */
1416         if (!ws)
1417             continue;
1418
1419         /* Check the fullscreen focus constraints. */
1420         if (!con_fullscreen_permits_focusing(current->con)) {
1421             LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
1422             ysuccess(false);
1423             return;
1424         }
1425
1426         /* In case this is a scratchpad window, call scratchpad_show(). */
1427         if (ws == __i3_scratch) {
1428             scratchpad_show(current->con);
1429             count++;
1430             /* While for the normal focus case we can change focus multiple
1431              * times and only a single window ends up focused, we could show
1432              * multiple scratchpad windows. So, rather break here. */
1433             break;
1434         }
1435
1436         /* If the container is not on the current workspace,
1437          * workspace_show() will switch to a different workspace and (if
1438          * enabled) trigger a mouse pointer warp to the currently focused
1439          * container (!) on the target workspace.
1440          *
1441          * Therefore, before calling workspace_show(), we make sure that
1442          * 'current' will be focused on the workspace. However, we cannot
1443          * just con_focus(current) because then the pointer will not be
1444          * warped at all (the code thinks we are already there).
1445          *
1446          * So we focus 'current' to make it the currently focused window of
1447          * the target workspace, then revert focus. */
1448         Con *currently_focused = focused;
1449         con_focus(current->con);
1450         con_focus(currently_focused);
1451
1452         /* Now switch to the workspace, then focus */
1453         workspace_show(ws);
1454         LOG("focusing %p / %s\n", current->con, current->con->name);
1455         con_focus(current->con);
1456         count++;
1457     }
1458
1459     if (count > 1)
1460         LOG("WARNING: Your criteria for the focus command matches %d containers, "
1461             "while only exactly one container can be focused at a time.\n", count);
1462
1463     cmd_output->needs_tree_render = true;
1464     // XXX: default reply for now, make this a better reply
1465     ysuccess(true);
1466 }
1467
1468 /*
1469  * Implementation of 'fullscreen [global]'.
1470  *
1471  */
1472 void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
1473     if (fullscreen_mode == NULL)
1474         fullscreen_mode = "output";
1475     DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
1476     owindow *current;
1477
1478     HANDLE_EMPTY_MATCH;
1479
1480     TAILQ_FOREACH(current, &owindows, owindows) {
1481         printf("matching: %p / %s\n", current->con, current->con->name);
1482         con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
1483     }
1484
1485     cmd_output->needs_tree_render = true;
1486     // XXX: default reply for now, make this a better reply
1487     ysuccess(true);
1488 }
1489
1490 /*
1491  * Implementation of 'move <direction> [<pixels> [px]]'.
1492  *
1493  */
1494 void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
1495     // 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
1496     int px = atoi(move_px);
1497
1498     /* TODO: make 'move' work with criteria. */
1499     DLOG("moving in direction %s, px %s\n", direction, move_px);
1500     if (con_is_floating(focused)) {
1501         DLOG("floating move with %d pixels\n", px);
1502         Rect newrect = focused->parent->rect;
1503         if (strcmp(direction, "left") == 0) {
1504             newrect.x -= px;
1505         } else if (strcmp(direction, "right") == 0) {
1506             newrect.x += px;
1507         } else if (strcmp(direction, "up") == 0) {
1508             newrect.y -= px;
1509         } else if (strcmp(direction, "down") == 0) {
1510             newrect.y += px;
1511         }
1512         floating_reposition(focused->parent, newrect);
1513     } else {
1514         tree_move((strcmp(direction, "right") == 0 ? D_RIGHT :
1515                    (strcmp(direction, "left") == 0 ? D_LEFT :
1516                     (strcmp(direction, "up") == 0 ? D_UP :
1517                      D_DOWN))));
1518         cmd_output->needs_tree_render = true;
1519     }
1520
1521     // XXX: default reply for now, make this a better reply
1522     ysuccess(true);
1523 }
1524
1525 /*
1526  * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
1527  *
1528  */
1529 void cmd_layout(I3_CMD, char *layout_str) {
1530     if (strcmp(layout_str, "stacking") == 0)
1531         layout_str = "stacked";
1532     owindow *current;
1533     int layout;
1534     /* default is a special case which will be handled in con_set_layout(). */
1535     if (strcmp(layout_str, "default") == 0)
1536         layout = L_DEFAULT;
1537     else if (strcmp(layout_str, "stacked") == 0)
1538         layout = L_STACKED;
1539     else if (strcmp(layout_str, "tabbed") == 0)
1540         layout = L_TABBED;
1541     else if (strcmp(layout_str, "splitv") == 0)
1542         layout = L_SPLITV;
1543     else if (strcmp(layout_str, "splith") == 0)
1544         layout = L_SPLITH;
1545     else {
1546         ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
1547         return;
1548     }
1549
1550     DLOG("changing layout to %s (%d)\n", layout_str, layout);
1551
1552     /* check if the match is empty, not if the result is empty */
1553     if (match_is_empty(current_match))
1554         con_set_layout(focused, layout);
1555     else {
1556         TAILQ_FOREACH(current, &owindows, owindows) {
1557             DLOG("matching: %p / %s\n", current->con, current->con->name);
1558             con_set_layout(current->con, layout);
1559         }
1560     }
1561
1562     cmd_output->needs_tree_render = true;
1563     // XXX: default reply for now, make this a better reply
1564     ysuccess(true);
1565 }
1566
1567 /*
1568  * Implementation of 'layout toggle [all|split]'.
1569  *
1570  */
1571 void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
1572     owindow *current;
1573
1574     if (toggle_mode == NULL)
1575         toggle_mode = "default";
1576
1577     DLOG("toggling layout (mode = %s)\n", toggle_mode);
1578
1579     /* check if the match is empty, not if the result is empty */
1580     if (match_is_empty(current_match))
1581         con_toggle_layout(focused, toggle_mode);
1582     else {
1583         TAILQ_FOREACH(current, &owindows, owindows) {
1584             DLOG("matching: %p / %s\n", current->con, current->con->name);
1585             con_toggle_layout(current->con, toggle_mode);
1586         }
1587     }
1588
1589     cmd_output->needs_tree_render = true;
1590     // XXX: default reply for now, make this a better reply
1591     ysuccess(true);
1592 }
1593
1594 /*
1595  * Implementation of 'exit'.
1596  *
1597  */
1598 void cmd_exit(I3_CMD) {
1599     LOG("Exiting due to user command.\n");
1600     xcb_disconnect(conn);
1601     exit(0);
1602
1603     /* unreached */
1604 }
1605
1606 /*
1607  * Implementation of 'reload'.
1608  *
1609  */
1610 void cmd_reload(I3_CMD) {
1611     LOG("reloading\n");
1612     kill_nagbar(&config_error_nagbar_pid, false);
1613     kill_nagbar(&command_error_nagbar_pid, false);
1614     load_configuration(conn, NULL, true);
1615     x_set_i3_atoms();
1616     /* Send an IPC event just in case the ws names have changed */
1617     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
1618
1619     // XXX: default reply for now, make this a better reply
1620     ysuccess(true);
1621 }
1622
1623 /*
1624  * Implementation of 'restart'.
1625  *
1626  */
1627 void cmd_restart(I3_CMD) {
1628     LOG("restarting i3\n");
1629     i3_restart(false);
1630
1631     // XXX: default reply for now, make this a better reply
1632     ysuccess(true);
1633 }
1634
1635 /*
1636  * Implementation of 'open'.
1637  *
1638  */
1639 void cmd_open(I3_CMD) {
1640     LOG("opening new container\n");
1641     Con *con = tree_open_con(NULL, NULL);
1642     con->layout = L_SPLITH;
1643     con_focus(con);
1644
1645     y(map_open);
1646     ystr("success");
1647     y(bool, true);
1648     ystr("id");
1649     y(integer, (long int)con);
1650     y(map_close);
1651
1652     cmd_output->needs_tree_render = true;
1653 }
1654
1655 /*
1656  * Implementation of 'focus output <output>'.
1657  *
1658  */
1659 void cmd_focus_output(I3_CMD, char *name) {
1660     owindow *current;
1661
1662     DLOG("name = %s\n", name);
1663
1664     HANDLE_EMPTY_MATCH;
1665
1666     /* get the output */
1667     Output *current_output = NULL;
1668     Output *output;
1669
1670     TAILQ_FOREACH(current, &owindows, owindows)
1671         current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
1672     assert(current_output != NULL);
1673
1674     output = get_output_from_string(current_output, name);
1675
1676     if (!output) {
1677         LOG("No such output found.\n");
1678         ysuccess(false);
1679         return;
1680     }
1681
1682     /* get visible workspace on output */
1683     Con *ws = NULL;
1684     GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1685     if (!ws) {
1686         ysuccess(false);
1687         return;
1688     }
1689
1690     workspace_show(ws);
1691
1692     cmd_output->needs_tree_render = true;
1693     // XXX: default reply for now, make this a better reply
1694     ysuccess(true);
1695 }
1696
1697 /*
1698  * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
1699  *
1700  */
1701 void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
1702
1703     int x = atoi(cx);
1704     int y = atoi(cy);
1705
1706     if (!con_is_floating(focused)) {
1707         ELOG("Cannot change position. The window/container is not floating\n");
1708         y(map_open);
1709         ystr("success");
1710         y(bool, false);
1711         ystr("error");
1712         ystr("Cannot change position. The window/container is not floating.");
1713         y(map_close);
1714         return;
1715     }
1716
1717     if (strcmp(method, "absolute") == 0) {
1718         focused->parent->rect.x = x;
1719         focused->parent->rect.y = y;
1720
1721         DLOG("moving to absolute position %d %d\n", x, y);
1722         floating_maybe_reassign_ws(focused->parent);
1723         cmd_output->needs_tree_render = true;
1724     }
1725
1726     if (strcmp(method, "position") == 0) {
1727         Rect newrect = focused->parent->rect;
1728
1729         DLOG("moving to position %d %d\n", x, y);
1730         newrect.x = x;
1731         newrect.y = y;
1732
1733         floating_reposition(focused->parent, newrect);
1734     }
1735
1736     // XXX: default reply for now, make this a better reply
1737     ysuccess(true);
1738 }
1739
1740 /*
1741  * Implementation of 'move [window|container] [to] [absolute] position center
1742  *
1743  */
1744 void cmd_move_window_to_center(I3_CMD, char *method) {
1745
1746     if (!con_is_floating(focused)) {
1747         ELOG("Cannot change position. The window/container is not floating\n");
1748         y(map_open);
1749         ystr("success");
1750         y(bool, false);
1751         ystr("error");
1752         ystr("Cannot change position. The window/container is not floating.");
1753         y(map_close);
1754     }
1755
1756     if (strcmp(method, "absolute") == 0) {
1757         Rect *rect = &focused->parent->rect;
1758
1759         DLOG("moving to absolute center\n");
1760         rect->x = croot->rect.width/2 - rect->width/2;
1761         rect->y = croot->rect.height/2 - rect->height/2;
1762
1763         floating_maybe_reassign_ws(focused->parent);
1764         cmd_output->needs_tree_render = true;
1765     }
1766
1767     if (strcmp(method, "position") == 0) {
1768         Rect *wsrect = &con_get_workspace(focused)->rect;
1769         Rect newrect = focused->parent->rect;
1770
1771         DLOG("moving to center\n");
1772         newrect.x = wsrect->width/2 - newrect.width/2;
1773         newrect.y = wsrect->height/2 - newrect.height/2;
1774
1775         floating_reposition(focused->parent, newrect);
1776     }
1777
1778     // XXX: default reply for now, make this a better reply
1779     ysuccess(true);
1780 }
1781
1782 /*
1783  * Implementation of 'move scratchpad'.
1784  *
1785  */
1786 void cmd_move_scratchpad(I3_CMD) {
1787     DLOG("should move window to scratchpad\n");
1788     owindow *current;
1789
1790     HANDLE_EMPTY_MATCH;
1791
1792     TAILQ_FOREACH(current, &owindows, owindows) {
1793         DLOG("matching: %p / %s\n", current->con, current->con->name);
1794         scratchpad_move(current->con);
1795     }
1796
1797     cmd_output->needs_tree_render = true;
1798     // XXX: default reply for now, make this a better reply
1799     ysuccess(true);
1800 }
1801
1802 /*
1803  * Implementation of 'scratchpad show'.
1804  *
1805  */
1806 void cmd_scratchpad_show(I3_CMD) {
1807     DLOG("should show scratchpad window\n");
1808     owindow *current;
1809
1810     if (match_is_empty(current_match)) {
1811         scratchpad_show(NULL);
1812     } else {
1813         TAILQ_FOREACH(current, &owindows, owindows) {
1814             DLOG("matching: %p / %s\n", current->con, current->con->name);
1815             scratchpad_show(current->con);
1816         }
1817     }
1818
1819     cmd_output->needs_tree_render = true;
1820     // XXX: default reply for now, make this a better reply
1821     ysuccess(true);
1822 }
1823
1824 /*
1825  * Implementation of 'rename workspace [<name>] to <name>'
1826  *
1827  */
1828 void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
1829     if (old_name) {
1830         LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
1831     } else {
1832         LOG("Renaming current workspace to \"%s\"\n", new_name);
1833     }
1834
1835     Con *output, *workspace = NULL;
1836     if (old_name) {
1837         TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
1838             GREP_FIRST(workspace, output_get_content(output),
1839                 !strcasecmp(child->name, old_name));
1840     } else {
1841         workspace = con_get_workspace(focused);
1842     }
1843
1844     if (!workspace) {
1845         // TODO: we should include the old workspace name here and use yajl for
1846         // generating the reply.
1847         y(map_open);
1848         ystr("success");
1849         y(bool, false);
1850         ystr("error");
1851         // TODO: better error message
1852         ystr("Old workspace not found");
1853         y(map_close);
1854         return;
1855     }
1856
1857     Con *check_dest = NULL;
1858     TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
1859         GREP_FIRST(check_dest, output_get_content(output),
1860             !strcasecmp(child->name, new_name));
1861
1862     if (check_dest != NULL) {
1863         // TODO: we should include the new workspace name here and use yajl for
1864         // generating the reply.
1865         y(map_open);
1866         ystr("success");
1867         y(bool, false);
1868         ystr("error");
1869         // TODO: better error message
1870         ystr("New workspace already exists");
1871         y(map_close);
1872         return;
1873     }
1874
1875     /* Change the name and try to parse it as a number. */
1876     FREE(workspace->name);
1877     workspace->name = sstrdup(new_name);
1878     char *endptr = NULL;
1879     long parsed_num = strtol(new_name, &endptr, 10);
1880     if (parsed_num == LONG_MIN ||
1881         parsed_num == LONG_MAX ||
1882         parsed_num < 0 ||
1883         endptr == new_name)
1884         workspace->num = -1;
1885     else workspace->num = parsed_num;
1886     LOG("num = %d\n", workspace->num);
1887
1888     /* By re-attaching, the sort order will be correct afterwards. */
1889     Con *previously_focused = focused;
1890     Con *parent = workspace->parent;
1891     con_detach(workspace);
1892     con_attach(workspace, parent, false);
1893     /* Restore the previous focus since con_attach messes with the focus. */
1894     con_focus(previously_focused);
1895
1896     cmd_output->needs_tree_render = true;
1897     ysuccess(true);
1898
1899     ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
1900 }