summaryrefslogtreecommitdiffstats
path: root/sway/input/cursor.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/cursor.c')
-rw-r--r--sway/input/cursor.c631
1 files changed, 252 insertions, 379 deletions
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index d89f64d8..08222494 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,12 +1,11 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE 200809L
2#include <math.h> 2#include <math.h>
3#ifdef __linux__ 3#include <libevdev/libevdev.h>
4#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
5#elif __FreeBSD__ 5#include <errno.h>
6#include <dev/evdev/input-event-codes.h>
7#endif
8#include <float.h> 6#include <float.h>
9#include <limits.h> 7#include <limits.h>
8#include <strings.h>
10#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
11#include <wlr/types/wlr_xcursor_manager.h> 10#include <wlr/types/wlr_xcursor_manager.h>
12#include <wlr/types/wlr_idle.h> 11#include <wlr/types/wlr_idle.h>
@@ -28,10 +27,6 @@
28#include "sway/tree/workspace.h" 27#include "sway/tree/workspace.h"
29#include "wlr-layer-shell-unstable-v1-protocol.h" 28#include "wlr-layer-shell-unstable-v1-protocol.h"
30 29
31// When doing a tiling drag, this is the thickness of the dropzone
32// when dragging to the edge of a layout container.
33#define DROP_LAYOUT_BORDER 30
34
35static uint32_t get_current_time_msec(void) { 30static uint32_t get_current_time_msec(void) {
36 struct timespec now; 31 struct timespec now;
37 clock_gettime(CLOCK_MONOTONIC, &now); 32 clock_gettime(CLOCK_MONOTONIC, &now);
@@ -60,7 +55,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
60 * Returns the node at the cursor's position. If there is a surface at that 55 * Returns the node at the cursor's position. If there is a surface at that
61 * location, it is stored in **surface (it may not be a view). 56 * location, it is stored in **surface (it may not be a view).
62 */ 57 */
63static struct sway_node *node_at_coords( 58struct sway_node *node_at_coords(
64 struct sway_seat *seat, double lx, double ly, 59 struct sway_seat *seat, double lx, double ly,
65 struct wlr_surface **surface, double *sx, double *sy) { 60 struct wlr_surface **surface, double *sx, double *sy) {
66 // check for unmanaged views first 61 // check for unmanaged views first
@@ -88,6 +83,10 @@ static struct sway_node *node_at_coords(
88 return NULL; 83 return NULL;
89 } 84 }
90 struct sway_output *output = wlr_output->data; 85 struct sway_output *output = wlr_output->data;
86 if (!output) {
87 // output is being destroyed
88 return NULL;
89 }
91 double ox = lx, oy = ly; 90 double ox = lx, oy = ly;
92 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); 91 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
93 92
@@ -223,323 +222,6 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
223 return edge; 222 return edge;
224} 223}
225 224
226static void handle_down_motion(struct sway_seat *seat,
227 struct sway_cursor *cursor, uint32_t time_msec) {
228 struct sway_container *con = seat->op_container;
229 if (seat_is_input_allowed(seat, con->view->surface)) {
230 double moved_x = cursor->cursor->x - seat->op_ref_lx;
231 double moved_y = cursor->cursor->y - seat->op_ref_ly;
232 double sx = seat->op_ref_con_lx + moved_x;
233 double sy = seat->op_ref_con_ly + moved_y;
234 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
235 }
236 seat->op_moved = true;
237}
238
239static void handle_move_floating_motion(struct sway_seat *seat,
240 struct sway_cursor *cursor) {
241 struct sway_container *con = seat->op_container;
242 desktop_damage_whole_container(con);
243 container_floating_translate(con,
244 cursor->cursor->x - cursor->previous.x,
245 cursor->cursor->y - cursor->previous.y);
246 desktop_damage_whole_container(con);
247}
248
249static void resize_box(struct wlr_box *box, enum wlr_edges edge,
250 int thickness) {
251 switch (edge) {
252 case WLR_EDGE_TOP:
253 box->height = thickness;
254 break;
255 case WLR_EDGE_LEFT:
256 box->width = thickness;
257 break;
258 case WLR_EDGE_RIGHT:
259 box->x = box->x + box->width - thickness;
260 box->width = thickness;
261 break;
262 case WLR_EDGE_BOTTOM:
263 box->y = box->y + box->height - thickness;
264 box->height = thickness;
265 break;
266 case WLR_EDGE_NONE:
267 box->x += thickness;
268 box->y += thickness;
269 box->width -= thickness * 2;
270 box->height -= thickness * 2;
271 break;
272 }
273}
274
275static void handle_move_tiling_motion(struct sway_seat *seat,
276 struct sway_cursor *cursor) {
277 struct wlr_surface *surface = NULL;
278 double sx, sy;
279 struct sway_node *node = node_at_coords(seat,
280 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
281 // Damage the old location
282 desktop_damage_box(&seat->op_drop_box);
283
284 if (!node) {
285 // Eg. hovered over a layer surface such as swaybar
286 seat->op_target_node = NULL;
287 seat->op_target_edge = WLR_EDGE_NONE;
288 return;
289 }
290
291 if (node->type == N_WORKSPACE) {
292 // Emtpy workspace
293 seat->op_target_node = node;
294 seat->op_target_edge = WLR_EDGE_NONE;
295 workspace_get_box(node->sway_workspace, &seat->op_drop_box);
296 desktop_damage_box(&seat->op_drop_box);
297 return;
298 }
299
300 // Deny moving within own workspace if this is the only child
301 struct sway_container *con = node->sway_container;
302 if (workspace_num_tiling_views(seat->op_container->workspace) == 1 &&
303 con->workspace == seat->op_container->workspace) {
304 seat->op_target_node = NULL;
305 seat->op_target_edge = WLR_EDGE_NONE;
306 return;
307 }
308
309 // Traverse the ancestors, trying to find a layout container perpendicular
310 // to the edge. Eg. close to the top or bottom of a horiz layout.
311 while (con) {
312 enum wlr_edges edge = WLR_EDGE_NONE;
313 enum sway_container_layout layout = container_parent_layout(con);
314 struct wlr_box parent;
315 con->parent ? container_get_box(con->parent, &parent) :
316 workspace_get_box(con->workspace, &parent);
317 if (layout == L_HORIZ || layout == L_TABBED) {
318 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
319 edge = WLR_EDGE_TOP;
320 } else if (cursor->cursor->y > parent.y + parent.height
321 - DROP_LAYOUT_BORDER) {
322 edge = WLR_EDGE_BOTTOM;
323 }
324 } else if (layout == L_VERT || layout == L_STACKED) {
325 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
326 edge = WLR_EDGE_LEFT;
327 } else if (cursor->cursor->x > parent.x + parent.width
328 - DROP_LAYOUT_BORDER) {
329 edge = WLR_EDGE_RIGHT;
330 }
331 }
332 if (edge) {
333 seat->op_target_node = node_get_parent(&con->node);
334 seat->op_target_edge = edge;
335 node_get_box(seat->op_target_node, &seat->op_drop_box);
336 resize_box(&seat->op_drop_box, edge, DROP_LAYOUT_BORDER);
337 desktop_damage_box(&seat->op_drop_box);
338 return;
339 }
340 con = con->parent;
341 }
342
343 // Use the hovered view - but we must be over the actual surface
344 con = node->sway_container;
345 if (!con->view->surface || node == &seat->op_container->node) {
346 seat->op_target_node = NULL;
347 seat->op_target_edge = WLR_EDGE_NONE;
348 return;
349 }
350
351 // Find the closest edge
352 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
353 size_t closest_dist = INT_MAX;
354 size_t dist;
355 seat->op_target_edge = WLR_EDGE_NONE;
356 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
357 closest_dist = dist;
358 seat->op_target_edge = WLR_EDGE_TOP;
359 }
360 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
361 closest_dist = dist;
362 seat->op_target_edge = WLR_EDGE_LEFT;
363 }
364 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
365 closest_dist = dist;
366 seat->op_target_edge = WLR_EDGE_RIGHT;
367 }
368 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
369 closest_dist = dist;
370 seat->op_target_edge = WLR_EDGE_BOTTOM;
371 }
372
373 if (closest_dist > thickness) {
374 seat->op_target_edge = WLR_EDGE_NONE;
375 }
376
377 seat->op_target_node = node;
378 seat->op_drop_box.x = con->content_x;
379 seat->op_drop_box.y = con->content_y;
380 seat->op_drop_box.width = con->content_width;
381 seat->op_drop_box.height = con->content_height;
382 resize_box(&seat->op_drop_box, seat->op_target_edge, thickness);
383 desktop_damage_box(&seat->op_drop_box);
384}
385
386static void calculate_floating_constraints(struct sway_container *con,
387 int *min_width, int *max_width, int *min_height, int *max_height) {
388 if (config->floating_minimum_width == -1) { // no minimum
389 *min_width = 0;
390 } else if (config->floating_minimum_width == 0) { // automatic
391 *min_width = 75;
392 } else {
393 *min_width = config->floating_minimum_width;
394 }
395
396 if (config->floating_minimum_height == -1) { // no minimum
397 *min_height = 0;
398 } else if (config->floating_minimum_height == 0) { // automatic
399 *min_height = 50;
400 } else {
401 *min_height = config->floating_minimum_height;
402 }
403
404 if (config->floating_maximum_width == -1) { // no maximum
405 *max_width = INT_MAX;
406 } else if (config->floating_maximum_width == 0) { // automatic
407 *max_width = con->workspace->width;
408 } else {
409 *max_width = config->floating_maximum_width;
410 }
411
412 if (config->floating_maximum_height == -1) { // no maximum
413 *max_height = INT_MAX;
414 } else if (config->floating_maximum_height == 0) { // automatic
415 *max_height = con->workspace->height;
416 } else {
417 *max_height = config->floating_maximum_height;
418 }
419}
420
421static void handle_resize_floating_motion(struct sway_seat *seat,
422 struct sway_cursor *cursor) {
423 struct sway_container *con = seat->op_container;
424 enum wlr_edges edge = seat->op_resize_edge;
425
426 // The amount the mouse has moved since the start of the resize operation
427 // Positive is down/right
428 double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
429 double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
430
431 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
432 mouse_move_x = 0;
433 }
434 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
435 mouse_move_y = 0;
436 }
437
438 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
439 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
440
441 if (seat->op_resize_preserve_ratio) {
442 double x_multiplier = grow_width / seat->op_ref_width;
443 double y_multiplier = grow_height / seat->op_ref_height;
444 double max_multiplier = fmax(x_multiplier, y_multiplier);
445 grow_width = seat->op_ref_width * max_multiplier;
446 grow_height = seat->op_ref_height * max_multiplier;
447 }
448
449 // Determine new width/height, and accommodate for floating min/max values
450 double width = seat->op_ref_width + grow_width;
451 double height = seat->op_ref_height + grow_height;
452 int min_width, max_width, min_height, max_height;
453 calculate_floating_constraints(con, &min_width, &max_width,
454 &min_height, &max_height);
455 width = fmax(min_width, fmin(width, max_width));
456 height = fmax(min_height, fmin(height, max_height));
457
458 // Apply the view's min/max size
459 if (con->view) {
460 double view_min_width, view_max_width, view_min_height, view_max_height;
461 view_get_constraints(con->view, &view_min_width, &view_max_width,
462 &view_min_height, &view_max_height);
463 width = fmax(view_min_width, fmin(width, view_max_width));
464 height = fmax(view_min_height, fmin(height, view_max_height));
465 }
466
467 // Recalculate these, in case we hit a min/max limit
468 grow_width = width - seat->op_ref_width;
469 grow_height = height - seat->op_ref_height;
470
471 // Determine grow x/y values - these are relative to the container's x/y at
472 // the start of the resize operation.
473 double grow_x = 0, grow_y = 0;
474 if (edge & WLR_EDGE_LEFT) {
475 grow_x = -grow_width;
476 } else if (edge & WLR_EDGE_RIGHT) {
477 grow_x = 0;
478 } else {
479 grow_x = -grow_width / 2;
480 }
481 if (edge & WLR_EDGE_TOP) {
482 grow_y = -grow_height;
483 } else if (edge & WLR_EDGE_BOTTOM) {
484 grow_y = 0;
485 } else {
486 grow_y = -grow_height / 2;
487 }
488
489 // Determine the amounts we need to bump everything relative to the current
490 // size.
491 int relative_grow_width = width - con->width;
492 int relative_grow_height = height - con->height;
493 int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
494 int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
495
496 // Actually resize stuff
497 con->x += relative_grow_x;
498 con->y += relative_grow_y;
499 con->width += relative_grow_width;
500 con->height += relative_grow_height;
501
502 con->content_x += relative_grow_x;
503 con->content_y += relative_grow_y;
504 con->content_width += relative_grow_width;
505 con->content_height += relative_grow_height;
506
507 arrange_container(con);
508}
509
510static void handle_resize_tiling_motion(struct sway_seat *seat,
511 struct sway_cursor *cursor) {
512 int amount_x = 0;
513 int amount_y = 0;
514 int moved_x = cursor->cursor->x - seat->op_ref_lx;
515 int moved_y = cursor->cursor->y - seat->op_ref_ly;
516 enum wlr_edges edge_x = WLR_EDGE_NONE;
517 enum wlr_edges edge_y = WLR_EDGE_NONE;
518 struct sway_container *con = seat->op_container;
519
520 if (seat->op_resize_edge & WLR_EDGE_TOP) {
521 amount_y = (seat->op_ref_height - moved_y) - con->height;
522 edge_y = WLR_EDGE_TOP;
523 } else if (seat->op_resize_edge & WLR_EDGE_BOTTOM) {
524 amount_y = (seat->op_ref_height + moved_y) - con->height;
525 edge_y = WLR_EDGE_BOTTOM;
526 }
527 if (seat->op_resize_edge & WLR_EDGE_LEFT) {
528 amount_x = (seat->op_ref_width - moved_x) - con->width;
529 edge_x = WLR_EDGE_LEFT;
530 } else if (seat->op_resize_edge & WLR_EDGE_RIGHT) {
531 amount_x = (seat->op_ref_width + moved_x) - con->width;
532 edge_x = WLR_EDGE_RIGHT;
533 }
534
535 if (amount_x != 0) {
536 container_resize_tiled(seat->op_container, edge_x, amount_x);
537 }
538 if (amount_y != 0) {
539 container_resize_tiled(seat->op_container, edge_y, amount_y);
540 }
541}
542
543static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, 225static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
544 struct sway_node *node, struct wlr_surface *surface, 226 struct sway_node *node, struct wlr_surface *surface,
545 double sx, double sy) { 227 double sx, double sy) {
@@ -589,6 +271,50 @@ void cursor_rebase(struct sway_cursor *cursor) {
589 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); 271 cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy);
590} 272}
591 273
274static int hide_notify(void *data) {
275 struct sway_cursor *cursor = data;
276 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
277 cursor->hidden = true;
278 return 1;
279}
280
281int cursor_get_timeout(struct sway_cursor *cursor){
282 struct seat_config *sc = seat_get_config(cursor->seat);
283 if (!sc) {
284 sc = seat_get_config_by_name("*");
285 }
286 int timeout = sc ? sc->hide_cursor_timeout : 0;
287 if (timeout < 0) {
288 timeout = 0;
289 }
290 return timeout;
291}
292
293void cursor_handle_activity(struct sway_cursor *cursor) {
294 wl_event_source_timer_update(
295 cursor->hide_source, cursor_get_timeout(cursor));
296
297 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
298 if (cursor->hidden) {
299 cursor_unhide(cursor);
300 }
301}
302
303void cursor_unhide(struct sway_cursor *cursor) {
304 cursor->hidden = false;
305 if (cursor->image_surface) {
306 cursor_set_image_surface(cursor,
307 cursor->image_surface,
308 cursor->hotspot_x,
309 cursor->hotspot_y,
310 cursor->image_client);
311 } else {
312 const char *image = cursor->image;
313 cursor->image = NULL;
314 cursor_set_image(cursor, image, cursor->image_client);
315 }
316}
317
592void cursor_send_pointer_motion(struct sway_cursor *cursor, 318void cursor_send_pointer_motion(struct sway_cursor *cursor,
593 uint32_t time_msec) { 319 uint32_t time_msec) {
594 if (time_msec == 0) { 320 if (time_msec == 0) {
@@ -598,26 +324,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
598 struct sway_seat *seat = cursor->seat; 324 struct sway_seat *seat = cursor->seat;
599 struct wlr_seat *wlr_seat = seat->wlr_seat; 325 struct wlr_seat *wlr_seat = seat->wlr_seat;
600 326
601 if (seat->operation != OP_NONE) { 327 if (seat_doing_seatop(seat)) {
602 switch (seat->operation) { 328 seatop_motion(seat, time_msec);
603 case OP_DOWN:
604 handle_down_motion(seat, cursor, time_msec);
605 break;
606 case OP_MOVE_FLOATING:
607 handle_move_floating_motion(seat, cursor);
608 break;
609 case OP_MOVE_TILING:
610 handle_move_tiling_motion(seat, cursor);
611 break;
612 case OP_RESIZE_FLOATING:
613 handle_resize_floating_motion(seat, cursor);
614 break;
615 case OP_RESIZE_TILING:
616 handle_resize_tiling_motion(seat, cursor);
617 break;
618 case OP_NONE:
619 break;
620 }
621 cursor->previous.x = cursor->cursor->x; 329 cursor->previous.x = cursor->cursor->x;
622 cursor->previous.y = cursor->cursor->y; 330 cursor->previous.y = cursor->cursor->y;
623 return; 331 return;
@@ -679,11 +387,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
679 387
680static void handle_cursor_motion(struct wl_listener *listener, void *data) { 388static void handle_cursor_motion(struct wl_listener *listener, void *data) {
681 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 389 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
682 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
683 struct wlr_event_pointer_motion *event = data; 390 struct wlr_event_pointer_motion *event = data;
684 wlr_cursor_move(cursor->cursor, event->device, 391 wlr_cursor_move(cursor->cursor, event->device,
685 event->delta_x, event->delta_y); 392 event->delta_x, event->delta_y);
686 cursor_send_pointer_motion(cursor, event->time_msec); 393 cursor_send_pointer_motion(cursor, event->time_msec);
394 cursor_handle_activity(cursor);
687 transaction_commit_dirty(); 395 transaction_commit_dirty();
688} 396}
689 397
@@ -691,10 +399,10 @@ static void handle_cursor_motion_absolute(
691 struct wl_listener *listener, void *data) { 399 struct wl_listener *listener, void *data) {
692 struct sway_cursor *cursor = 400 struct sway_cursor *cursor =
693 wl_container_of(listener, cursor, motion_absolute); 401 wl_container_of(listener, cursor, motion_absolute);
694 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
695 struct wlr_event_pointer_motion_absolute *event = data; 402 struct wlr_event_pointer_motion_absolute *event = data;
696 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 403 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
697 cursor_send_pointer_motion(cursor, event->time_msec); 404 cursor_send_pointer_motion(cursor, event->time_msec);
405 cursor_handle_activity(cursor);
698 transaction_commit_dirty(); 406 transaction_commit_dirty();
699} 407}
700 408
@@ -794,11 +502,16 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
794 struct sway_seat *seat = cursor->seat; 502 struct sway_seat *seat = cursor->seat;
795 503
796 // Handle existing seat operation 504 // Handle existing seat operation
797 if (cursor->seat->operation != OP_NONE) { 505 if (seat_doing_seatop(seat)) {
798 if (button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { 506 if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) {
799 seat_end_mouse_operation(seat); 507 seatop_finish(seat);
800 seat_pointer_notify_button(seat, time_msec, button, state); 508 seat_pointer_notify_button(seat, time_msec, button, state);
801 } 509 }
510 if (state == WLR_BUTTON_PRESSED) {
511 state_add_button(cursor, button);
512 } else {
513 state_erase_button(cursor, button);
514 }
802 return; 515 return;
803 } 516 }
804 517
@@ -864,7 +577,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
864 if (cont && resize_edge && button == BTN_LEFT && 577 if (cont && resize_edge && button == BTN_LEFT &&
865 state == WLR_BUTTON_PRESSED && !is_floating) { 578 state == WLR_BUTTON_PRESSED && !is_floating) {
866 seat_set_focus_container(seat, cont); 579 seat_set_focus_container(seat, cont);
867 seat_begin_resize_tiling(seat, cont, button, edge); 580 seatop_begin_resize_tiling(seat, cont, button, edge);
868 return; 581 return;
869 } 582 }
870 583
@@ -894,7 +607,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
894 } 607 }
895 cursor_set_image(seat->cursor, image, NULL); 608 cursor_set_image(seat->cursor, image, NULL);
896 seat_set_focus_container(seat, cont); 609 seat_set_focus_container(seat, cont);
897 seat_begin_resize_tiling(seat, cont, button, edge); 610 seatop_begin_resize_tiling(seat, cont, button, edge);
898 return; 611 return;
899 } 612 }
900 } 613 }
@@ -909,7 +622,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
909 cont = cont->parent; 622 cont = cont->parent;
910 } 623 }
911 seat_set_focus_container(seat, cont); 624 seat_set_focus_container(seat, cont);
912 seat_begin_move_floating(seat, cont, button); 625 seatop_begin_move_floating(seat, cont, button);
913 return; 626 return;
914 } 627 }
915 } 628 }
@@ -919,7 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
919 state == WLR_BUTTON_PRESSED) { 632 state == WLR_BUTTON_PRESSED) {
920 // Via border 633 // Via border
921 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 634 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
922 seat_begin_resize_floating(seat, cont, button, resize_edge); 635 seatop_begin_resize_floating(seat, cont, button, resize_edge);
923 return; 636 return;
924 } 637 }
925 638
@@ -936,16 +649,30 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
936 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 649 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
937 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 650 edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
938 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 651 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
939 seat_begin_resize_floating(seat, floater, button, edge); 652 seatop_begin_resize_floating(seat, floater, button, edge);
940 return; 653 return;
941 } 654 }
942 } 655 }
943 656
944 // Handle moving a tiling container 657 // Handle moving a tiling container
945 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && 658 if (config->tiling_drag && (mod_pressed || on_titlebar) &&
946 !is_floating_or_child && cont && !cont->is_fullscreen) { 659 state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
660 cont && !cont->is_fullscreen) {
661 struct sway_container *focus = seat_get_focused_container(seat);
662 bool focused = focus == cont || container_has_ancestor(focus, cont);
663 if (on_titlebar && !focused) {
664 node = seat_get_focus_inactive(seat, &cont->node);
665 seat_set_focus(seat, node);
666 }
667
947 seat_pointer_notify_button(seat, time_msec, button, state); 668 seat_pointer_notify_button(seat, time_msec, button, state);
948 seat_begin_move_tiling(seat, cont, button); 669
670 // If moving a container by it's title bar, use a threshold for the drag
671 if (!mod_pressed && config->tiling_drag_threshold > 0) {
672 seatop_begin_move_tiling_threshold(seat, cont, button);
673 } else {
674 seatop_begin_move_tiling(seat, cont, button);
675 }
949 return; 676 return;
950 } 677 }
951 678
@@ -953,7 +680,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
953 if (surface && cont && state == WLR_BUTTON_PRESSED) { 680 if (surface && cont && state == WLR_BUTTON_PRESSED) {
954 seat_set_focus_container(seat, cont); 681 seat_set_focus_container(seat, cont);
955 seat_pointer_notify_button(seat, time_msec, button, state); 682 seat_pointer_notify_button(seat, time_msec, button, state);
956 seat_begin_down(seat, cont, button, sx, sy); 683 seatop_begin_down(seat, cont, button, sx, sy);
957 return; 684 return;
958 } 685 }
959 686
@@ -970,18 +697,32 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
970 697
971static void handle_cursor_button(struct wl_listener *listener, void *data) { 698static void handle_cursor_button(struct wl_listener *listener, void *data) {
972 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 699 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
973 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
974 struct wlr_event_pointer_button *event = data; 700 struct wlr_event_pointer_button *event = data;
975 dispatch_cursor_button(cursor, event->device, 701 dispatch_cursor_button(cursor, event->device,
976 event->time_msec, event->button, event->state); 702 event->time_msec, event->button, event->state);
703 cursor_handle_activity(cursor);
977 transaction_commit_dirty(); 704 transaction_commit_dirty();
978} 705}
979 706
980static void dispatch_cursor_axis(struct sway_cursor *cursor, 707static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
708 switch (event->orientation) {
709 case WLR_AXIS_ORIENTATION_VERTICAL:
710 return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
711 case WLR_AXIS_ORIENTATION_HORIZONTAL:
712 return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT;
713 default:
714 wlr_log(WLR_DEBUG, "Unknown axis orientation");
715 return 0;
716 }
717}
718
719void dispatch_cursor_axis(struct sway_cursor *cursor,
981 struct wlr_event_pointer_axis *event) { 720 struct wlr_event_pointer_axis *event) {
982 struct sway_seat *seat = cursor->seat; 721 struct sway_seat *seat = cursor->seat;
983 struct sway_input_device *input_device = event->device->data; 722 struct sway_input_device *input_device =
984 struct input_config *ic = input_device_get_config(input_device); 723 event->device ? event->device->data : NULL;
724 struct input_config *ic =
725 input_device ? input_device_get_config(input_device) : NULL;
985 726
986 // Determine what's under the cursor 727 // Determine what's under the cursor
987 struct wlr_surface *surface = NULL; 728 struct wlr_surface *surface = NULL;
@@ -993,11 +734,35 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
993 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; 734 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
994 bool on_border = edge != WLR_EDGE_NONE; 735 bool on_border = edge != WLR_EDGE_NONE;
995 bool on_titlebar = cont && !on_border && !surface; 736 bool on_titlebar = cont && !on_border && !surface;
737 bool on_titlebar_border = cont && on_border &&
738 cursor->cursor->y < cont->content_y;
739 bool on_contents = cont && !on_border && surface;
996 float scroll_factor = 740 float scroll_factor =
997 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; 741 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
998 742
999 // Scrolling on a tabbed or stacked title bar 743 bool handled = false;
1000 if (on_titlebar) { 744
745 // Gather information needed for mouse bindings
746 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
747 uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
748 struct wlr_input_device *device =
749 input_device ? input_device->wlr_device : NULL;
750 char *dev_id = device ? input_device_get_identifier(device) : strdup("*");
751 uint32_t button = wl_axis_to_button(event);
752
753 // Handle mouse bindings - x11 mouse buttons 4-7 - press event
754 struct sway_binding *binding = NULL;
755 state_add_button(cursor, button);
756 binding = get_active_mouse_binding(cursor,
757 config->current_mode->mouse_bindings, modifiers, false,
758 on_titlebar, on_border, on_contents, dev_id);
759 if (binding) {
760 seat_execute_command(seat, binding);
761 handled = true;
762 }
763
764 // Scrolling on a tabbed or stacked title bar (handled as press event)
765 if (!handled && (on_titlebar || on_titlebar_border)) {
1001 enum sway_container_layout layout = container_parent_layout(cont); 766 enum sway_container_layout layout = container_parent_layout(cont);
1002 if (layout == L_TABBED || layout == L_STACKED) { 767 if (layout == L_TABBED || layout == L_STACKED) {
1003 struct sway_node *tabcontainer = node_get_parent(node); 768 struct sway_node *tabcontainer = node_get_parent(node);
@@ -1024,20 +789,33 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1024 seat_set_raw_focus(seat, new_focus); 789 seat_set_raw_focus(seat, new_focus);
1025 seat_set_raw_focus(seat, old_focus); 790 seat_set_raw_focus(seat, old_focus);
1026 } 791 }
1027 return; 792 handled = true;
1028 } 793 }
1029 } 794 }
1030 795
1031 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 796 // Handle mouse bindings - x11 mouse buttons 4-7 - release event
1032 event->orientation, scroll_factor * event->delta, 797 binding = get_active_mouse_binding(cursor,
1033 round(scroll_factor * event->delta_discrete), event->source); 798 config->current_mode->mouse_bindings, modifiers, true,
799 on_titlebar, on_border, on_contents, dev_id);
800 state_erase_button(cursor, button);
801 if (binding) {
802 seat_execute_command(seat, binding);
803 handled = true;
804 }
805 free(dev_id);
806
807 if (!handled) {
808 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
809 event->orientation, scroll_factor * event->delta,
810 round(scroll_factor * event->delta_discrete), event->source);
811 }
1034} 812}
1035 813
1036static void handle_cursor_axis(struct wl_listener *listener, void *data) { 814static void handle_cursor_axis(struct wl_listener *listener, void *data) {
1037 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 815 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
1038 wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat);
1039 struct wlr_event_pointer_axis *event = data; 816 struct wlr_event_pointer_axis *event = data;
1040 dispatch_cursor_axis(cursor, event); 817 dispatch_cursor_axis(cursor, event);
818 cursor_handle_activity(cursor);
1041 transaction_commit_dirty(); 819 transaction_commit_dirty();
1042} 820}
1043 821
@@ -1209,7 +987,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1209 void *data) { 987 void *data) {
1210 struct sway_cursor *cursor = 988 struct sway_cursor *cursor =
1211 wl_container_of(listener, cursor, request_set_cursor); 989 wl_container_of(listener, cursor, request_set_cursor);
1212 if (cursor->seat->operation != OP_NONE) { 990 if (seat_doing_seatop(cursor->seat)) {
1213 return; 991 return;
1214 } 992 }
1215 struct wlr_seat_pointer_request_set_cursor_event *event = data; 993 struct wlr_seat_pointer_request_set_cursor_event *event = data;
@@ -1228,10 +1006,8 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1228 return; 1006 return;
1229 } 1007 }
1230 1008
1231 wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, 1009 cursor_set_image_surface(cursor, event->surface, event->hotspot_x,
1232 event->hotspot_y); 1010 event->hotspot_y, focused_client);
1233 cursor->image = NULL;
1234 cursor->image_client = focused_client;
1235} 1011}
1236 1012
1237void cursor_set_image(struct sway_cursor *cursor, const char *image, 1013void cursor_set_image(struct sway_cursor *cursor, const char *image,
@@ -1239,14 +1015,43 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image,
1239 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) { 1015 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
1240 return; 1016 return;
1241 } 1017 }
1018
1019 const char *current_image = cursor->image;
1020 cursor->image = image;
1021 cursor->image_surface = NULL;
1022 cursor->hotspot_x = cursor->hotspot_y = 0;
1023 cursor->image_client = client;
1024
1025 if (cursor->hidden) {
1026 return;
1027 }
1028
1242 if (!image) { 1029 if (!image) {
1243 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); 1030 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1244 } else if (!cursor->image || strcmp(cursor->image, image) != 0) { 1031 } else if (!current_image || strcmp(current_image, image) != 0) {
1245 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 1032 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
1246 cursor->cursor); 1033 cursor->cursor);
1247 } 1034 }
1248 cursor->image = image; 1035}
1036
1037void cursor_set_image_surface(struct sway_cursor *cursor,
1038 struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y,
1039 struct wl_client *client) {
1040 if (!(cursor->seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER)) {
1041 return;
1042 }
1043
1044 cursor->image = NULL;
1045 cursor->image_surface = surface;
1046 cursor->hotspot_x = hotspot_x;
1047 cursor->hotspot_y = hotspot_y;
1249 cursor->image_client = client; 1048 cursor->image_client = client;
1049
1050 if (cursor->hidden) {
1051 return;
1052 }
1053
1054 wlr_cursor_set_surface(cursor->cursor, surface, hotspot_x, hotspot_y);
1250} 1055}
1251 1056
1252void sway_cursor_destroy(struct sway_cursor *cursor) { 1057void sway_cursor_destroy(struct sway_cursor *cursor) {
@@ -1254,6 +1059,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
1254 return; 1059 return;
1255 } 1060 }
1256 1061
1062 wl_event_source_remove(cursor->hide_source);
1063
1257 wlr_xcursor_manager_destroy(cursor->xcursor_manager); 1064 wlr_xcursor_manager_destroy(cursor->xcursor_manager);
1258 wlr_cursor_destroy(cursor->cursor); 1065 wlr_cursor_destroy(cursor->cursor);
1259 free(cursor); 1066 free(cursor);
@@ -1277,6 +1084,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1277 cursor->seat = seat; 1084 cursor->seat = seat;
1278 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); 1085 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);
1279 1086
1087 cursor->hide_source = wl_event_loop_add_timer(server.wl_event_loop,
1088 hide_notify, cursor);
1089
1280 // input events 1090 // input events
1281 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1091 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
1282 cursor->motion.notify = handle_cursor_motion; 1092 cursor->motion.notify = handle_cursor_motion;
@@ -1362,3 +1172,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor,
1362 1172
1363 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1173 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1364} 1174}
1175
1176uint32_t get_mouse_bindsym(const char *name, char **error) {
1177 if (strncasecmp(name, "button", strlen("button")) == 0) {
1178 // Map to x11 mouse buttons
1179 int number = name[strlen("button")] - '0';
1180 if (number < 1 || number > 9 || strlen(name) > strlen("button0")) {
1181 *error = strdup("Only buttons 1-9 are supported. For other mouse "
1182 "buttons, use the name of the event code.");
1183 return 0;
1184 }
1185 static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
1186 SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,
1187 SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};
1188 return buttons[number - 1];
1189 } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) {
1190 // Get event code from name
1191 int code = libevdev_event_code_from_name(EV_KEY, name);
1192 if (code == -1) {
1193 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1;
1194 *error = malloc(len);
1195 if (*error) {
1196 snprintf(*error, len, "Unknown event %s", name);
1197 }
1198 return 0;
1199 }
1200 return code;
1201 }
1202 return 0;
1203}
1204
1205uint32_t get_mouse_bindcode(const char *name, char **error) {
1206 // Validate event code
1207 errno = 0;
1208 char *endptr;
1209 int code = strtol(name, &endptr, 10);
1210 if (endptr == name && code <= 0) {
1211 *error = strdup("Button event code must be a positive integer.");
1212 return 0;
1213 } else if (errno == ERANGE) {
1214 *error = strdup("Button event code out of range.");
1215 return 0;
1216 }
1217 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1218 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1219 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button",
1220 code, event) + 1;
1221 *error = malloc(len);
1222 if (*error) {
1223 snprintf(*error, len, "Event code %d (%s) is not a button",
1224 code, event);
1225 }
1226 return 0;
1227 }
1228 return code;
1229}
1230
1231uint32_t get_mouse_button(const char *name, char **error) {
1232 uint32_t button = get_mouse_bindsym(name, error);
1233 if (!button && !*error) {
1234 button = get_mouse_bindcode(name, error);
1235 }
1236 return button;
1237}