diff --git a/include/container.h b/include/container.h index 8c54ee24..cb18de49 100644 --- a/include/container.h +++ b/include/container.h @@ -63,7 +63,7 @@ struct sway_container { /** * The coordinates that this view appear at, relative to the output they - * are located on. + * are located on (output containers have absolute coordinates). */ double x, y; diff --git a/include/output.h b/include/output.h index 10ff0596..1307ead8 100644 --- a/include/output.h +++ b/include/output.h @@ -4,7 +4,16 @@ #include "container.h" #include "focus.h" -swayc_t *output_by_name(const char* name); -swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir); +// Position is absolute coordinates on the edge where the adjacent output +// should be searched for. +swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos); +swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir, const struct wlc_point *abs_pos, bool pick_closest); + +// Place absolute coordinates for given container into given wlc_point. +void get_absolute_position(swayc_t *container, struct wlc_point *point); + +// Place absolute coordinates for the center point of given container into +// given wlc_point. +void get_absolute_center_position(swayc_t *container, struct wlc_point *point); #endif diff --git a/sway/commands.c b/sway/commands.c index f194681e..dfb3c12d 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -377,11 +377,13 @@ static struct cmd_results *cmd_focus(int argc, char **argv) { struct cmd_results *error = NULL; if (argc > 0 && strcasecmp(argv[0], "output") == 0) { swayc_t *output = NULL; + struct wlc_point abs_pos; + get_absolute_center_position(get_focused_container(&root_container), &abs_pos); if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) { return error; - } else if (!(output = output_by_name(argv[1]))) { + } else if (!(output = output_by_name(argv[1], &abs_pos))) { return cmd_results_new(CMD_FAILURE, "focus output", - "Can't find output with name/at direction %s", argv[1]); + "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y); } else if (!workspace_switch(swayc_active_workspace_for(output))) { return cmd_results_new(CMD_FAILURE, "focus output", "Switching to workspace on output '%s' was blocked", argv[1]); @@ -591,11 +593,13 @@ static struct cmd_results *cmd_move(int argc, char **argv) { } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) { // move container to output x swayc_t *output = NULL; + struct wlc_point abs_pos; + get_absolute_center_position(view, &abs_pos); if (view->type != C_CONTAINER && view->type != C_VIEW) { return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); - } else if (!(output = output_by_name(argv[3]))) { + } else if (!(output = output_by_name(argv[3], &abs_pos))) { return cmd_results_new(CMD_FAILURE, "move", - "Can't find output with name/direction '%s'", argv[3]); + "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y); } else { swayc_t *container = get_focused_container(output); if (container->is_floating) { @@ -610,13 +614,15 @@ static struct cmd_results *cmd_move(int argc, char **argv) { } else if (strcasecmp(argv[0], "workspace") == 0) { // move workspace (to output x) swayc_t *output = NULL; + struct wlc_point abs_pos; + get_absolute_center_position(view, &abs_pos); if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { return error; } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) { return cmd_results_new(CMD_INVALID, "move", expected_syntax); - } else if (!(output = output_by_name(argv[3]))) { + } else if (!(output = output_by_name(argv[3], &abs_pos))) { return cmd_results_new(CMD_FAILURE, "move workspace", - "Can't find output with name/at direction '%s'", argv[3]); + "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y); } if (view->type == C_WORKSPACE) { // This probably means we're moving an empty workspace, but diff --git a/sway/handlers.c b/sway/handlers.c index cadfce5c..3f3c1bdd 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -305,28 +305,39 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) { swayc_t *output = swayc_active_output(), *adjacent = NULL; + struct wlc_point abs_pos = *origin; + abs_pos.x += output->x; + abs_pos.y += output->y; if (origin->x == 0) { // Left edge - if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT))) { + if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { new_origin.x = adjacent->width; + // adjust for differently aligned outputs (well, this is + // only correct when the two outputs have the same + // resolution or the same dpi I guess, it should take + // physical attributes into account) + new_origin.y += (output->y - adjacent->y); } } } else if ((double)origin->x == output->width) { // Right edge - if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT))) { + if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { new_origin.x = 0; + new_origin.y += (output->y - adjacent->y); } } } else if (origin->y == 0) { // Top edge - if ((adjacent = swayc_adjacent_output(output, MOVE_UP))) { + if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { new_origin.y = adjacent->height; + new_origin.x += (output->x - adjacent->x); } } } else if ((double)origin->y == output->height) { // Bottom edge - if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN))) { + if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) { if (workspace_switch(swayc_active_workspace_for(adjacent))) { new_origin.y = 0; + new_origin.x += (output->x - adjacent->x); } } } diff --git a/sway/layout.c b/sway/layout.c index fe7d820a..741addf1 100644 --- a/sway/layout.c +++ b/sway/layout.c @@ -533,13 +533,17 @@ swayc_t *get_swayc_in_direction_under(swayc_t *container, enum movement_directio return parent; } } + // If moving to an adjacent output we need a starting position (since this + // output might border to multiple outputs). + struct wlc_point abs_pos; + get_absolute_center_position(container, &abs_pos); while (true) { // Test if we can even make a difference here bool can_move = false; int diff = 0; if (parent->type == C_ROOT) { sway_log(L_DEBUG, "Moving between outputs"); - return swayc_adjacent_output(container, dir); + return swayc_adjacent_output(container, dir, &abs_pos, true); } else { if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { if (parent->layout == L_HORIZ) { diff --git a/sway/output.c b/sway/output.c index 5044b7aa..cf8ed9a5 100644 --- a/sway/output.c +++ b/sway/output.c @@ -2,15 +2,15 @@ #include "output.h" #include "log.h" -swayc_t *output_by_name(const char* name) { +swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) { if (strcasecmp(name, "left") == 0) { - return swayc_adjacent_output(NULL, MOVE_LEFT); + return swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true); } else if (strcasecmp(name, "right") == 0) { - return swayc_adjacent_output(NULL, MOVE_RIGHT); + return swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true); } else if (strcasecmp(name, "up") == 0) { - return swayc_adjacent_output(NULL, MOVE_UP); + return swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true); } else if (strcasecmp(name, "down") == 0) { - return swayc_adjacent_output(NULL, MOVE_DOWN); + return swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true); } else { for(int i = 0; i < root_container.children->length; ++i) { swayc_t *c = root_container.children->items[i]; @@ -22,65 +22,125 @@ swayc_t *output_by_name(const char* name) { return NULL; } -swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir) { - // TODO: This implementation is naïve: We assume all outputs are - // perfectly aligned (ie. only a single output per edge which covers - // the whole edge). +// Position is where on the edge (as absolute position) the adjacent output should be searched for. +swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir, + const struct wlc_point *abs_pos, bool pick_closest) { + if (!output) { output = swayc_active_output(); } + // In order to find adjacent outputs we need to test that the outputs are + // aligned on one axis (decided by the direction given) and that the given + // position is within the edge of the adjacent output. If no such output + // exists we pick the adjacent output within the edge that is closest to + // the given position, if any. swayc_t *adjacent = NULL; + char *dir_text = NULL; switch(dir) { case MOVE_LEFT: + case MOVE_RIGHT: ; + double delta_y = 0; for(int i = 0; i < root_container.children->length; ++i) { swayc_t *c = root_container.children->items[i]; if (c == output || c->type != C_OUTPUT) { continue; } - if (c->y == output->y && c->x + c->width == output->x) { - sway_log(L_DEBUG, "%s is left of current output %s", c->name, output->name); + bool x_aligned = dir == MOVE_LEFT ? + c->x + c->width == output->x : + c->x == output->x + output->width; + if (!x_aligned) { + continue; + } + if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) { + delta_y = 0; adjacent = c; break; + } else if (pick_closest) { + // track closest adjacent output + double top_y = c->y, bottom_y = c->y + c->height; + if (top_y >= output->y && top_y <= output->y + output->height) { + double delta = top_y - abs_pos->y; + if (delta < 0) delta = -delta; + if (delta < delta_y || !adjacent) { + delta_y = delta; + adjacent = c; + } + } + // we check both points and pick the closest + if (bottom_y >= output->y && bottom_y <= output->y + output->height) { + double delta = bottom_y - abs_pos->y; + if (delta < 0) delta = -delta; + if (delta < delta_y || !adjacent) { + delta_y = delta; + adjacent = c; + } + } } } - break; - case MOVE_RIGHT: - for(int i = 0; i < root_container.children->length; ++i) { - swayc_t *c = root_container.children->items[i]; - if (c == output || c->type != C_OUTPUT) { - continue; - } - if (c->y == output->y && output->x + output->width == c->x) { - sway_log(L_DEBUG, "%s is right of current output %s", c->name, output->name); - adjacent = c; - break; - } + dir_text = dir == MOVE_LEFT ? "left of" : "right of"; + if (adjacent && delta_y == 0) { + sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)", + adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, + dir_text, output->name, abs_pos->y); + } else if (adjacent) { + // so we end up picking the closest adjacent output because + // there is no directly adjacent to the given position + sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)", + adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, + dir_text, output->name, abs_pos->y, delta_y); } break; case MOVE_UP: + case MOVE_DOWN: ; + double delta_x = 0; for(int i = 0; i < root_container.children->length; ++i) { swayc_t *c = root_container.children->items[i]; if (c == output || c->type != C_OUTPUT) { continue; } - if (output->x == c->x && c->y + c->height == output->y) { - sway_log(L_DEBUG, "%s is above current output %s", c->name, output->name); + bool y_aligned = dir == MOVE_UP ? + c->y + c->height == output->y : + c->y == output->y + output->height; + if (!y_aligned) { + continue; + } + if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) { + delta_x = 0; adjacent = c; break; + } else if (pick_closest) { + // track closest adjacent output + double left_x = c->x, right_x = c->x + c->width; + if (left_x >= output->x && left_x <= output->x + output->width) { + double delta = left_x - abs_pos->x; + if (delta < 0) delta = -delta; + if (delta < delta_x || !adjacent) { + delta_x = delta; + adjacent = c; + } + } + // we check both points and pick the closest + if (right_x >= output->x && right_x <= output->x + output->width) { + double delta = right_x - abs_pos->x; + if (delta < 0) delta = -delta; + if (delta < delta_x || !adjacent) { + delta_x = delta; + adjacent = c; + } + } } } - break; - case MOVE_DOWN: - for(int i = 0; i < root_container.children->length; ++i) { - swayc_t *c = root_container.children->items[i]; - if (c == output || c->type != C_OUTPUT) { - continue; - } - if (output->x == c->x && output->y + output->height == c->y) { - sway_log(L_DEBUG, "%s is below current output %s", c->name, output->name); - adjacent = c; - break; - } + dir_text = dir == MOVE_UP ? "above" : "below"; + if (adjacent && delta_x == 0) { + sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)", + adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, + dir_text, output->name, abs_pos->x); + } else if (adjacent) { + // so we end up picking the closest adjacent output because + // there is no directly adjacent to the given position + sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)", + adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y, + dir_text, output->name, abs_pos->x, delta_x); } break; default: @@ -89,3 +149,31 @@ swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir) { } return adjacent; } + +void get_absolute_position(swayc_t *container, struct wlc_point *point) { + if (!container || !point) + sway_abort("Need container and wlc_point (was %p, %p).", container, point); + + if (container->type == C_OUTPUT) { + // Coordinates are already absolute. + point->x = container->x; + point->y = container->y; + } else { + swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); + if (container->type == C_WORKSPACE) { + // Workspace coordinates are actually wrong/arbitrary, but should + // be same as output. + point->x = output->x; + point->y = output->y; + } else { + point->x = output->x + container->x; + point->y = output->y + container->y; + } + } +} + +void get_absolute_center_position(swayc_t *container, struct wlc_point *point) { + get_absolute_position(container, point); + point->x += container->width/2; + point->y += container->height/2; +}