i3
commands.c
Go to the documentation of this file.
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 #include "shmlog.h"
17 
18 // Macros to make the YAJL API a bit easier to use.
19 #define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_##x(cmd_output->json_gen, ##__VA_ARGS__) : 0)
20 #define ystr(str) (cmd_output->json_gen != NULL ? yajl_gen_string(cmd_output->json_gen, (unsigned char *)str, strlen(str)) : 0)
21 #define ysuccess(success) \
22  do { \
23  if (cmd_output->json_gen != NULL) { \
24  y(map_open); \
25  ystr("success"); \
26  y(bool, success); \
27  y(map_close); \
28  } \
29  } while (0)
30 #define yerror(format, ...) \
31  do { \
32  if (cmd_output->json_gen != NULL) { \
33  char *message; \
34  sasprintf(&message, format, ##__VA_ARGS__); \
35  y(map_open); \
36  ystr("success"); \
37  y(bool, false); \
38  ystr("error"); \
39  ystr(message); \
40  y(map_close); \
41  free(message); \
42  } \
43  } while (0)
44 
50 #define HANDLE_EMPTY_MATCH \
51  do { \
52  if (match_is_empty(current_match)) { \
53  owindow *ow = smalloc(sizeof(owindow)); \
54  ow->con = focused; \
55  TAILQ_INIT(&owindows); \
56  TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
57  } \
58  } while (0)
59 
60 /*
61  * Returns true if a is definitely greater than b (using the given epsilon)
62  *
63  */
64 static bool definitelyGreaterThan(float a, float b, float epsilon) {
65  return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
66 }
67 
68 /*
69  * Returns an 'output' corresponding to one of left/right/down/up or a specific
70  * output name.
71  *
72  */
73 static Output *get_output_from_string(Output *current_output, const char *output_str) {
74  Output *output;
75 
76  if (strcasecmp(output_str, "left") == 0)
77  output = get_output_next_wrap(D_LEFT, current_output);
78  else if (strcasecmp(output_str, "right") == 0)
79  output = get_output_next_wrap(D_RIGHT, current_output);
80  else if (strcasecmp(output_str, "up") == 0)
81  output = get_output_next_wrap(D_UP, current_output);
82  else if (strcasecmp(output_str, "down") == 0)
83  output = get_output_next_wrap(D_DOWN, current_output);
84  else
85  output = get_output_by_name(output_str);
86 
87  return output;
88 }
89 
90 /*
91  * Returns the output containing the given container.
92  */
93 static Output *get_output_of_con(Con *con) {
94  Con *output_con = con_get_output(con);
95  Output *output = get_output_by_name(output_con->name);
96  assert(output != NULL);
97 
98  return output;
99 }
100 
101 /*
102  * Checks whether we switched to a new workspace and returns false in that case,
103  * signaling that further workspace switching should be done by the calling function
104  * If not, calls workspace_back_and_forth() if workspace_auto_back_and_forth is set
105  * and return true, signaling that no further workspace switching should occur in the calling function.
106  *
107  */
108 static bool maybe_back_and_forth(struct CommandResultIR *cmd_output, char *name) {
110 
111  /* If we switched to a different workspace, do nothing */
112  if (strcmp(ws->name, name) != 0)
113  return false;
114 
115  DLOG("This workspace is already focused.\n");
118  cmd_output->needs_tree_render = true;
119  }
120  return true;
121 }
122 
123 /*
124  * Return the passed workspace unless it is the current one and auto back and
125  * forth is enabled, in which case the back_and_forth workspace is returned.
126  */
128  Con *current, *baf;
129 
131  return workspace;
132 
133  current = con_get_workspace(focused);
134 
135  if (current == workspace) {
137  if (baf != NULL) {
138  DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
139  return baf;
140  }
141  }
142 
143  return workspace;
144 }
145 
146 // This code is commented out because we might recycle it for popping up error
147 // messages on parser errors.
148 #if 0
149 static pid_t migration_pid = -1;
150 
151 /*
152  * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
153  * it exited (or could not be started, depending on the exit code).
154  *
155  */
156 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
157  ev_child_stop(EV_A_ watcher);
158  if (!WIFEXITED(watcher->rstatus)) {
159  fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
160  return;
161  }
162 
163  int exitcode = WEXITSTATUS(watcher->rstatus);
164  printf("i3-nagbar process exited with status %d\n", exitcode);
165  if (exitcode == 2) {
166  fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
167  }
168 
169  migration_pid = -1;
170 }
171 
172 /* We need ev >= 4 for the following code. Since it is not *that* important (it
173  * only makes sure that there are no i3-nagbar instances left behind) we still
174  * support old systems with libev 3. */
175 #if EV_VERSION_MAJOR >= 4
176 /*
177  * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
178  * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
179  *
180  */
181 static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
182  if (migration_pid != -1) {
183  LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", migration_pid);
184  kill(migration_pid, SIGKILL);
185  }
186 }
187 #endif
188 
189 void cmd_MIGRATION_start_nagbar(void) {
190  if (migration_pid != -1) {
191  fprintf(stderr, "i3-nagbar already running.\n");
192  return;
193  }
194  fprintf(stderr, "Starting i3-nagbar, command parsing differs from expected output.\n");
195  ELOG("Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile:\n");
196  ELOG("i3-dump-log | gzip -9c > /tmp/i3.log.gz\n");
197  ELOG("FYI: Your i3 version is " I3_VERSION "\n");
198  migration_pid = fork();
199  if (migration_pid == -1) {
200  warn("Could not fork()");
201  return;
202  }
203 
204  /* child */
205  if (migration_pid == 0) {
206  char *pageraction;
207  sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
208  char *argv[] = {
209  NULL, /* will be replaced by the executable path */
210  "-t",
211  "error",
212  "-m",
213  "You found a parsing error. Please, please, please, report it!",
214  "-b",
215  "show errors",
216  pageraction,
217  NULL
218  };
219  exec_i3_utility("i3-nagbar", argv);
220  }
221 
222  /* parent */
223  /* install a child watcher */
224  ev_child *child = smalloc(sizeof(ev_child));
225  ev_child_init(child, &nagbar_exited, migration_pid, 0);
226  ev_child_start(main_loop, child);
227 
228 /* We need ev >= 4 for the following code. Since it is not *that* important (it
229  * only makes sure that there are no i3-nagbar instances left behind) we still
230  * support old systems with libev 3. */
231 #if EV_VERSION_MAJOR >= 4
232  /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
233  * still running) */
234  ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
235  ev_cleanup_init(cleanup, nagbar_cleanup);
236  ev_cleanup_start(main_loop, cleanup);
237 #endif
238 }
239 
240 #endif
241 
242 /*******************************************************************************
243  * Criteria functions.
244  ******************************************************************************/
245 
246 /*
247  * Helper data structure for an operation window (window on which the operation
248  * will be performed). Used to build the TAILQ owindows.
249  *
250  */
251 typedef struct owindow {
253  TAILQ_ENTRY(owindow) owindows;
254 } owindow;
255 
256 typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
257 
258 static owindows_head owindows;
259 
260 /*
261  * Initializes the specified 'Match' data structure and the initial state of
262  * commands.c for matching target windows of a command.
263  *
264  */
266  Con *con;
267  owindow *ow;
268 
269  DLOG("Initializing criteria, current_match = %p\n", current_match);
272  while (!TAILQ_EMPTY(&owindows)) {
273  ow = TAILQ_FIRST(&owindows);
274  TAILQ_REMOVE(&owindows, ow, owindows);
275  free(ow);
276  }
277  TAILQ_INIT(&owindows);
278  /* copy all_cons */
280  ow = smalloc(sizeof(owindow));
281  ow->con = con;
282  TAILQ_INSERT_TAIL(&owindows, ow, owindows);
283  }
284 }
285 
286 /*
287  * A match specification just finished (the closing square bracket was found),
288  * so we filter the list of owindows.
289  *
290  */
292  owindow *next, *current;
293 
294  DLOG("match specification finished, matching...\n");
295  /* copy the old list head to iterate through it and start with a fresh
296  * list which will contain only matching windows */
297  struct owindows_head old = owindows;
298  TAILQ_INIT(&owindows);
299  for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
300  /* make a copy of the next pointer and advance the pointer to the
301  * next element as we are going to invalidate the element’s
302  * next/prev pointers by calling TAILQ_INSERT_TAIL later */
303  current = next;
304  next = TAILQ_NEXT(next, owindows);
305 
306  DLOG("checking if con %p / %s matches\n", current->con, current->con->name);
307  if (current_match->con_id != NULL) {
308  if (current_match->con_id == current->con) {
309  DLOG("matches container!\n");
310  TAILQ_INSERT_TAIL(&owindows, current, owindows);
311  } else {
312  DLOG("doesnt match\n");
313  free(current);
314  }
315  } else if (current_match->mark != NULL && current->con->mark != NULL &&
316  regex_matches(current_match->mark, current->con->mark)) {
317  DLOG("match by mark\n");
318  TAILQ_INSERT_TAIL(&owindows, current, owindows);
319  } else {
320  if (current->con->window && match_matches_window(current_match, current->con->window)) {
321  DLOG("matches window!\n");
322  TAILQ_INSERT_TAIL(&owindows, current, owindows);
323  } else {
324  DLOG("doesnt match\n");
325  free(current);
326  }
327  }
328  }
329 
330  TAILQ_FOREACH(current, &owindows, owindows) {
331  DLOG("matching: %p / %s\n", current->con, current->con->name);
332  }
333 }
334 
335 /*
336  * Interprets a ctype=cvalue pair and adds it to the current match
337  * specification.
338  *
339  */
340 void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
341  DLOG("ctype=*%s*, cvalue=*%s*\n", ctype, cvalue);
342 
343  if (strcmp(ctype, "class") == 0) {
344  current_match->class = regex_new(cvalue);
345  return;
346  }
347 
348  if (strcmp(ctype, "instance") == 0) {
349  current_match->instance = regex_new(cvalue);
350  return;
351  }
352 
353  if (strcmp(ctype, "window_role") == 0) {
355  return;
356  }
357 
358  if (strcmp(ctype, "con_id") == 0) {
359  char *end;
360  long parsed = strtol(cvalue, &end, 10);
361  if (parsed == LONG_MIN ||
362  parsed == LONG_MAX ||
363  parsed < 0 ||
364  (end && *end != '\0')) {
365  ELOG("Could not parse con id \"%s\"\n", cvalue);
366  } else {
367  current_match->con_id = (Con *)parsed;
368  DLOG("id as int = %p\n", current_match->con_id);
369  }
370  return;
371  }
372 
373  if (strcmp(ctype, "id") == 0) {
374  char *end;
375  long parsed = strtol(cvalue, &end, 10);
376  if (parsed == LONG_MIN ||
377  parsed == LONG_MAX ||
378  parsed < 0 ||
379  (end && *end != '\0')) {
380  ELOG("Could not parse window id \"%s\"\n", cvalue);
381  } else {
382  current_match->id = parsed;
383  DLOG("window id as int = %d\n", current_match->id);
384  }
385  return;
386  }
387 
388  if (strcmp(ctype, "con_mark") == 0) {
389  current_match->mark = regex_new(cvalue);
390  return;
391  }
392 
393  if (strcmp(ctype, "title") == 0) {
394  current_match->title = regex_new(cvalue);
395  return;
396  }
397 
398  if (strcmp(ctype, "urgent") == 0) {
399  if (strcasecmp(cvalue, "latest") == 0 ||
400  strcasecmp(cvalue, "newest") == 0 ||
401  strcasecmp(cvalue, "recent") == 0 ||
402  strcasecmp(cvalue, "last") == 0) {
403  current_match->urgent = U_LATEST;
404  } else if (strcasecmp(cvalue, "oldest") == 0 ||
405  strcasecmp(cvalue, "first") == 0) {
406  current_match->urgent = U_OLDEST;
407  }
408  return;
409  }
410 
411  ELOG("Unknown criterion: %s\n", ctype);
412 }
413 
414 /*
415  * Implementation of 'move [window|container] [to] workspace
416  * next|prev|next_on_output|prev_on_output|current'.
417  *
418  */
419 void cmd_move_con_to_workspace(I3_CMD, char *which) {
420  owindow *current;
421 
422  DLOG("which=%s\n", which);
423 
424  /* We have nothing to move:
425  * when criteria was specified but didn't match any window or
426  * when criteria wasn't specified and we don't have any window focused. */
427  if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
428  (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
430  ysuccess(false);
431  return;
432  }
433 
435 
436  /* get the workspace */
437  Con *ws;
438  if (strcmp(which, "next") == 0)
439  ws = workspace_next();
440  else if (strcmp(which, "prev") == 0)
441  ws = workspace_prev();
442  else if (strcmp(which, "next_on_output") == 0)
444  else if (strcmp(which, "prev_on_output") == 0)
446  else if (strcmp(which, "current") == 0)
448  else {
449  ELOG("BUG: called with which=%s\n", which);
450  ysuccess(false);
451  return;
452  }
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 
469  owindow *current;
470  Con *ws;
471 
473 
474  if (ws == NULL) {
475  yerror("No workspace was previously active.");
476  return;
477  }
478 
480 
481  TAILQ_FOREACH(current, &owindows, owindows) {
482  DLOG("matching: %p / %s\n", current->con, current->con->name);
483  con_move_to_workspace(current->con, ws, true, false);
484  }
485 
486  cmd_output->needs_tree_render = true;
487  // XXX: default reply for now, make this a better reply
488  ysuccess(true);
489 }
490 
491 /*
492  * Implementation of 'move [window|container] [to] workspace <name>'.
493  *
494  */
496  if (strncasecmp(name, "__", strlen("__")) == 0) {
497  LOG("You cannot move containers to i3-internal workspaces (\"%s\").\n", name);
498  ysuccess(false);
499  return;
500  }
501 
502  owindow *current;
503 
504  /* We have nothing to move:
505  * when criteria was specified but didn't match any window or
506  * when criteria wasn't specified and we don't have any window focused. */
507  if (!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) {
508  ELOG("No windows match your criteria, cannot move.\n");
509  ysuccess(false);
510  return;
511  } else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
513  ysuccess(false);
514  return;
515  }
516 
517  LOG("should move window to workspace %s\n", name);
518  /* get the workspace */
519  Con *ws = NULL;
520  Con *output = NULL;
521 
522  /* first look for a workspace with this name */
523  TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
524  GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
525  }
526 
527  /* if the name is plain digits, we interpret this as a "workspace number"
528  * command */
529  if (!ws && name_is_digits(name)) {
530  long parsed_num = ws_name_to_number(name);
531  TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
532  GREP_FIRST(ws, output_get_content(output),
533  child->num == parsed_num);
534  }
535  }
536 
537  /* if no workspace was found, make a new one */
538  if (!ws)
539  ws = workspace_get(name, NULL);
540 
542 
544 
545  TAILQ_FOREACH(current, &owindows, owindows) {
546  DLOG("matching: %p / %s\n", current->con, current->con->name);
547  con_move_to_workspace(current->con, ws, true, false);
548  }
549 
550  cmd_output->needs_tree_render = true;
551  // XXX: default reply for now, make this a better reply
552  ysuccess(true);
553 }
554 
555 /*
556  * Implementation of 'move [window|container] [to] workspace number <name>'.
557  *
558  */
560  owindow *current;
561 
562  /* We have nothing to move:
563  * when criteria was specified but didn't match any window or
564  * when criteria wasn't specified and we don't have any window focused. */
565  if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
566  (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
568  ysuccess(false);
569  return;
570  }
571 
572  LOG("should move window to workspace %s\n", which);
573  /* get the workspace */
574  Con *output, *workspace = NULL;
575 
576  long parsed_num = ws_name_to_number(which);
577 
578  if (parsed_num == -1) {
579  LOG("Could not parse initial part of \"%s\" as a number.\n", which);
580  yerror("Could not parse number \"%s\"", which);
581  return;
582  }
583 
584  TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
585  GREP_FIRST(workspace, output_get_content(output),
586  child->num == parsed_num);
587 
588  if (!workspace) {
589  workspace = workspace_get(which, NULL);
590  }
591 
592  workspace = maybe_auto_back_and_forth_workspace(workspace);
593 
595 
596  TAILQ_FOREACH(current, &owindows, owindows) {
597  DLOG("matching: %p / %s\n", current->con, current->con->name);
598  con_move_to_workspace(current->con, workspace, true, false);
599  }
600 
601  cmd_output->needs_tree_render = true;
602  // XXX: default reply for now, make this a better reply
603  ysuccess(true);
604 }
605 
606 static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
607  LOG("floating resize\n");
608  Rect old_rect = floating_con->rect;
609  Con *focused_con = con_descend_focused(floating_con);
610 
611  /* ensure that resize will take place even if pixel increment is smaller than
612  * height increment or width increment.
613  * fixes #1011 */
614  if (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0 ||
615  strcmp(direction, "height") == 0) {
616  if (px < 0)
617  px = (-px < focused_con->height_increment) ? -focused_con->height_increment : px;
618  else
619  px = (px < focused_con->height_increment) ? focused_con->height_increment : px;
620  } else if (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0) {
621  if (px < 0)
622  px = (-px < focused_con->width_increment) ? -focused_con->width_increment : px;
623  else
624  px = (px < focused_con->width_increment) ? focused_con->width_increment : px;
625  }
626 
627  if (strcmp(direction, "up") == 0) {
628  floating_con->rect.height += px;
629  } else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
630  floating_con->rect.height += px;
631  } else if (strcmp(direction, "left") == 0) {
632  floating_con->rect.width += px;
633  } else {
634  floating_con->rect.width += px;
635  }
636 
637  floating_check_size(floating_con);
638 
639  /* Did we actually resize anything or did the size constraints prevent us?
640  * If we could not resize, exit now to not move the window. */
641  if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0)
642  return;
643 
644  if (strcmp(direction, "up") == 0) {
645  floating_con->rect.y -= (floating_con->rect.height - old_rect.height);
646  } else if (strcmp(direction, "left") == 0) {
647  floating_con->rect.x -= (floating_con->rect.width - old_rect.width);
648  }
649 
650  /* If this is a scratchpad window, don't auto center it from now on. */
651  if (floating_con->scratchpad_state == SCRATCHPAD_FRESH)
652  floating_con->scratchpad_state = SCRATCHPAD_CHANGED;
653 }
654 
655 static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) {
656  LOG("tiling resize\n");
657  Con *second = NULL;
658  Con *first = current;
659  direction_t search_direction;
660  if (!strcmp(direction, "left"))
661  search_direction = D_LEFT;
662  else if (!strcmp(direction, "right"))
663  search_direction = D_RIGHT;
664  else if (!strcmp(direction, "up"))
665  search_direction = D_UP;
666  else
667  search_direction = D_DOWN;
668 
669  bool res = resize_find_tiling_participants(&first, &second, search_direction);
670  if (!res) {
671  LOG("No second container in this direction found.\n");
672  ysuccess(false);
673  return false;
674  }
675 
676  /* get the default percentage */
677  int children = con_num_children(first->parent);
678  LOG("ins. %d children\n", children);
679  double percentage = 1.0 / children;
680  LOG("default percentage = %f\n", percentage);
681 
682  /* resize */
683  LOG("second->percent = %f\n", second->percent);
684  LOG("first->percent before = %f\n", first->percent);
685  if (first->percent == 0.0)
686  first->percent = percentage;
687  if (second->percent == 0.0)
688  second->percent = percentage;
689  double new_first_percent = first->percent + ((double)ppt / 100.0);
690  double new_second_percent = second->percent - ((double)ppt / 100.0);
691  LOG("new_first_percent = %f\n", new_first_percent);
692  LOG("new_second_percent = %f\n", new_second_percent);
693  /* Ensure that the new percentages are positive and greater than
694  * 0.05 to have a reasonable minimum size. */
695  if (definitelyGreaterThan(new_first_percent, 0.05, DBL_EPSILON) &&
696  definitelyGreaterThan(new_second_percent, 0.05, DBL_EPSILON)) {
697  first->percent += ((double)ppt / 100.0);
698  second->percent -= ((double)ppt / 100.0);
699  LOG("first->percent after = %f\n", first->percent);
700  LOG("second->percent after = %f\n", second->percent);
701  } else {
702  LOG("Not resizing, already at minimum size\n");
703  }
704 
705  return true;
706 }
707 
708 static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt) {
709  LOG("width/height resize\n");
710  /* get the appropriate current container (skip stacked/tabbed cons) */
711  while (current->parent->layout == L_STACKED ||
712  current->parent->layout == L_TABBED)
713  current = current->parent;
714 
715  /* Then further go up until we find one with the matching orientation. */
716  orientation_t search_orientation =
717  (strcmp(direction, "width") == 0 ? HORIZ : VERT);
718 
719  while (current->type != CT_WORKSPACE &&
720  current->type != CT_FLOATING_CON &&
721  con_orientation(current->parent) != search_orientation)
722  current = current->parent;
723 
724  /* get the default percentage */
725  int children = con_num_children(current->parent);
726  LOG("ins. %d children\n", children);
727  double percentage = 1.0 / children;
728  LOG("default percentage = %f\n", percentage);
729 
730  orientation_t orientation = con_orientation(current->parent);
731 
732  if ((orientation == HORIZ &&
733  strcmp(direction, "height") == 0) ||
734  (orientation == VERT &&
735  strcmp(direction, "width") == 0)) {
736  LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
737  (orientation == HORIZ ? "horizontal" : "vertical"));
738  ysuccess(false);
739  return false;
740  }
741 
742  if (children == 1) {
743  LOG("This is the only container, cannot resize.\n");
744  ysuccess(false);
745  return false;
746  }
747 
748  /* Ensure all the other children have a percentage set. */
749  Con *child;
750  TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
751  LOG("child->percent = %f (child %p)\n", child->percent, child);
752  if (child->percent == 0.0)
753  child->percent = percentage;
754  }
755 
756  double new_current_percent = current->percent + ((double)ppt / 100.0);
757  double subtract_percent = ((double)ppt / 100.0) / (children - 1);
758  LOG("new_current_percent = %f\n", new_current_percent);
759  LOG("subtract_percent = %f\n", subtract_percent);
760  /* Ensure that the new percentages are positive and greater than
761  * 0.05 to have a reasonable minimum size. */
762  TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
763  if (child == current)
764  continue;
765  if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
766  LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent);
767  ysuccess(false);
768  return false;
769  }
770  }
771  if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) {
772  LOG("Not resizing, already at minimum size\n");
773  ysuccess(false);
774  return false;
775  }
776 
777  current->percent += ((double)ppt / 100.0);
778  LOG("current->percent after = %f\n", current->percent);
779 
780  TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
781  if (child == current)
782  continue;
783  child->percent -= subtract_percent;
784  LOG("child->percent after (%p) = %f\n", child, child->percent);
785  }
786 
787  return true;
788 }
789 
790 /*
791  * Implementation of 'resize grow|shrink <direction> [<px> px] [or <ppt> ppt]'.
792  *
793  */
794 void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resize_ppt) {
795  /* resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt] */
796  DLOG("resizing in way %s, direction %s, px %s or ppt %s\n", way, direction, resize_px, resize_ppt);
797  // 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
798  int px = atoi(resize_px);
799  int ppt = atoi(resize_ppt);
800  if (strcmp(way, "shrink") == 0) {
801  px *= -1;
802  ppt *= -1;
803  }
804 
806 
807  owindow *current;
808  TAILQ_FOREACH(current, &owindows, owindows) {
809  /* Don't handle dock windows (issue #1201) */
810  if (current->con->window && current->con->window->dock) {
811  DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
812  continue;
813  }
814 
815  Con *floating_con;
816  if ((floating_con = con_inside_floating(current->con))) {
817  cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
818  } else {
819  if (strcmp(direction, "width") == 0 ||
820  strcmp(direction, "height") == 0) {
821  if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt))
822  return;
823  } else {
824  if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt))
825  return;
826  }
827  }
828  }
829 
830  cmd_output->needs_tree_render = true;
831  // XXX: default reply for now, make this a better reply
832  ysuccess(true);
833 }
834 
835 /*
836  * Implementation of 'border normal|none|1pixel|toggle|pixel'.
837  *
838  */
839 void cmd_border(I3_CMD, char *border_style_str, char *border_width) {
840  DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width);
841  owindow *current;
842 
844 
845  TAILQ_FOREACH(current, &owindows, owindows) {
846  DLOG("matching: %p / %s\n", current->con, current->con->name);
847  int border_style = current->con->border_style;
848  char *end;
849  int tmp_border_width = -1;
850  tmp_border_width = strtol(border_width, &end, 10);
851  if (end == border_width) {
852  /* no valid digits found */
853  tmp_border_width = -1;
854  }
855  if (strcmp(border_style_str, "toggle") == 0) {
856  border_style++;
857  border_style %= 3;
858  if (border_style == BS_NORMAL)
859  tmp_border_width = 2;
860  else if (border_style == BS_NONE)
861  tmp_border_width = 0;
862  else if (border_style == BS_PIXEL)
863  tmp_border_width = 1;
864  } else {
865  if (strcmp(border_style_str, "normal") == 0)
866  border_style = BS_NORMAL;
867  else if (strcmp(border_style_str, "pixel") == 0)
868  border_style = BS_PIXEL;
869  else if (strcmp(border_style_str, "1pixel") == 0) {
870  border_style = BS_PIXEL;
871  tmp_border_width = 1;
872  } else if (strcmp(border_style_str, "none") == 0)
873  border_style = BS_NONE;
874  else {
875  ELOG("BUG: called with border_style=%s\n", border_style_str);
876  ysuccess(false);
877  return;
878  }
879  }
880  con_set_border_style(current->con, border_style, tmp_border_width);
881  }
882 
883  cmd_output->needs_tree_render = true;
884  // XXX: default reply for now, make this a better reply
885  ysuccess(true);
886 }
887 
888 /*
889  * Implementation of 'nop <comment>'.
890  *
891  */
892 void cmd_nop(I3_CMD, char *comment) {
893  LOG("-------------------------------------------------\n");
894  LOG(" NOP: %s\n", comment);
895  LOG("-------------------------------------------------\n");
896 }
897 
898 /*
899  * Implementation of 'append_layout <path>'.
900  *
901  */
902 void cmd_append_layout(I3_CMD, char *path) {
903  LOG("Appending layout \"%s\"\n", path);
904 
905  /* Make sure we allow paths like '~/.i3/layout.json' */
906  path = resolve_tilde(path);
907 
908  json_content_t content = json_determine_content(path);
909  LOG("JSON content = %d\n", content);
910  if (content == JSON_CONTENT_UNKNOWN) {
911  ELOG("Could not determine the contents of \"%s\", not loading.\n", path);
912  yerror("Could not determine the contents of \"%s\".", path);
913  free(path);
914  return;
915  }
916 
917  Con *parent = focused;
918  if (content == JSON_CONTENT_WORKSPACE) {
919  parent = output_get_content(con_get_output(parent));
920  } else {
921  /* We need to append the layout to a split container, since a leaf
922  * container must not have any children (by definition).
923  * Note that we explicitly check for workspaces, since they are okay for
924  * this purpose, but con_accepts_window() returns false for workspaces. */
925  while (parent->type != CT_WORKSPACE && !con_accepts_window(parent))
926  parent = parent->parent;
927  }
928  DLOG("Appending to parent=%p instead of focused=%p\n", parent, focused);
929  char *errormsg = NULL;
930  tree_append_json(parent, path, &errormsg);
931  if (errormsg != NULL) {
932  yerror(errormsg);
933  free(errormsg);
934  /* Note that we continue executing since tree_append_json() has
935  * side-effects — user-provided layouts can be partly valid, partly
936  * invalid, leading to half of the placeholder containers being
937  * created. */
938  } else {
939  ysuccess(true);
940  }
941 
942  // XXX: This is a bit of a kludge. Theoretically, render_con(parent,
943  // false); should be enough, but when sending 'workspace 4; append_layout
944  // /tmp/foo.json', the needs_tree_render == true of the workspace command
945  // is not executed yet and will be batched with append_layout’s
946  // needs_tree_render after the parser finished. We should check if that is
947  // necessary at all.
948  render_con(croot, false);
949 
951 
952  if (content == JSON_CONTENT_WORKSPACE)
953  ipc_send_workspace_event("restored", parent, NULL);
954 
955  free(path);
956  cmd_output->needs_tree_render = true;
957 }
958 
959 /*
960  * Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
961  *
962  */
963 void cmd_workspace(I3_CMD, char *which) {
964  Con *ws;
965 
966  DLOG("which=%s\n", which);
967 
969  LOG("Cannot switch workspace while in global fullscreen\n");
970  ysuccess(false);
971  return;
972  }
973 
974  if (strcmp(which, "next") == 0)
975  ws = workspace_next();
976  else if (strcmp(which, "prev") == 0)
977  ws = workspace_prev();
978  else if (strcmp(which, "next_on_output") == 0)
980  else if (strcmp(which, "prev_on_output") == 0)
982  else {
983  ELOG("BUG: called with which=%s\n", which);
984  ysuccess(false);
985  return;
986  }
987 
988  workspace_show(ws);
989 
990  cmd_output->needs_tree_render = true;
991  // XXX: default reply for now, make this a better reply
992  ysuccess(true);
993 }
994 
995 /*
996  * Implementation of 'workspace number <name>'
997  *
998  */
999 void cmd_workspace_number(I3_CMD, char *which) {
1000  Con *output, *workspace = NULL;
1001 
1003  LOG("Cannot switch workspace while in global fullscreen\n");
1004  ysuccess(false);
1005  return;
1006  }
1007 
1008  long parsed_num = ws_name_to_number(which);
1009 
1010  if (parsed_num == -1) {
1011  LOG("Could not parse initial part of \"%s\" as a number.\n", which);
1012  yerror("Could not parse number \"%s\"", which);
1013  return;
1014  }
1015 
1016  TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
1017  GREP_FIRST(workspace, output_get_content(output),
1018  child->num == parsed_num);
1019 
1020  if (!workspace) {
1021  LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
1022  ysuccess(true);
1023  workspace_show_by_name(which);
1024  cmd_output->needs_tree_render = true;
1025  return;
1026  }
1027  if (maybe_back_and_forth(cmd_output, workspace->name))
1028  return;
1029  workspace_show(workspace);
1030 
1031  cmd_output->needs_tree_render = true;
1032  // XXX: default reply for now, make this a better reply
1033  ysuccess(true);
1034 }
1035 
1036 /*
1037  * Implementation of 'workspace back_and_forth'.
1038  *
1039  */
1042  LOG("Cannot switch workspace while in global fullscreen\n");
1043  ysuccess(false);
1044  return;
1045  }
1046 
1048 
1049  cmd_output->needs_tree_render = true;
1050  // XXX: default reply for now, make this a better reply
1051  ysuccess(true);
1052 }
1053 
1054 /*
1055  * Implementation of 'workspace <name>'
1056  *
1057  */
1058 void cmd_workspace_name(I3_CMD, char *name) {
1059  if (strncasecmp(name, "__", strlen("__")) == 0) {
1060  LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name);
1061  ysuccess(false);
1062  return;
1063  }
1064 
1066  LOG("Cannot switch workspace while in global fullscreen\n");
1067  ysuccess(false);
1068  return;
1069  }
1070 
1071  DLOG("should switch to workspace %s\n", name);
1072  if (maybe_back_and_forth(cmd_output, name))
1073  return;
1074 
1075  Con *ws = NULL;
1076  Con *output = NULL;
1077 
1078  /* first look for a workspace with this name */
1079  TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
1080  GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
1081  }
1082 
1083  /* if the name is only digits, we interpret this as a "workspace number"
1084  * command */
1085  if (!ws && name_is_digits(name)) {
1086  long parsed_num = ws_name_to_number(name);
1087  TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
1088  GREP_FIRST(ws, output_get_content(output),
1089  child->num == parsed_num);
1090  }
1091  }
1092 
1093  /* if no workspace was found, make a new one */
1094  if (!ws)
1095  ws = workspace_get(name, NULL);
1096 
1097  workspace_show(ws);
1098 
1099  cmd_output->needs_tree_render = true;
1100  // XXX: default reply for now, make this a better reply
1101  ysuccess(true);
1102 }
1103 
1104 /*
1105  * Implementation of 'mark <mark>'
1106  *
1107  */
1108 void cmd_mark(I3_CMD, char *mark) {
1109  DLOG("Clearing all windows which have that mark first\n");
1110 
1111  Con *con;
1112  TAILQ_FOREACH(con, &all_cons, all_cons) {
1113  if (con->mark && strcmp(con->mark, mark) == 0)
1114  FREE(con->mark);
1115  }
1116 
1117  DLOG("marking window with str %s\n", mark);
1118  owindow *current;
1119 
1121 
1122  TAILQ_FOREACH(current, &owindows, owindows) {
1123  DLOG("matching: %p / %s\n", current->con, current->con->name);
1124  current->con->mark = sstrdup(mark);
1125  }
1126 
1127  cmd_output->needs_tree_render = true;
1128  // XXX: default reply for now, make this a better reply
1129  ysuccess(true);
1130 }
1131 
1132 /*
1133  * Implementation of 'unmark [mark]'
1134  *
1135  */
1136 void cmd_unmark(I3_CMD, char *mark) {
1137  if (mark == NULL) {
1138  Con *con;
1139  TAILQ_FOREACH(con, &all_cons, all_cons) {
1140  FREE(con->mark);
1141  }
1142  DLOG("removed all window marks");
1143  } else {
1144  Con *con;
1145  TAILQ_FOREACH(con, &all_cons, all_cons) {
1146  if (con->mark && strcmp(con->mark, mark) == 0)
1147  FREE(con->mark);
1148  }
1149  DLOG("removed window mark %s\n", mark);
1150  }
1151 
1152  cmd_output->needs_tree_render = true;
1153  // XXX: default reply for now, make this a better reply
1154  ysuccess(true);
1155 }
1156 
1157 /*
1158  * Implementation of 'mode <string>'.
1159  *
1160  */
1161 void cmd_mode(I3_CMD, char *mode) {
1162  DLOG("mode=%s\n", mode);
1163  switch_mode(mode);
1164 
1165  // XXX: default reply for now, make this a better reply
1166  ysuccess(true);
1167 }
1168 
1169 /*
1170  * Implementation of 'move [window|container] [to] output <str>'.
1171  *
1172  */
1173 void cmd_move_con_to_output(I3_CMD, char *name) {
1174  owindow *current;
1175 
1176  DLOG("should move window to output %s\n", name);
1177 
1179 
1180  /* get the output */
1181  Output *current_output = NULL;
1182  Output *output;
1183 
1184  // TODO: fix the handling of criteria
1185  TAILQ_FOREACH(current, &owindows, owindows)
1186  current_output = get_output_of_con(current->con);
1187 
1188  assert(current_output != NULL);
1189 
1190  // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
1191  if (strcasecmp(name, "up") == 0)
1192  output = get_output_next_wrap(D_UP, current_output);
1193  else if (strcasecmp(name, "down") == 0)
1194  output = get_output_next_wrap(D_DOWN, current_output);
1195  else if (strcasecmp(name, "left") == 0)
1196  output = get_output_next_wrap(D_LEFT, current_output);
1197  else if (strcasecmp(name, "right") == 0)
1198  output = get_output_next_wrap(D_RIGHT, current_output);
1199  else
1200  output = get_output_by_name(name);
1201 
1202  if (!output) {
1203  LOG("No such output found.\n");
1204  ysuccess(false);
1205  return;
1206  }
1207 
1208  /* get visible workspace on output */
1209  Con *ws = NULL;
1210  GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1211  if (!ws) {
1212  ysuccess(false);
1213  return;
1214  }
1215 
1216  TAILQ_FOREACH(current, &owindows, owindows) {
1217  DLOG("matching: %p / %s\n", current->con, current->con->name);
1218  con_move_to_workspace(current->con, ws, true, false);
1219  }
1220 
1221  cmd_output->needs_tree_render = true;
1222  // XXX: default reply for now, make this a better reply
1223  ysuccess(true);
1224 }
1225 
1226 /*
1227  * Implementation of 'floating enable|disable|toggle'
1228  *
1229  */
1230 void cmd_floating(I3_CMD, char *floating_mode) {
1231  owindow *current;
1232 
1233  DLOG("floating_mode=%s\n", floating_mode);
1234 
1236 
1237  TAILQ_FOREACH(current, &owindows, owindows) {
1238  DLOG("matching: %p / %s\n", current->con, current->con->name);
1239  if (strcmp(floating_mode, "toggle") == 0) {
1240  DLOG("should toggle mode\n");
1241  toggle_floating_mode(current->con, false);
1242  } else {
1243  DLOG("should switch mode to %s\n", floating_mode);
1244  if (strcmp(floating_mode, "enable") == 0) {
1245  floating_enable(current->con, false);
1246  } else {
1247  floating_disable(current->con, false);
1248  }
1249  }
1250  }
1251 
1252  cmd_output->needs_tree_render = true;
1253  // XXX: default reply for now, make this a better reply
1254  ysuccess(true);
1255 }
1256 
1257 /*
1258  * Implementation of 'move workspace to [output] <str>'.
1259  *
1260  */
1262  DLOG("should move workspace to output %s\n", name);
1263 
1265 
1266  owindow *current;
1267  TAILQ_FOREACH(current, &owindows, owindows) {
1268  Output *current_output = get_output_of_con(current->con);
1269  if (!current_output) {
1270  ELOG("Cannot get current output. This is a bug in i3.\n");
1271  ysuccess(false);
1272  return;
1273  }
1274  Output *output = get_output_from_string(current_output, name);
1275  if (!output) {
1276  ELOG("Could not get output from string \"%s\"\n", name);
1277  ysuccess(false);
1278  return;
1279  }
1280 
1281  Con *content = output_get_content(output->con);
1282  LOG("got output %p with content %p\n", output, content);
1283 
1284  Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
1285  LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
1286 
1287  Con *ws = con_get_workspace(current->con);
1288  LOG("should move workspace %p / %s\n", ws, ws->name);
1289  bool workspace_was_visible = workspace_is_visible(ws);
1290 
1291  if (con_num_children(ws->parent) == 1) {
1292  LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
1293 
1294  /* check if we can find a workspace assigned to this output */
1295  bool used_assignment = false;
1296  struct Workspace_Assignment *assignment;
1298  if (strcmp(assignment->output, current_output->name) != 0)
1299  continue;
1300 
1301  /* check if this workspace is already attached to the tree */
1302  Con *workspace = NULL, *out;
1303  TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
1304  GREP_FIRST(workspace, output_get_content(out),
1305  !strcasecmp(child->name, assignment->name));
1306  if (workspace != NULL)
1307  continue;
1308 
1309  /* so create the workspace referenced to by this assignment */
1310  LOG("Creating workspace from assignment %s.\n", assignment->name);
1311  workspace_get(assignment->name, NULL);
1312  used_assignment = true;
1313  break;
1314  }
1315 
1316  /* if we couldn't create the workspace using an assignment, create
1317  * it on the output */
1318  if (!used_assignment)
1319  create_workspace_on_output(current_output, ws->parent);
1320 
1321  /* notify the IPC listeners */
1322  ipc_send_workspace_event("init", ws, NULL);
1323  }
1324  DLOG("Detaching\n");
1325 
1326  /* detach from the old output and attach to the new output */
1327  Con *old_content = ws->parent;
1328  con_detach(ws);
1329  if (workspace_was_visible) {
1330  /* The workspace which we just detached was visible, so focus
1331  * the next one in the focus-stack. */
1332  Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
1333  LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
1334  workspace_show(focus_ws);
1335  }
1336  con_attach(ws, content, false);
1337 
1338  /* fix the coordinates of the floating containers */
1339  Con *floating_con;
1340  TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
1341  floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
1342 
1343  ipc_send_workspace_event("move", ws, NULL);
1344  if (workspace_was_visible) {
1345  /* Focus the moved workspace on the destination output. */
1346  workspace_show(ws);
1347  }
1348 
1349  /* NB: We cannot simply work with previously_visible_ws since it might
1350  * have been cleaned up by workspace_show() already, depending on the
1351  * focus order/number of other workspaces on the output.
1352  * Instead, we loop through the available workspaces and only work with
1353  * previously_visible_ws if we still find it. */
1354  TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
1355  if (ws != previously_visible_ws)
1356  continue;
1357 
1358  /* Call the on_remove_child callback of the workspace which previously
1359  * was visible on the destination output. Since it is no longer
1360  * visible, it might need to get cleaned up. */
1361  CALL(previously_visible_ws, on_remove_child);
1362  break;
1363  }
1364  }
1365 
1366  cmd_output->needs_tree_render = true;
1367  // XXX: default reply for now, make this a better reply
1368  ysuccess(true);
1369 }
1370 
1371 /*
1372  * Implementation of 'split v|h|vertical|horizontal'.
1373  *
1374  */
1375 void cmd_split(I3_CMD, char *direction) {
1376  owindow *current;
1377  /* TODO: use matches */
1378  LOG("splitting in direction %c\n", direction[0]);
1380  tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
1381  else {
1382  TAILQ_FOREACH(current, &owindows, owindows) {
1383  DLOG("matching: %p / %s\n", current->con, current->con->name);
1384  tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
1385  }
1386  }
1387 
1388  cmd_output->needs_tree_render = true;
1389  // XXX: default reply for now, make this a better reply
1390  ysuccess(true);
1391 }
1392 
1393 /*
1394  * Implementation of 'kill [window|client]'.
1395  *
1396  */
1397 void cmd_kill(I3_CMD, char *kill_mode_str) {
1398  if (kill_mode_str == NULL)
1399  kill_mode_str = "window";
1400  owindow *current;
1401 
1402  DLOG("kill_mode=%s\n", kill_mode_str);
1403 
1404  int kill_mode;
1405  if (strcmp(kill_mode_str, "window") == 0)
1406  kill_mode = KILL_WINDOW;
1407  else if (strcmp(kill_mode_str, "client") == 0)
1408  kill_mode = KILL_CLIENT;
1409  else {
1410  ELOG("BUG: called with kill_mode=%s\n", kill_mode_str);
1411  ysuccess(false);
1412  return;
1413  }
1414 
1415  /* check if the match is empty, not if the result is empty */
1417  tree_close_con(kill_mode);
1418  else {
1419  TAILQ_FOREACH(current, &owindows, owindows) {
1420  DLOG("matching: %p / %s\n", current->con, current->con->name);
1421  tree_close(current->con, kill_mode, false, false);
1422  }
1423  }
1424 
1425  cmd_output->needs_tree_render = true;
1426  // XXX: default reply for now, make this a better reply
1427  ysuccess(true);
1428 }
1429 
1430 /*
1431  * Implementation of 'exec [--no-startup-id] <command>'.
1432  *
1433  */
1434 void cmd_exec(I3_CMD, char *nosn, char *command) {
1435  bool no_startup_id = (nosn != NULL);
1436 
1437  DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
1438  start_application(command, no_startup_id);
1439 
1440  // XXX: default reply for now, make this a better reply
1441  ysuccess(true);
1442 }
1443 
1444 /*
1445  * Implementation of 'focus left|right|up|down'.
1446  *
1447  */
1448 void cmd_focus_direction(I3_CMD, char *direction) {
1449  DLOG("direction = *%s*\n", direction);
1450 
1451  if (strcmp(direction, "left") == 0)
1452  tree_next('p', HORIZ);
1453  else if (strcmp(direction, "right") == 0)
1454  tree_next('n', HORIZ);
1455  else if (strcmp(direction, "up") == 0)
1456  tree_next('p', VERT);
1457  else if (strcmp(direction, "down") == 0)
1458  tree_next('n', VERT);
1459  else {
1460  ELOG("Invalid focus direction (%s)\n", direction);
1461  ysuccess(false);
1462  return;
1463  }
1464 
1465  cmd_output->needs_tree_render = true;
1466  // XXX: default reply for now, make this a better reply
1467  ysuccess(true);
1468 }
1469 
1470 /*
1471  * Implementation of 'focus tiling|floating|mode_toggle'.
1472  *
1473  */
1474 void cmd_focus_window_mode(I3_CMD, char *window_mode) {
1475  DLOG("window_mode = %s\n", window_mode);
1476 
1477  Con *ws = con_get_workspace(focused);
1478  Con *current;
1479  if (ws != NULL) {
1480  if (strcmp(window_mode, "mode_toggle") == 0) {
1481  current = TAILQ_FIRST(&(ws->focus_head));
1482  if (current != NULL && current->type == CT_FLOATING_CON)
1483  window_mode = "tiling";
1484  else
1485  window_mode = "floating";
1486  }
1487  TAILQ_FOREACH(current, &(ws->focus_head), focused) {
1488  if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
1489  (strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
1490  continue;
1491 
1492  con_focus(con_descend_focused(current));
1493  break;
1494  }
1495  }
1496 
1497  cmd_output->needs_tree_render = true;
1498  // XXX: default reply for now, make this a better reply
1499  ysuccess(true);
1500 }
1501 
1502 /*
1503  * Implementation of 'focus parent|child'.
1504  *
1505  */
1506 void cmd_focus_level(I3_CMD, char *level) {
1507  DLOG("level = %s\n", level);
1508  bool success = false;
1509 
1510  /* Focusing the parent can only be allowed if the newly
1511  * focused container won't escape the fullscreen container. */
1512  if (strcmp(level, "parent") == 0) {
1513  if (focused && focused->parent) {
1515  success = level_up();
1516  else
1517  ELOG("'focus parent': Currently in fullscreen, not going up\n");
1518  }
1519  }
1520 
1521  /* Focusing a child should always be allowed. */
1522  else
1523  success = level_down();
1524 
1525  cmd_output->needs_tree_render = success;
1526  // XXX: default reply for now, make this a better reply
1527  ysuccess(success);
1528 }
1529 
1530 /*
1531  * Implementation of 'focus'.
1532  *
1533  */
1535  DLOG("current_match = %p\n", current_match);
1536 
1538  ELOG("You have to specify which window/container should be focused.\n");
1539  ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
1540 
1541  yerror("You have to specify which window/container should be focused");
1542 
1543  return;
1544  }
1545 
1546  Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
1547  int count = 0;
1548  owindow *current;
1549  TAILQ_FOREACH(current, &owindows, owindows) {
1550  Con *ws = con_get_workspace(current->con);
1551  /* If no workspace could be found, this was a dock window.
1552  * Just skip it, you cannot focus dock windows. */
1553  if (!ws)
1554  continue;
1555 
1556  /* Check the fullscreen focus constraints. */
1557  if (!con_fullscreen_permits_focusing(current->con)) {
1558  LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
1559  ysuccess(false);
1560  return;
1561  }
1562 
1563  /* In case this is a scratchpad window, call scratchpad_show(). */
1564  if (ws == __i3_scratch) {
1565  scratchpad_show(current->con);
1566  count++;
1567  /* While for the normal focus case we can change focus multiple
1568  * times and only a single window ends up focused, we could show
1569  * multiple scratchpad windows. So, rather break here. */
1570  break;
1571  }
1572 
1573  /* If the container is not on the current workspace,
1574  * workspace_show() will switch to a different workspace and (if
1575  * enabled) trigger a mouse pointer warp to the currently focused
1576  * container (!) on the target workspace.
1577  *
1578  * Therefore, before calling workspace_show(), we make sure that
1579  * 'current' will be focused on the workspace. However, we cannot
1580  * just con_focus(current) because then the pointer will not be
1581  * warped at all (the code thinks we are already there).
1582  *
1583  * So we focus 'current' to make it the currently focused window of
1584  * the target workspace, then revert focus. */
1585  Con *currently_focused = focused;
1586  con_focus(current->con);
1587  con_focus(currently_focused);
1588 
1589  /* Now switch to the workspace, then focus */
1590  workspace_show(ws);
1591  LOG("focusing %p / %s\n", current->con, current->con->name);
1592  con_focus(current->con);
1593  count++;
1594  }
1595 
1596  if (count > 1)
1597  LOG("WARNING: Your criteria for the focus command matches %d containers, "
1598  "while only exactly one container can be focused at a time.\n",
1599  count);
1600 
1601  cmd_output->needs_tree_render = true;
1602  // XXX: default reply for now, make this a better reply
1603  ysuccess(true);
1604 }
1605 
1606 /*
1607  * Implementation of 'fullscreen enable|toggle [global]' and
1608  * 'fullscreen disable'
1609  *
1610  */
1611 void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) {
1612  fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
1613  DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
1614  owindow *current;
1615 
1617 
1618  TAILQ_FOREACH(current, &owindows, owindows) {
1619  DLOG("matching: %p / %s\n", current->con, current->con->name);
1620  if (strcmp(action, "toggle") == 0) {
1621  con_toggle_fullscreen(current->con, mode);
1622  } else if (strcmp(action, "enable") == 0) {
1623  con_enable_fullscreen(current->con, mode);
1624  } else if (strcmp(action, "disable") == 0) {
1625  con_disable_fullscreen(current->con);
1626  }
1627  }
1628 
1629  cmd_output->needs_tree_render = true;
1630  // XXX: default reply for now, make this a better reply
1631  ysuccess(true);
1632 }
1633 
1634 /*
1635  * Implementation of 'move <direction> [<pixels> [px]]'.
1636  *
1637  */
1638 void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
1639  // 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
1640  int px = atoi(move_px);
1641 
1642  owindow *current;
1644 
1645  Con *initially_focused = focused;
1646 
1647  TAILQ_FOREACH(current, &owindows, owindows) {
1648  DLOG("moving in direction %s, px %s\n", direction, move_px);
1649  if (con_is_floating(current->con)) {
1650  DLOG("floating move with %d pixels\n", px);
1651  Rect newrect = current->con->parent->rect;
1652  if (strcmp(direction, "left") == 0) {
1653  newrect.x -= px;
1654  } else if (strcmp(direction, "right") == 0) {
1655  newrect.x += px;
1656  } else if (strcmp(direction, "up") == 0) {
1657  newrect.y -= px;
1658  } else if (strcmp(direction, "down") == 0) {
1659  newrect.y += px;
1660  }
1661  floating_reposition(current->con->parent, newrect);
1662  } else {
1663  tree_move(current->con, (strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
1664  cmd_output->needs_tree_render = true;
1665  }
1666  }
1667 
1668  /* the move command should not disturb focus */
1669  if (focused != initially_focused)
1670  con_focus(initially_focused);
1671 
1672  // XXX: default reply for now, make this a better reply
1673  ysuccess(true);
1674 }
1675 
1676 /*
1677  * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
1678  *
1679  */
1680 void cmd_layout(I3_CMD, char *layout_str) {
1681  if (strcmp(layout_str, "stacking") == 0)
1682  layout_str = "stacked";
1683  owindow *current;
1684  layout_t layout;
1685  /* default is a special case which will be handled in con_set_layout(). */
1686  if (strcmp(layout_str, "default") == 0)
1687  layout = L_DEFAULT;
1688  else if (strcmp(layout_str, "stacked") == 0)
1689  layout = L_STACKED;
1690  else if (strcmp(layout_str, "tabbed") == 0)
1691  layout = L_TABBED;
1692  else if (strcmp(layout_str, "splitv") == 0)
1693  layout = L_SPLITV;
1694  else if (strcmp(layout_str, "splith") == 0)
1695  layout = L_SPLITH;
1696  else {
1697  ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
1698  return;
1699  }
1700 
1701  DLOG("changing layout to %s (%d)\n", layout_str, layout);
1702 
1703  /* check if the match is empty, not if the result is empty */
1705  con_set_layout(focused, layout);
1706  else {
1707  TAILQ_FOREACH(current, &owindows, owindows) {
1708  DLOG("matching: %p / %s\n", current->con, current->con->name);
1709  con_set_layout(current->con, layout);
1710  }
1711  }
1712 
1713  cmd_output->needs_tree_render = true;
1714  // XXX: default reply for now, make this a better reply
1715  ysuccess(true);
1716 }
1717 
1718 /*
1719  * Implementation of 'layout toggle [all|split]'.
1720  *
1721  */
1722 void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
1723  owindow *current;
1724 
1725  if (toggle_mode == NULL)
1726  toggle_mode = "default";
1727 
1728  DLOG("toggling layout (mode = %s)\n", toggle_mode);
1729 
1730  /* check if the match is empty, not if the result is empty */
1732  con_toggle_layout(focused, toggle_mode);
1733  else {
1734  TAILQ_FOREACH(current, &owindows, owindows) {
1735  DLOG("matching: %p / %s\n", current->con, current->con->name);
1736  con_toggle_layout(current->con, toggle_mode);
1737  }
1738  }
1739 
1740  cmd_output->needs_tree_render = true;
1741  // XXX: default reply for now, make this a better reply
1742  ysuccess(true);
1743 }
1744 
1745 /*
1746  * Implementation of 'exit'.
1747  *
1748  */
1750  LOG("Exiting due to user command.\n");
1751  ipc_shutdown();
1752  unlink(config.ipc_socket_path);
1753  xcb_disconnect(conn);
1754  exit(0);
1755 
1756  /* unreached */
1757 }
1758 
1759 /*
1760  * Implementation of 'reload'.
1761  *
1762  */
1764  LOG("reloading\n");
1767  load_configuration(conn, NULL, true);
1768  x_set_i3_atoms();
1769  /* Send an IPC event just in case the ws names have changed */
1770  ipc_send_workspace_event("reload", NULL, NULL);
1771  /* Send an update event for the barconfig just in case it has changed */
1772  update_barconfig();
1773 
1774  // XXX: default reply for now, make this a better reply
1775  ysuccess(true);
1776 }
1777 
1778 /*
1779  * Implementation of 'restart'.
1780  *
1781  */
1783  LOG("restarting i3\n");
1784  ipc_shutdown();
1785  unlink(config.ipc_socket_path);
1786  /* We need to call this manually since atexit handlers don’t get called
1787  * when exec()ing */
1789  i3_restart(false);
1790 
1791  // XXX: default reply for now, make this a better reply
1792  ysuccess(true);
1793 }
1794 
1795 /*
1796  * Implementation of 'open'.
1797  *
1798  */
1800  LOG("opening new container\n");
1801  Con *con = tree_open_con(NULL, NULL);
1802  con->layout = L_SPLITH;
1803  con_focus(con);
1804 
1805  y(map_open);
1806  ystr("success");
1807  y(bool, true);
1808  ystr("id");
1809  y(integer, (long int)con);
1810  y(map_close);
1811 
1812  cmd_output->needs_tree_render = true;
1813 }
1814 
1815 /*
1816  * Implementation of 'focus output <output>'.
1817  *
1818  */
1820  owindow *current;
1821 
1822  DLOG("name = %s\n", name);
1823 
1825 
1826  /* get the output */
1827  Output *current_output = NULL;
1828  Output *output;
1829 
1830  TAILQ_FOREACH(current, &owindows, owindows)
1831  current_output = get_output_of_con(current->con);
1832  assert(current_output != NULL);
1833 
1834  output = get_output_from_string(current_output, name);
1835 
1836  if (!output) {
1837  LOG("No such output found.\n");
1838  ysuccess(false);
1839  return;
1840  }
1841 
1842  /* get visible workspace on output */
1843  Con *ws = NULL;
1844  GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1845  if (!ws) {
1846  ysuccess(false);
1847  return;
1848  }
1849 
1850  workspace_show(ws);
1851 
1852  cmd_output->needs_tree_render = true;
1853  // XXX: default reply for now, make this a better reply
1854  ysuccess(true);
1855 }
1856 
1857 /*
1858  * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
1859  *
1860  */
1861 void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
1862  int x = atoi(cx);
1863  int y = atoi(cy);
1864  bool has_error = false;
1865 
1866  owindow *current;
1868 
1869  TAILQ_FOREACH(current, &owindows, owindows) {
1870  if (!con_is_floating(current->con)) {
1871  ELOG("Cannot change position. The window/container is not floating\n");
1872 
1873  if (!has_error) {
1874  yerror("Cannot change position of a window/container because it is not floating.");
1875  has_error = true;
1876  }
1877 
1878  continue;
1879  }
1880 
1881  if (strcmp(method, "absolute") == 0) {
1882  current->con->parent->rect.x = x;
1883  current->con->parent->rect.y = y;
1884 
1885  DLOG("moving to absolute position %d %d\n", x, y);
1887  cmd_output->needs_tree_render = true;
1888  }
1889 
1890  if (strcmp(method, "position") == 0) {
1891  Rect newrect = current->con->parent->rect;
1892 
1893  DLOG("moving to position %d %d\n", x, y);
1894  newrect.x = x;
1895  newrect.y = y;
1896 
1897  floating_reposition(current->con->parent, newrect);
1898  }
1899  }
1900 
1901  // XXX: default reply for now, make this a better reply
1902  if (!has_error)
1903  ysuccess(true);
1904 }
1905 
1906 /*
1907  * Implementation of 'move [window|container] [to] [absolute] position center
1908  *
1909  */
1910 void cmd_move_window_to_center(I3_CMD, char *method) {
1911  if (!con_is_floating(focused)) {
1912  ELOG("Cannot change position. The window/container is not floating\n");
1913  yerror("Cannot change position. The window/container is not floating.");
1914  return;
1915  }
1916 
1917  if (strcmp(method, "absolute") == 0) {
1918  Rect *rect = &focused->parent->rect;
1919 
1920  DLOG("moving to absolute center\n");
1921  rect->x = croot->rect.width / 2 - rect->width / 2;
1922  rect->y = croot->rect.height / 2 - rect->height / 2;
1923 
1925  cmd_output->needs_tree_render = true;
1926  }
1927 
1928  if (strcmp(method, "position") == 0) {
1929  Rect *wsrect = &con_get_workspace(focused)->rect;
1930  Rect newrect = focused->parent->rect;
1931 
1932  DLOG("moving to center\n");
1933  newrect.x = wsrect->width / 2 - newrect.width / 2;
1934  newrect.y = wsrect->height / 2 - newrect.height / 2;
1935 
1936  floating_reposition(focused->parent, newrect);
1937  }
1938 
1939  // XXX: default reply for now, make this a better reply
1940  ysuccess(true);
1941 }
1942 
1943 /*
1944  * Implementation of 'move scratchpad'.
1945  *
1946  */
1948  DLOG("should move window to scratchpad\n");
1949  owindow *current;
1950 
1952 
1953  TAILQ_FOREACH(current, &owindows, owindows) {
1954  DLOG("matching: %p / %s\n", current->con, current->con->name);
1955  scratchpad_move(current->con);
1956  }
1957 
1958  cmd_output->needs_tree_render = true;
1959  // XXX: default reply for now, make this a better reply
1960  ysuccess(true);
1961 }
1962 
1963 /*
1964  * Implementation of 'scratchpad show'.
1965  *
1966  */
1968  DLOG("should show scratchpad window\n");
1969  owindow *current;
1970 
1972  scratchpad_show(NULL);
1973  } else {
1974  TAILQ_FOREACH(current, &owindows, owindows) {
1975  DLOG("matching: %p / %s\n", current->con, current->con->name);
1976  scratchpad_show(current->con);
1977  }
1978  }
1979 
1980  cmd_output->needs_tree_render = true;
1981  // XXX: default reply for now, make this a better reply
1982  ysuccess(true);
1983 }
1984 
1985 /*
1986  * Implementation of 'rename workspace [<name>] to <name>'
1987  *
1988  */
1989 void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
1990  if (strncasecmp(new_name, "__", strlen("__")) == 0) {
1991  LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name);
1992  ysuccess(false);
1993  return;
1994  }
1995  if (old_name) {
1996  LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
1997  } else {
1998  LOG("Renaming current workspace to \"%s\"\n", new_name);
1999  }
2000 
2001  Con *output, *workspace = NULL;
2002  if (old_name) {
2003  TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
2004  GREP_FIRST(workspace, output_get_content(output),
2005  !strcasecmp(child->name, old_name));
2006  } else {
2007  workspace = con_get_workspace(focused);
2008  }
2009 
2010  if (!workspace) {
2011  yerror("Old workspace \"%s\" not found", old_name);
2012  return;
2013  }
2014 
2015  Con *check_dest = NULL;
2016  TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
2017  GREP_FIRST(check_dest, output_get_content(output),
2018  !strcasecmp(child->name, new_name));
2019 
2020  if (check_dest != NULL) {
2021  yerror("New workspace \"%s\" already exists", new_name);
2022  return;
2023  }
2024 
2025  /* Change the name and try to parse it as a number. */
2026  FREE(workspace->name);
2027  workspace->name = sstrdup(new_name);
2028 
2029  workspace->num = ws_name_to_number(new_name);
2030  LOG("num = %d\n", workspace->num);
2031 
2032  /* By re-attaching, the sort order will be correct afterwards. */
2033  Con *previously_focused = focused;
2034  Con *parent = workspace->parent;
2035  con_detach(workspace);
2036  con_attach(workspace, parent, false);
2037  /* Restore the previous focus since con_attach messes with the focus. */
2038  con_focus(previously_focused);
2039 
2040  cmd_output->needs_tree_render = true;
2041  ysuccess(true);
2042 
2043  ipc_send_workspace_event("rename", workspace, NULL);
2047 }
2048 
2049 /*
2050  * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
2051  *
2052  */
2053 bool cmd_bar_mode(char *bar_mode, char *bar_id) {
2054  int mode = M_DOCK;
2055  bool toggle = false;
2056  if (strcmp(bar_mode, "dock") == 0)
2057  mode = M_DOCK;
2058  else if (strcmp(bar_mode, "hide") == 0)
2059  mode = M_HIDE;
2060  else if (strcmp(bar_mode, "invisible") == 0)
2061  mode = M_INVISIBLE;
2062  else if (strcmp(bar_mode, "toggle") == 0)
2063  toggle = true;
2064  else {
2065  ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
2066  return false;
2067  }
2068 
2069  bool changed_sth = false;
2070  Barconfig *current = NULL;
2071  TAILQ_FOREACH(current, &barconfigs, configs) {
2072  if (bar_id && strcmp(current->id, bar_id) != 0)
2073  continue;
2074 
2075  if (toggle)
2076  mode = (current->mode + 1) % 2;
2077 
2078  DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
2079  current->mode = mode;
2080  changed_sth = true;
2081 
2082  if (bar_id)
2083  break;
2084  }
2085 
2086  if (bar_id && !changed_sth) {
2087  DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
2088  return false;
2089  }
2090 
2091  return true;
2092 }
2093 
2094 /*
2095  * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
2096  *
2097  */
2098 bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
2099  int hidden_state = S_SHOW;
2100  bool toggle = false;
2101  if (strcmp(bar_hidden_state, "hide") == 0)
2102  hidden_state = S_HIDE;
2103  else if (strcmp(bar_hidden_state, "show") == 0)
2104  hidden_state = S_SHOW;
2105  else if (strcmp(bar_hidden_state, "toggle") == 0)
2106  toggle = true;
2107  else {
2108  ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
2109  return false;
2110  }
2111 
2112  bool changed_sth = false;
2113  Barconfig *current = NULL;
2114  TAILQ_FOREACH(current, &barconfigs, configs) {
2115  if (bar_id && strcmp(current->id, bar_id) != 0)
2116  continue;
2117 
2118  if (toggle)
2119  hidden_state = (current->hidden_state + 1) % 2;
2120 
2121  DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
2122  current->hidden_state = hidden_state;
2123  changed_sth = true;
2124 
2125  if (bar_id)
2126  break;
2127  }
2128 
2129  if (bar_id && !changed_sth) {
2130  DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
2131  return false;
2132  }
2133 
2134  return true;
2135 }
2136 
2137 /*
2138  * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
2139  *
2140  */
2141 void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) {
2142  bool ret;
2143  if (strcmp(bar_type, "mode") == 0)
2144  ret = cmd_bar_mode(bar_value, bar_id);
2145  else if (strcmp(bar_type, "hidden_state") == 0)
2146  ret = cmd_bar_hidden_state(bar_value, bar_id);
2147  else {
2148  ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
2149  ret = false;
2150  }
2151 
2152  ysuccess(ret);
2153  if (!ret)
2154  return;
2155 
2156  update_barconfig();
2157 }
2158 
2159 /*
2160  * Implementation of 'shmlog <size>|toggle|on|off'
2161  *
2162  */
2163 void cmd_shmlog(I3_CMD, char *argument) {
2164  if (!strcmp(argument, "toggle"))
2165  /* Toggle shm log, if size is not 0. If it is 0, set it to default. */
2167  else if (!strcmp(argument, "on"))
2169  else if (!strcmp(argument, "off"))
2170  shmlog_size = 0;
2171  else {
2172  /* If shm logging now, restart logging with the new size. */
2173  if (shmlog_size > 0) {
2174  shmlog_size = 0;
2175  LOG("Restarting shm logging...\n");
2176  init_logging();
2177  }
2178  shmlog_size = atoi(argument);
2179  /* Make a weakly attempt at ensuring the argument is valid. */
2180  if (shmlog_size <= 0)
2182  }
2183  LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling");
2184  init_logging();
2186  // XXX: default reply for now, make this a better reply
2187  ysuccess(true);
2188 }
2189 
2190 /*
2191  * Implementation of 'debuglog toggle|on|off'
2192  *
2193  */
2194 void cmd_debuglog(I3_CMD, char *argument) {
2195  bool logging = get_debug_logging();
2196  if (!strcmp(argument, "toggle")) {
2197  LOG("%s debug logging\n", logging ? "Disabling" : "Enabling");
2198  set_debug_logging(!logging);
2199  } else if (!strcmp(argument, "on") && !logging) {
2200  LOG("Enabling debug logging\n");
2201  set_debug_logging(true);
2202  } else if (!strcmp(argument, "off") && logging) {
2203  LOG("Disabling debug logging\n");
2204  set_debug_logging(false);
2205  }
2206  // XXX: default reply for now, make this a better reply
2207  ysuccess(true);
2208 }
enum Con::@20 scratchpad_state
Definition: data.h:95
struct Con * parent
Definition: data.h:532
void tree_next(char way, orientation_t orientation)
Changes focus in the given way (next/previous) and given orientation (horizontal/vertical).
Definition: tree.c:689
static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent)
Definition: util.c:413
long ws_name_to_number(const char *name)
Parses the workspace name as a number.
Definition: util.c:76
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
bool floating_maybe_reassign_ws(Con *con)
Checks if con’s coordinates are within its workspace and re-assigns it to the actual workspace if no...
Definition: floating.c:398
void ipc_shutdown(void)
Calls shutdown() on each socket and closes it.
Definition: ipc.c:98
char * name
Definition: config.h:79
void con_set_layout(Con *con, layout_t layout)
This function changes the layout of a given container.
Definition: con.c:1276
Output * get_output_by_name(const char *name)
Returns the output with the given name if it is active (!) or NULL.
Definition: randr.c:51
char * name
Name of the output.
Definition: data.h:328
bool con_is_floating(Con *con)
Returns true if the node is floating.
Definition: con.c:400
bool get_debug_logging(void)
Checks if debug logging is active.
Definition: log.c:193
direction_t
Definition: data.h:53
Definition: data.h:61
json_content_t json_determine_content(const char *filename)
Definition: load_layout.c:454
int height_increment
Definition: data.h:565
fullscreen_mode_t
Fullscreen modes.
Definition: data.h:491
void scratchpad_move(Con *con)
Moves the specified window to the __i3_scratch workspace, making it floating and setting the appropri...
Definition: scratchpad.c:21
void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode)
Enables fullscreen mode for the given container, if necessary.
Definition: con.c:623
uint32_t y
Definition: data.h:132
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:402
void con_attach(Con *con, Con *parent, bool ignore_focus)
Attaches the given container to the given parent.
Definition: con.c:83
void exec_i3_utility(char *name, char *argv[])
exec()s an i3 utility, for example the config file migration script or i3-nagbar. ...
Definition: util.c:116
Config config
Definition: config.c:17
xcb_connection_t * conn
Definition: main.c:43
void cmd_scratchpad_show(I3_CMD)
Implementation of 'scratchpad show'.
Definition: commands.c:1967
void cmd_move_con_to_workspace_name(I3_CMD, char *name)
Implementation of 'move [window|container] [to] workspace '.
Definition: commands.c:495
double percent
Definition: data.h:550
Con * con_get_workspace(Con *con)
Gets the workspace container this node is on.
Definition: con.c:298
static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt)
Definition: commands.c:655
Stores which workspace (by name or number) goes to which output.
Definition: data.h:180
Definition: data.h:56
Con * workspace_next(void)
Returns the next workspace.
Definition: workspace.c:476
static bool maybe_back_and_forth(struct CommandResultIR *cmd_output, char *name)
Definition: commands.c:108
Stores a rectangle, for example the size of a window, the child window etc.
Definition: data.h:130
#define LOG(fmt,...)
Definition: libi3.h:76
bool workspace_is_visible(Con *ws)
Returns true if the workspace is currently visible.
Definition: workspace.c:228
void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload)
Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
Definition: config.c:134
bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id)
Definition: commands.c:2098
enum Barconfig::@5 mode
Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mo...
void con_detach(Con *con)
Detaches the given container from its current parent.
Definition: con.c:177
void cmd_shmlog(I3_CMD, char *argument)
Definition: commands.c:2163
void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resize_ppt)
Implementation of 'resize grow|shrink [ px] [or ppt]'.
Definition: commands.c:794
Definition: data.h:62
Con * workspace_prev_on_output(void)
Returns the previous workspace on the same output.
Definition: workspace.c:672
void cmd_nop(I3_CMD, char *comment)
Implementation of 'nop '.
Definition: commands.c:892
void con_disable_fullscreen(Con *con)
Disables fullscreen mode for the given container, if necessary.
Definition: con.c:669
void floating_disable(Con *con, bool automatic)
Disables floating mode for the given container by re-attaching the container to its old parent...
Definition: floating.c:319
void ewmh_update_desktop_names(void)
Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops.
Definition: ewmh.c:68
static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px)
Definition: commands.c:606
void workspace_show(Con *workspace)
Switches to the given workspace.
Definition: workspace.c:458
Con * con_descend_focused(Con *con)
Returns the focused con inside this client, descending the tree as far as possible.
Definition: con.c:1029
struct Rect rect
Definition: data.h:534
bool con_accepts_window(Con *con)
Returns true if this node accepts a window (if the node swallows windows, it might already have swall...
Definition: con.c:265
struct all_cons_head all_cons
Definition: tree.c:17
char * mark
Definition: data.h:548
void cmd_criteria_match_windows(I3_CMD)
A match specification just finished (the closing square bracket was found), so we filter the list of ...
Definition: commands.c:291
struct regex * window_role
Definition: data.h:410
Con * tree_open_con(Con *con, i3Window *window)
Opens an empty container in the current container.
Definition: tree.c:134
struct regex * mark
Definition: data.h:409
#define ystr(str)
Definition: commands.c:20
void cmd_floating(I3_CMD, char *floating_mode)
Implementation of 'floating enable|disable|toggle'.
Definition: commands.c:1230
void ewmh_update_desktop_viewport(void)
Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that define the top left corne...
Definition: ewmh.c:106
void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp)
Moves the given container to the currently focused container on the given workspace.
Definition: con.c:702
void cmd_move_window_to_center(I3_CMD, char *method)
Implementation of 'move [window|container] [to] [absolute] position center.
Definition: commands.c:1910
void cmd_move_direction(I3_CMD, char *direction, char *move_px)
Implementation of 'move [ [px]]'.
Definition: commands.c:1638
struct ev_loop * main_loop
Definition: main.c:65
#define TAILQ_FIRST(head)
Definition: queue.h:336
Definition: data.h:53
void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy)
Implementation of 'move [window|container] [to] [absolute] position [px] [px]...
Definition: commands.c:1861
bool con_has_children(Con *con)
Returns true if this node has regular or floating children.
Definition: con.c:238
struct Window * window
Definition: data.h:567
static Output * get_output_of_con(Con *con)
Definition: commands.c:93
void purge_zerobyte_logfile(void)
Deletes the unused log files.
Definition: log.c:331
static Con * maybe_auto_back_and_forth_workspace(Con *workspace)
Definition: commands.c:127
static Output * get_output_from_string(Output *current_output, const char *output_str)
Definition: commands.c:73
void cmd_focus(I3_CMD)
Implementation of 'focus'.
Definition: commands.c:1534
void cmd_workspace(I3_CMD, char *which)
Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
Definition: commands.c:963
bool level_up(void)
Moves focus one level up.
Definition: tree.c:444
Con * create_workspace_on_output(Output *output, Con *content)
Definition: workspace.c:114
bool workspace_auto_back_and_forth
Automatic workspace back and forth switching.
Definition: config.h:161
An Output is a physical output on your graphics driver.
Definition: data.h:313
layout_t
Container layouts.
Definition: data.h:92
void cmd_focus_window_mode(I3_CMD, char *window_mode)
Implementation of 'focus tiling|floating|mode_toggle'.
Definition: commands.c:1474
void floating_reposition(Con *con, Rect newrect)
Repositions the CT_FLOATING_CON to have the coordinates specified by newrect, but only if the coordin...
Definition: floating.c:767
#define TAILQ_NEXT(elm, field)
Definition: queue.h:338
void tree_move(Con *con, int direction)
Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT, TOK_UP, TOK_DOWN from cmdparse...
Definition: move.c:139
void tree_append_json(Con *con, const char *filename, char **errormsg)
Definition: load_layout.c:511
border_style_t border_style
Definition: data.h:599
void workspace_show_by_name(const char *num)
Looks up the workspace by name and switches to it.
Definition: workspace.c:466
Output * get_output_next_wrap(direction_t direction, Output *current)
Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
Definition: randr.c:129
void update_barconfig()
Sends the current bar configuration as an event to all barconfig_update listeners.
Definition: config.c:35
void cmd_split(I3_CMD, char *direction)
Implementation of 'split v|h|vertical|horizontal'.
Definition: commands.c:1375
void i3_restart(bool forget_layout)
Restart i3 in-place appends -a to argument list to disable autostart.
Definition: util.c:303
void con_set_border_style(Con *con, int border_style, int border_width)
Sets the given border style on con, correctly keeping the position/size of a floating window...
Definition: con.c:1234
pid_t command_error_nagbar_pid
Definition: bindings.c:13
enum Con::@18 type
void switch_mode(const char *new_mode)
Switches the key bindings to the given mode, if the mode exists.
Definition: bindings.c:306
static Match current_match
Definition: data.h:93
#define DLOG(fmt,...)
Definition: libi3.h:86
void cmd_move_con_to_workspace_number(I3_CMD, char *which)
Implementation of 'move [window|container] [to] workspace number '.
Definition: commands.c:559
pid_t config_error_nagbar_pid
Definition: config_parser.c:46
void cmd_mark(I3_CMD, char *mark)
Implementation of 'mark '.
Definition: commands.c:1108
struct regex * regex_new(const char *pattern)
Creates a new 'regex' struct containing the given pattern and a PCRE compiled regular expression...
Definition: regex.c:24
void cmd_border(I3_CMD, char *border_style_str, char *border_width)
Implementation of 'border normal|none|1pixel|toggle'.
Definition: commands.c:839
struct regex * instance
Definition: data.h:408
void cmd_layout(I3_CMD, char *layout_str)
Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
Definition: commands.c:1680
json_content_t
Definition: load_layout.h:13
Con * workspace_back_and_forth_get(void)
Returns the previously focused workspace con, or NULL if unavailable.
Definition: workspace.c:742
void cmd_open(I3_CMD)
Implementation of 'open'.
Definition: commands.c:1799
bool cmd_bar_mode(char *bar_mode, char *bar_id)
Definition: commands.c:2053
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
Definition: data.h:94
void tree_split(Con *con, orientation_t orientation)
Splits (horizontally or vertically) the given container by creating a new container which contains th...
Definition: tree.c:389
void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue)
Interprets a ctype=cvalue pair and adds it to the current match specification.
Definition: commands.c:340
void cmd_mode(I3_CMD, char *mode)
Implementation of 'mode '.
Definition: commands.c:1161
void cmd_move_con_to_output(I3_CMD, char *name)
Implementation of 'move [window|container] [to] output '.
Definition: commands.c:1173
xcb_window_t id
Definition: data.h:423
bool match_matches_window(Match *match, i3Window *window)
Check if a match data structure matches the given window.
Definition: match.c:84
void floating_check_size(Con *floating_con)
Called when a floating window is created or resized.
Definition: floating.c:37
Con * con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode)
Returns the first fullscreen node below this node.
Definition: con.c:346
bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool force_set_focus)
Closes the given container including all children.
Definition: tree.c:190
void workspace_back_and_forth(void)
Focuses the previously focused workspace.
Definition: workspace.c:729
uint32_t height
Definition: data.h:134
int shmlog_size
Definition: log.c:47
Definition: data.h:54
char * errorfilename
Definition: log.c:38
Con * focused
Definition: tree.c:15
Con * con
Pointer to the Con which represents this output.
Definition: data.h:331
Con * con_id
Definition: data.h:427
char * id
Automatically generated ID for this bar config.
Definition: config.h:226
int num
the workspace number, if this Con is of type CT_WORKSPACE and the workspace is not a named workspace ...
Definition: data.h:530
static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt)
Definition: commands.c:708
void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it)
Kills the i3-nagbar process, if *nagbar_pid != -1.
Definition: util.c:469
char * name
Definition: data.h:540
void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name)
Implementation of 'rename workspace to '.
Definition: commands.c:1989
Definition: data.h:99
void ewmh_update_current_desktop(void)
Updates _NET_CURRENT_DESKTOP with the current desktop number.
Definition: ewmh.c:21
bool level_down(void)
Moves focus one level down.
Definition: tree.c:467
bool con_fullscreen_permits_focusing(Con *con)
Returns true if changing the focus to con would be allowed considering the fullscreen focus constrain...
Definition: con.c:1545
#define TAILQ_INIT(head)
Definition: queue.h:360
char * ipc_socket_path
Definition: config.h:94
void con_focus(Con *con)
Sets input focus to the given container.
Definition: con.c:193
void cmd_workspace_back_and_forth(I3_CMD)
Implementation of 'workspace back_and_forth'.
Definition: commands.c:1040
#define TAILQ_EMPTY(head)
Definition: queue.h:344
int con_num_children(Con *con)
Returns the number of children of this container.
Definition: con.c:507
void cmd_move_con_to_workspace(I3_CMD, char *which)
Implementation of 'move [window|container] [to] workspace next|prev|next_on_output|prev_on_output'.
Definition: commands.c:419
const int default_shmlog_size
Definition: main.c:70
void cmd_move_workspace_to_output(I3_CMD, char *name)
Implementation of 'move workspace to [output] '.
Definition: commands.c:1261
static bool definitelyGreaterThan(float a, float b, float epsilon)
Definition: commands.c:64
void restore_open_placeholder_windows(Con *parent)
Open placeholder windows for all children of parent.
char * resolve_tilde(const char *path)
This function resolves ~ in pathnames.
Definition: util.c:168
enum Window::@11 dock
Whether the window says it is a dock window.
Definition: data.h:55
#define TAILQ_ENTRY(type)
Definition: queue.h:327
#define ELOG(fmt,...)
Definition: libi3.h:81
Definition: data.h:58
void scratchpad_show(Con *con)
Either shows the top-most scratchpad window (con == NULL) or shows the specified con (if it is scratc...
Definition: scratchpad.c:89
#define I3_CMD
The beginning of the prototype for every cmd_ function.
Definition: commands.h:15
static void nagbar_exited(EV_P_ ev_child *watcher, int revents)
Definition: util.c:391
#define GREP_FIRST(dest, head, condition)
Definition: util.h:39
layout_t layout
Definition: data.h:598
orientation_t con_orientation(Con *con)
Returns the orientation of the given container (for stacked containers, vertical orientation is used ...
Definition: con.c:889
enum Match::@12 urgent
A 'Con' represents everything from the X11 root window down to a single X11 window.
Definition: data.h:499
uint32_t x
Definition: data.h:131
void cmd_focus_direction(I3_CMD, char *direction)
Implementation of 'focus left|right|up|down'.
Definition: commands.c:1448
enum Barconfig::@6 hidden_state
void ipc_send_workspace_event(const char *change, Con *current, Con *old)
For the workspace events we send, along with the usual "change" field, also the workspace container i...
Definition: ipc.c:1159
void cmd_append_layout(I3_CMD, char *path)
Implementation of 'append_layout '.
Definition: commands.c:902
Con * workspace_get(const char *num, bool *created)
Returns a pointer to the workspace with the given number (starting at 0), creating the workspace if n...
Definition: workspace.c:45
struct regex * title
Definition: data.h:405
orientation_t
Definition: data.h:57
#define y(x,...)
Definition: commands.c:19
void cmd_focus_output(I3_CMD, char *name)
Implementation of 'focus output '.
Definition: commands.c:1819
void cmd_layout_toggle(I3_CMD, char *toggle_mode)
Implementation of 'layout toggle [all|split]'.
Definition: commands.c:1722
#define TAILQ_END(head)
Definition: queue.h:337
Con * workspace_prev(void)
Returns the previous workspace.
Definition: workspace.c:546
#define ysuccess(success)
Definition: commands.c:21
void cmd_reload(I3_CMD)
Implementation of 'reload'.
Definition: commands.c:1763
void con_toggle_layout(Con *con, const char *toggle_mode)
This function toggles the layout of a given container.
Definition: con.c:1371
bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction)
Definition: resize.c:54
Con * con
Definition: commands.c:252
#define CALL(obj, member,...)
Definition: util.h:56
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:376
void cmd_exit(I3_CMD)
Implementation of 'exit'.
Definition: commands.c:1749
Con * workspace_next_on_output(void)
Returns the next workspace on the same output.
Definition: workspace.c:617
void cmd_exec(I3_CMD, char *nosn, char *command)
Implementation of 'exec [–no-startup-id] '.
Definition: commands.c:1434
struct Con * croot
Definition: tree.c:14
void cmd_workspace_number(I3_CMD, char *which)
Implementation of 'workspace number '.
Definition: commands.c:999
bool regex_matches(struct regex *regex, const char *input)
Checks if the given regular expression matches the given input and returns true if it does...
Definition: regex.c:75
bool match_is_empty(Match *match)
Check if a match is empty.
Definition: match.c:39
void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id)
Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) []'...
Definition: commands.c:2141
typedef TAILQ_HEAD(owindows_head, owindow)
Definition: commands.c:256
struct barconfig_head barconfigs
Definition: config.c:19
void cmd_criteria_init(I3_CMD)
Initializes the specified 'Match' data structure and the initial state of commands.c for matching target windows of a command.
void init_logging(void)
Initializes logging by creating an error logfile in /tmp (or XDG_RUNTIME_DIR, see get_process_filenam...
Definition: log.c:81
Con * output_get_content(Con *output)
Returns the output container below the given output container.
Definition: output.c:18
Con * con_get_output(Con *con)
Gets the output container (first container with CT_OUTPUT in hierarchy) this node is on...
Definition: con.c:284
void cmd_workspace_name(I3_CMD, char *name)
Implementation of 'workspace '.
Definition: commands.c:1058
Con * con_inside_floating(Con *con)
Checks if the given container is either floating or inside some floating container.
Definition: con.c:411
void start_application(const char *command, bool no_startup_id)
Starts the given application by passing it through a shell.
Definition: startup.c:133
void floating_enable(Con *con, bool automatic)
Enables floating mode for the given container by detaching it from its parent, creating a new contain...
Definition: floating.c:105
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
void x_set_i3_atoms(void)
Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH)
Definition: x.c:1127
void update_shmlog_atom()
Set up the SHMLOG_PATH atom.
Definition: x.c:1117
void cmd_kill(I3_CMD, char *kill_mode_str)
Implementation of 'kill [window|client]'.
Definition: commands.c:1397
void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect)
Fixes the coordinates of the floating window whenever the window gets reassigned to a different outpu...
Definition: floating.c:791
#define HANDLE_EMPTY_MATCH
When the command did not include match criteria (!), we use the currently focused container...
Definition: commands.c:50
void cmd_focus_level(I3_CMD, char *level)
Implementation of 'focus parent|child'.
Definition: commands.c:1506
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:347
void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode)
Implementation of 'fullscreen [enable|disable|toggle] [global]'.
Definition: commands.c:1611
void cmd_move_scratchpad(I3_CMD)
Implementation of 'move scratchpad'.
Definition: commands.c:1947
Definition: data.h:59
void toggle_floating_mode(Con *con, bool automatic)
Calls floating_enable() for tiling containers and floating_disable() for floating containers...
Definition: floating.c:371
Holds the status bar configuration (i3bar).
Definition: config.h:223
void cmd_move_con_to_workspace_back_and_forth(I3_CMD)
Implementation of 'move [window|container] [to] workspace back_and_forth'.
Definition: commands.c:468
void cmd_restart(I3_CMD)
Implementation of 'restart'.
Definition: commands.c:1782
uint32_t x
Definition: data.h:30
void cmd_unmark(I3_CMD, char *mark)
Implementation of 'unmark [mark]'.
Definition: commands.c:1136
void tree_close_con(kill_window_t kill_window)
Closes the current container using tree_close().
Definition: tree.c:360
#define FREE(pointer)
Definition: util.h:48
struct ws_assignments_head ws_assignments
Definition: main.c:86
#define yerror(format,...)
Definition: commands.c:30
Definition: data.h:60
void cmd_debuglog(I3_CMD, char *argument)
Definition: commands.c:2194
void match_init(Match *match)
Definition: match.c:28
void con_toggle_fullscreen(Con *con, int fullscreen_mode)
Toggles fullscreen mode for the given container.
Definition: con.c:568
int width_increment
Definition: data.h:564
void set_debug_logging(const bool _debug_logging)
Set debug logging.
Definition: log.c:201
void render_con(Con *con, bool render_fullscreen)
"Renders" the given container (and its children), meaning that all rects are updated correctly...
Definition: render.c:126
struct regex * class
Definition: data.h:407
uint32_t width
Definition: data.h:133
Definition: data.h:98
void match_free(Match *match)
Frees the given match.
Definition: match.c:191