} while (0)
/** When the command did not include match criteria (!), we use the currently
- * focused command. Do not confuse this case with a command which included
+ * focused container. Do not confuse this case with a command which included
* criteria but which did not match any windows. This macro has to be called in
* every command.
*/
/*
* Implementation of 'move [window|container] [to] workspace
- * next|prev|next_on_output|prev_on_output'.
+ * next|prev|next_on_output|prev_on_output|current'.
*
*/
void cmd_move_con_to_workspace(I3_CMD, char *which) {
DLOG("which=%s\n", which);
+ /* 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)) {
+ ysuccess(false);
+ return;
+ }
+
HANDLE_EMPTY_MATCH;
/* get the workspace */
ws = workspace_next_on_output();
else if (strcmp(which, "prev_on_output") == 0)
ws = workspace_prev_on_output();
+ else if (strcmp(which, "current") == 0)
+ ws = con_get_workspace(focused);
else {
ELOG("BUG: called with which=%s\n", which);
ysuccess(false);
owindow *current;
- /* Error out early to not create a non-existing workspace (in
- * workspace_get()) if we are not actually able to move anything. */
- if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+ /* 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)) {
ysuccess(false);
return;
}
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
owindow *current;
- /* Error out early to not create a non-existing workspace (in
- * workspace_get()) if we are not actually able to move anything. */
- if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+ /* 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)) {
ysuccess(false);
return;
}
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;
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 false;
- }
+ 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 false;
}
+
LOG("other->percent = %f\n", other->percent);
LOG("current->percent before = %f\n", current->percent);
if (current->percent == 0.0)
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 */
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) ||
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;
}
*
*/
void cmd_focus_level(I3_CMD, char *level) {
- if (focused &&
- focused->type != CT_WORKSPACE &&
- focused->fullscreen_mode != CF_NONE) {
- LOG("Cannot change focus while in fullscreen mode.\n");
- ysuccess(false);
- return;
- }
-
DLOG("level = %s\n", level);
+ bool success = false;
+
+ /* Focusing the parent can only be allowed if the newly
+ * focused container won't escape the fullscreen container. */
+ if (strcmp(level, "parent") == 0) {
+ if (focused && focused->parent) {
+ if (con_fullscreen_permits_focusing(focused->parent))
+ success = level_up();
+ else
+ LOG("Currently in fullscreen, not going up\n");
+ }
+ }
- if (strcmp(level, "parent") == 0)
- level_up();
- else level_down();
+ /* Focusing a child should always be allowed. */
+ else success = level_down();
- cmd_output->needs_tree_render = true;
+ cmd_output->needs_tree_render = success;
// XXX: default reply for now, make this a better reply
- ysuccess(true);
+ ysuccess(success);
}
/*
if (!ws)
continue;
- /* Don't allow the focus switch if the focused and current
- * containers are in the same workspace. */
- if (focused &&
- focused->type != CT_WORKSPACE &&
- focused->fullscreen_mode != CF_NONE &&
- con_get_workspace(focused) == ws) {
- LOG("Cannot change focus while in fullscreen mode (same workspace).\n");
+ /* Check the fullscreen focus constraints. */
+ if (!con_fullscreen_permits_focusing(current->con)) {
+ LOG("Cannot change focus while in fullscreen mode (fullscreen rules).\n");
ysuccess(false);
return;
}
}
/*
- * 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;
+
+ 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))
ysuccess(true);
}
+/*
+ * 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->parent, 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);
+}
+
/*
* Implementaiton of 'exit'.
*
*/
void cmd_exit(I3_CMD) {
LOG("Exiting due to user command.\n");
+ xcb_disconnect(conn);
exit(0);
/* unreached */
void cmd_reload(I3_CMD) {
LOG("reloading\n");
kill_configerror_nagbar(false);
+ kill_commanderror_nagbar(false);
load_configuration(conn, NULL, true);
x_set_i3_atoms();
/* Send an IPC event just in case the ws names have changed */
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);
}
/* 1: If the container is a workspace container, we need to create a new
- * split-container with the same orientation and make that one floating. We
+ * split-container with the same layout and make that one floating. We
* cannot touch the workspace container itself because floating containers
* are children of the workspace. */
if (con->type == CT_WORKSPACE) {
/* TODO: refactor this with src/con.c:con_set_layout */
Con *new = con_new(NULL, NULL);
new->parent = con;
- new->orientation = con->orientation;
+ new->layout = con->layout;
/* since the new container will be set into floating mode directly
* afterwards, we need to copy the workspace rect. */
* otherwise. */
Con *ws = con_get_workspace(con);
nc->parent = ws;
- nc->orientation = NO_ORIENTATION;
+ nc->split = true;
nc->type = CT_FLOATING_CON;
+ nc->layout = L_SPLITH;
/* We insert nc already, even though its rect is not yet calculated. This
* is necessary because otherwise the workspace might be empty (and get
* closed in tree_close()) even though it’s not. */
*
*/
void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) {
- DLOG("Fixing coordinates of floating window %p\n", con);
+ DLOG("Fixing coordinates of floating window %p (rect (%d, %d), %d x %d)\n",
+ con, con->rect.x, con->rect.y, con->rect.width, con->rect.height);
+ DLOG("old_rect = (%d, %d), %d x %d\n",
+ old_rect->x, old_rect->y, old_rect->width, old_rect->height);
+ DLOG("new_rect = (%d, %d), %d x %d\n",
+ new_rect->x, new_rect->y, new_rect->width, new_rect->height);
/* First we get the x/y coordinates relative to the x/y coordinates
* of the output on which the window is on */
- uint32_t rel_x = (con->rect.x - old_rect->x);
- uint32_t rel_y = (con->rect.y - old_rect->y);
+ int32_t rel_x = (con->rect.x - old_rect->x);
+ int32_t rel_y = (con->rect.y - old_rect->y);
/* Then we calculate a fraction, for example 0.63 for a window
* which is at y = 1212 of a 1920 px high output */
- double fraction_x = ((double)rel_x / (int32_t)old_rect->width);
- double fraction_y = ((double)rel_y / (int32_t)old_rect->height);
DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
- rel_x, rel_y, fraction_x, fraction_y, old_rect->width, old_rect->height);
- con->rect.x = (int32_t)new_rect->x + (fraction_x * (int32_t)new_rect->width);
- con->rect.y = (int32_t)new_rect->y + (fraction_y * (int32_t)new_rect->height);
+ rel_x, rel_y, (double)rel_x / old_rect->width, (double)rel_y / old_rect->height,
+ old_rect->width, old_rect->height);
+ /* Here we have to multiply at first. Or we will lose precision when not compiled with -msse2 */
- con->rect.x = new_rect->x + (double)(rel_x * new_rect->width) / old_rect->width;
- con->rect.y = new_rect->y + (double)(rel_y * new_rect->height) / old_rect->height;
++ con->rect.x = (int32_t)new_rect->x + (double)(rel_x * (int32_t)new_rect->width) / (int32_t)old_rect->width;
++ con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height) / (int32_t)old_rect->height;
DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
}
Con *topdock = con_new(NULL, NULL);
topdock->type = CT_DOCKAREA;
topdock->layout = L_DOCKAREA;
- topdock->orientation = VERT;
/* this container swallows dock clients */
Match *match = scalloc(sizeof(Match));
match_init(match);
DLOG("adding main content container\n");
Con *content = con_new(NULL, NULL);
content->type = CT_CON;
+ content->layout = L_SPLITH;
FREE(content->name);
content->name = sstrdup("content");
Con *bottomdock = con_new(NULL, NULL);
bottomdock->type = CT_DOCKAREA;
bottomdock->layout = L_DOCKAREA;
- bottomdock->orientation = VERT;
/* this container swallows dock clients */
match = scalloc(sizeof(Match));
match_init(match);
workspace_show(previous);
}
+ /* Render the output on which the workspace was to get correct Rects.
+ * Then, we need to work with the "content" container, since we cannot
+ * be sure that the workspace itself was rendered at all (in case it’s
+ * invisible, it won’t be rendered). */
+ render_con(workspace_out, false);
+ Con *ws_out_content = output_get_content(workspace_out);
+
+ Con *floating_con;
+ TAILQ_FOREACH(floating_con, &(workspace->floating_head), floating_windows)
+ /* NB: We use output->con here because content is not yet rendered,
+ * so it has a rect of {0, 0, 0, 0}. */
+ floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect));
+
con_detach(workspace);
con_attach(workspace, content, false);
if (con_num_children(workspace) > 1)
continue;
- workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ;
- DLOG("Setting workspace [%d,%s]'s orientation to %d.\n", workspace->num, workspace->name, workspace->orientation);
+ workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
+ DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout);
if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) {
- child->orientation = workspace->orientation;
- DLOG("Setting child [%d,%s]'s orientation to %d.\n", child->num, child->name, child->orientation);
+ child->layout = workspace->layout;
+ DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout);
}
}
}
DLOG("Fixing the coordinates of floating containers\n");
Con *floating_con;
TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows)
- floating_fix_coordinates(floating_con, &(old_content->rect), &(first_content->rect));
+ floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
DLOG("Done, next\n");
}
DLOG("re-attached all workspaces\n");