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  if (border_style == BS_NORMAL)
840  tmp_border_width = 2;
841  else if (border_style == BS_NONE)
842  tmp_border_width = 0;
843  else if (border_style == BS_PIXEL)
844  tmp_border_width = 1;
845  } else {
846  if (strcmp(border_style_str, "normal") == 0)
847  border_style = BS_NORMAL;
848  else if (strcmp(border_style_str, "pixel") == 0)
849  border_style = BS_PIXEL;
850  else if (strcmp(border_style_str, "1pixel") == 0) {
851  border_style = BS_PIXEL;
852  tmp_border_width = 1;
853  } else if (strcmp(border_style_str, "none") == 0)
854  border_style = BS_NONE;
855  else {
856  ELOG("BUG: called with border_style=%s\n", border_style_str);
857  ysuccess(false);
858  return;
859  }
860  }
861  con_set_border_style(current->con, border_style, tmp_border_width);
862  }
863 
864  cmd_output->needs_tree_render = true;
865  // XXX: default reply for now, make this a better reply
866  ysuccess(true);
867 }
868 
869 /*
870  * Implementation of 'nop <comment>'.
871  *
872  */
873 void cmd_nop(I3_CMD, char *comment) {
874  LOG("-------------------------------------------------\n");
875  LOG(" NOP: %s\n", comment);
876  LOG("-------------------------------------------------\n");
877 }
878 
879 /*
880  * Implementation of 'append_layout <path>'.
881  *
882  */
883 void cmd_append_layout(I3_CMD, char *path) {
884  LOG("Appending layout \"%s\"\n", path);
885  Con *parent = focused;
886  /* We need to append the layout to a split container, since a leaf
887  * container must not have any children (by definition).
888  * Note that we explicitly check for workspaces, since they are okay for
889  * this purpose, but con_accepts_window() returns false for workspaces. */
890  while (parent->type != CT_WORKSPACE && !con_accepts_window(parent))
891  parent = parent->parent;
892  DLOG("Appending to parent=%p instead of focused=%p\n",
893  parent, focused);
894  char *errormsg = NULL;
895  tree_append_json(parent, path, &errormsg);
896  if (errormsg != NULL) {
897  yerror(errormsg);
898  free(errormsg);
899  /* Note that we continue executing since tree_append_json() has
900  * side-effects — user-provided layouts can be partly valid, partly
901  * invalid, leading to half of the placeholder containers being
902  * created. */
903  } else {
904  ysuccess(true);
905  }
906 
907  // XXX: This is a bit of a kludge. Theoretically, render_con(parent,
908  // false); should be enough, but when sending 'workspace 4; append_layout
909  // /tmp/foo.json', the needs_tree_render == true of the workspace command
910  // is not executed yet and will be batched with append_layout’s
911  // needs_tree_render after the parser finished. We should check if that is
912  // necessary at all.
913  render_con(croot, false);
914 
916 
917  cmd_output->needs_tree_render = true;
918 }
919 
920 /*
921  * Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
922  *
923  */
924 void cmd_workspace(I3_CMD, char *which) {
925  Con *ws;
926 
927  DLOG("which=%s\n", which);
928 
929  if (strcmp(which, "next") == 0)
930  ws = workspace_next();
931  else if (strcmp(which, "prev") == 0)
932  ws = workspace_prev();
933  else if (strcmp(which, "next_on_output") == 0)
935  else if (strcmp(which, "prev_on_output") == 0)
937  else {
938  ELOG("BUG: called with which=%s\n", which);
939  ysuccess(false);
940  return;
941  }
942 
943  workspace_show(ws);
944 
945  cmd_output->needs_tree_render = true;
946  // XXX: default reply for now, make this a better reply
947  ysuccess(true);
948 }
949 
950 /*
951  * Implementation of 'workspace number <name>'
952  *
953  */
954 void cmd_workspace_number(I3_CMD, char *which) {
955  Con *output, *workspace = NULL;
956 
957  char *endptr = NULL;
958  long parsed_num = strtol(which, &endptr, 10);
959  if (parsed_num == LONG_MIN ||
960  parsed_num == LONG_MAX ||
961  parsed_num < 0 ||
962  endptr == which) {
963  LOG("Could not parse initial part of \"%s\" as a number.\n", which);
964  // TODO: better error message
965  yerror("Could not parse number");
966 
967  return;
968  }
969 
970  TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
971  GREP_FIRST(workspace, output_get_content(output),
972  child->num == parsed_num);
973 
974  if (!workspace) {
975  LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
976  ysuccess(true);
977  workspace_show_by_name(which);
978  cmd_output->needs_tree_render = true;
979  return;
980  }
981  if (maybe_back_and_forth(cmd_output, workspace->name))
982  return;
983  workspace_show(workspace);
984 
985  cmd_output->needs_tree_render = true;
986  // XXX: default reply for now, make this a better reply
987  ysuccess(true);
988 }
989 
990 /*
991  * Implementation of 'workspace back_and_forth'.
992  *
993  */
996 
997  cmd_output->needs_tree_render = true;
998  // XXX: default reply for now, make this a better reply
999  ysuccess(true);
1000 }
1001 
1002 /*
1003  * Implementation of 'workspace <name>'
1004  *
1005  */
1006 void cmd_workspace_name(I3_CMD, char *name) {
1007  if (strncasecmp(name, "__", strlen("__")) == 0) {
1008  LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name);
1009  ysuccess(false);
1010  return;
1011  }
1012 
1013  DLOG("should switch to workspace %s\n", name);
1014  if (maybe_back_and_forth(cmd_output, name))
1015  return;
1016  workspace_show_by_name(name);
1017 
1018  cmd_output->needs_tree_render = true;
1019  // XXX: default reply for now, make this a better reply
1020  ysuccess(true);
1021 }
1022 
1023 /*
1024  * Implementation of 'mark <mark>'
1025  *
1026  */
1027 void cmd_mark(I3_CMD, char *mark) {
1028  DLOG("Clearing all windows which have that mark first\n");
1029 
1030  Con *con;
1031  TAILQ_FOREACH (con, &all_cons, all_cons) {
1032  if (con->mark && strcmp(con->mark, mark) == 0)
1033  FREE(con->mark);
1034  }
1035 
1036  DLOG("marking window with str %s\n", mark);
1037  owindow *current;
1038 
1040 
1041  TAILQ_FOREACH (current, &owindows, owindows) {
1042  DLOG("matching: %p / %s\n", current->con, current->con->name);
1043  current->con->mark = sstrdup(mark);
1044  }
1045 
1046  cmd_output->needs_tree_render = true;
1047  // XXX: default reply for now, make this a better reply
1048  ysuccess(true);
1049 }
1050 
1051 /*
1052  * Implementation of 'unmark [mark]'
1053  *
1054  */
1055 void cmd_unmark(I3_CMD, char *mark) {
1056  if (mark == NULL) {
1057  Con *con;
1058  TAILQ_FOREACH (con, &all_cons, all_cons) {
1059  FREE(con->mark);
1060  }
1061  DLOG("removed all window marks");
1062  } else {
1063  Con *con;
1064  TAILQ_FOREACH (con, &all_cons, all_cons) {
1065  if (con->mark && strcmp(con->mark, mark) == 0)
1066  FREE(con->mark);
1067  }
1068  DLOG("removed window mark %s\n", mark);
1069  }
1070 
1071  cmd_output->needs_tree_render = true;
1072  // XXX: default reply for now, make this a better reply
1073  ysuccess(true);
1074 }
1075 
1076 /*
1077  * Implementation of 'mode <string>'.
1078  *
1079  */
1080 void cmd_mode(I3_CMD, char *mode) {
1081  DLOG("mode=%s\n", mode);
1082  switch_mode(mode);
1083 
1084  // XXX: default reply for now, make this a better reply
1085  ysuccess(true);
1086 }
1087 
1088 /*
1089  * Implementation of 'move [window|container] [to] output <str>'.
1090  *
1091  */
1092 void cmd_move_con_to_output(I3_CMD, char *name) {
1093  owindow *current;
1094 
1095  DLOG("should move window to output %s\n", name);
1096 
1098 
1099  /* get the output */
1100  Output *current_output = NULL;
1101  Output *output;
1102 
1103  // TODO: fix the handling of criteria
1104  TAILQ_FOREACH (current, &owindows, owindows)
1105  current_output = get_output_of_con(current->con);
1106 
1107  assert(current_output != NULL);
1108 
1109  // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
1110  if (strcasecmp(name, "up") == 0)
1111  output = get_output_next_wrap(D_UP, current_output);
1112  else if (strcasecmp(name, "down") == 0)
1113  output = get_output_next_wrap(D_DOWN, current_output);
1114  else if (strcasecmp(name, "left") == 0)
1115  output = get_output_next_wrap(D_LEFT, current_output);
1116  else if (strcasecmp(name, "right") == 0)
1117  output = get_output_next_wrap(D_RIGHT, current_output);
1118  else
1119  output = get_output_by_name(name);
1120 
1121  if (!output) {
1122  LOG("No such output found.\n");
1123  ysuccess(false);
1124  return;
1125  }
1126 
1127  /* get visible workspace on output */
1128  Con *ws = NULL;
1129  GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1130  if (!ws) {
1131  ysuccess(false);
1132  return;
1133  }
1134 
1135  TAILQ_FOREACH (current, &owindows, owindows) {
1136  DLOG("matching: %p / %s\n", current->con, current->con->name);
1137  con_move_to_workspace(current->con, ws, true, false);
1138  }
1139 
1140  cmd_output->needs_tree_render = true;
1141  // XXX: default reply for now, make this a better reply
1142  ysuccess(true);
1143 }
1144 
1145 /*
1146  * Implementation of 'floating enable|disable|toggle'
1147  *
1148  */
1149 void cmd_floating(I3_CMD, char *floating_mode) {
1150  owindow *current;
1151 
1152  DLOG("floating_mode=%s\n", floating_mode);
1153 
1155 
1156  TAILQ_FOREACH (current, &owindows, owindows) {
1157  DLOG("matching: %p / %s\n", current->con, current->con->name);
1158  if (strcmp(floating_mode, "toggle") == 0) {
1159  DLOG("should toggle mode\n");
1160  toggle_floating_mode(current->con, false);
1161  } else {
1162  DLOG("should switch mode to %s\n", floating_mode);
1163  if (strcmp(floating_mode, "enable") == 0) {
1164  floating_enable(current->con, false);
1165  } else {
1166  floating_disable(current->con, false);
1167  }
1168  }
1169  }
1170 
1171  cmd_output->needs_tree_render = true;
1172  // XXX: default reply for now, make this a better reply
1173  ysuccess(true);
1174 }
1175 
1176 /*
1177  * Implementation of 'move workspace to [output] <str>'.
1178  *
1179  */
1181  DLOG("should move workspace to output %s\n", name);
1182 
1184 
1185  owindow *current;
1186  TAILQ_FOREACH (current, &owindows, owindows) {
1187  Output *current_output = get_output_of_con(current->con);
1188  if (!current_output) {
1189  ELOG("Cannot get current output. This is a bug in i3.\n");
1190  ysuccess(false);
1191  return;
1192  }
1193  Output *output = get_output_from_string(current_output, name);
1194  if (!output) {
1195  ELOG("Could not get output from string \"%s\"\n", name);
1196  ysuccess(false);
1197  return;
1198  }
1199 
1200  Con *content = output_get_content(output->con);
1201  LOG("got output %p with content %p\n", output, content);
1202 
1203  Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
1204  LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
1205 
1206  Con *ws = con_get_workspace(current->con);
1207  LOG("should move workspace %p / %s\n", ws, ws->name);
1208  bool workspace_was_visible = workspace_is_visible(ws);
1209 
1210  if (con_num_children(ws->parent) == 1) {
1211  LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
1212 
1213  /* check if we can find a workspace assigned to this output */
1214  bool used_assignment = false;
1215  struct Workspace_Assignment *assignment;
1216  TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
1217  if (strcmp(assignment->output, current_output->name) != 0)
1218  continue;
1219 
1220  /* check if this workspace is already attached to the tree */
1221  Con *workspace = NULL, *out;
1222  TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
1223  GREP_FIRST(workspace, output_get_content(out),
1224  !strcasecmp(child->name, assignment->name));
1225  if (workspace != NULL)
1226  continue;
1227 
1228  /* so create the workspace referenced to by this assignment */
1229  LOG("Creating workspace from assignment %s.\n", assignment->name);
1230  workspace_get(assignment->name, NULL);
1231  used_assignment = true;
1232  break;
1233  }
1234 
1235  /* if we couldn't create the workspace using an assignment, create
1236  * it on the output */
1237  if (!used_assignment)
1238  create_workspace_on_output(current_output, ws->parent);
1239 
1240  /* notify the IPC listeners */
1241  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
1242  }
1243  DLOG("Detaching\n");
1244 
1245  /* detach from the old output and attach to the new output */
1246  Con *old_content = ws->parent;
1247  con_detach(ws);
1248  if (workspace_was_visible) {
1249  /* The workspace which we just detached was visible, so focus
1250  * the next one in the focus-stack. */
1251  Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
1252  LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
1253  workspace_show(focus_ws);
1254  }
1255  con_attach(ws, content, false);
1256 
1257  /* fix the coordinates of the floating containers */
1258  Con *floating_con;
1259  TAILQ_FOREACH (floating_con, &(ws->floating_head), floating_windows)
1260  floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
1261 
1262  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
1263  if (workspace_was_visible) {
1264  /* Focus the moved workspace on the destination output. */
1265  workspace_show(ws);
1266  }
1267 
1268  /* NB: We cannot simply work with previously_visible_ws since it might
1269  * have been cleaned up by workspace_show() already, depending on the
1270  * focus order/number of other workspaces on the output.
1271  * Instead, we loop through the available workspaces and only work with
1272  * previously_visible_ws if we still find it. */
1273  TAILQ_FOREACH (ws, &(content->nodes_head), nodes) {
1274  if (ws != previously_visible_ws)
1275  continue;
1276 
1277  /* Call the on_remove_child callback of the workspace which previously
1278  * was visible on the destination output. Since it is no longer
1279  * visible, it might need to get cleaned up. */
1280  CALL(previously_visible_ws, on_remove_child);
1281  break;
1282  }
1283  }
1284 
1285  cmd_output->needs_tree_render = true;
1286  // XXX: default reply for now, make this a better reply
1287  ysuccess(true);
1288 }
1289 
1290 /*
1291  * Implementation of 'split v|h|vertical|horizontal'.
1292  *
1293  */
1294 void cmd_split(I3_CMD, char *direction) {
1295  owindow *current;
1296  /* TODO: use matches */
1297  LOG("splitting in direction %c\n", direction[0]);
1299  tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
1300  else {
1301  TAILQ_FOREACH (current, &owindows, owindows) {
1302  DLOG("matching: %p / %s\n", current->con, current->con->name);
1303  tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
1304  }
1305  }
1306 
1307  cmd_output->needs_tree_render = true;
1308  // XXX: default reply for now, make this a better reply
1309  ysuccess(true);
1310 }
1311 
1312 /*
1313  * Implementation of 'kill [window|client]'.
1314  *
1315  */
1316 void cmd_kill(I3_CMD, char *kill_mode_str) {
1317  if (kill_mode_str == NULL)
1318  kill_mode_str = "window";
1319  owindow *current;
1320 
1321  DLOG("kill_mode=%s\n", kill_mode_str);
1322 
1323  int kill_mode;
1324  if (strcmp(kill_mode_str, "window") == 0)
1325  kill_mode = KILL_WINDOW;
1326  else if (strcmp(kill_mode_str, "client") == 0)
1327  kill_mode = KILL_CLIENT;
1328  else {
1329  ELOG("BUG: called with kill_mode=%s\n", kill_mode_str);
1330  ysuccess(false);
1331  return;
1332  }
1333 
1334  /* check if the match is empty, not if the result is empty */
1336  tree_close_con(kill_mode);
1337  else {
1338  TAILQ_FOREACH (current, &owindows, owindows) {
1339  DLOG("matching: %p / %s\n", current->con, current->con->name);
1340  tree_close(current->con, kill_mode, false, false);
1341  }
1342  }
1343 
1344  cmd_output->needs_tree_render = true;
1345  // XXX: default reply for now, make this a better reply
1346  ysuccess(true);
1347 }
1348 
1349 /*
1350  * Implementation of 'exec [--no-startup-id] <command>'.
1351  *
1352  */
1353 void cmd_exec(I3_CMD, char *nosn, char *command) {
1354  bool no_startup_id = (nosn != NULL);
1355 
1356  DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
1357  start_application(command, no_startup_id);
1358 
1359  // XXX: default reply for now, make this a better reply
1360  ysuccess(true);
1361 }
1362 
1363 /*
1364  * Implementation of 'focus left|right|up|down'.
1365  *
1366  */
1367 void cmd_focus_direction(I3_CMD, char *direction) {
1368  DLOG("direction = *%s*\n", direction);
1369 
1370  if (strcmp(direction, "left") == 0)
1371  tree_next('p', HORIZ);
1372  else if (strcmp(direction, "right") == 0)
1373  tree_next('n', HORIZ);
1374  else if (strcmp(direction, "up") == 0)
1375  tree_next('p', VERT);
1376  else if (strcmp(direction, "down") == 0)
1377  tree_next('n', VERT);
1378  else {
1379  ELOG("Invalid focus direction (%s)\n", direction);
1380  ysuccess(false);
1381  return;
1382  }
1383 
1384  cmd_output->needs_tree_render = true;
1385  // XXX: default reply for now, make this a better reply
1386  ysuccess(true);
1387 }
1388 
1389 /*
1390  * Implementation of 'focus tiling|floating|mode_toggle'.
1391  *
1392  */
1393 void cmd_focus_window_mode(I3_CMD, char *window_mode) {
1394  DLOG("window_mode = %s\n", window_mode);
1395 
1396  Con *ws = con_get_workspace(focused);
1397  Con *current;
1398  if (ws != NULL) {
1399  if (strcmp(window_mode, "mode_toggle") == 0) {
1400  current = TAILQ_FIRST(&(ws->focus_head));
1401  if (current != NULL && current->type == CT_FLOATING_CON)
1402  window_mode = "tiling";
1403  else
1404  window_mode = "floating";
1405  }
1406  TAILQ_FOREACH (current, &(ws->focus_head), focused) {
1407  if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
1408  (strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
1409  continue;
1410 
1411  con_focus(con_descend_focused(current));
1412  break;
1413  }
1414  }
1415 
1416  cmd_output->needs_tree_render = true;
1417  // XXX: default reply for now, make this a better reply
1418  ysuccess(true);
1419 }
1420 
1421 /*
1422  * Implementation of 'focus parent|child'.
1423  *
1424  */
1425 void cmd_focus_level(I3_CMD, char *level) {
1426  DLOG("level = %s\n", level);
1427  bool success = false;
1428 
1429  /* Focusing the parent can only be allowed if the newly
1430  * focused container won't escape the fullscreen container. */
1431  if (strcmp(level, "parent") == 0) {
1432  if (focused && focused->parent) {
1434  success = level_up();
1435  else
1436  ELOG("'focus parent': Currently in fullscreen, not going up\n");
1437  }
1438  }
1439 
1440  /* Focusing a child should always be allowed. */
1441  else
1442  success = level_down();
1443 
1444  cmd_output->needs_tree_render = success;
1445  // XXX: default reply for now, make this a better reply
1446  ysuccess(success);
1447 }
1448 
1449 /*
1450  * Implementation of 'focus'.
1451  *
1452  */
1454  DLOG("current_match = %p\n", current_match);
1455 
1457  ELOG("You have to specify which window/container should be focused.\n");
1458  ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
1459 
1460  yerror("You have to specify which window/container should be focused");
1461 
1462  return;
1463  }
1464 
1465  Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
1466  int count = 0;
1467  owindow *current;
1468  TAILQ_FOREACH (current, &owindows, owindows) {
1469  Con *ws = con_get_workspace(current->con);
1470  /* If no workspace could be found, this was a dock window.
1471  * Just skip it, you cannot focus dock windows. */
1472  if (!ws)
1473  continue;
1474 
1475  /* Check the fullscreen focus constraints. */
1476  if (!con_fullscreen_permits_focusing(current->con)) {
1477  LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
1478  ysuccess(false);
1479  return;
1480  }
1481 
1482  /* In case this is a scratchpad window, call scratchpad_show(). */
1483  if (ws == __i3_scratch) {
1484  scratchpad_show(current->con);
1485  count++;
1486  /* While for the normal focus case we can change focus multiple
1487  * times and only a single window ends up focused, we could show
1488  * multiple scratchpad windows. So, rather break here. */
1489  break;
1490  }
1491 
1492  /* If the container is not on the current workspace,
1493  * workspace_show() will switch to a different workspace and (if
1494  * enabled) trigger a mouse pointer warp to the currently focused
1495  * container (!) on the target workspace.
1496  *
1497  * Therefore, before calling workspace_show(), we make sure that
1498  * 'current' will be focused on the workspace. However, we cannot
1499  * just con_focus(current) because then the pointer will not be
1500  * warped at all (the code thinks we are already there).
1501  *
1502  * So we focus 'current' to make it the currently focused window of
1503  * the target workspace, then revert focus. */
1504  Con *currently_focused = focused;
1505  con_focus(current->con);
1506  con_focus(currently_focused);
1507 
1508  /* Now switch to the workspace, then focus */
1509  workspace_show(ws);
1510  LOG("focusing %p / %s\n", current->con, current->con->name);
1511  con_focus(current->con);
1512  count++;
1513  }
1514 
1515  if (count > 1)
1516  LOG("WARNING: Your criteria for the focus command matches %d containers, "
1517  "while only exactly one container can be focused at a time.\n",
1518  count);
1519 
1520  cmd_output->needs_tree_render = true;
1521  // XXX: default reply for now, make this a better reply
1522  ysuccess(true);
1523 }
1524 
1525 /*
1526  * Implementation of 'fullscreen [global]'.
1527  *
1528  */
1529 void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
1530  if (fullscreen_mode == NULL)
1531  fullscreen_mode = "output";
1532  DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
1533  owindow *current;
1534 
1536 
1537  TAILQ_FOREACH (current, &owindows, owindows) {
1538  DLOG("matching: %p / %s\n", current->con, current->con->name);
1539  con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
1540  }
1541 
1542  cmd_output->needs_tree_render = true;
1543  // XXX: default reply for now, make this a better reply
1544  ysuccess(true);
1545 }
1546 
1547 /*
1548  * Implementation of 'move <direction> [<pixels> [px]]'.
1549  *
1550  */
1551 void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
1552  // 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
1553  int px = atoi(move_px);
1554 
1555  /* TODO: make 'move' work with criteria. */
1556  DLOG("moving in direction %s, px %s\n", direction, move_px);
1557  if (con_is_floating(focused)) {
1558  DLOG("floating move with %d pixels\n", px);
1559  Rect newrect = focused->parent->rect;
1560  if (strcmp(direction, "left") == 0) {
1561  newrect.x -= px;
1562  } else if (strcmp(direction, "right") == 0) {
1563  newrect.x += px;
1564  } else if (strcmp(direction, "up") == 0) {
1565  newrect.y -= px;
1566  } else if (strcmp(direction, "down") == 0) {
1567  newrect.y += px;
1568  }
1569  floating_reposition(focused->parent, newrect);
1570  } else {
1571  tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
1572  cmd_output->needs_tree_render = true;
1573  }
1574 
1575  // XXX: default reply for now, make this a better reply
1576  ysuccess(true);
1577 }
1578 
1579 /*
1580  * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
1581  *
1582  */
1583 void cmd_layout(I3_CMD, char *layout_str) {
1584  if (strcmp(layout_str, "stacking") == 0)
1585  layout_str = "stacked";
1586  owindow *current;
1587  layout_t layout;
1588  /* default is a special case which will be handled in con_set_layout(). */
1589  if (strcmp(layout_str, "default") == 0)
1590  layout = L_DEFAULT;
1591  else if (strcmp(layout_str, "stacked") == 0)
1592  layout = L_STACKED;
1593  else if (strcmp(layout_str, "tabbed") == 0)
1594  layout = L_TABBED;
1595  else if (strcmp(layout_str, "splitv") == 0)
1596  layout = L_SPLITV;
1597  else if (strcmp(layout_str, "splith") == 0)
1598  layout = L_SPLITH;
1599  else {
1600  ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
1601  return;
1602  }
1603 
1604  DLOG("changing layout to %s (%d)\n", layout_str, layout);
1605 
1606  /* check if the match is empty, not if the result is empty */
1608  con_set_layout(focused, layout);
1609  else {
1610  TAILQ_FOREACH (current, &owindows, owindows) {
1611  DLOG("matching: %p / %s\n", current->con, current->con->name);
1612  con_set_layout(current->con, layout);
1613  }
1614  }
1615 
1616  cmd_output->needs_tree_render = true;
1617  // XXX: default reply for now, make this a better reply
1618  ysuccess(true);
1619 }
1620 
1621 /*
1622  * Implementation of 'layout toggle [all|split]'.
1623  *
1624  */
1625 void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
1626  owindow *current;
1627 
1628  if (toggle_mode == NULL)
1629  toggle_mode = "default";
1630 
1631  DLOG("toggling layout (mode = %s)\n", toggle_mode);
1632 
1633  /* check if the match is empty, not if the result is empty */
1635  con_toggle_layout(focused, toggle_mode);
1636  else {
1637  TAILQ_FOREACH (current, &owindows, owindows) {
1638  DLOG("matching: %p / %s\n", current->con, current->con->name);
1639  con_toggle_layout(current->con, toggle_mode);
1640  }
1641  }
1642 
1643  cmd_output->needs_tree_render = true;
1644  // XXX: default reply for now, make this a better reply
1645  ysuccess(true);
1646 }
1647 
1648 /*
1649  * Implementation of 'exit'.
1650  *
1651  */
1653  LOG("Exiting due to user command.\n");
1654  ipc_shutdown();
1655  unlink(config.ipc_socket_path);
1656  xcb_disconnect(conn);
1657  exit(0);
1658 
1659  /* unreached */
1660 }
1661 
1662 /*
1663  * Implementation of 'reload'.
1664  *
1665  */
1667  LOG("reloading\n");
1670  load_configuration(conn, NULL, true);
1671  x_set_i3_atoms();
1672  /* Send an IPC event just in case the ws names have changed */
1673  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
1674  /* Send an update event for the barconfig just in case it has changed */
1675  update_barconfig();
1676 
1677  // XXX: default reply for now, make this a better reply
1678  ysuccess(true);
1679 }
1680 
1681 /*
1682  * Implementation of 'restart'.
1683  *
1684  */
1686  LOG("restarting i3\n");
1687  ipc_shutdown();
1688  /* We need to call this manually since atexit handlers don’t get called
1689  * when exec()ing */
1691  /* The unlink call is intentionally after the purge_zerobyte_logfile() so
1692  * that the latter does not remove the directory yet. We need to store the
1693  * restart layout state in there. */
1694  unlink(config.ipc_socket_path);
1695  i3_restart(false);
1696 
1697  // XXX: default reply for now, make this a better reply
1698  ysuccess(true);
1699 }
1700 
1701 /*
1702  * Implementation of 'open'.
1703  *
1704  */
1706  LOG("opening new container\n");
1707  Con *con = tree_open_con(NULL, NULL);
1708  con->layout = L_SPLITH;
1709  con_focus(con);
1710 
1711  y(map_open);
1712  ystr("success");
1713  y(bool, true);
1714  ystr("id");
1715  y(integer, (long int)con);
1716  y(map_close);
1717 
1718  cmd_output->needs_tree_render = true;
1719 }
1720 
1721 /*
1722  * Implementation of 'focus output <output>'.
1723  *
1724  */
1726  owindow *current;
1727 
1728  DLOG("name = %s\n", name);
1729 
1731 
1732  /* get the output */
1733  Output *current_output = NULL;
1734  Output *output;
1735 
1736  TAILQ_FOREACH (current, &owindows, owindows)
1737  current_output = get_output_of_con(current->con);
1738  assert(current_output != NULL);
1739 
1740  output = get_output_from_string(current_output, name);
1741 
1742  if (!output) {
1743  LOG("No such output found.\n");
1744  ysuccess(false);
1745  return;
1746  }
1747 
1748  /* get visible workspace on output */
1749  Con *ws = NULL;
1750  GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1751  if (!ws) {
1752  ysuccess(false);
1753  return;
1754  }
1755 
1756  workspace_show(ws);
1757 
1758  cmd_output->needs_tree_render = true;
1759  // XXX: default reply for now, make this a better reply
1760  ysuccess(true);
1761 }
1762 
1763 /*
1764  * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
1765  *
1766  */
1767 void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
1768  int x = atoi(cx);
1769  int y = atoi(cy);
1770 
1771  if (!con_is_floating(focused)) {
1772  ELOG("Cannot change position. The window/container is not floating\n");
1773  yerror("Cannot change position. The window/container is not floating.");
1774  return;
1775  }
1776 
1777  if (strcmp(method, "absolute") == 0) {
1778  focused->parent->rect.x = x;
1779  focused->parent->rect.y = y;
1780 
1781  DLOG("moving to absolute position %d %d\n", x, y);
1783  cmd_output->needs_tree_render = true;
1784  }
1785 
1786  if (strcmp(method, "position") == 0) {
1787  Rect newrect = focused->parent->rect;
1788 
1789  DLOG("moving to position %d %d\n", x, y);
1790  newrect.x = x;
1791  newrect.y = y;
1792 
1793  floating_reposition(focused->parent, newrect);
1794  }
1795 
1796  // XXX: default reply for now, make this a better reply
1797  ysuccess(true);
1798 }
1799 
1800 /*
1801  * Implementation of 'move [window|container] [to] [absolute] position center
1802  *
1803  */
1804 void cmd_move_window_to_center(I3_CMD, char *method) {
1805  if (!con_is_floating(focused)) {
1806  ELOG("Cannot change position. The window/container is not floating\n");
1807  yerror("Cannot change position. The window/container is not floating.");
1808  return;
1809  }
1810 
1811  if (strcmp(method, "absolute") == 0) {
1812  Rect *rect = &focused->parent->rect;
1813 
1814  DLOG("moving to absolute center\n");
1815  rect->x = croot->rect.width / 2 - rect->width / 2;
1816  rect->y = croot->rect.height / 2 - rect->height / 2;
1817 
1819  cmd_output->needs_tree_render = true;
1820  }
1821 
1822  if (strcmp(method, "position") == 0) {
1823  Rect *wsrect = &con_get_workspace(focused)->rect;
1824  Rect newrect = focused->parent->rect;
1825 
1826  DLOG("moving to center\n");
1827  newrect.x = wsrect->width / 2 - newrect.width / 2;
1828  newrect.y = wsrect->height / 2 - newrect.height / 2;
1829 
1830  floating_reposition(focused->parent, newrect);
1831  }
1832 
1833  // XXX: default reply for now, make this a better reply
1834  ysuccess(true);
1835 }
1836 
1837 /*
1838  * Implementation of 'move scratchpad'.
1839  *
1840  */
1842  DLOG("should move window to scratchpad\n");
1843  owindow *current;
1844 
1846 
1847  TAILQ_FOREACH (current, &owindows, owindows) {
1848  DLOG("matching: %p / %s\n", current->con, current->con->name);
1849  scratchpad_move(current->con);
1850  }
1851 
1852  cmd_output->needs_tree_render = true;
1853  // XXX: default reply for now, make this a better reply
1854  ysuccess(true);
1855 }
1856 
1857 /*
1858  * Implementation of 'scratchpad show'.
1859  *
1860  */
1862  DLOG("should show scratchpad window\n");
1863  owindow *current;
1864 
1866  scratchpad_show(NULL);
1867  } else {
1868  TAILQ_FOREACH (current, &owindows, owindows) {
1869  DLOG("matching: %p / %s\n", current->con, current->con->name);
1870  scratchpad_show(current->con);
1871  }
1872  }
1873 
1874  cmd_output->needs_tree_render = true;
1875  // XXX: default reply for now, make this a better reply
1876  ysuccess(true);
1877 }
1878 
1879 /*
1880  * Implementation of 'rename workspace [<name>] to <name>'
1881  *
1882  */
1883 void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
1884  if (strncasecmp(new_name, "__", strlen("__")) == 0) {
1885  LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name);
1886  ysuccess(false);
1887  return;
1888  }
1889  if (old_name) {
1890  LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
1891  } else {
1892  LOG("Renaming current workspace to \"%s\"\n", new_name);
1893  }
1894 
1895  Con *output, *workspace = NULL;
1896  if (old_name) {
1897  TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
1898  GREP_FIRST(workspace, output_get_content(output),
1899  !strcasecmp(child->name, old_name));
1900  } else {
1901  workspace = con_get_workspace(focused);
1902  }
1903 
1904  if (!workspace) {
1905  // TODO: we should include the old workspace name here and use yajl for
1906  // generating the reply.
1907  // TODO: better error message
1908  yerror("Old workspace not found");
1909  return;
1910  }
1911 
1912  Con *check_dest = NULL;
1913  TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
1914  GREP_FIRST(check_dest, output_get_content(output),
1915  !strcasecmp(child->name, new_name));
1916 
1917  if (check_dest != NULL) {
1918  // TODO: we should include the new workspace name here and use yajl for
1919  // generating the reply.
1920  // TODO: better error message
1921  yerror("New workspace already exists");
1922  return;
1923  }
1924 
1925  /* Change the name and try to parse it as a number. */
1926  FREE(workspace->name);
1927  workspace->name = sstrdup(new_name);
1928  char *endptr = NULL;
1929  long parsed_num = strtol(new_name, &endptr, 10);
1930  if (parsed_num == LONG_MIN ||
1931  parsed_num == LONG_MAX ||
1932  parsed_num < 0 ||
1933  endptr == new_name)
1934  workspace->num = -1;
1935  else
1936  workspace->num = parsed_num;
1937  LOG("num = %d\n", workspace->num);
1938 
1939  /* By re-attaching, the sort order will be correct afterwards. */
1940  Con *previously_focused = focused;
1941  Con *parent = workspace->parent;
1942  con_detach(workspace);
1943  con_attach(workspace, parent, false);
1944  /* Restore the previous focus since con_attach messes with the focus. */
1945  con_focus(previously_focused);
1946 
1947  cmd_output->needs_tree_render = true;
1948  ysuccess(true);
1949 
1950  ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
1951 }
1952 
1953 /*
1954  * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
1955  *
1956  */
1957 bool cmd_bar_mode(char *bar_mode, char *bar_id) {
1958  int mode = M_DOCK;
1959  bool toggle = false;
1960  if (strcmp(bar_mode, "dock") == 0)
1961  mode = M_DOCK;
1962  else if (strcmp(bar_mode, "hide") == 0)
1963  mode = M_HIDE;
1964  else if (strcmp(bar_mode, "invisible") == 0)
1965  mode = M_INVISIBLE;
1966  else if (strcmp(bar_mode, "toggle") == 0)
1967  toggle = true;
1968  else {
1969  ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
1970  return false;
1971  }
1972 
1973  bool changed_sth = false;
1974  Barconfig *current = NULL;
1975  TAILQ_FOREACH (current, &barconfigs, configs) {
1976  if (bar_id && strcmp(current->id, bar_id) != 0)
1977  continue;
1978 
1979  if (toggle)
1980  mode = (current->mode + 1) % 2;
1981 
1982  DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
1983  current->mode = mode;
1984  changed_sth = true;
1985 
1986  if (bar_id)
1987  break;
1988  }
1989 
1990  if (bar_id && !changed_sth) {
1991  DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
1992  return false;
1993  }
1994 
1995  return true;
1996 }
1997 
1998 /*
1999  * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
2000  *
2001  */
2002 bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
2003  int hidden_state = S_SHOW;
2004  bool toggle = false;
2005  if (strcmp(bar_hidden_state, "hide") == 0)
2006  hidden_state = S_HIDE;
2007  else if (strcmp(bar_hidden_state, "show") == 0)
2008  hidden_state = S_SHOW;
2009  else if (strcmp(bar_hidden_state, "toggle") == 0)
2010  toggle = true;
2011  else {
2012  ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
2013  return false;
2014  }
2015 
2016  bool changed_sth = false;
2017  Barconfig *current = NULL;
2018  TAILQ_FOREACH (current, &barconfigs, configs) {
2019  if (bar_id && strcmp(current->id, bar_id) != 0)
2020  continue;
2021 
2022  if (toggle)
2023  hidden_state = (current->hidden_state + 1) % 2;
2024 
2025  DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
2026  current->hidden_state = hidden_state;
2027  changed_sth = true;
2028 
2029  if (bar_id)
2030  break;
2031  }
2032 
2033  if (bar_id && !changed_sth) {
2034  DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
2035  return false;
2036  }
2037 
2038  return true;
2039 }
2040 
2041 /*
2042  * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
2043  *
2044  */
2045 void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) {
2046  bool ret;
2047  if (strcmp(bar_type, "mode") == 0)
2048  ret = cmd_bar_mode(bar_value, bar_id);
2049  else if (strcmp(bar_type, "hidden_state") == 0)
2050  ret = cmd_bar_hidden_state(bar_value, bar_id);
2051  else {
2052  ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
2053  ret = false;
2054  }
2055 
2056  ysuccess(ret);
2057  if (!ret)
2058  return;
2059 
2060  update_barconfig();
2061 }
2062 
2063 /*
2064  * Implementation of 'shmlog <size>|toggle|on|off'
2065  *
2066  */
2067 void cmd_shmlog(I3_CMD, char *argument) {
2068  if (!strcmp(argument, "toggle"))
2069  /* Toggle shm log, if size is not 0. If it is 0, set it to default. */
2071  else if (!strcmp(argument, "on"))
2073  else if (!strcmp(argument, "off"))
2074  shmlog_size = 0;
2075  else {
2076  /* If shm logging now, restart logging with the new size. */
2077  if (shmlog_size > 0) {
2078  shmlog_size = 0;
2079  LOG("Restarting shm logging...\n");
2080  init_logging();
2081  }
2082  shmlog_size = atoi(argument);
2083  /* Make a weakly attempt at ensuring the argument is valid. */
2084  if (shmlog_size <= 0)
2086  }
2087  LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling");
2088  init_logging();
2090  // XXX: default reply for now, make this a better reply
2091  ysuccess(true);
2092 }
2093 
2094 /*
2095  * Implementation of 'debuglog toggle|on|off'
2096  *
2097  */
2098 void cmd_debuglog(I3_CMD, char *argument) {
2099  bool logging = get_debug_logging();
2100  if (!strcmp(argument, "toggle")) {
2101  LOG("%s debug logging\n", logging ? "Disabling" : "Enabling");
2102  set_debug_logging(!logging);
2103  } else if (!strcmp(argument, "on") && !logging) {
2104  LOG("Enabling debug logging\n");
2105  set_debug_logging(true);
2106  } else if (!strcmp(argument, "off") && logging) {
2107  LOG("Disabling debug logging\n");
2108  set_debug_logging(false);
2109  }
2110  // XXX: default reply for now, make this a better reply
2111  ysuccess(true);
2112 }
enum Con::@20 scratchpad_state
Definition: data.h:87
struct Con * parent
Definition: data.h:512
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 not...
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
Definition: data.h:56
int height_increment
Definition: data.h:545
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:1861
void cmd_move_con_to_workspace_name(I3_CMD, char *name)
Implementation of 'move [window|container] [to] workspace <name>'.
Definition: commands.c:492
double percent
Definition: data.h:530
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:2002
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:2067
void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resize_ppt)
Implementation of 'resize grow|shrink <direction> [<px> px] [or <ppt> 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 <comment>'.
Definition: commands.c:873
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:514
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:528
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:396
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:395
#define ystr(str)
Definition: commands.c:20
void cmd_floating(I3_CMD, char *floating_mode)
Implementation of 'floating enable|disable|toggle'.
Definition: commands.c:1149
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:1804
void cmd_move_direction(I3_CMD, char *direction, char *move_px)
Implementation of 'move <direction> [<pixels> [px]]'.
Definition: commands.c:1551
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] <px> [px]...
Definition: commands.c:1767
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:547
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:1453
void cmd_workspace(I3_CMD, char *which)
Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
Definition: commands.c:924
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:1393
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:579
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:1294
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 <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 <mark>'.
Definition: commands.c:1027
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:394
void cmd_layout(I3_CMD, char *layout_str)
Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
Definition: commands.c:1583
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:1705
bool cmd_bar_mode(char *bar_mode, char *bar_id)
Definition: commands.c:1957
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 <string>'.
Definition: commands.c:1080
void cmd_move_con_to_output(I3_CMD, char *name)
Implementation of 'move [window|container] [to] output <str>'.
Definition: commands.c:1092
xcb_window_t id
Definition: data.h:409
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
#define yerror(message)
Definition: commands.c:30
uint32_t height
Definition: data.h:126
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:319
Con * con_id
Definition: data.h:411
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:510
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:520
void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name)
Implementation of 'rename workspace <name> to <name>'.
Definition: commands.c:1883
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:994
#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] <str>'.
Definition: commands.c:1180
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:1529
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:578
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:479
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:1367
enum Barconfig::@6 hidden_state
void cmd_append_layout(I3_CMD, char *path)
Implementation of 'append_layout <path>'.
Definition: commands.c:883
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:391
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 <output>'.
Definition: commands.c:1725
void cmd_layout_toggle(I3_CMD, char *toggle_mode)
Implementation of 'layout toggle [all|split]'.
Definition: commands.c:1625
#define TAILQ_END(head)
Definition: queue.h:324
Con * workspace_prev(void)
Returns the previous workspace.
Definition: workspace.c:535
#define ysuccess(success)
Definition: commands.c:21
void cmd_reload(I3_CMD)
Implementation of 'reload'.
Definition: commands.c:1666
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:1652
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] <command>'.
Definition: commands.c:1353
struct Con * croot
Definition: tree.c:14
void cmd_workspace_number(I3_CMD, char *which)
Implementation of 'workspace number <number>'.
Definition: commands.c:954
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) [<bar_id>]'...
Definition: commands.c:2045
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 <name>'.
Definition: commands.c:1006
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:1126
void update_shmlog_atom()
Set up the SHMLOG_PATH atom.
Definition: x.c:1116
void cmd_kill(I3_CMD, char *kill_mode_str)
Implementation of 'kill [window|client]'.
Definition: commands.c:1316
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:1425
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:334
void cmd_move_scratchpad(I3_CMD)
Implementation of 'move scratchpad'.
Definition: commands.c:1841
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:1685
uint32_t x
Definition: data.h:30
void cmd_unmark(I3_CMD, char *mark)
Implementation of 'unmark [mark]'.
Definition: commands.c:1055
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:2098
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:544
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:393
uint32_t width
Definition: data.h:125
Definition: data.h:90
void match_free(Match *match)
Frees the given match.
Definition: match.c:191