aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar S. Christoffer Eliesen <christoffer@eliesen.no>2015-11-16 00:35:25 +0100
committerLibravatar S. Christoffer Eliesen <christoffer@eliesen.no>2015-11-16 21:32:18 +0100
commit236f26f62e56cef8278d88f6111720b738d4a85f (patch)
treebdac5630c32099785a6eade4dfa8ceb04a3c11dd /sway
parentMerge pull request #232 from sce/replace_output_config (diff)
downloadsway-236f26f62e56cef8278d88f6111720b738d4a85f.tar.gz
sway-236f26f62e56cef8278d88f6111720b738d4a85f.tar.zst
sway-236f26f62e56cef8278d88f6111720b738d4a85f.zip
output: Support multiple adjacent outputs.
When querying for an adjacent output we now need an absolute position in order to know which adjacent output that matches. (The position is either the current mouse position or the center of the currently focused container, depending on context.) If two outputs have one edge each that at least partially align with each other they now count as adjacent. Seamless mouse is affected by this and now properly moves and positions itself between outputs with "uneven" placement (as long as they have at least some part of the edge adjacent to each other). When focusing or moving a container in a specified direction the center of the current focused container decides where to look for an adjacent output. So if e.g. an output has two adjacent outputs to the right and a "focus right" command is issued then it's the placement of the currently focused container that decides which output actually gets focused. Also, if an output has at least one output adjacent in some direction but the entire edge is not covered (ie. it has "holes" with no outputs), then the algorithm will choose the output that is closest to the currently focused container (this does not apply to seamless mouse, the pointer will just stop at the edge in that case).
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c18
-rw-r--r--sway/handlers.c19
-rw-r--r--sway/layout.c6
-rw-r--r--sway/output.c158
4 files changed, 155 insertions, 46 deletions
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) {
377 struct cmd_results *error = NULL; 377 struct cmd_results *error = NULL;
378 if (argc > 0 && strcasecmp(argv[0], "output") == 0) { 378 if (argc > 0 && strcasecmp(argv[0], "output") == 0) {
379 swayc_t *output = NULL; 379 swayc_t *output = NULL;
380 struct wlc_point abs_pos;
381 get_absolute_center_position(get_focused_container(&root_container), &abs_pos);
380 if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) { 382 if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) {
381 return error; 383 return error;
382 } else if (!(output = output_by_name(argv[1]))) { 384 } else if (!(output = output_by_name(argv[1], &abs_pos))) {
383 return cmd_results_new(CMD_FAILURE, "focus output", 385 return cmd_results_new(CMD_FAILURE, "focus output",
384 "Can't find output with name/at direction %s", argv[1]); 386 "Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y);
385 } else if (!workspace_switch(swayc_active_workspace_for(output))) { 387 } else if (!workspace_switch(swayc_active_workspace_for(output))) {
386 return cmd_results_new(CMD_FAILURE, "focus output", 388 return cmd_results_new(CMD_FAILURE, "focus output",
387 "Switching to workspace on output '%s' was blocked", argv[1]); 389 "Switching to workspace on output '%s' was blocked", argv[1]);
@@ -591,11 +593,13 @@ static struct cmd_results *cmd_move(int argc, char **argv) {
591 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) { 593 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
592 // move container to output x 594 // move container to output x
593 swayc_t *output = NULL; 595 swayc_t *output = NULL;
596 struct wlc_point abs_pos;
597 get_absolute_center_position(view, &abs_pos);
594 if (view->type != C_CONTAINER && view->type != C_VIEW) { 598 if (view->type != C_CONTAINER && view->type != C_VIEW) {
595 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); 599 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
596 } else if (!(output = output_by_name(argv[3]))) { 600 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
597 return cmd_results_new(CMD_FAILURE, "move", 601 return cmd_results_new(CMD_FAILURE, "move",
598 "Can't find output with name/direction '%s'", argv[3]); 602 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
599 } else { 603 } else {
600 swayc_t *container = get_focused_container(output); 604 swayc_t *container = get_focused_container(output);
601 if (container->is_floating) { 605 if (container->is_floating) {
@@ -610,13 +614,15 @@ static struct cmd_results *cmd_move(int argc, char **argv) {
610 } else if (strcasecmp(argv[0], "workspace") == 0) { 614 } else if (strcasecmp(argv[0], "workspace") == 0) {
611 // move workspace (to output x) 615 // move workspace (to output x)
612 swayc_t *output = NULL; 616 swayc_t *output = NULL;
617 struct wlc_point abs_pos;
618 get_absolute_center_position(view, &abs_pos);
613 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { 619 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
614 return error; 620 return error;
615 } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) { 621 } else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
616 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 622 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
617 } else if (!(output = output_by_name(argv[3]))) { 623 } else if (!(output = output_by_name(argv[3], &abs_pos))) {
618 return cmd_results_new(CMD_FAILURE, "move workspace", 624 return cmd_results_new(CMD_FAILURE, "move workspace",
619 "Can't find output with name/at direction '%s'", argv[3]); 625 "Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
620 } 626 }
621 if (view->type == C_WORKSPACE) { 627 if (view->type == C_WORKSPACE) {
622 // This probably means we're moving an empty workspace, but 628 // 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
305 !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) { 305 !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) {
306 306
307 swayc_t *output = swayc_active_output(), *adjacent = NULL; 307 swayc_t *output = swayc_active_output(), *adjacent = NULL;
308 struct wlc_point abs_pos = *origin;
309 abs_pos.x += output->x;
310 abs_pos.y += output->y;
308 if (origin->x == 0) { // Left edge 311 if (origin->x == 0) { // Left edge
309 if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT))) { 312 if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) {
310 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 313 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
311 new_origin.x = adjacent->width; 314 new_origin.x = adjacent->width;
315 // adjust for differently aligned outputs (well, this is
316 // only correct when the two outputs have the same
317 // resolution or the same dpi I guess, it should take
318 // physical attributes into account)
319 new_origin.y += (output->y - adjacent->y);
312 } 320 }
313 } 321 }
314 } else if ((double)origin->x == output->width) { // Right edge 322 } else if ((double)origin->x == output->width) { // Right edge
315 if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT))) { 323 if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) {
316 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 324 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
317 new_origin.x = 0; 325 new_origin.x = 0;
326 new_origin.y += (output->y - adjacent->y);
318 } 327 }
319 } 328 }
320 } else if (origin->y == 0) { // Top edge 329 } else if (origin->y == 0) { // Top edge
321 if ((adjacent = swayc_adjacent_output(output, MOVE_UP))) { 330 if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) {
322 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 331 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
323 new_origin.y = adjacent->height; 332 new_origin.y = adjacent->height;
333 new_origin.x += (output->x - adjacent->x);
324 } 334 }
325 } 335 }
326 } else if ((double)origin->y == output->height) { // Bottom edge 336 } else if ((double)origin->y == output->height) { // Bottom edge
327 if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN))) { 337 if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) {
328 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 338 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
329 new_origin.y = 0; 339 new_origin.y = 0;
340 new_origin.x += (output->x - adjacent->x);
330 } 341 }
331 } 342 }
332 } 343 }
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
533 return parent; 533 return parent;
534 } 534 }
535 } 535 }
536 // If moving to an adjacent output we need a starting position (since this
537 // output might border to multiple outputs).
538 struct wlc_point abs_pos;
539 get_absolute_center_position(container, &abs_pos);
536 while (true) { 540 while (true) {
537 // Test if we can even make a difference here 541 // Test if we can even make a difference here
538 bool can_move = false; 542 bool can_move = false;
539 int diff = 0; 543 int diff = 0;
540 if (parent->type == C_ROOT) { 544 if (parent->type == C_ROOT) {
541 sway_log(L_DEBUG, "Moving between outputs"); 545 sway_log(L_DEBUG, "Moving between outputs");
542 return swayc_adjacent_output(container, dir); 546 return swayc_adjacent_output(container, dir, &abs_pos, true);
543 } else { 547 } else {
544 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 548 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
545 if (parent->layout == L_HORIZ) { 549 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 @@
2#include "output.h" 2#include "output.h"
3#include "log.h" 3#include "log.h"
4 4
5swayc_t *output_by_name(const char* name) { 5swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
6 if (strcasecmp(name, "left") == 0) { 6 if (strcasecmp(name, "left") == 0) {
7 return swayc_adjacent_output(NULL, MOVE_LEFT); 7 return swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
8 } else if (strcasecmp(name, "right") == 0) { 8 } else if (strcasecmp(name, "right") == 0) {
9 return swayc_adjacent_output(NULL, MOVE_RIGHT); 9 return swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
10 } else if (strcasecmp(name, "up") == 0) { 10 } else if (strcasecmp(name, "up") == 0) {
11 return swayc_adjacent_output(NULL, MOVE_UP); 11 return swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
12 } else if (strcasecmp(name, "down") == 0) { 12 } else if (strcasecmp(name, "down") == 0) {
13 return swayc_adjacent_output(NULL, MOVE_DOWN); 13 return swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
14 } else { 14 } else {
15 for(int i = 0; i < root_container.children->length; ++i) { 15 for(int i = 0; i < root_container.children->length; ++i) {
16 swayc_t *c = root_container.children->items[i]; 16 swayc_t *c = root_container.children->items[i];
@@ -22,66 +22,126 @@ swayc_t *output_by_name(const char* name) {
22 return NULL; 22 return NULL;
23} 23}
24 24
25swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir) { 25// Position is where on the edge (as absolute position) the adjacent output should be searched for.
26 // TODO: This implementation is naïve: We assume all outputs are 26swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
27 // perfectly aligned (ie. only a single output per edge which covers 27 const struct wlc_point *abs_pos, bool pick_closest) {
28 // the whole edge). 28
29 if (!output) { 29 if (!output) {
30 output = swayc_active_output(); 30 output = swayc_active_output();
31 } 31 }
32 // In order to find adjacent outputs we need to test that the outputs are
33 // aligned on one axis (decided by the direction given) and that the given
34 // position is within the edge of the adjacent output. If no such output
35 // exists we pick the adjacent output within the edge that is closest to
36 // the given position, if any.
32 swayc_t *adjacent = NULL; 37 swayc_t *adjacent = NULL;
38 char *dir_text = NULL;
33 switch(dir) { 39 switch(dir) {
34 case MOVE_LEFT: 40 case MOVE_LEFT:
41 case MOVE_RIGHT: ;
42 double delta_y = 0;
35 for(int i = 0; i < root_container.children->length; ++i) { 43 for(int i = 0; i < root_container.children->length; ++i) {
36 swayc_t *c = root_container.children->items[i]; 44 swayc_t *c = root_container.children->items[i];
37 if (c == output || c->type != C_OUTPUT) { 45 if (c == output || c->type != C_OUTPUT) {
38 continue; 46 continue;
39 } 47 }
40 if (c->y == output->y && c->x + c->width == output->x) { 48 bool x_aligned = dir == MOVE_LEFT ?
41 sway_log(L_DEBUG, "%s is left of current output %s", c->name, output->name); 49 c->x + c->width == output->x :
42 adjacent = c; 50 c->x == output->x + output->width;
43 break; 51 if (!x_aligned) {
44 }
45 }
46 break;
47 case MOVE_RIGHT:
48 for(int i = 0; i < root_container.children->length; ++i) {
49 swayc_t *c = root_container.children->items[i];
50 if (c == output || c->type != C_OUTPUT) {
51 continue; 52 continue;
52 } 53 }
53 if (c->y == output->y && output->x + output->width == c->x) { 54 if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
54 sway_log(L_DEBUG, "%s is right of current output %s", c->name, output->name); 55 delta_y = 0;
55 adjacent = c; 56 adjacent = c;
56 break; 57 break;
58 } else if (pick_closest) {
59 // track closest adjacent output
60 double top_y = c->y, bottom_y = c->y + c->height;
61 if (top_y >= output->y && top_y <= output->y + output->height) {
62 double delta = top_y - abs_pos->y;
63 if (delta < 0) delta = -delta;
64 if (delta < delta_y || !adjacent) {
65 delta_y = delta;
66 adjacent = c;
67 }
68 }
69 // we check both points and pick the closest
70 if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
71 double delta = bottom_y - abs_pos->y;
72 if (delta < 0) delta = -delta;
73 if (delta < delta_y || !adjacent) {
74 delta_y = delta;
75 adjacent = c;
76 }
77 }
57 } 78 }
58 } 79 }
80 dir_text = dir == MOVE_LEFT ? "left of" : "right of";
81 if (adjacent && delta_y == 0) {
82 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
83 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
84 dir_text, output->name, abs_pos->y);
85 } else if (adjacent) {
86 // so we end up picking the closest adjacent output because
87 // there is no directly adjacent to the given position
88 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
89 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
90 dir_text, output->name, abs_pos->y, delta_y);
91 }
59 break; 92 break;
60 case MOVE_UP: 93 case MOVE_UP:
94 case MOVE_DOWN: ;
95 double delta_x = 0;
61 for(int i = 0; i < root_container.children->length; ++i) { 96 for(int i = 0; i < root_container.children->length; ++i) {
62 swayc_t *c = root_container.children->items[i]; 97 swayc_t *c = root_container.children->items[i];
63 if (c == output || c->type != C_OUTPUT) { 98 if (c == output || c->type != C_OUTPUT) {
64 continue; 99 continue;
65 } 100 }
66 if (output->x == c->x && c->y + c->height == output->y) { 101 bool y_aligned = dir == MOVE_UP ?
67 sway_log(L_DEBUG, "%s is above current output %s", c->name, output->name); 102 c->y + c->height == output->y :
68 adjacent = c; 103 c->y == output->y + output->height;
69 break; 104 if (!y_aligned) {
70 }
71 }
72 break;
73 case MOVE_DOWN:
74 for(int i = 0; i < root_container.children->length; ++i) {
75 swayc_t *c = root_container.children->items[i];
76 if (c == output || c->type != C_OUTPUT) {
77 continue; 105 continue;
78 } 106 }
79 if (output->x == c->x && output->y + output->height == c->y) { 107 if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
80 sway_log(L_DEBUG, "%s is below current output %s", c->name, output->name); 108 delta_x = 0;
81 adjacent = c; 109 adjacent = c;
82 break; 110 break;
111 } else if (pick_closest) {
112 // track closest adjacent output
113 double left_x = c->x, right_x = c->x + c->width;
114 if (left_x >= output->x && left_x <= output->x + output->width) {
115 double delta = left_x - abs_pos->x;
116 if (delta < 0) delta = -delta;
117 if (delta < delta_x || !adjacent) {
118 delta_x = delta;
119 adjacent = c;
120 }
121 }
122 // we check both points and pick the closest
123 if (right_x >= output->x && right_x <= output->x + output->width) {
124 double delta = right_x - abs_pos->x;
125 if (delta < 0) delta = -delta;
126 if (delta < delta_x || !adjacent) {
127 delta_x = delta;
128 adjacent = c;
129 }
130 }
83 } 131 }
84 } 132 }
133 dir_text = dir == MOVE_UP ? "above" : "below";
134 if (adjacent && delta_x == 0) {
135 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
136 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
137 dir_text, output->name, abs_pos->x);
138 } else if (adjacent) {
139 // so we end up picking the closest adjacent output because
140 // there is no directly adjacent to the given position
141 sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
142 adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
143 dir_text, output->name, abs_pos->x, delta_x);
144 }
85 break; 145 break;
86 default: 146 default:
87 sway_abort("Function called with invalid argument."); 147 sway_abort("Function called with invalid argument.");
@@ -89,3 +149,31 @@ swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir) {
89 } 149 }
90 return adjacent; 150 return adjacent;
91} 151}
152
153void get_absolute_position(swayc_t *container, struct wlc_point *point) {
154 if (!container || !point)
155 sway_abort("Need container and wlc_point (was %p, %p).", container, point);
156
157 if (container->type == C_OUTPUT) {
158 // Coordinates are already absolute.
159 point->x = container->x;
160 point->y = container->y;
161 } else {
162 swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
163 if (container->type == C_WORKSPACE) {
164 // Workspace coordinates are actually wrong/arbitrary, but should
165 // be same as output.
166 point->x = output->x;
167 point->y = output->y;
168 } else {
169 point->x = output->x + container->x;
170 point->y = output->y + container->y;
171 }
172 }
173}
174
175void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
176 get_absolute_position(container, point);
177 point->x += container->width/2;
178 point->y += container->height/2;
179}