diff --git a/include/sway/desktop.h b/include/sway/desktop.h index f1ad759a1..348fb1870 100644 --- a/include/sway/desktop.h +++ b/include/sway/desktop.h @@ -1,4 +1,8 @@ #include +struct sway_container; + void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole); + +void desktop_damage_whole_container(struct sway_container *con); diff --git a/include/sway/output.h b/include/sway/output.h index b6cda83c2..bd25e76e8 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -97,4 +97,6 @@ void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); +struct sway_container *output_get_active_workspace(struct sway_output *output); + #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 728daa847..a69da9db8 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -297,4 +297,10 @@ bool container_is_floating(struct sway_container *container); */ void container_get_box(struct sway_container *container, struct wlr_box *box); +/** + * Move a floating container to a new layout-local position. + */ +void container_floating_move_to(struct sway_container *con, + double lx, double ly); + #endif diff --git a/sway/commands/move.c b/sway/commands/move.c index a4fae3887..a1c1e018d 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,11 +1,13 @@ #define _XOPEN_SOURCE 500 #include #include +#include #include #include #include #include "sway/commands.h" #include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/output.h" #include "sway/tree/arrange.h" @@ -184,11 +186,49 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, } static struct cmd_results *move_in_direction(struct sway_container *container, - enum movement_direction direction, int move_amt) { + 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->type == C_VIEW && container->sway_view->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. @@ -208,31 +248,78 @@ static struct cmd_results *move_in_direction(struct sway_container *container, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static const char* expected_position_syntax = + "Expected 'move [absolute] position ' or " + "'move [absolute] position mouse'"; + +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); + } + if (strcmp(argv[0], "absolute") == 0) { + --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], "mouse") == 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); + } + 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"); + } + ly = (double)strtol(argv[1], &inv, 10); + if (*inv != '\0' && strcasecmp(inv, "px") != 0) { + return cmd_results_new(CMD_FAILURE, "move", + "Invalid position specified"); + } + container_floating_move_to(container, lx, ly); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; - int move_amt = 10; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { return error; } struct sway_container *current = config->handler_context.current_container; - if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) { - 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 (strcasecmp(argv[0], "left") == 0) { - return move_in_direction(current, MOVE_LEFT, move_amt); + return move_in_direction(current, MOVE_LEFT, argc, argv); } else if (strcasecmp(argv[0], "right") == 0) { - return move_in_direction(current, MOVE_RIGHT, move_amt); + return move_in_direction(current, MOVE_RIGHT, argc, argv); } else if (strcasecmp(argv[0], "up") == 0) { - return move_in_direction(current, MOVE_UP, move_amt); + return move_in_direction(current, MOVE_UP, argc, argv); } else if (strcasecmp(argv[0], "down") == 0) { - return move_in_direction(current, MOVE_DOWN, move_amt); + return move_in_direction(current, MOVE_DOWN, argc, argv); } else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) { return cmd_move_container(current, argc, argv); @@ -244,8 +331,9 @@ struct cmd_results *cmd_move(int argc, char **argv) { // TODO: scratchpad return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); } else if (strcasecmp(argv[0], "position") == 0) { - // TODO: floating - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + 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); } diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index e495790c8..6575519d4 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, } } } + +void desktop_damage_whole_container(struct sway_container *con) { + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *cont = root_container.children->items[i]; + if (cont->type == C_OUTPUT) { + output_damage_whole_container(cont->sway_output, con); + } + } +} diff --git a/sway/tree/container.c b/sway/tree/container.c index 5fdcb6e3b..2df2332c3 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -11,11 +11,14 @@ #include "cairo.h" #include "pango.h" #include "sway/config.h" +#include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -989,3 +992,82 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { box->width = container->width; box->height = container->height; } + +/** + * Translate the container's position as well as all children. + */ +static void container_floating_translate(struct sway_container *con, + double x_amount, double y_amount) { + con->x += x_amount; + con->y += y_amount; + con->current.swayc_x += x_amount; + con->current.swayc_y += y_amount; + if (con->type == C_VIEW) { + con->sway_view->x += x_amount; + con->sway_view->y += y_amount; + con->current.view_x += x_amount; + con->current.view_y += y_amount; + } else { + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + container_floating_translate(child, x_amount, y_amount); + } + } +} + +/** + * Choose an output for the floating container's new position. + * + * If the center of the container intersects an output then we'll choose that + * one, otherwise we'll choose whichever output is closest to the container's + * center. + */ +static struct sway_container *container_floating_find_output( + struct sway_container *con) { + double center_x = con->x + con->width / 2; + double center_y = con->y + con->height / 2; + struct sway_container *closest_output = NULL; + double closest_distance = DBL_MAX; + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + struct wlr_box output_box; + double closest_x, closest_y; + container_get_box(output, &output_box); + wlr_box_closest_point(&output_box, center_x, center_y, + &closest_x, &closest_y); + if (center_x == closest_x && center_y == closest_y) { + // The center of the floating container is on this output + return output; + } + double x_dist = closest_x - center_x; + double y_dist = closest_y - center_y; + double distance = x_dist * x_dist + y_dist * y_dist; + if (distance < closest_distance) { + closest_output = output; + closest_distance = distance; + } + } + return closest_output; +} + +void container_floating_move_to(struct sway_container *con, + double lx, double ly) { + desktop_damage_whole_container(con); + container_floating_translate(con, lx - con->x, ly - con->y); + desktop_damage_whole_container(con); + struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); + struct sway_container *new_output = container_floating_find_output(con); + if (!sway_assert(new_output, "Unable to find any output")) { + return; + } + struct sway_container *new_workspace = + output_get_active_workspace(new_output->sway_output); + if (old_workspace != new_workspace) { + container_remove_child(con); + container_add_child(new_workspace->sway_workspace->floating, con); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(old_workspace, transaction); + arrange_windows(new_workspace, transaction); + transaction_commit(transaction); + } +}