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