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