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