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