diff options
author | Ryan Dwyer <ryandwyer1@gmail.com> | 2018-07-18 16:13:28 +1000 |
---|---|---|
committer | Ryan Dwyer <ryandwyer1@gmail.com> | 2018-07-22 23:10:19 +1000 |
commit | 9fbe13b9be18c732b58033a57a22a299af91a170 (patch) | |
tree | a24901c1bb4eff87877c0d9fb96767b662a9d533 /sway/input/cursor.c | |
parent | Merge pull request #2320 from RedSoxFan/reset-outputs-on-reload (diff) | |
download | sway-9fbe13b9be18c732b58033a57a22a299af91a170.tar.gz sway-9fbe13b9be18c732b58033a57a22a299af91a170.tar.zst sway-9fbe13b9be18c732b58033a57a22a299af91a170.zip |
Implement floating_modifier and mouse operations for floating views
This implements the following:
* `floating_modifier` configuration directive
* Drag a floating window by its title bar
* Hold mod + drag a floating window from anywhere
* Resize a floating view by dragging the border
* Resize a floating view by holding mod and right clicking anywhere on
the view
* Resize a floating view and keep aspect ratio by holding shift while
resizing using either method
* Mouse cursor turns into resize when hovering floating border or corner
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r-- | sway/input/cursor.c | 322 |
1 files changed, 311 insertions, 11 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c76c20b3..6ad214b5 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -5,15 +5,19 @@ | |||
5 | #elif __FreeBSD__ | 5 | #elif __FreeBSD__ |
6 | #include <dev/evdev/input-event-codes.h> | 6 | #include <dev/evdev/input-event-codes.h> |
7 | #endif | 7 | #endif |
8 | #include <limits.h> | ||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_xcursor_manager.h> | 10 | #include <wlr/types/wlr_xcursor_manager.h> |
10 | #include <wlr/types/wlr_idle.h> | 11 | #include <wlr/types/wlr_idle.h> |
11 | #include "list.h" | 12 | #include "list.h" |
12 | #include "log.h" | 13 | #include "log.h" |
14 | #include "sway/desktop.h" | ||
13 | #include "sway/desktop/transaction.h" | 15 | #include "sway/desktop/transaction.h" |
14 | #include "sway/input/cursor.h" | 16 | #include "sway/input/cursor.h" |
17 | #include "sway/input/keyboard.h" | ||
15 | #include "sway/layers.h" | 18 | #include "sway/layers.h" |
16 | #include "sway/output.h" | 19 | #include "sway/output.h" |
20 | #include "sway/tree/arrange.h" | ||
17 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
18 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
19 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 23 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
@@ -127,7 +131,7 @@ static struct sway_container *container_at_coords( | |||
127 | return ws; | 131 | return ws; |
128 | } | 132 | } |
129 | 133 | ||
130 | c = seat_get_focus_inactive(seat, output->swayc); | 134 | c = seat_get_active_child(seat, output->swayc); |
131 | if (c) { | 135 | if (c) { |
132 | return c; | 136 | return c; |
133 | } | 137 | } |
@@ -139,6 +143,173 @@ static struct sway_container *container_at_coords( | |||
139 | return output->swayc; | 143 | return output->swayc; |
140 | } | 144 | } |
141 | 145 | ||
146 | static enum resize_edge find_resize_edge(struct sway_container *cont, | ||
147 | struct sway_cursor *cursor) { | ||
148 | if (cont->type != C_VIEW) { | ||
149 | return RESIZE_EDGE_NONE; | ||
150 | } | ||
151 | struct sway_view *view = cont->sway_view; | ||
152 | if (view->border == B_NONE || !view->border_thickness || view->using_csd) { | ||
153 | return RESIZE_EDGE_NONE; | ||
154 | } | ||
155 | |||
156 | enum resize_edge edge = 0; | ||
157 | if (cursor->cursor->x < cont->x + view->border_thickness) { | ||
158 | edge |= RESIZE_EDGE_LEFT; | ||
159 | } | ||
160 | if (cursor->cursor->y < cont->y + view->border_thickness) { | ||
161 | edge |= RESIZE_EDGE_TOP; | ||
162 | } | ||
163 | if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { | ||
164 | edge |= RESIZE_EDGE_RIGHT; | ||
165 | } | ||
166 | if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { | ||
167 | edge |= RESIZE_EDGE_BOTTOM; | ||
168 | } | ||
169 | return edge; | ||
170 | } | ||
171 | |||
172 | static void handle_drag_motion(struct sway_seat *seat, | ||
173 | struct sway_cursor *cursor) { | ||
174 | struct sway_container *con = seat->op_container; | ||
175 | desktop_damage_whole_container(con); | ||
176 | container_floating_translate(con, | ||
177 | cursor->cursor->x - cursor->previous.x, | ||
178 | cursor->cursor->y - cursor->previous.y); | ||
179 | desktop_damage_whole_container(con); | ||
180 | } | ||
181 | |||
182 | static void calculate_floating_constraints(struct sway_container *con, | ||
183 | int *min_width, int *max_width, int *min_height, int *max_height) { | ||
184 | if (config->floating_minimum_width == -1) { // no minimum | ||
185 | *min_width = 0; | ||
186 | } else if (config->floating_minimum_width == 0) { // automatic | ||
187 | *min_width = 75; | ||
188 | } else { | ||
189 | *min_width = config->floating_minimum_width; | ||
190 | } | ||
191 | |||
192 | if (config->floating_minimum_height == -1) { // no minimum | ||
193 | *min_height = 0; | ||
194 | } else if (config->floating_minimum_height == 0) { // automatic | ||
195 | *min_height = 50; | ||
196 | } else { | ||
197 | *min_height = config->floating_minimum_height; | ||
198 | } | ||
199 | |||
200 | if (config->floating_maximum_width == -1) { // no maximum | ||
201 | *max_width = INT_MAX; | ||
202 | } else if (config->floating_maximum_width == 0) { // automatic | ||
203 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
204 | *max_width = ws->width; | ||
205 | } else { | ||
206 | *max_width = config->floating_maximum_width; | ||
207 | } | ||
208 | |||
209 | if (config->floating_maximum_height == -1) { // no maximum | ||
210 | *max_height = INT_MAX; | ||
211 | } else if (config->floating_maximum_height == 0) { // automatic | ||
212 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
213 | *max_height = ws->height; | ||
214 | } else { | ||
215 | *max_height = config->floating_maximum_height; | ||
216 | } | ||
217 | } | ||
218 | static void handle_resize_motion(struct sway_seat *seat, | ||
219 | struct sway_cursor *cursor) { | ||
220 | struct sway_container *con = seat->op_container; | ||
221 | double center_lx = con->x + con->width / 2; | ||
222 | double center_ly = con->y + con->height / 2; | ||
223 | enum resize_edge edge = seat->op_resize_edge; | ||
224 | |||
225 | // The amount the mouse has moved since the start of the resize operation | ||
226 | // Positive is down/right | ||
227 | double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; | ||
228 | double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; | ||
229 | |||
230 | if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { | ||
231 | mouse_move_x = 0; | ||
232 | } | ||
233 | if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { | ||
234 | mouse_move_y = 0; | ||
235 | } | ||
236 | |||
237 | double grow_width = seat->op_ref_lx > center_lx ? | ||
238 | mouse_move_x : -mouse_move_x; | ||
239 | double grow_height = seat->op_ref_ly > center_ly ? | ||
240 | mouse_move_y : -mouse_move_y; | ||
241 | |||
242 | if (seat->op_resize_preserve_ratio) { | ||
243 | double x_multiplier = grow_width / seat->op_ref_width; | ||
244 | double y_multiplier = grow_height / seat->op_ref_height; | ||
245 | double avg_multiplier = (x_multiplier + y_multiplier) / 2; | ||
246 | grow_width = seat->op_ref_width * avg_multiplier; | ||
247 | grow_height = seat->op_ref_height * avg_multiplier; | ||
248 | } | ||
249 | |||
250 | // If we're resizing from the center (mod + right click), we need to double | ||
251 | // the amount we're growing because we're doing it in both directions. | ||
252 | if (edge == RESIZE_EDGE_NONE) { | ||
253 | grow_width *= 2; | ||
254 | grow_height *= 2; | ||
255 | } | ||
256 | |||
257 | // Determine new width/height, and accommodate for min/max values | ||
258 | double width = seat->op_ref_width + grow_width; | ||
259 | double height = seat->op_ref_height + grow_height; | ||
260 | int min_width, max_width, min_height, max_height; | ||
261 | calculate_floating_constraints(con, &min_width, &max_width, | ||
262 | &min_height, &max_height); | ||
263 | width = fmax(min_width, fmin(width, max_width)); | ||
264 | height = fmax(min_height, fmin(height, max_height)); | ||
265 | |||
266 | // Recalculate these, in case we hit a min/max limit | ||
267 | grow_width = width - seat->op_ref_width; | ||
268 | grow_height = height - seat->op_ref_height; | ||
269 | |||
270 | // Determine grow x/y values - these are relative to the container's x/y at | ||
271 | // the start of the resize operation. | ||
272 | double grow_x = 0, grow_y = 0; | ||
273 | if (edge & RESIZE_EDGE_LEFT) { | ||
274 | grow_x = -grow_width; | ||
275 | } else if (edge & RESIZE_EDGE_RIGHT) { | ||
276 | grow_x = 0; | ||
277 | } else { | ||
278 | grow_x = -grow_width / 2; | ||
279 | } | ||
280 | if (edge & RESIZE_EDGE_TOP) { | ||
281 | grow_y = -grow_height; | ||
282 | } else if (edge & RESIZE_EDGE_BOTTOM) { | ||
283 | grow_y = 0; | ||
284 | } else { | ||
285 | grow_y = -grow_height / 2; | ||
286 | } | ||
287 | |||
288 | // Determine the amounts we need to bump everything relative to the current | ||
289 | // size. | ||
290 | int relative_grow_width = width - con->width; | ||
291 | int relative_grow_height = height - con->height; | ||
292 | int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; | ||
293 | int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; | ||
294 | |||
295 | // Actually resize stuff | ||
296 | con->x += relative_grow_x; | ||
297 | con->y += relative_grow_y; | ||
298 | con->width += relative_grow_width; | ||
299 | con->height += relative_grow_height; | ||
300 | |||
301 | if (con->type == C_VIEW) { | ||
302 | struct sway_view *view = con->sway_view; | ||
303 | view->x += relative_grow_x; | ||
304 | view->y += relative_grow_y; | ||
305 | view->width += relative_grow_width; | ||
306 | view->height += relative_grow_height; | ||
307 | } | ||
308 | |||
309 | arrange_windows(con); | ||
310 | transaction_commit_dirty(); | ||
311 | } | ||
312 | |||
142 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 313 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
143 | bool allow_refocusing) { | 314 | bool allow_refocusing) { |
144 | if (time_msec == 0) { | 315 | if (time_msec == 0) { |
@@ -146,6 +317,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
146 | } | 317 | } |
147 | 318 | ||
148 | struct sway_seat *seat = cursor->seat; | 319 | struct sway_seat *seat = cursor->seat; |
320 | |||
321 | if (seat->operation != OP_NONE) { | ||
322 | if (seat->operation == OP_DRAG) { | ||
323 | handle_drag_motion(seat, cursor); | ||
324 | } else { | ||
325 | handle_resize_motion(seat, cursor); | ||
326 | } | ||
327 | cursor->previous.x = cursor->cursor->x; | ||
328 | cursor->previous.y = cursor->cursor->y; | ||
329 | return; | ||
330 | } | ||
331 | |||
149 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 332 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
150 | struct wlr_surface *surface = NULL; | 333 | struct wlr_surface *surface = NULL; |
151 | double sx, sy; | 334 | double sx, sy; |
@@ -194,15 +377,54 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
194 | } | 377 | } |
195 | } | 378 | } |
196 | 379 | ||
197 | // reset cursor if switching between clients | 380 | // Handle cursor image |
198 | struct wl_client *client = NULL; | 381 | if (surface) { |
199 | if (surface != NULL) { | 382 | // Reset cursor if switching between clients |
200 | client = wl_resource_get_client(surface->resource); | 383 | struct wl_client *client = wl_resource_get_client(surface->resource); |
201 | } | 384 | if (client != cursor->image_client) { |
202 | if (client != cursor->image_client) { | 385 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, |
386 | "left_ptr", cursor->cursor); | ||
387 | cursor->image_client = client; | ||
388 | } | ||
389 | } else if (c && container_is_floating(c)) { | ||
390 | // Try a floating container's resize edge | ||
391 | enum resize_edge edge = find_resize_edge(c, cursor); | ||
392 | if (edge == RESIZE_EDGE_NONE) { | ||
393 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
394 | "left_ptr", cursor->cursor); | ||
395 | } else if (edge == RESIZE_EDGE_TOP) { | ||
396 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
397 | "top_side", cursor->cursor); | ||
398 | } else if (edge == RESIZE_EDGE_RIGHT) { | ||
399 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
400 | "right_side", cursor->cursor); | ||
401 | } else if (edge == RESIZE_EDGE_BOTTOM) { | ||
402 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
403 | "bottom_side", cursor->cursor); | ||
404 | } else if (edge == RESIZE_EDGE_LEFT) { | ||
405 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
406 | "left_side", cursor->cursor); | ||
407 | } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { | ||
408 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
409 | "top_left_corner", cursor->cursor); | ||
410 | } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { | ||
411 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
412 | "top_right_corner", cursor->cursor); | ||
413 | } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { | ||
414 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
415 | "bottom_left_corner", cursor->cursor); | ||
416 | } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { | ||
417 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
418 | "bottom_right_corner", cursor->cursor); | ||
419 | } else { | ||
420 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | ||
421 | "left_ptr", cursor->cursor); | ||
422 | } | ||
423 | cursor->image_client = NULL; | ||
424 | } else { | ||
203 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | 425 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, |
204 | "left_ptr", cursor->cursor); | 426 | "left_ptr", cursor->cursor); |
205 | cursor->image_client = client; | 427 | cursor->image_client = NULL; |
206 | } | 428 | } |
207 | 429 | ||
208 | // send pointer enter/leave | 430 | // send pointer enter/leave |
@@ -243,8 +465,79 @@ static void handle_cursor_motion_absolute( | |||
243 | transaction_commit_dirty(); | 465 | transaction_commit_dirty(); |
244 | } | 466 | } |
245 | 467 | ||
468 | static void handle_end_operation(struct sway_seat *seat) { | ||
469 | if (seat->operation == OP_DRAG) { | ||
470 | // We "move" the container to its own location so it discovers its | ||
471 | // output again. | ||
472 | struct sway_container *con = seat->op_container; | ||
473 | container_floating_move_to(con, con->x, con->y); | ||
474 | seat->operation = OP_NONE; | ||
475 | seat->op_container = NULL; | ||
476 | } else { | ||
477 | // OP_RESIZE | ||
478 | seat->operation = OP_NONE; | ||
479 | seat->op_container = NULL; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | static void dispatch_cursor_button_floating(struct sway_cursor *cursor, | ||
484 | uint32_t time_msec, uint32_t button, enum wlr_button_state state, | ||
485 | struct wlr_surface *surface, double sx, double sy, | ||
486 | struct sway_container *cont) { | ||
487 | struct sway_seat *seat = cursor->seat; | ||
488 | |||
489 | // Deny dragging or resizing a fullscreen view | ||
490 | if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { | ||
491 | wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); | ||
492 | return; | ||
493 | } | ||
494 | |||
495 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | ||
496 | bool mod_pressed = keyboard && | ||
497 | (keyboard->modifiers.depressed & config->floating_mod); | ||
498 | enum resize_edge edge = find_resize_edge(cont, cursor); | ||
499 | bool over_title = edge == RESIZE_EDGE_NONE && !surface; | ||
500 | |||
501 | // Check for beginning drag | ||
502 | if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && | ||
503 | (mod_pressed || over_title)) { | ||
504 | seat->operation = OP_DRAG; | ||
505 | seat->op_container = cont; | ||
506 | seat->op_button = button; | ||
507 | return; | ||
508 | } | ||
509 | |||
510 | // Check for beginning resize | ||
511 | bool resizing_via_border = button == BTN_LEFT && edge; | ||
512 | bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; | ||
513 | if ((resizing_via_border || resizing_via_mod) && | ||
514 | state == WLR_BUTTON_PRESSED) { | ||
515 | seat->operation = OP_RESIZE; | ||
516 | seat->op_container = cont; | ||
517 | seat->op_resize_preserve_ratio = keyboard && | ||
518 | (keyboard->modifiers.depressed & 1); // Shift | ||
519 | seat->op_resize_edge = edge; | ||
520 | seat->op_button = button; | ||
521 | seat->op_ref_lx = cursor->cursor->x; | ||
522 | seat->op_ref_ly = cursor->cursor->y; | ||
523 | seat->op_ref_con_lx = cont->x; | ||
524 | seat->op_ref_con_ly = cont->y; | ||
525 | seat->op_ref_width = cont->width; | ||
526 | seat->op_ref_height = cont->height; | ||
527 | return; | ||
528 | } | ||
529 | |||
530 | // Send event to surface | ||
531 | wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); | ||
532 | } | ||
533 | |||
246 | void dispatch_cursor_button(struct sway_cursor *cursor, | 534 | void dispatch_cursor_button(struct sway_cursor *cursor, |
247 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { | 535 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { |
536 | if (cursor->seat->operation != OP_NONE && | ||
537 | button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { | ||
538 | handle_end_operation(cursor->seat); | ||
539 | return; | ||
540 | } | ||
248 | if (time_msec == 0) { | 541 | if (time_msec == 0) { |
249 | time_msec = get_current_time_msec(); | 542 | time_msec = get_current_time_msec(); |
250 | } | 543 | } |
@@ -259,6 +552,11 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
259 | if (layer->current.keyboard_interactive) { | 552 | if (layer->current.keyboard_interactive) { |
260 | seat_set_focus_layer(cursor->seat, layer); | 553 | seat_set_focus_layer(cursor->seat, layer); |
261 | } | 554 | } |
555 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | ||
556 | time_msec, button, state); | ||
557 | } else if (cont && container_is_floating(cont)) { | ||
558 | dispatch_cursor_button_floating(cursor, time_msec, button, state, | ||
559 | surface, sx, sy, cont); | ||
262 | } else if (surface && cont && cont->type != C_VIEW) { | 560 | } else if (surface && cont && cont->type != C_VIEW) { |
263 | // Avoid moving keyboard focus from a surface that accepts it to one | 561 | // Avoid moving keyboard focus from a surface that accepts it to one |
264 | // that does not unless the change would move us to a new workspace. | 562 | // that does not unless the change would move us to a new workspace. |
@@ -275,12 +573,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
275 | if (new_ws != old_ws) { | 573 | if (new_ws != old_ws) { |
276 | seat_set_focus(cursor->seat, cont); | 574 | seat_set_focus(cursor->seat, cont); |
277 | } | 575 | } |
576 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | ||
577 | time_msec, button, state); | ||
278 | } else if (cont) { | 578 | } else if (cont) { |
279 | seat_set_focus(cursor->seat, cont); | 579 | seat_set_focus(cursor->seat, cont); |
580 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | ||
581 | time_msec, button, state); | ||
280 | } | 582 | } |
281 | 583 | ||
282 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | ||
283 | time_msec, button, state); | ||
284 | transaction_commit_dirty(); | 584 | transaction_commit_dirty(); |
285 | } | 585 | } |
286 | 586 | ||