2 #define I3__FILE__ "commands.c"
18 #define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__)
19 #define ystr(str) yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str))
20 #define ysuccess(success) do { \
32 #define HANDLE_EMPTY_MATCH do { \
33 if (match_is_empty(current_match)) { \
34 owindow *ow = smalloc(sizeof(owindow)); \
36 TAILQ_INIT(&owindows); \
37 TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
47 return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
58 if (strcasecmp(output_str,
"left") == 0)
60 else if (strcasecmp(output_str,
"right") == 0)
62 else if (strcasecmp(output_str,
"up") == 0)
64 else if (strcasecmp(output_str,
"down") == 0)
82 if (strcmp(ws->
name, name) != 0)
85 DLOG(
"This workspace is already focused.\n");
105 if (current == workspace) {
108 DLOG(
"Substituting workspace with back_and_forth, as it is focused.\n");
119 static pid_t migration_pid = -1;
126 static void nagbar_exited(EV_P_ ev_child *watcher,
int revents) {
127 ev_child_stop(EV_A_ watcher);
128 if (!WIFEXITED(watcher->rstatus)) {
129 fprintf(stderr,
"ERROR: i3-nagbar did not exit normally.\n");
133 int exitcode = WEXITSTATUS(watcher->rstatus);
134 printf(
"i3-nagbar process exited with status %d\n", exitcode);
136 fprintf(stderr,
"ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
145 #if EV_VERSION_MAJOR >= 4
151 static void nagbar_cleanup(EV_P_ ev_cleanup *watcher,
int revent) {
152 if (migration_pid != -1) {
153 LOG(
"Sending SIGKILL (9) to i3-nagbar with PID %d\n", migration_pid);
154 kill(migration_pid, SIGKILL);
159 void cmd_MIGRATION_start_nagbar(
void) {
160 if (migration_pid != -1) {
161 fprintf(stderr,
"i3-nagbar already running.\n");
164 fprintf(stderr,
"Starting i3-nagbar, command parsing differs from expected output.\n");
165 ELOG(
"Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile:\n");
166 ELOG(
"i3-dump-log | gzip -9c > /tmp/i3.log.gz\n");
167 ELOG(
"FYI: Your i3 version is " I3_VERSION
"\n");
168 migration_pid = fork();
169 if (migration_pid == -1) {
170 warn(
"Could not fork()");
175 if (migration_pid == 0) {
183 "You found a parsing error. Please, please, please, report it!",
194 ev_child *child =
smalloc(
sizeof(ev_child));
201 #if EV_VERSION_MAJOR >= 4
204 ev_cleanup *cleanup =
smalloc(
sizeof(ev_cleanup));
228 static owindows_head owindows;
263 DLOG(
"match specification finished, matching...\n");
266 struct owindows_head old = owindows;
275 DLOG(
"checking if con %p / %s matches\n", current->
con, current->
con->
name);
278 DLOG(
"matches container!\n");
283 DLOG(
"match by mark\n");
289 DLOG(
"matches window!\n");
292 DLOG(
"doesnt match\n");
309 DLOG(
"ctype=*%s*, cvalue=*%s*\n", ctype, cvalue);
311 if (strcmp(ctype,
"class") == 0) {
316 if (strcmp(ctype,
"instance") == 0) {
321 if (strcmp(ctype,
"window_role") == 0) {
326 if (strcmp(ctype,
"con_id") == 0) {
328 long parsed = strtol(cvalue, &end, 10);
329 if (parsed == LONG_MIN ||
330 parsed == LONG_MAX ||
332 (end && *end !=
'\0')) {
333 ELOG(
"Could not parse con id \"%s\"\n", cvalue);
341 if (strcmp(ctype,
"id") == 0) {
343 long parsed = strtol(cvalue, &end, 10);
344 if (parsed == LONG_MIN ||
345 parsed == LONG_MAX ||
347 (end && *end !=
'\0')) {
348 ELOG(
"Could not parse window id \"%s\"\n", cvalue);
356 if (strcmp(ctype,
"con_mark") == 0) {
361 if (strcmp(ctype,
"title") == 0) {
366 if (strcmp(ctype,
"urgent") == 0) {
367 if (strcasecmp(cvalue,
"latest") == 0 ||
368 strcasecmp(cvalue,
"newest") == 0 ||
369 strcasecmp(cvalue,
"recent") == 0 ||
370 strcasecmp(cvalue,
"last") == 0) {
372 }
else if (strcasecmp(cvalue,
"oldest") == 0 ||
373 strcasecmp(cvalue,
"first") == 0) {
379 ELOG(
"Unknown criterion: %s\n", ctype);
390 DLOG(
"which=%s\n", which);
406 if (strcmp(which,
"next") == 0)
408 else if (strcmp(which,
"prev") == 0)
410 else if (strcmp(which,
"next_on_output") == 0)
412 else if (strcmp(which,
"prev_on_output") == 0)
414 else if (strcmp(which,
"current") == 0)
417 ELOG(
"BUG: called with which=%s\n", which);
427 cmd_output->needs_tree_render =
true;
447 ystr(
"No workspace was previously active.");
459 cmd_output->needs_tree_render =
true;
469 if (strncasecmp(name,
"__i3_", strlen(
"__i3_")) == 0) {
470 LOG(
"You cannot switch to the i3 internal workspaces.\n");
481 ELOG(
"No windows match your criteria, cannot move.\n");
491 LOG(
"should move window to workspace %s\n", name);
504 cmd_output->needs_tree_render =
true;
526 LOG(
"should move window to workspace %s\n", which);
528 Con *output, *workspace = NULL;
531 long parsed_num = strtol(which, &endptr, 10);
532 if (parsed_num == LONG_MIN ||
533 parsed_num == LONG_MAX ||
536 LOG(
"Could not parse initial part of \"%s\" as a number.\n", which);
542 ystr(
"Could not parse number");
549 child->
num == parsed_num);
564 cmd_output->needs_tree_render =
true;
570 LOG(
"floating resize\n");
573 if (strcmp(direction,
"up") == 0) {
575 }
else if (strcmp(direction,
"down") == 0 || strcmp(direction,
"height") == 0) {
577 }
else if (strcmp(direction,
"left") == 0) {
587 if (memcmp(&old_rect, &(floating_con->
rect),
sizeof(
Rect)) == 0)
590 if (strcmp(direction,
"up") == 0) {
592 }
else if (strcmp(direction,
"left") == 0) {
602 LOG(
"tiling resize\n");
605 double percentage = 0;
608 current = current->
parent;
612 (strcmp(direction,
"left") == 0 || strcmp(direction,
"right") == 0 ?
HORIZ :
VERT);
616 current = current->
parent;
622 LOG(
"ins. %d children\n", children);
623 percentage = 1.0 / children;
624 LOG(
"default percentage = %f\n", percentage);
628 if ((orientation ==
HORIZ &&
629 (strcmp(direction,
"up") == 0 || strcmp(direction,
"down") == 0)) ||
630 (orientation ==
VERT &&
631 (strcmp(direction,
"left") == 0 || strcmp(direction,
"right") == 0))) {
632 LOG(
"You cannot resize in that direction. Your focus is in a %s split container currently.\n",
633 (orientation ==
HORIZ ?
"horizontal" :
"vertical"));
638 if (strcmp(direction,
"up") == 0 || strcmp(direction,
"left") == 0) {
639 other =
TAILQ_PREV(current, nodes_head, nodes);
644 LOG(
"No other container in this direction found, trying to look further up in the tree...\n");
645 current = current->
parent;
649 }
while (current->
type != CT_WORKSPACE &&
650 current->
type != CT_FLOATING_CON);
653 LOG(
"No other container in this direction found, trying to look further up in the tree...\n");
659 LOG(
"current->percent before = %f\n", current->
percent);
664 double new_current_percent = current->
percent + ((double)ppt / 100.0);
665 double new_other_percent = other->
percent - ((double)ppt / 100.0);
666 LOG(
"new_current_percent = %f\n", new_current_percent);
667 LOG(
"new_other_percent = %f\n", new_other_percent);
672 current->
percent += ((double)ppt / 100.0);
673 other->
percent -= ((double)ppt / 100.0);
674 LOG(
"current->percent after = %f\n", current->
percent);
675 LOG(
"other->percent after = %f\n", other->
percent);
677 LOG(
"Not resizing, already at minimum size\n");
684 LOG(
"width/height resize\n");
688 current = current->
parent;
692 (strcmp(direction,
"width") == 0 ?
HORIZ :
VERT);
694 while (current->
type != CT_WORKSPACE &&
695 current->
type != CT_FLOATING_CON &&
697 current = current->
parent;
701 LOG(
"ins. %d children\n", children);
702 double percentage = 1.0 / children;
703 LOG(
"default percentage = %f\n", percentage);
707 if ((orientation ==
HORIZ &&
708 strcmp(direction,
"height") == 0) ||
709 (orientation ==
VERT &&
710 strcmp(direction,
"width") == 0)) {
711 LOG(
"You cannot resize in that direction. Your focus is in a %s split container currently.\n",
712 (orientation ==
HORIZ ?
"horizontal" :
"vertical"));
718 LOG(
"This is the only container, cannot resize.\n");
726 LOG(
"child->percent = %f (child %p)\n", child->
percent, child);
731 double new_current_percent = current->
percent + ((double)ppt / 100.0);
732 double subtract_percent = ((double)ppt / 100.0) / (children - 1);
733 LOG(
"new_current_percent = %f\n", new_current_percent);
734 LOG(
"subtract_percent = %f\n", subtract_percent);
738 if (child == current)
741 LOG(
"Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->
percent - subtract_percent);
747 LOG(
"Not resizing, already at minimum size\n");
752 current->
percent += ((double)ppt / 100.0);
753 LOG(
"current->percent after = %f\n", current->
percent);
756 if (child == current)
758 child->
percent -= subtract_percent;
759 LOG(
"child->percent after (%p) = %f\n", child, child->
percent);
771 DLOG(
"resizing in way %s, direction %s, px %s or ppt %s\n", way, direction, resize_px, resize_ppt);
773 int px = atoi(resize_px);
774 int ppt = atoi(resize_ppt);
775 if (strcmp(way,
"shrink") == 0) {
788 if (strcmp(direction,
"width") == 0 ||
789 strcmp(direction,
"height") == 0) {
799 cmd_output->needs_tree_render =
true;
809 DLOG(
"border style should be changed to %s with border width %s\n", border_style_str, border_width);
818 int tmp_border_width = -1;
819 tmp_border_width = strtol(border_width, &end, 10);
820 if (end == border_width) {
822 tmp_border_width = -1;
824 if (strcmp(border_style_str,
"toggle") == 0) {
828 tmp_border_width = 2;
829 else if (border_style ==
BS_NONE)
830 tmp_border_width = 0;
832 tmp_border_width = 1;
834 if (strcmp(border_style_str,
"normal") == 0)
836 else if (strcmp(border_style_str,
"pixel") == 0)
838 else if (strcmp(border_style_str,
"1pixel") == 0){
840 tmp_border_width = 1;
841 }
else if (strcmp(border_style_str,
"none") == 0)
844 ELOG(
"BUG: called with border_style=%s\n", border_style_str);
852 cmd_output->needs_tree_render =
true;
862 LOG(
"-------------------------------------------------\n");
863 LOG(
" NOP: %s\n", comment);
864 LOG(
"-------------------------------------------------\n");
872 LOG(
"Appending layout \"%s\"\n", path);
875 cmd_output->needs_tree_render =
true;
887 DLOG(
"which=%s\n", which);
889 if (strcmp(which,
"next") == 0)
891 else if (strcmp(which,
"prev") == 0)
893 else if (strcmp(which,
"next_on_output") == 0)
895 else if (strcmp(which,
"prev_on_output") == 0)
898 ELOG(
"BUG: called with which=%s\n", which);
905 cmd_output->needs_tree_render =
true;
915 Con *output, *workspace = NULL;
918 long parsed_num = strtol(which, &endptr, 10);
919 if (parsed_num == LONG_MIN ||
920 parsed_num == LONG_MAX ||
923 LOG(
"Could not parse initial part of \"%s\" as a number.\n", which);
929 ystr(
"Could not parse number");
937 child->
num == parsed_num);
940 LOG(
"There is no workspace with number %ld, creating a new one.\n", parsed_num);
943 cmd_output->needs_tree_render =
true;
950 cmd_output->needs_tree_render =
true;
962 cmd_output->needs_tree_render =
true;
972 if (strncasecmp(name,
"__i3_", strlen(
"__i3_")) == 0) {
973 LOG(
"You cannot switch to the i3 internal workspaces.\n");
978 DLOG(
"should switch to workspace %s\n", name);
983 cmd_output->needs_tree_render =
true;
993 DLOG(
"Clearing all windows which have that mark first\n");
997 if (con->
mark && strcmp(con->
mark, mark) == 0)
1001 DLOG(
"marking window with str %s\n", mark);
1011 cmd_output->needs_tree_render =
true;
1021 DLOG(
"mode=%s\n", mode);
1035 DLOG(
"should move window to output %s\n", name);
1040 Output *current_output = NULL;
1047 assert(current_output != NULL);
1050 if (strcasecmp(name,
"up") == 0)
1052 else if (strcasecmp(name,
"down") == 0)
1054 else if (strcasecmp(name,
"left") == 0)
1056 else if (strcasecmp(name,
"right") == 0)
1062 LOG(
"No such output found.\n");
1080 cmd_output->needs_tree_render =
true;
1092 DLOG(
"floating_mode=%s\n", floating_mode);
1098 if (strcmp(floating_mode,
"toggle") == 0) {
1099 DLOG(
"should toggle mode\n");
1102 DLOG(
"should switch mode to %s\n", floating_mode);
1103 if (strcmp(floating_mode,
"enable") == 0) {
1111 cmd_output->needs_tree_render =
true;
1121 DLOG(
"should move workspace to output %s\n", name);
1129 if (!current_output) {
1130 ELOG(
"Cannot get current output. This is a bug in i3.\n");
1136 ELOG(
"Could not get output from string \"%s\"\n", name);
1142 LOG(
"got output %p with content %p\n", output, content);
1144 Con *previously_visible_ws =
TAILQ_FIRST(&(content->nodes_head));
1145 LOG(
"Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->
name);
1148 LOG(
"should move workspace %p / %s\n", ws, ws->
name);
1152 LOG(
"Creating a new workspace to replace \"%s\" (last on its output).\n", ws->
name);
1155 bool used_assignment =
false;
1158 if (strcmp(assignment->
output, current_output->
name) != 0)
1162 Con *workspace = NULL, *out;
1165 !strcasecmp(child->name, assignment->
name));
1166 if (workspace != NULL)
1170 LOG(
"Creating workspace from assignment %s.\n", assignment->
name);
1172 used_assignment =
true;
1178 if (!used_assignment)
1182 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE,
"{\"change\":\"init\"}");
1184 DLOG(
"Detaching\n");
1189 if (workspace_was_visible) {
1193 LOG(
"workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->
name);
1200 TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
1203 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE,
"{\"change\":\"move\"}");
1204 if (workspace_was_visible) {
1215 if (ws != previously_visible_ws)
1221 CALL(previously_visible_ws, on_remove_child);
1226 cmd_output->needs_tree_render =
true;
1238 LOG(
"splitting in direction %c\n", direction[0]);
1248 cmd_output->needs_tree_render =
true;
1258 if (kill_mode_str == NULL)
1259 kill_mode_str =
"window";
1262 DLOG(
"kill_mode=%s\n", kill_mode_str);
1265 if (strcmp(kill_mode_str,
"window") == 0)
1267 else if (strcmp(kill_mode_str,
"client") == 0)
1270 ELOG(
"BUG: called with kill_mode=%s\n", kill_mode_str);
1285 cmd_output->needs_tree_render =
true;
1295 bool no_startup_id = (nosn != NULL);
1297 DLOG(
"should execute %s, no_startup_id = %d\n", command, no_startup_id);
1309 DLOG(
"direction = *%s*\n", direction);
1311 if (strcmp(direction,
"left") == 0)
1313 else if (strcmp(direction,
"right") == 0)
1315 else if (strcmp(direction,
"up") == 0)
1317 else if (strcmp(direction,
"down") == 0)
1320 ELOG(
"Invalid focus direction (%s)\n", direction);
1325 cmd_output->needs_tree_render =
true;
1335 DLOG(
"window_mode = %s\n", window_mode);
1340 if (strcmp(window_mode,
"mode_toggle") == 0) {
1342 if (current != NULL && current->
type == CT_FLOATING_CON)
1343 window_mode =
"tiling";
1344 else window_mode =
"floating";
1347 if ((strcmp(window_mode,
"floating") == 0 && current->
type != CT_FLOATING_CON) ||
1348 (strcmp(window_mode,
"tiling") == 0 && current->
type == CT_FLOATING_CON))
1356 cmd_output->needs_tree_render =
true;
1366 DLOG(
"level = %s\n", level);
1367 bool success =
false;
1371 if (strcmp(level,
"parent") == 0) {
1376 ELOG(
"'focus parent': Currently in fullscreen, not going up\n");
1383 cmd_output->needs_tree_render = success;
1396 ELOG(
"You have to specify which window/container should be focused.\n");
1397 ELOG(
"Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
1403 ystr(
"You have to specify which window/container should be focused");
1421 LOG(
"Cannot change focus while in fullscreen mode (fullscreen rules).\n");
1427 if (ws == __i3_scratch) {
1454 LOG(
"focusing %p / %s\n", current->
con, current->
con->
name);
1460 LOG(
"WARNING: Your criteria for the focus command matches %d containers, "
1461 "while only exactly one container can be focused at a time.\n", count);
1463 cmd_output->needs_tree_render =
true;
1473 if (fullscreen_mode == NULL)
1474 fullscreen_mode =
"output";
1475 DLOG(
"toggling fullscreen, mode = %s\n", fullscreen_mode);
1481 printf(
"matching: %p / %s\n", current->
con, current->
con->
name);
1485 cmd_output->needs_tree_render =
true;
1496 int px = atoi(move_px);
1499 DLOG(
"moving in direction %s, px %s\n", direction, move_px);
1501 DLOG(
"floating move with %d pixels\n", px);
1503 if (strcmp(direction,
"left") == 0) {
1505 }
else if (strcmp(direction,
"right") == 0) {
1507 }
else if (strcmp(direction,
"up") == 0) {
1509 }
else if (strcmp(direction,
"down") == 0) {
1515 (strcmp(direction,
"left") == 0 ?
D_LEFT :
1516 (strcmp(direction,
"up") == 0 ?
D_UP :
1518 cmd_output->needs_tree_render =
true;
1530 if (strcmp(layout_str,
"stacking") == 0)
1531 layout_str =
"stacked";
1535 if (strcmp(layout_str,
"default") == 0)
1537 else if (strcmp(layout_str,
"stacked") == 0)
1539 else if (strcmp(layout_str,
"tabbed") == 0)
1541 else if (strcmp(layout_str,
"splitv") == 0)
1543 else if (strcmp(layout_str,
"splith") == 0)
1546 ELOG(
"Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
1550 DLOG(
"changing layout to %s (%d)\n", layout_str, layout);
1562 cmd_output->needs_tree_render =
true;
1574 if (toggle_mode == NULL)
1575 toggle_mode =
"default";
1577 DLOG(
"toggling layout (mode = %s)\n", toggle_mode);
1589 cmd_output->needs_tree_render =
true;
1599 LOG(
"Exiting due to user command.\n");
1600 xcb_disconnect(
conn);
1617 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE,
"{\"change\":\"reload\"}");
1628 LOG(
"restarting i3\n");
1640 LOG(
"opening new container\n");
1649 y(integer, (
long int)con);
1652 cmd_output->needs_tree_render =
true;
1662 DLOG(
"name = %s\n", name);
1667 Output *current_output = NULL;
1672 assert(current_output != NULL);
1677 LOG(
"No such output found.\n");
1692 cmd_output->needs_tree_render =
true;
1707 ELOG(
"Cannot change position. The window/container is not floating\n");
1712 ystr(
"Cannot change position. The window/container is not floating.");
1717 if (strcmp(method,
"absolute") == 0) {
1721 DLOG(
"moving to absolute position %d %d\n", x, y);
1723 cmd_output->needs_tree_render =
true;
1726 if (strcmp(method,
"position") == 0) {
1729 DLOG(
"moving to position %d %d\n", x, y);
1747 ELOG(
"Cannot change position. The window/container is not floating\n");
1752 ystr(
"Cannot change position. The window/container is not floating.");
1756 if (strcmp(method,
"absolute") == 0) {
1759 DLOG(
"moving to absolute center\n");
1764 cmd_output->needs_tree_render =
true;
1767 if (strcmp(method,
"position") == 0) {
1771 DLOG(
"moving to center\n");
1787 DLOG(
"should move window to scratchpad\n");
1797 cmd_output->needs_tree_render =
true;
1807 DLOG(
"should show scratchpad window\n");
1819 cmd_output->needs_tree_render =
true;
1830 LOG(
"Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
1832 LOG(
"Renaming current workspace to \"%s\"\n", new_name);
1839 !strcasecmp(child->name, old_name));
1852 ystr(
"Old workspace not found");
1857 Con *check_dest = NULL;
1860 !strcasecmp(child->name, new_name));
1862 if (check_dest != NULL) {
1870 ystr(
"New workspace already exists");
1878 char *endptr = NULL;
1879 long parsed_num = strtol(new_name, &endptr, 10);
1880 if (parsed_num == LONG_MIN ||
1881 parsed_num == LONG_MAX ||
1884 workspace->
num = -1;
1885 else workspace->
num = parsed_num;
1886 LOG(
"num = %d\n", workspace->
num);
1896 cmd_output->needs_tree_render =
true;
1899 ipc_send_event(
"workspace", I3_IPC_EVENT_WORKSPACE,
"{\"change\":\"rename\"}");