#define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include "sway/commands.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" #include "stringop.h" #include "list.h" #include "log.h" #include "util.h" static const char *expected_syntax = "Expected 'move <[px] px>' or " "'move [--no-auto-back-and-forth] [to] workspace ' or " "'move [to] output ' or " "'move [to] mark '"; enum wlr_direction opposite_direction(enum wlr_direction d) { switch (d) { case WLR_DIRECTION_UP: return WLR_DIRECTION_DOWN; case WLR_DIRECTION_DOWN: return WLR_DIRECTION_UP; case WLR_DIRECTION_RIGHT: return WLR_DIRECTION_LEFT; default: return WLR_DIRECTION_RIGHT; } } static struct sway_container *output_in_direction(const char *direction_string, struct wlr_output *reference, int ref_lx, int ref_ly) { struct { char *name; enum wlr_direction direction; } names[] = { { "up", WLR_DIRECTION_UP }, { "down", WLR_DIRECTION_DOWN }, { "left", WLR_DIRECTION_LEFT }, { "right", WLR_DIRECTION_RIGHT }, }; enum wlr_direction direction = 0; for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { if (strcasecmp(names[i].name, direction_string) == 0) { direction = names[i].direction; break; } } if (direction) { struct wlr_output *target = wlr_output_layout_adjacent_output( root_container.sway_root->output_layout, direction, reference, ref_lx, ref_ly); if (!target) { target = wlr_output_layout_farthest_output( root_container.sway_root->output_layout, opposite_direction(direction), reference, ref_lx, ref_ly); } if (target) { struct sway_output *sway_output = target->data; return sway_output->swayc; } } return output_by_name(direction_string); } static void container_move_to(struct sway_container *container, struct sway_container *destination) { if (!sway_assert(container->type == C_CONTAINER || container->type == C_VIEW, "Expected a container or view")) { return; } if (container == destination || container_has_ancestor(container, destination)) { return; } struct sway_container *old_parent = NULL; struct sway_container *new_parent = NULL; if (container_is_floating(container)) { // Resolve destination into a workspace struct sway_container *new_ws = NULL; if (destination->type == C_OUTPUT) { new_ws = output_get_active_workspace(destination->sway_output); } else if (destination->type == C_WORKSPACE) { new_ws = destination; } else { new_ws = container_parent(destination, C_WORKSPACE); } if (!new_ws) { // This can happen if the user has run "move container to mark foo", // where mark foo is on a hidden scratchpad container. return; } struct sway_container *old_output = container_parent(container, C_OUTPUT); old_parent = container_remove_child(container); workspace_add_floating(new_ws, container); container_handle_fullscreen_reparent(container, old_parent); // If changing output, center it within the workspace if (old_output != new_ws->parent && !container->is_fullscreen) { container_floating_move_to_center(container); } } else { old_parent = container_remove_child(container); container->width = container->height = 0; container->saved_width = container->saved_height = 0; if (destination->type == C_VIEW) { new_parent = container_add_sibling(destination, container); } else { new_parent = destination; container_add_child(destination, container); } } if (container->type == C_VIEW) { ipc_event_window(container, "move"); } container_notify_subtree_changed(old_parent); container_notify_subtree_changed(new_parent); // If view was moved to a fullscreen workspace, refocus the fullscreen view struct sway_container *new_workspace = container; if (new_workspace->type != C_WORKSPACE) { new_workspace = container_parent(new_workspace, C_WORKSPACE); } if (new_workspace->sway_workspace->fullscreen) { struct sway_seat *seat; struct sway_container *focus, *focus_ws; wl_list_for_each(seat, &input_manager->seats, link) { focus = seat_get_focus(seat); focus_ws = focus; if (focus_ws->type != C_WORKSPACE) { focus_ws = container_parent(focus_ws, C_WORKSPACE); } if (focus_ws == new_workspace) { struct sway_container *new_focus = seat_get_focus_inactive(seat, new_workspace->sway_workspace->fullscreen); seat_set_focus(seat, new_focus); } } } // Update workspace urgent state struct sway_container *old_workspace = old_parent; if (old_workspace->type != C_WORKSPACE) { old_workspace = container_parent(old_workspace, C_WORKSPACE); } if (new_workspace != old_workspace) { workspace_detect_urgent(new_workspace); if (old_workspace) { workspace_detect_urgent(old_workspace); } } } static bool is_parallel(enum sway_container_layout layout, enum movement_direction dir) { switch (layout) { case L_TABBED: case L_HORIZ: return dir == MOVE_LEFT || dir == MOVE_RIGHT; case L_STACKED: case L_VERT: return dir == MOVE_UP || dir == MOVE_DOWN; default: return false; } } static enum movement_direction invert_movement(enum movement_direction dir) { switch (dir) { case MOVE_LEFT: return MOVE_RIGHT; case MOVE_RIGHT: return MOVE_LEFT; case MOVE_UP: return MOVE_DOWN; case MOVE_DOWN: return MOVE_UP; default: sway_assert(0, "This function expects left|right|up|down"); return MOVE_LEFT; } } static int move_offs(enum movement_direction move_dir) { return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; } /* Gets the index of the most extreme member based on the movement offset */ static int container_limit(struct sway_container *container, enum movement_direction move_dir) { return move_offs(move_dir) < 0 ? 0 : container->children->length; } /* Takes one child, sets it aside, wraps the rest of the children in a new * container, switches the layout of the workspace, and drops the child back in. * In other words, rejigger it. */ static void workspace_rejigger(struct sway_container *ws, struct sway_container *child, enum movement_direction move_dir) { struct sway_container *original_parent = child->parent; struct sway_container *new_parent = container_split(ws, ws->layout); container_remove_child(child); for (int i = 0; i < ws->children->length; ++i) { struct sway_container *_child = ws->children->items[i]; container_move_to(new_parent, _child); } int index = move_offs(move_dir); container_insert_child(ws, child, index < 0 ? 0 : 1); ws->layout = move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; container_flatten(ws); container_reap_empty(original_parent); container_create_notify(new_parent); } static void move_out_of_tabs_stacks(struct sway_container *container, struct sway_container *current, enum movement_direction move_dir, int offs) { if (container->parent == current->parent && current->parent->children->length == 1) { wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); current->parent->layout = move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; return; } wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); bool is_workspace = current->parent->type == C_WORKSPACE; struct sway_container *new_parent = container_split(current->parent, move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); if (is_workspace) { container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1); } else { container_insert_child(new_parent, container, offs < 0 ? 0 : 1); container_reap_empty(new_parent->parent); container_flatten(new_parent->parent); } container_create_notify(new_parent); container_notify_subtree_changed(new_parent); } static void container_move(struct sway_container *container, enum movement_direction move_dir, int move_amt) { if (!sway_assert( container->type != C_CONTAINER || container->type != C_VIEW, "Can only move containers and views")) { return; } int offs = move_offs(move_dir); struct sway_container *sibling = NULL; struct sway_container *current = container; struct sway_container *parent = current->parent; struct sway_container *top = &root_container; // If moving a fullscreen view, only consider outputs if (container->is_fullscreen) { current = container_parent(container, C_OUTPUT); } else if (container_is_fullscreen_or_child(container) || container_is_floating_or_child(container)) { // If we've fullscreened a split container, only allow the child to move // around within the fullscreen parent. // Same with floating a split container. struct sway_container *ws = container_parent(container, C_WORKSPACE); top = ws->sway_workspace->fullscreen; } struct sway_container *new_parent = container_flatten(parent); if (new_parent != parent) { // Special case: we were the last one in this container, so leave return; } while (!sibling) { if (current == top) { return; } parent = current->parent; wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, container_type_to_str(current->type), current->name); int index = container_sibling_index(current); switch (current->type) { case C_OUTPUT: { enum wlr_direction wlr_dir = 0; if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), "got invalid direction: %d", move_dir)) { return; } double ref_lx = current->x + current->width / 2; double ref_ly = current->y + current->height / 2; struct wlr_output *next = wlr_output_layout_adjacent_output( root_container.sway_root->output_layout, wlr_dir, current->sway_output->wlr_output, ref_lx, ref_ly); if (!next) { wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); return; } struct sway_output *next_output = next->data; current = next_output->swayc; wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); // Select workspace and get outta here current = seat_get_focus_inactive( config->handler_context.seat, current); if (current->type != C_WORKSPACE) { current = container_parent(current, C_WORKSPACE); } sibling = current; break; } case C_WORKSPACE: if (!is_parallel(current->layout, move_dir)) { if (current->children->length >= 2) { wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", current->children->length); workspace_rejigger(current, container, move_dir); return; } else { wlr_log(WLR_DEBUG, "Selecting output"); current = current->parent; } } else if (current->layout == L_TABBED || current->layout == L_STACKED) { wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); workspace_rejigger(current, container, move_dir); } else { wlr_log(WLR_DEBUG, "Selecting output"); current = current->parent; } break; case C_CONTAINER: case C_VIEW: if (is_parallel(parent->layout, move_dir)) { if ((index == parent->children->length - 1 && offs > 0) || (index == 0 && offs < 0)) { if (current->parent == container->parent) { if (!parent->is_fullscreen && (parent->layout == L_TABBED || parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; } else { wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); current = current->parent; } } else { wlr_log(WLR_DEBUG, "Hit limit, " "promoting descendant to sibling"); // Special case container_insert_child(current->parent, container, index + (offs < 0 ? 0 : 1)); container->width = container->height = 0; return; } } else { sibling = parent->children->items[index + offs]; wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); } } else if (!parent->is_fullscreen && (parent->layout == L_TABBED || parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; } else { wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); current = current->parent; } break; default: sway_assert(0, "Not expecting to see container of type %s here", container_type_to_str(current->type)); return; } } // Part two: move stuff around int index = container_sibling_index(container); struct sway_container *old_parent = container->parent; while (sibling) { switch (sibling->type) { case C_VIEW: if (sibling->parent == container->parent) { wlr_log(WLR_DEBUG, "Swapping siblings"); sibling->parent->children->items[index + offs] = container; sibling->parent->children->items[index] = sibling; } else { wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); container_insert_child(sibling->parent, container, container_sibling_index(sibling) + (offs > 0 ? 0 : 1)); container->width = container->height = 0; } sibling = NULL; break; case C_WORKSPACE: // Note: only in the case of moving between outputs case C_CONTAINER: if (is_parallel(sibling->layout, move_dir)) { int limit = container_limit(sibling, invert_movement(move_dir)); wlr_log(WLR_DEBUG, "limit: %d", limit); wlr_log(WLR_DEBUG, "Reparenting container (parallel) to index %d " "(move dir: %d)", limit, move_dir); container_insert_child(sibling, container, limit); container->width = container->height = 0; sibling = NULL; } else { wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); struct sway_container *focus_inactive = seat_get_focus_inactive( config->handler_context.seat, sibling); if (focus_inactive && focus_inactive != sibling) { while (focus_inactive->parent != sibling) { focus_inactive = focus_inactive->parent; } wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", focus_inactive->id); sibling = focus_inactive; continue; } else if (sibling->children->length) { wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); container_remove_child(container); container_add_sibling(sibling->children->items[0], container); } else { wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); container_remove_child(container); container_add_child(sibling, container); } container->width = container->height = 0; sibling = NULL; } break; default: sway_assert(0, "Not expecting to see container of type %s here", container_type_to_str(sibling->type)); return; } } container_notify_subtree_changed(old_parent); container_notify_subtree_changed(container->parent); if (container->type == C_VIEW) { ipc_event_window(container, "move"); } if (old_parent) { seat_set_focus(config->handler_context.seat, old_parent); seat_set_focus(config->handler_context.seat, container); } struct sway_container *last_ws = old_parent; struct sway_container *next_ws = container->parent; if (last_ws && last_ws->type != C_WORKSPACE) { last_ws = container_parent(last_ws, C_WORKSPACE); } if (next_ws && next_ws->type != C_WORKSPACE) { next_ws = container_parent(next_ws, C_WORKSPACE); } if (last_ws && next_ws && last_ws != next_ws) { ipc_event_workspace(last_ws, next_ws, "focus"); workspace_detect_urgent(last_ws); workspace_detect_urgent(next_ws); } container_end_mouse_operation(container); } static struct cmd_results *cmd_move_container(struct sway_container *current, int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 3))) { return error; } if (current->type == C_WORKSPACE) { if (current->children->length == 0) { return cmd_results_new(CMD_FAILURE, "move", "Can't move an empty workspace"); } current = workspace_wrap_children(current); } else if (current->type != C_CONTAINER && current->type != C_VIEW) { return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); } bool no_auto_back_and_forth = false; while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { no_auto_back_and_forth = true; if (--argc < 3) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } ++argv; } while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) { no_auto_back_and_forth = true; if (--argc < 3) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } argv++; } while (strcasecmp(argv[1], "to") == 0) { if (--argc < 3) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } argv++; } struct sway_seat *seat = config->handler_context.seat; struct sway_container *old_parent = current->parent; struct sway_container *old_ws = container_parent(current, C_WORKSPACE); struct sway_container *old_output = container_parent(current, C_OUTPUT); struct sway_container *destination = NULL; // determine destination if (strcasecmp(argv[1], "workspace") == 0) { // move container to workspace x struct sway_container *ws = NULL; char *ws_name = NULL; if (strcasecmp(argv[2], "next") == 0 || strcasecmp(argv[2], "prev") == 0 || strcasecmp(argv[2], "next_on_output") == 0 || strcasecmp(argv[2], "prev_on_output") == 0 || strcasecmp(argv[2], "current") == 0) { ws = workspace_by_name(argv[2]); } else if (strcasecmp(argv[2], "back_and_forth") == 0) { if (!(ws = workspace_by_name(argv[2]))) { if (prev_workspace_name) { ws_name = strdup(prev_workspace_name); } else { return cmd_results_new(CMD_FAILURE, "move", "No workspace was previously active."); } } } else { if (strcasecmp(argv[2], "number") == 0) { // move "container to workspace number x" if (argc < 4) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } if (!isdigit(argv[3][0])) { return cmd_results_new(CMD_INVALID, "move", "Invalid workspace number '%s'", argv[3]); } ws_name = join_args(argv + 3, argc - 3); ws = workspace_by_number(ws_name); } else { ws_name = join_args(argv + 2, argc - 2); ws = workspace_by_name(ws_name); } if (!no_auto_back_and_forth && config->auto_back_and_forth && prev_workspace_name) { // auto back and forth move if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) { // if target workspace is the current one free(ws_name); ws_name = strdup(prev_workspace_name); ws = workspace_by_name(ws_name); } } } if (!ws) { // We have to create the workspace, but if the container is // sticky and the workspace is going to be created on the same // output, we'll bail out first. if (current->is_sticky) { struct sway_container *new_output = workspace_get_initial_output(ws_name); if (old_output == new_output) { free(ws_name); return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky container to another workspace " "on the same output"); } } ws = workspace_create(NULL, ws_name); } free(ws_name); destination = seat_get_focus_inactive(seat, ws); } else if (strcasecmp(argv[1], "output") == 0) { struct sway_container *dest_output = output_in_direction(argv[2], old_output->sway_output->wlr_output, current->x, current->y); if (!dest_output) { return cmd_results_new(CMD_FAILURE, "move workspace", "Can't find output with name/direction '%s'", argv[2]); } destination = seat_get_focus_inactive(seat, dest_output); if (!destination) { // We've never been to this output before destination = dest_output->children->items[0]; } } else if (strcasecmp(argv[1], "mark") == 0) { struct sway_view *dest_view = view_find_mark(argv[2]); if (dest_view == NULL) { return cmd_results_new(CMD_FAILURE, "move", "Mark '%s' not found", argv[2]); } destination = dest_view->swayc; } else { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } struct sway_container *new_output = destination->type == C_OUTPUT ? destination : container_parent(destination, C_OUTPUT); if (current->is_sticky && old_output == new_output) { return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " "container to another workspace on the same output"); } struct sway_container *new_output_last_ws = old_output == new_output ? NULL : seat_get_active_child(seat, new_output); struct sway_container *new_workspace = destination->type == C_WORKSPACE ? destination : container_parent(destination, C_WORKSPACE); // move container, arrange windows and return focus container_move_to(current, destination); if (new_output_last_ws && new_output_last_ws != new_workspace) { // change focus on destination output back to its last active workspace struct sway_container *new_output_last_focus = seat_get_focus_inactive(seat, new_output_last_ws); seat_set_focus_warp(seat, new_output_last_focus, false, false); } struct sway_container *focus = seat_get_focus_inactive(seat, old_parent); seat_set_focus_warp(seat, focus, true, false); container_reap_empty(old_parent); container_reap_empty(destination->parent); // TODO: Ideally we would arrange the surviving parent after reaping, // but container_reap_empty does not return it, so we arrange the // workspace instead. arrange_windows(old_ws); arrange_windows(destination->parent); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } static void workspace_move_to_output(struct sway_container *workspace, struct sway_container *output) { if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { return; } if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { return; } if (workspace->parent == output) { return; } struct sway_container *old_output = container_remove_child(workspace); struct sway_seat *seat = input_manager_get_default_seat(input_manager); struct sway_container *new_output_focus = seat_get_focus_inactive(seat, output); container_add_child(output, workspace); // If moving the last workspace from the old output, create a new workspace // on the old output if (old_output->children->length == 0) { char *ws_name = workspace_next_name(old_output->name); struct sway_container *ws = workspace_create(old_output, ws_name); free(ws_name); seat_set_focus(seat, ws); } // Try to remove an empty workspace from the destination output. container_reap_empty(new_output_focus); output_sort_workspaces(output); seat_set_focus(seat, output); workspace_output_raise_priority(workspace, old_output, output); ipc_event_workspace(NULL, workspace, "move"); container_notify_subtree_changed(old_output); container_notify_subtree_changed(output); } static struct cmd_results *cmd_move_workspace(struct sway_container *current, int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { return error; } while (strcasecmp(argv[1], "to") == 0) { if (--argc < 3) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } ++argv; } if (strcasecmp(argv[1], "output") != 0) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } struct sway_container *source = container_parent(current, C_OUTPUT); int center_x = current->width / 2 + current->x, center_y = current->height / 2 + current->y; struct sway_container *destination = output_in_direction(argv[2], source->sway_output->wlr_output, center_x, center_y); if (!destination) { return cmd_results_new(CMD_FAILURE, "move workspace", "Can't find output with name/direction '%s'", argv[2]); } if (current->type != C_WORKSPACE) { current = container_parent(current, C_WORKSPACE); } workspace_move_to_output(current, destination); arrange_windows(source); arrange_windows(destination); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } static struct cmd_results *move_in_direction(struct sway_container *container, enum movement_direction direction, int argc, char **argv) { int move_amt = 10; if (argc > 1) { char *inv; move_amt = (int)strtol(argv[1], &inv, 10); if (*inv != '\0' && strcasecmp(inv, "px") != 0) { return cmd_results_new(CMD_FAILURE, "move", "Invalid distance specified"); } } if (container->type == C_WORKSPACE) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move workspaces in a direction"); } if (container_is_floating(container)) { if (container->is_fullscreen) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move fullscreen floating container"); } double lx = container->x; double ly = container->y; switch (direction) { case MOVE_LEFT: lx -= move_amt; break; case MOVE_RIGHT: lx += move_amt; break; case MOVE_UP: ly -= move_amt; break; case MOVE_DOWN: ly += move_amt; break; case MOVE_PARENT: case MOVE_CHILD: return cmd_results_new(CMD_FAILURE, "move", "Cannot move floating container to parent or child"); } container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } // For simplicity, we'll arrange the entire workspace. The reason for this // is moving the container might reap the old parent, and container_move // does not return a surviving parent. // TODO: Make container_move return the surviving parent so we can arrange // just that. struct sway_container *old_ws = container_parent(container, C_WORKSPACE); container_move(container, direction, move_amt); struct sway_container *new_ws = container_parent(container, C_WORKSPACE); arrange_windows(old_ws); if (new_ws != old_ws) { arrange_windows(new_ws); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } static const char *expected_position_syntax = "Expected 'move [absolute] position [px] [px]' or " "'move [absolute] position center' or " "'move position cursor|mouse|pointer'"; static struct cmd_results *move_to_position(struct sway_container *container, int argc, char **argv) { if (!container_is_floating(container)) { return cmd_results_new(CMD_FAILURE, "move", "Only floating containers " "can be moved to an absolute position"); } if (!argc) { return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); } bool absolute = false; if (strcmp(argv[0], "absolute") == 0) { absolute = true; --argc; ++argv; } if (!argc) { return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); } if (strcmp(argv[0], "position") == 0) { --argc; ++argv; } if (!argc) { return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); } if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || strcmp(argv[0], "pointer") == 0) { struct sway_seat *seat = config->handler_context.seat; if (!seat->cursor) { return cmd_results_new(CMD_FAILURE, "move", "No cursor device"); } double lx = seat->cursor->cursor->x - container->width / 2; double ly = seat->cursor->cursor->y - container->height / 2; container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } else if (strcmp(argv[0], "center") == 0) { double lx, ly; if (absolute) { lx = root_container.x + (root_container.width - container->width) / 2; ly = root_container.y + (root_container.height - container->height) / 2; } else { struct sway_container *ws = container_parent(container, C_WORKSPACE); lx = ws->x + (ws->width - container->width) / 2; ly = ws->y + (ws->height - container->height) / 2; } container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } if (argc < 2) { return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); } double lx, ly; char *inv; lx = (double)strtol(argv[0], &inv, 10); if (*inv != '\0' && strcasecmp(inv, "px") != 0) { return cmd_results_new(CMD_FAILURE, "move", "Invalid position specified"); } if (strcmp(argv[1], "px") == 0) { --argc; ++argv; } if (argc > 3) { return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); } ly = (double)strtol(argv[1], &inv, 10); if ((*inv != '\0' && strcasecmp(inv, "px") != 0) || (argc == 3 && strcmp(argv[2], "px") != 0)) { return cmd_results_new(CMD_FAILURE, "move", "Invalid position specified"); } if (!absolute) { struct sway_container *ws = container_parent(container, C_WORKSPACE); lx += ws->x; ly += ws->y; } container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } static struct cmd_results *move_to_scratchpad(struct sway_container *con) { if (con->type == C_WORKSPACE && con->children->length == 0) { return cmd_results_new(CMD_INVALID, "move", "Can't move an empty workspace to the scratchpad"); } if (con->type == C_WORKSPACE) { // Wrap the workspace's children in a container struct sway_container *workspace = con; con = workspace_wrap_children(con); workspace->layout = L_HORIZ; } // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { while (con->parent->type != C_WORKSPACE) { con = con->parent; } } if (con->scratchpad) { return cmd_results_new(CMD_INVALID, "move", "Container is already in the scratchpad"); } root_scratchpad_add_container(con); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { return error; } struct sway_container *current = config->handler_context.current_container; if (strcasecmp(argv[0], "left") == 0) { return move_in_direction(current, MOVE_LEFT, argc, argv); } else if (strcasecmp(argv[0], "right") == 0) { return move_in_direction(current, MOVE_RIGHT, argc, argv); } else if (strcasecmp(argv[0], "up") == 0) { return move_in_direction(current, MOVE_UP, argc, argv); } else if (strcasecmp(argv[0], "down") == 0) { return move_in_direction(current, MOVE_DOWN, argc, argv); } else if ((strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) || (strcasecmp(argv[0], "--no-auto-back-and-forth") && (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0))) { return cmd_move_container(current, argc, argv); } else if (strcasecmp(argv[0], "workspace") == 0) { return cmd_move_workspace(current, argc, argv); } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && argc == 2 && strcasecmp(argv[1], "scratchpad") == 0)) { return move_to_scratchpad(current); } else if (strcasecmp(argv[0], "position") == 0) { return move_to_position(current, argc, argv); } else if (strcasecmp(argv[0], "absolute") == 0) { return move_to_position(current, argc, argv); } else { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); }