aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/seatop_move_tiling.c
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2019-01-10 22:04:42 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2019-01-10 22:04:42 +1000
commited5aafd90bd850ad27dcb36ac4438ed926480394 (patch)
tree46d0b5fe8488e5d9415cbc9a732e86c1e7100ffe /sway/input/seatop_move_tiling.c
parentMerge pull request #3341 from RedSoxFan/mouse-bindings-improved (diff)
downloadsway-ed5aafd90bd850ad27dcb36ac4438ed926480394.tar.gz
sway-ed5aafd90bd850ad27dcb36ac4438ed926480394.tar.zst
sway-ed5aafd90bd850ad27dcb36ac4438ed926480394.zip
Refactor seat operations to use an interface
This splits each seat operation (drag/move tiling/floating etc) into a separate file and introduces a struct sway_seatop_impl to abstract the operation. The move_tiling_threshold operation has been merged into move_tiling. The main logic for each operation is untouched aside from variable renames. The following previously-static functions have been made public: * node_at_coords * container_raise_floating * render_rect * premultiply_alpha * scale_box
Diffstat (limited to 'sway/input/seatop_move_tiling.c')
-rw-r--r--sway/input/seatop_move_tiling.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
new file mode 100644
index 00000000..8b541f80
--- /dev/null
+++ b/sway/input/seatop_move_tiling.c
@@ -0,0 +1,335 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h>
5#include "sway/desktop.h"
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/output.h"
9#include "sway/tree/arrange.h"
10#include "sway/tree/node.h"
11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
13
14// Thickness of the dropzone when dragging to the edge of a layout container
15#define DROP_LAYOUT_BORDER 30
16
17struct seatop_move_tiling_event {
18 struct sway_container *con;
19 struct sway_node *target_node;
20 enum wlr_edges target_edge;
21 struct wlr_box drop_box;
22 double ref_lx, ref_ly; // cursor's x/y at start of op
23 bool threshold_reached;
24};
25
26static void handle_render(struct sway_seat *seat,
27 struct sway_output *output, pixman_region32_t *damage) {
28 struct seatop_move_tiling_event *e = seat->seatop_data;
29 if (!e->threshold_reached) {
30 return;
31 }
32 if (e->target_node && node_get_output(e->target_node) == output) {
33 float color[4];
34 memcpy(&color, config->border_colors.focused.indicator,
35 sizeof(float) * 4);
36 premultiply_alpha(color, 0.5);
37 struct wlr_box box;
38 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
39 scale_box(&box, output->wlr_output->scale);
40 render_rect(output->wlr_output, damage, &box, color);
41 }
42}
43
44static void handle_motion_prethreshold(struct sway_seat *seat) {
45 struct seatop_move_tiling_event *e = seat->seatop_data;
46 double cx = seat->cursor->cursor->x;
47 double cy = seat->cursor->cursor->y;
48 double sx = e->ref_lx;
49 double sy = e->ref_ly;
50
51 // Get the scaled threshold for the output. Even if the operation goes
52 // across multiple outputs of varying scales, just use the scale for the
53 // output that the cursor is currently on for simplicity.
54 struct wlr_output *wlr_output = wlr_output_layout_output_at(
55 root->output_layout, cx, cy);
56 double output_scale = wlr_output ? wlr_output->scale : 1;
57 double threshold = config->tiling_drag_threshold * output_scale;
58 threshold *= threshold;
59
60 // If the threshold has been exceeded, start the actual drag
61 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
62 e->threshold_reached = true;
63 cursor_set_image(seat->cursor, "grab", NULL);
64 }
65}
66
67static void resize_box(struct wlr_box *box, enum wlr_edges edge,
68 int thickness) {
69 switch (edge) {
70 case WLR_EDGE_TOP:
71 box->height = thickness;
72 break;
73 case WLR_EDGE_LEFT:
74 box->width = thickness;
75 break;
76 case WLR_EDGE_RIGHT:
77 box->x = box->x + box->width - thickness;
78 box->width = thickness;
79 break;
80 case WLR_EDGE_BOTTOM:
81 box->y = box->y + box->height - thickness;
82 box->height = thickness;
83 break;
84 case WLR_EDGE_NONE:
85 box->x += thickness;
86 box->y += thickness;
87 box->width -= thickness * 2;
88 box->height -= thickness * 2;
89 break;
90 }
91}
92
93static void handle_motion_postthreshold(struct sway_seat *seat) {
94 struct seatop_move_tiling_event *e = seat->seatop_data;
95 struct wlr_surface *surface = NULL;
96 double sx, sy;
97 struct sway_cursor *cursor = seat->cursor;
98 struct sway_node *node = node_at_coords(seat,
99 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
100 // Damage the old location
101 desktop_damage_box(&e->drop_box);
102
103 if (!node) {
104 // Eg. hovered over a layer surface such as swaybar
105 e->target_node = NULL;
106 e->target_edge = WLR_EDGE_NONE;
107 return;
108 }
109
110 if (node->type == N_WORKSPACE) {
111 // Emtpy workspace
112 e->target_node = node;
113 e->target_edge = WLR_EDGE_NONE;
114 workspace_get_box(node->sway_workspace, &e->drop_box);
115 desktop_damage_box(&e->drop_box);
116 return;
117 }
118
119 // Deny moving within own workspace if this is the only child
120 struct sway_container *con = node->sway_container;
121 if (workspace_num_tiling_views(e->con->workspace) == 1 &&
122 con->workspace == e->con->workspace) {
123 e->target_node = NULL;
124 e->target_edge = WLR_EDGE_NONE;
125 return;
126 }
127
128 // Traverse the ancestors, trying to find a layout container perpendicular
129 // to the edge. Eg. close to the top or bottom of a horiz layout.
130 while (con) {
131 enum wlr_edges edge = WLR_EDGE_NONE;
132 enum sway_container_layout layout = container_parent_layout(con);
133 struct wlr_box parent;
134 con->parent ? container_get_box(con->parent, &parent) :
135 workspace_get_box(con->workspace, &parent);
136 if (layout == L_HORIZ || layout == L_TABBED) {
137 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
138 edge = WLR_EDGE_TOP;
139 } else if (cursor->cursor->y > parent.y + parent.height
140 - DROP_LAYOUT_BORDER) {
141 edge = WLR_EDGE_BOTTOM;
142 }
143 } else if (layout == L_VERT || layout == L_STACKED) {
144 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
145 edge = WLR_EDGE_LEFT;
146 } else if (cursor->cursor->x > parent.x + parent.width
147 - DROP_LAYOUT_BORDER) {
148 edge = WLR_EDGE_RIGHT;
149 }
150 }
151 if (edge) {
152 e->target_node = node_get_parent(&con->node);
153 e->target_edge = edge;
154 node_get_box(e->target_node, &e->drop_box);
155 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
156 desktop_damage_box(&e->drop_box);
157 return;
158 }
159 con = con->parent;
160 }
161
162 // Use the hovered view - but we must be over the actual surface
163 con = node->sway_container;
164 if (!con->view->surface || node == &e->con->node) {
165 e->target_node = NULL;
166 e->target_edge = WLR_EDGE_NONE;
167 return;
168 }
169
170 // Find the closest edge
171 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
172 size_t closest_dist = INT_MAX;
173 size_t dist;
174 e->target_edge = WLR_EDGE_NONE;
175 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
176 closest_dist = dist;
177 e->target_edge = WLR_EDGE_TOP;
178 }
179 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
180 closest_dist = dist;
181 e->target_edge = WLR_EDGE_LEFT;
182 }
183 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
184 closest_dist = dist;
185 e->target_edge = WLR_EDGE_RIGHT;
186 }
187 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
188 closest_dist = dist;
189 e->target_edge = WLR_EDGE_BOTTOM;
190 }
191
192 if (closest_dist > thickness) {
193 e->target_edge = WLR_EDGE_NONE;
194 }
195
196 e->target_node = node;
197 e->drop_box.x = con->content_x;
198 e->drop_box.y = con->content_y;
199 e->drop_box.width = con->content_width;
200 e->drop_box.height = con->content_height;
201 resize_box(&e->drop_box, e->target_edge, thickness);
202 desktop_damage_box(&e->drop_box);
203}
204
205static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
206 struct seatop_move_tiling_event *e = seat->seatop_data;
207 if (e->threshold_reached) {
208 handle_motion_postthreshold(seat);
209 } else {
210 handle_motion_prethreshold(seat);
211 }
212}
213
214static void handle_abort(struct sway_seat *seat) {
215 cursor_set_image(seat->cursor, "left_ptr", NULL);
216}
217
218static bool is_parallel(enum sway_container_layout layout,
219 enum wlr_edges edge) {
220 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
221 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
222 return layout_is_horiz == edge_is_horiz;
223}
224
225static void handle_finish(struct sway_seat *seat) {
226 struct seatop_move_tiling_event *e = seat->seatop_data;
227
228 if (!e->target_node) {
229 handle_abort(seat);
230 return;
231 }
232
233 struct sway_container *con = e->con;
234 struct sway_container *old_parent = con->parent;
235 struct sway_workspace *old_ws = con->workspace;
236 struct sway_node *target_node = e->target_node;
237 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
238 target_node->sway_workspace : target_node->sway_container->workspace;
239 enum wlr_edges edge = e->target_edge;
240 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
241
242 container_detach(con);
243
244 // Moving container into empty workspace
245 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
246 workspace_add_tiling(new_ws, con);
247 } else if (target_node->type == N_CONTAINER) {
248 // Moving container before/after another
249 struct sway_container *target = target_node->sway_container;
250 enum sway_container_layout layout = container_parent_layout(target);
251 if (edge && !is_parallel(layout, edge)) {
252 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
253 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
254 container_split(target, new_layout);
255 }
256 container_add_sibling(target, con, after);
257 } else {
258 // Target is a workspace which requires splitting
259 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
260 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
261 workspace_split(new_ws, new_layout);
262 workspace_insert_tiling(new_ws, con, after);
263 }
264
265 if (old_parent) {
266 container_reap_empty(old_parent);
267 }
268
269 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
270 // I don't think there's any other way to make it consistent without
271 // changing how we auto-size containers.
272 list_t *siblings = container_get_siblings(con);
273 if (siblings->length > 1) {
274 int index = list_find(siblings, con);
275 struct sway_container *sibling = index == 0 ?
276 siblings->items[1] : siblings->items[index - 1];
277 con->width = sibling->width;
278 con->height = sibling->height;
279 }
280
281 arrange_workspace(old_ws);
282 if (new_ws != old_ws) {
283 arrange_workspace(new_ws);
284 }
285
286 cursor_set_image(seat->cursor, "left_ptr", NULL);
287}
288
289static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
290 struct seatop_move_tiling_event *e = seat->seatop_data;
291 if (e->target_node == &con->node) { // Drop target
292 e->target_node = NULL;
293 }
294 if (e->con == con) { // The container being moved
295 seatop_abort(seat);
296 }
297}
298
299static const struct sway_seatop_impl seatop_impl = {
300 .motion = handle_motion,
301 .finish = handle_finish,
302 .abort = handle_abort,
303 .unref = handle_unref,
304 .render = handle_render,
305};
306
307void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
308 struct sway_container *con, uint32_t button) {
309 seatop_abort(seat);
310
311 struct seatop_move_tiling_event *e =
312 calloc(1, sizeof(struct seatop_move_tiling_event));
313 if (!e) {
314 return;
315 }
316 e->con = con;
317 e->ref_lx = seat->cursor->cursor->x;
318 e->ref_ly = seat->cursor->cursor->y;
319
320 seat->seatop_impl = &seatop_impl;
321 seat->seatop_data = e;
322 seat->seatop_button = button;
323
324 container_raise_floating(con);
325}
326
327void seatop_begin_move_tiling(struct sway_seat *seat,
328 struct sway_container *con, uint32_t button) {
329 seatop_begin_move_tiling_threshold(seat, con, button);
330 struct seatop_move_tiling_event *e = seat->seatop_data;
331 if (e) {
332 e->threshold_reached = true;
333 cursor_set_image(seat->cursor, "grab", NULL);
334 }
335}