X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcommands.c;h=2404a811785591fd996c8ed37b815e0bdc252a37;hb=49979c9d15eb5095ce9dd47a757bf0cae26bc197;hp=f30d661231520e02b42d27e41f21682eec276a51;hpb=b9e6d37efb3ae3c55305ee334fd2b0fa7dec14df;p=i3%2Fi3 diff --git a/src/commands.c b/src/commands.c index f30d6612..2404a811 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "commands.c" /* * vim:ts=4:sw=4:expandtab * @@ -36,7 +38,6 @@ } \ } while (0) -static owindows_head owindows; /* * Returns true if a is definitely greater than b (using the given epsilon) @@ -54,23 +55,15 @@ static bool definitelyGreaterThan(float a, float b, float epsilon) { static Output *get_output_from_string(Output *current_output, const char *output_str) { Output *output; - if (strcasecmp(output_str, "left") == 0) { - output = get_output_next(D_LEFT, current_output); - if (!output) - output = get_output_most(D_RIGHT, current_output); - } else if (strcasecmp(output_str, "right") == 0) { - output = get_output_next(D_RIGHT, current_output); - if (!output) - output = get_output_most(D_LEFT, current_output); - } else if (strcasecmp(output_str, "up") == 0) { - output = get_output_next(D_UP, current_output); - if (!output) - output = get_output_most(D_DOWN, current_output); - } else if (strcasecmp(output_str, "down") == 0) { - output = get_output_next(D_DOWN, current_output); - if (!output) - output = get_output_most(D_UP, current_output); - } else output = get_output_by_name(output_str); + if (strcasecmp(output_str, "left") == 0) + output = get_output_next_wrap(D_LEFT, current_output); + else if (strcasecmp(output_str, "right") == 0) + output = get_output_next_wrap(D_RIGHT, current_output); + else if (strcasecmp(output_str, "up") == 0) + output = get_output_next_wrap(D_UP, current_output); + else if (strcasecmp(output_str, "down") == 0) + output = get_output_next_wrap(D_DOWN, current_output); + else output = get_output_by_name(output_str); return output; } @@ -97,6 +90,29 @@ static bool maybe_back_and_forth(struct CommandResult *cmd_output, char *name) { return true; } +/* + * Return the passed workspace unless it is the current one and auto back and + * forth is enabled, in which case the back_and_forth workspace is returned. + */ +static Con *maybe_auto_back_and_forth_workspace(Con *workspace) { + Con *current, *baf; + + if (!config.workspace_auto_back_and_forth) + return workspace; + + current = con_get_workspace(focused); + + if (current == workspace) { + baf = workspace_back_and_forth_get(); + if (baf != NULL) { + DLOG("Substituting workspace with back_and_forth, as it is focused.\n"); + return baf; + } + } + + return workspace; +} + // This code is commented out because we might recycle it for popping up error // messages on parser errors. #if 0 @@ -197,6 +213,20 @@ void cmd_MIGRATION_start_nagbar(void) { * Criteria functions. ******************************************************************************/ +/* + * Helper data structure for an operation window (window on which the operation + * will be performed). Used to build the TAILQ owindows. + * + */ +typedef struct owindow { + Con *con; + TAILQ_ENTRY(owindow) owindows; +} owindow; + +typedef TAILQ_HEAD(owindows_head, owindow) owindows_head; + +static owindows_head owindows; + /* * Initializes the specified 'Match' data structure and the initial state of * commands.c for matching target windows of a command. @@ -363,7 +393,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { * when criteria was specified but didn't match any window or * when criteria wasn't specified and we don't have any window focused. */ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || - (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { + (match_is_empty(current_match) && focused->type == CT_WORKSPACE && + !con_has_children(focused))) { ysuccess(false); return; } @@ -398,6 +429,38 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { ysuccess(true); } +/** + * Implementation of 'move [window|container] [to] workspace back_and_forth'. + * + */ +void cmd_move_con_to_workspace_back_and_forth(I3_CMD) { + owindow *current; + Con *ws; + + ws = workspace_back_and_forth_get(); + + if (ws == NULL) { + y(map_open); + ystr("success"); + y(bool, false); + ystr("error"); + ystr("No workspace was previously active."); + y(map_close); + return; + } + + HANDLE_EMPTY_MATCH; + + TAILQ_FOREACH(current, &owindows, owindows) { + DLOG("matching: %p / %s\n", current->con, current->con->name); + con_move_to_workspace(current->con, ws, true, false); + } + + cmd_output->needs_tree_render = true; + // XXX: default reply for now, make this a better reply + ysuccess(true); +} + /* * Implementation of 'move [window|container] [to] workspace '. * @@ -414,8 +477,13 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { /* We have nothing to move: * when criteria was specified but didn't match any window or * when criteria wasn't specified and we don't have any window focused. */ - if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || - (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { + if (!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) { + ELOG("No windows match your criteria, cannot move.\n"); + ysuccess(false); + return; + } + else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE && + !con_has_children(focused)) { ysuccess(false); return; } @@ -424,6 +492,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { /* get the workspace */ Con *ws = workspace_get(name, NULL); + ws = maybe_auto_back_and_forth_workspace(ws); + HANDLE_EMPTY_MATCH; TAILQ_FOREACH(current, &owindows, owindows) { @@ -437,7 +507,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { } /* - * Implementation of 'move [window|container] [to] workspace number '. + * Implementation of 'move [window|container] [to] workspace number '. * */ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { @@ -447,12 +517,13 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { * when criteria was specified but didn't match any window or * when criteria wasn't specified and we don't have any window focused. */ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) || - (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) { + (match_is_empty(current_match) && focused->type == CT_WORKSPACE && + !con_has_children(focused))) { ysuccess(false); return; } - LOG("should move window to workspace with number %d\n", which); + LOG("should move window to workspace %s\n", which); /* get the workspace */ Con *output, *workspace = NULL; @@ -461,8 +532,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { if (parsed_num == LONG_MIN || parsed_num == LONG_MAX || parsed_num < 0 || - *endptr != '\0') { - LOG("Could not parse \"%s\" as a number.\n", which); + endptr == which) { + LOG("Could not parse initial part of \"%s\" as a number.\n", which); y(map_open); ystr("success"); y(bool, false); @@ -478,16 +549,11 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { child->num == parsed_num); if (!workspace) { - y(map_open); - ystr("success"); - y(bool, false); - ystr("error"); - // TODO: better error message - ystr("No such workspace"); - y(map_close); - return; + workspace = workspace_get(which, NULL); } + workspace = maybe_auto_back_and_forth_workspace(workspace); + HANDLE_EMPTY_MATCH; TAILQ_FOREACH(current, &owindows, owindows) { @@ -502,23 +568,41 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) { LOG("floating resize\n"); + Rect old_rect = floating_con->rect; + if (strcmp(direction, "up") == 0) { - floating_con->rect.y -= px; floating_con->rect.height += px; - } else if (strcmp(direction, "down") == 0) { + } else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) { floating_con->rect.height += px; } else if (strcmp(direction, "left") == 0) { - floating_con->rect.x -= px; floating_con->rect.width += px; } else { floating_con->rect.width += px; } + + floating_check_size(floating_con); + + /* Did we actually resize anything or did the size constraints prevent us? + * If we could not resize, exit now to not move the window. */ + if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0) + return; + + if (strcmp(direction, "up") == 0) { + floating_con->rect.y -= (floating_con->rect.height - old_rect.height); + } else if (strcmp(direction, "left") == 0) { + floating_con->rect.x -= (floating_con->rect.width - old_rect.width); + } + + /* If this is a scratchpad window, don't auto center it from now on. */ + if (floating_con->scratchpad_state == SCRATCHPAD_FRESH) + floating_con->scratchpad_state = SCRATCHPAD_CHANGED; } -static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int ppt) { +static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) { LOG("tiling resize\n"); /* get the appropriate current container (skip stacked/tabbed cons) */ - Con *current = focused; + Con *other = NULL; + double percentage = 0; while (current->parent->layout == L_STACKED || current->parent->layout == L_TABBED) current = current->parent; @@ -527,40 +611,50 @@ static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int orientation_t search_orientation = (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT); - while (current->type != CT_WORKSPACE && - current->type != CT_FLOATING_CON && - current->parent->orientation != search_orientation) - current = current->parent; + do { + if (con_orientation(current->parent) != search_orientation) { + current = current->parent; + continue; + } - /* get the default percentage */ - int children = con_num_children(current->parent); - Con *other; - LOG("ins. %d children\n", children); - double percentage = 1.0 / children; - LOG("default percentage = %f\n", percentage); + /* get the default percentage */ + int children = con_num_children(current->parent); + LOG("ins. %d children\n", children); + percentage = 1.0 / children; + LOG("default percentage = %f\n", percentage); - orientation_t orientation = current->parent->orientation; + orientation_t orientation = con_orientation(current->parent); - if ((orientation == HORIZ && - (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || - (orientation == VERT && - (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { - LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", - (orientation == HORIZ ? "horizontal" : "vertical")); - ysuccess(false); - return; - } + if ((orientation == HORIZ && + (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) || + (orientation == VERT && + (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) { + LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", + (orientation == HORIZ ? "horizontal" : "vertical")); + ysuccess(false); + return false; + } - if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { - other = TAILQ_PREV(current, nodes_head, nodes); - } else { - other = TAILQ_NEXT(current, nodes); - } - if (other == TAILQ_END(workspaces)) { - LOG("No other container in this direction found, cannot resize.\n"); + if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) { + other = TAILQ_PREV(current, nodes_head, nodes); + } else { + other = TAILQ_NEXT(current, nodes); + } + if (other == TAILQ_END(workspaces)) { + LOG("No other container in this direction found, trying to look further up in the tree...\n"); + current = current->parent; + continue; + } + break; + } while (current->type != CT_WORKSPACE && + current->type != CT_FLOATING_CON); + + if (other == NULL) { + LOG("No other container in this direction found, trying to look further up in the tree...\n"); ysuccess(false); - return; + return false; } + LOG("other->percent = %f\n", other->percent); LOG("current->percent before = %f\n", current->percent); if (current->percent == 0.0) @@ -582,12 +676,13 @@ static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int } else { LOG("Not resizing, already at minimum size\n"); } + + return true; } -static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, int ppt) { +static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt) { LOG("width/height resize\n"); /* get the appropriate current container (skip stacked/tabbed cons) */ - Con *current = focused; while (current->parent->layout == L_STACKED || current->parent->layout == L_TABBED) current = current->parent; @@ -598,7 +693,7 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i while (current->type != CT_WORKSPACE && current->type != CT_FLOATING_CON && - current->parent->orientation != search_orientation) + con_orientation(current->parent) != search_orientation) current = current->parent; /* get the default percentage */ @@ -607,7 +702,7 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i double percentage = 1.0 / children; LOG("default percentage = %f\n", percentage); - orientation_t orientation = current->parent->orientation; + orientation_t orientation = con_orientation(current->parent); if ((orientation == HORIZ && strcmp(direction, "height") == 0) || @@ -616,13 +711,13 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n", (orientation == HORIZ ? "horizontal" : "vertical")); ysuccess(false); - return; + return false; } if (children == 1) { LOG("This is the only container, cannot resize.\n"); ysuccess(false); - return; + return false; } /* Ensure all the other children have a percentage set. */ @@ -645,13 +740,13 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) { LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent); ysuccess(false); - return; + return false; } } if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) { LOG("Not resizing, already at minimum size\n"); ysuccess(false); - return; + return false; } current->percent += ((double)ppt / 100.0); @@ -663,6 +758,8 @@ static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, i child->percent -= subtract_percent; LOG("child->percent after (%p) = %f\n", child, child->percent); } + + return true; } /* @@ -680,14 +777,23 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz ppt *= -1; } - Con *floating_con; - if ((floating_con = con_inside_floating(focused))) { - cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px); - } else { - if (strcmp(direction, "width") == 0 || - strcmp(direction, "height") == 0) - cmd_resize_tiling_width_height(current_match, cmd_output, way, direction, ppt); - else cmd_resize_tiling_direction(current_match, cmd_output, way, direction, ppt); + HANDLE_EMPTY_MATCH; + + owindow *current; + TAILQ_FOREACH(current, &owindows, owindows) { + Con *floating_con; + if ((floating_con = con_inside_floating(current->con))) { + cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px); + } else { + if (strcmp(direction, "width") == 0 || + strcmp(direction, "height") == 0) { + if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt)) + return; + } else { + if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt)) + return; + } + } } cmd_output->needs_tree_render = true; @@ -696,11 +802,11 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz } /* - * Implementation of 'border normal|none|1pixel|toggle'. + * Implementation of 'border normal|none|1pixel|toggle|pixel'. * */ -void cmd_border(I3_CMD, char *border_style_str) { - DLOG("border style should be changed to %s\n", border_style_str); +void cmd_border(I3_CMD, char *border_style_str, char *border_width ) { + DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width); owindow *current; HANDLE_EMPTY_MATCH; @@ -708,23 +814,39 @@ void cmd_border(I3_CMD, char *border_style_str) { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); int border_style = current->con->border_style; + char *end; + int tmp_border_width = -1; + tmp_border_width = strtol(border_width, &end, 10); + if (end == border_width) { + /* no valid digits found */ + tmp_border_width = -1; + } if (strcmp(border_style_str, "toggle") == 0) { border_style++; border_style %= 3; + if (border_style == BS_NORMAL) + tmp_border_width = 2; + else if (border_style == BS_NONE) + tmp_border_width = 0; + else if (border_style == BS_PIXEL) + tmp_border_width = 1; } else { if (strcmp(border_style_str, "normal") == 0) border_style = BS_NORMAL; - else if (strcmp(border_style_str, "none") == 0) + else if (strcmp(border_style_str, "pixel") == 0) + border_style = BS_PIXEL; + else if (strcmp(border_style_str, "1pixel") == 0){ + border_style = BS_PIXEL; + tmp_border_width = 1; + } else if (strcmp(border_style_str, "none") == 0) border_style = BS_NONE; - else if (strcmp(border_style_str, "1pixel") == 0) - border_style = BS_1PIXEL; else { ELOG("BUG: called with border_style=%s\n", border_style_str); ysuccess(false); return; } } - con_set_border_style(current->con, border_style); + con_set_border_style(current->con, border_style, tmp_border_width); } cmd_output->needs_tree_render = true; @@ -786,7 +908,7 @@ void cmd_workspace(I3_CMD, char *which) { } /* - * Implementation of 'workspace number ' + * Implementation of 'workspace number ' * */ void cmd_workspace_number(I3_CMD, char *which) { @@ -797,8 +919,8 @@ void cmd_workspace_number(I3_CMD, char *which) { if (parsed_num == LONG_MIN || parsed_num == LONG_MAX || parsed_num < 0 || - *endptr != '\0') { - LOG("Could not parse \"%s\" as a number.\n", which); + endptr == which) { + LOG("Could not parse initial part of \"%s\" as a number.\n", which); y(map_open); ystr("success"); y(bool, false); @@ -815,17 +937,13 @@ void cmd_workspace_number(I3_CMD, char *which) { child->num == parsed_num); if (!workspace) { - LOG("There is no workspace with number %d, creating a new one.\n", parsed_num); + LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num); ysuccess(true); - /* terminate the which string after the endposition of the number */ - *endptr = '\0'; - if (maybe_back_and_forth(cmd_output, which)) - return; workspace_show_by_name(which); cmd_output->needs_tree_render = true; return; } - if (maybe_back_and_forth(cmd_output, which)) + if (maybe_back_and_forth(cmd_output, workspace->name)) return; workspace_show(workspace); @@ -930,13 +1048,13 @@ void cmd_move_con_to_output(I3_CMD, char *name) { // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser if (strcasecmp(name, "up") == 0) - output = get_output_next(D_UP, current_output); + output = get_output_next_wrap(D_UP, current_output); else if (strcasecmp(name, "down") == 0) - output = get_output_next(D_DOWN, current_output); + output = get_output_next_wrap(D_DOWN, current_output); else if (strcasecmp(name, "left") == 0) - output = get_output_next(D_LEFT, current_output); + output = get_output_next_wrap(D_LEFT, current_output); else if (strcasecmp(name, "right") == 0) - output = get_output_next(D_RIGHT, current_output); + output = get_output_next_wrap(D_RIGHT, current_output); else output = get_output_by_name(name); @@ -1008,9 +1126,14 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { TAILQ_FOREACH(current, &owindows, owindows) { Output *current_output = get_output_containing(current->con->rect.x, current->con->rect.y); + if (!current_output) { + ELOG("Cannot get current output. This is a bug in i3.\n"); + ysuccess(false); + return; + } Output *output = get_output_from_string(current_output, name); if (!output) { - LOG("No such output\n"); + ELOG("Could not get output from string \"%s\"\n", name); ysuccess(false); return; } @@ -1018,8 +1141,12 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { Con *content = output_get_content(output->con); LOG("got output %p with content %p\n", output, content); + Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head)); + LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name); + Con *ws = con_get_workspace(current->con); LOG("should move workspace %p / %s\n", ws, ws->name); + bool workspace_was_visible = workspace_is_visible(ws); if (con_num_children(ws->parent) == 1) { LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name); @@ -1054,9 +1181,9 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { /* notify the IPC listeners */ ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); } + DLOG("Detaching\n"); /* detach from the old output and attach to the new output */ - bool workspace_was_visible = workspace_is_visible(ws); Con *old_content = ws->parent; con_detach(ws); if (workspace_was_visible) { @@ -1078,6 +1205,22 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { /* Focus the moved workspace on the destination output. */ workspace_show(ws); } + + /* NB: We cannot simply work with previously_visible_ws since it might + * have been cleaned up by workspace_show() already, depending on the + * focus order/number of other workspaces on the output. + * Instead, we loop through the available workspaces and only work with + * previously_visible_ws if we still find it. */ + TAILQ_FOREACH(ws, &(content->nodes_head), nodes) { + if (ws != previously_visible_ws) + continue; + + /* Call the on_remove_child callback of the workspace which previously + * was visible on the destination output. Since it is no longer + * visible, it might need to get cleaned up. */ + CALL(previously_visible_ws, on_remove_child); + break; + } } cmd_output->needs_tree_render = true; @@ -1090,9 +1233,17 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { * */ void cmd_split(I3_CMD, char *direction) { + owindow *current; /* TODO: use matches */ LOG("splitting in direction %c\n", direction[0]); - tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ)); + if (match_is_empty(current_match)) + tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ)); + else { + TAILQ_FOREACH(current, &owindows, owindows) { + DLOG("matching: %p / %s\n", current->con, current->con->name); + tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ)); + } + } cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -1100,7 +1251,7 @@ void cmd_split(I3_CMD, char *direction) { } /* - * Implementaiton of 'kill [window|client]'. + * Implementation of 'kill [window|client]'. * */ void cmd_kill(I3_CMD, char *kill_mode_str) { @@ -1155,14 +1306,6 @@ void cmd_exec(I3_CMD, char *nosn, char *command) { * */ void cmd_focus_direction(I3_CMD, char *direction) { - if (focused && - focused->type != CT_WORKSPACE && - focused->fullscreen_mode != CF_NONE) { - LOG("Cannot change focus while in fullscreen mode.\n"); - ysuccess(false); - return; - } - DLOG("direction = *%s*\n", direction); if (strcmp(direction, "left") == 0) @@ -1189,14 +1332,6 @@ void cmd_focus_direction(I3_CMD, char *direction) { * */ void cmd_focus_window_mode(I3_CMD, char *window_mode) { - if (focused && - focused->type != CT_WORKSPACE && - focused->fullscreen_mode != CF_NONE) { - LOG("Cannot change focus while in fullscreen mode.\n"); - ysuccess(false); - return; - } - DLOG("window_mode = %s\n", window_mode); Con *ws = con_get_workspace(focused); @@ -1238,7 +1373,7 @@ void cmd_focus_level(I3_CMD, char *level) { if (con_fullscreen_permits_focusing(focused->parent)) success = level_up(); else - LOG("Currently in fullscreen, not going up\n"); + ELOG("'focus parent': Currently in fullscreen, not going up\n"); } } @@ -1271,6 +1406,7 @@ void cmd_focus(I3_CMD) { return; } + Con *__i3_scratch = workspace_get("__i3_scratch", NULL); int count = 0; owindow *current; TAILQ_FOREACH(current, &owindows, owindows) { @@ -1287,6 +1423,16 @@ void cmd_focus(I3_CMD) { return; } + /* In case this is a scratchpad window, call scratchpad_show(). */ + if (ws == __i3_scratch) { + scratchpad_show(current->con); + count++; + /* While for the normal focus case we can change focus multiple + * times and only a single window ends up focused, we could show + * multiple scratchpad windows. So, rather break here. */ + break; + } + /* If the container is not on the current workspace, * workspace_show() will switch to a different workspace and (if * enabled) trigger a mouse pointer warp to the currently focused @@ -1377,21 +1523,35 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) { } /* - * Implementation of 'layout default|stacked|stacking|tabbed'. + * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'. * */ void cmd_layout(I3_CMD, char *layout_str) { if (strcmp(layout_str, "stacking") == 0) layout_str = "stacked"; - DLOG("changing layout to %s\n", layout_str); owindow *current; - int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT : - (strcmp(layout_str, "stacked") == 0 ? L_STACKED : - L_TABBED)); + int layout; + /* default is a special case which will be handled in con_set_layout(). */ + if (strcmp(layout_str, "default") == 0) + layout = L_DEFAULT; + else if (strcmp(layout_str, "stacked") == 0) + layout = L_STACKED; + else if (strcmp(layout_str, "tabbed") == 0) + layout = L_TABBED; + else if (strcmp(layout_str, "splitv") == 0) + layout = L_SPLITV; + else if (strcmp(layout_str, "splith") == 0) + layout = L_SPLITH; + else { + ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str); + return; + } + + DLOG("changing layout to %s (%d)\n", layout_str, layout); /* check if the match is empty, not if the result is empty */ if (match_is_empty(current_match)) - con_set_layout(focused->parent, layout); + con_set_layout(focused, layout); else { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); @@ -1405,7 +1565,34 @@ void cmd_layout(I3_CMD, char *layout_str) { } /* - * Implementaiton of 'exit'. + * Implementation of 'layout toggle [all|split]'. + * + */ +void cmd_layout_toggle(I3_CMD, char *toggle_mode) { + owindow *current; + + if (toggle_mode == NULL) + toggle_mode = "default"; + + DLOG("toggling layout (mode = %s)\n", toggle_mode); + + /* check if the match is empty, not if the result is empty */ + if (match_is_empty(current_match)) + con_toggle_layout(focused, toggle_mode); + else { + TAILQ_FOREACH(current, &owindows, owindows) { + DLOG("matching: %p / %s\n", current->con, current->con->name); + con_toggle_layout(current->con, toggle_mode); + } + } + + cmd_output->needs_tree_render = true; + // XXX: default reply for now, make this a better reply + ysuccess(true); +} + +/* + * Implementation of 'exit'. * */ void cmd_exit(I3_CMD) { @@ -1417,12 +1604,13 @@ void cmd_exit(I3_CMD) { } /* - * Implementaiton of 'reload'. + * Implementation of 'reload'. * */ void cmd_reload(I3_CMD) { LOG("reloading\n"); - kill_configerror_nagbar(false); + kill_nagbar(&config_error_nagbar_pid, false); + kill_nagbar(&command_error_nagbar_pid, false); load_configuration(conn, NULL, true); x_set_i3_atoms(); /* Send an IPC event just in case the ws names have changed */ @@ -1433,7 +1621,7 @@ void cmd_reload(I3_CMD) { } /* - * Implementaiton of 'restart'. + * Implementation of 'restart'. * */ void cmd_restart(I3_CMD) { @@ -1445,12 +1633,13 @@ void cmd_restart(I3_CMD) { } /* - * Implementaiton of 'open'. + * Implementation of 'open'. * */ void cmd_open(I3_CMD) { LOG("opening new container\n"); Con *con = tree_open_con(NULL, NULL); + con->layout = L_SPLITH; con_focus(con); y(map_open); @@ -1633,16 +1822,24 @@ void cmd_scratchpad_show(I3_CMD) { } /* - * Implementation of 'rename workspace to ' + * Implementation of 'rename workspace [] to ' * */ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { - LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name); + if (old_name) { + LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name); + } else { + LOG("Renaming current workspace to \"%s\"\n", new_name); + } Con *output, *workspace = NULL; - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), - !strcasecmp(child->name, old_name)); + if (old_name) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), + !strcasecmp(child->name, old_name)); + } else { + workspace = con_get_workspace(focused); + } if (!workspace) { // TODO: we should include the old workspace name here and use yajl for