-rw-r--r-- src/cursor.c
1 #include <inttypes.h> 2 #include <linux/input-event-codes.h> 3 #include <unistd.h> 4 #include <wlr/types/wlr_cursor.h> 5 #include <wlr/types/wlr_layer_shell_v1.h> 6 #include <wlr/types/wlr_pointer_gestures_v1.h> 7 #include <wlr/types/wlr_xcursor_manager.h> 8 9 #include "action.h" 10 #include "cursor.h" 11 #include "shell.h" 12 #include "types.h" 13 14 #define SNAP_WIDTH 42 15 16 17 bool try_snap() { 18 double x = wimp.cursor->x; 19 double y = wimp.cursor->y; 20 struct wlr_box snap_to; 21 bool can_snap = false; 22 struct wlr_output *output = wlr_output_layout_output_at(wimp.output_layout, x, y); 23 struct wlr_box *outgeo = wlr_output_layout_get_box(wimp.output_layout, output); 24 25 if (x < SNAP_WIDTH) { 26 can_snap = true; 27 snap_to.x = outgeo->x; 28 snap_to.width = outgeo->width / 2; 29 if (y <= outgeo->height / 3) { 30 snap_to.y = outgeo->y; 31 snap_to.height = outgeo->height / 2; 32 } else if (y <= 2 * outgeo->height / 3) { 33 snap_to.y = outgeo->y; 34 snap_to.height = outgeo->height; 35 } else { 36 snap_to.y = outgeo->y + outgeo->height / 2; 37 snap_to.height = outgeo->height / 2; 38 } 39 } 40 41 else if (x > outgeo->width - SNAP_WIDTH) { 42 can_snap = true; 43 snap_to.x = outgeo->width / 2; 44 snap_to.width = outgeo->width / 2; 45 if (y <= outgeo->height / 3) { 46 snap_to.y = outgeo->y; 47 snap_to.height = outgeo->height / 2; 48 } else if (y <= 2 * outgeo->height / 3) { 49 snap_to.y = outgeo->y; 50 snap_to.height = outgeo->height; 51 } else { 52 snap_to.y = outgeo->y + outgeo->height / 2; 53 snap_to.height = outgeo->height / 2; 54 } 55 } 56 57 else if (y < SNAP_WIDTH) { 58 can_snap = true; 59 snap_to.y = outgeo->y; 60 snap_to.height = outgeo->height / 2; 61 if (x <= outgeo->width / 3) { 62 snap_to.x = outgeo->x; 63 snap_to.width = outgeo->width / 2; 64 } else if (x < 2 * outgeo->width / 3) { 65 snap_to.x = outgeo->x; 66 snap_to.width = outgeo->width; 67 } else { 68 snap_to.x = outgeo->x + outgeo->width / 2; 69 snap_to.width = outgeo->width / 2; 70 } 71 } 72 73 else if (y > outgeo->height - SNAP_WIDTH) { 74 can_snap = true; 75 snap_to.y = outgeo->height / 2; 76 snap_to.height = outgeo->height / 2; 77 if (x <= outgeo->width / 3) { 78 snap_to.x = outgeo->x; 79 snap_to.width = outgeo->width / 2; 80 } else if (x <= 2 * outgeo->width / 3) { 81 snap_to.x = outgeo->x; 82 snap_to.width = outgeo->width; 83 } else { 84 snap_to.x = outgeo->x + outgeo->width / 2; 85 snap_to.width = outgeo->width / 2; 86 } 87 } 88 89 wimp.snap_geobox = snap_to; 90 return can_snap; 91 } 92 93 94 void centre_cursor() { 95 struct wlr_box *extents = wlr_output_layout_get_box(wimp.output_layout, NULL); 96 double cx, cy; 97 wlr_output_layout_closest_point( 98 wimp.output_layout, NULL, extents->width / 2, extents->height / 2, &cx, &cy 99 ); 100 wlr_cursor_warp_closest(wimp.cursor, NULL, cx, cy); 101 } 102 103 104 static void on_request_cursor(struct wl_listener *listener, void *data) { 105 struct wlr_seat_pointer_request_set_cursor_event *event = data; 106 if (wimp.seat->pointer_state.focused_client == event->seat_client) { 107 wlr_cursor_set_surface( 108 wimp.cursor, event->surface, event->hotspot_x, event->hotspot_y 109 ); 110 } 111 } 112 113 114 static void process_cursor_move(uint32_t time, double zoom) { 115 wimp.grabbed_view->x = (wimp.cursor->x - wimp.grab_x) / zoom; 116 wimp.grabbed_view->y = (wimp.cursor->y - wimp.grab_y) / zoom; 117 } 118 119 120 static void process_cursor_resize(uint32_t time, double zoom) { 121 struct view *view = wimp.grabbed_view; 122 double cx = (wimp.cursor->x - wimp.grab_x) / zoom; 123 double cy = (wimp.cursor->y - wimp.grab_y) / zoom; 124 struct wlr_box new; 125 memcpy(&new, &wimp.grab_geobox, sizeof(struct wlr_box)); 126 127 if (wimp.resize_edges & WLR_EDGE_TOP) { 128 new.height -= cy - new.y; 129 new.y = cy; 130 } else if (wimp.resize_edges & WLR_EDGE_BOTTOM) { 131 new.height += cy - new.y; 132 } 133 if (wimp.resize_edges & WLR_EDGE_LEFT) { 134 new.width -= cx - new.x; 135 new.x = cx; 136 } else if (wimp.resize_edges & WLR_EDGE_RIGHT) { 137 new.width += cx - new.x; 138 } 139 140 view_apply_geometry(view, &new); 141 } 142 143 144 void *under_pointer(struct wlr_surface **surface, double *sx, double *sy, bool *is_layer) { 145 double x = wimp.cursor->x; 146 double y = wimp.cursor->y; 147 double tsx, tsy; 148 struct view *view; 149 struct layer_view *lview; 150 struct wlr_surface *tsurface; 151 struct wlr_output *wlr_output = wlr_output_layout_output_at(wimp.output_layout, x, y); 152 struct output *output = wlr_output->data; 153 int border_width = wimp.current_desk->border_width; 154 155 // overlay and top layers 156 uint32_t above[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP }; 157 size_t nlayers = sizeof(above) / sizeof(above[0]); 158 for (size_t i = 0; i < nlayers; i++) { 159 wl_list_for_each(lview, &output->layer_views[above[i]], link) { 160 tsurface = wlr_layer_surface_v1_surface_at( 161 lview->surface, x - lview->geo.x, y - lview->geo.y, &tsx, &tsy 162 ); 163 if (tsurface) { 164 *sx = tsx; 165 *sy = tsy; 166 *surface = tsurface; 167 *is_layer = true; 168 return lview; 169 } 170 } 171 } 172 173 // scratchpads 174 struct scratchpad *scratchpad; 175 wl_list_for_each(scratchpad, &wimp.scratchpads, link) { 176 if (scratchpad->is_mapped) { 177 view = scratchpad->view; 178 tsurface = wlr_xdg_surface_surface_at( 179 view->surface, x - view->x, y - view->y, &tsx, &tsy 180 ); 181 if (tsurface) { 182 *sx = tsx; 183 *sy = tsy; 184 *surface = tsurface; 185 *is_layer = false; 186 return view; 187 } 188 } 189 } 190 191 // clients 192 double zoom = wimp.current_desk->zoom; 193 double zx = x / zoom; 194 double zy = y / zoom; 195 196 wl_list_for_each(view, &wimp.current_desk->views, link) { 197 tsurface = wlr_xdg_surface_surface_at( 198 view->surface, zx - view->x, zy - view->y, &tsx, &tsy 199 ); 200 if (tsurface) { 201 *sx = tsx; 202 *sy = tsy; 203 *surface = tsurface; 204 *is_layer = false; 205 return view; 206 } 207 if (border_width) { 208 struct wlr_box bordered = { 209 .x = (view->x - border_width) * zoom, 210 .y = (view->y - border_width) * zoom, 211 .width = (view->surface->geometry.width + border_width * 2) * zoom, 212 .height = (view->surface->geometry.height + border_width * 2) * zoom, 213 }; 214 if (wlr_box_contains_point(&bordered, zx, zy)) { 215 *sx = tsx; 216 *sy = tsy; 217 *surface = view->surface->surface; 218 *is_layer = false; 219 return view; 220 } 221 } 222 } 223 224 // bottom and background layers 225 uint32_t below[] = { ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND }; 226 for (size_t i = 0; i < nlayers; i++) { 227 wl_list_for_each(lview, &output->layer_views[below[i]], link) { 228 tsurface = wlr_layer_surface_v1_surface_at( 229 lview->surface, x - lview->geo.x, y - lview->geo.y, &tsx, &tsy 230 ); 231 if (tsurface) { 232 *sx = tsx; 233 *sy = tsy; 234 *surface = tsurface; 235 *is_layer = true; 236 return lview; 237 } 238 } 239 } 240 241 return NULL; 242 } 243 244 245 static enum wlr_edges pointer_in_view_corner(struct view *view) { 246 /* It is assumed that the pointer is above the view. */ 247 double x = wimp.cursor->x; 248 double y = wimp.cursor->y; 249 double zoom = wimp.current_desk->zoom; 250 int border_width = wimp.current_desk->border_width; 251 enum wlr_edges edges = WLR_EDGE_NONE; 252 253 struct wlr_box inner = { 254 .x = view->x * zoom, 255 .y = view->y * zoom, 256 .width = view->surface->geometry.width * zoom, 257 .height = view->surface->geometry.height * zoom, 258 }; 259 struct wlr_box outer = { 260 .x = inner.x - border_width * zoom, 261 .y = inner.y - border_width * zoom, 262 .width = inner.width + border_width * 2 * zoom, 263 .height = inner.height + border_width * 2 * zoom, 264 }; 265 266 if (x <= inner.x) { 267 edges |= WLR_EDGE_LEFT; 268 if (y <= outer.y + CORNER) { 269 edges |= WLR_EDGE_TOP; 270 } else if (outer.y + outer.height - CORNER <= y) { 271 edges |= WLR_EDGE_BOTTOM; 272 } 273 return edges; 274 } 275 if (inner.x + inner.width <= x) { 276 edges |= WLR_EDGE_RIGHT; 277 if (y <= outer.y + CORNER) { 278 edges |= WLR_EDGE_TOP; 279 } else if (outer.y + outer.height - CORNER <= y) { 280 edges |= WLR_EDGE_BOTTOM; 281 } 282 return edges; 283 } 284 if (y <= inner.y) { 285 edges |= WLR_EDGE_TOP; 286 if (x <= outer.x + CORNER) { 287 edges |= WLR_EDGE_LEFT; 288 } else if (outer.x + outer.width - CORNER <= x) { 289 edges |= WLR_EDGE_RIGHT; 290 } 291 return edges; 292 } 293 if (inner.y + inner.height <= y) { 294 edges |= WLR_EDGE_BOTTOM; 295 if (x <= outer.x + CORNER) { 296 edges |= WLR_EDGE_LEFT; 297 } else if (outer.x + outer.width - CORNER <= x) { 298 edges |= WLR_EDGE_RIGHT; 299 } 300 return edges; 301 } 302 return edges; 303 } 304 305 306 static void process_cursor_motion(uint32_t time, double dx, double dy) { 307 double sx, sy; 308 struct wlr_seat *seat; 309 struct wlr_surface *surface; 310 double zoom = wimp.current_desk->zoom; 311 312 switch (wimp.cursor_mode) { 313 case CURSOR_PASSTHROUGH: 314 seat = wimp.seat; 315 surface = NULL; 316 bool is_layer; 317 struct view *view = under_pointer(&surface, &sx, &sy, &is_layer); 318 if (!view) { 319 wlr_xcursor_manager_set_cursor_image(wimp.cursor_manager, "left_ptr", wimp.cursor); 320 } 321 if (surface) { 322 wlr_seat_pointer_notify_enter(seat, surface, sx, sy); 323 if (seat->pointer_state.focused_surface == surface) { 324 wlr_seat_pointer_notify_motion(seat, time, sx, sy); 325 } 326 if (wimp.auto_focus && seat->keyboard_state.focused_surface != surface) { 327 focus(view, surface, is_layer); 328 } 329 } else { 330 wlr_seat_pointer_clear_focus(seat); 331 } 332 break; 333 334 case CURSOR_MOD: 335 if (wimp.on_mouse_motion) { 336 struct motion motion = { 337 .dx = dx / zoom, 338 .dy = dy / zoom, 339 .is_percentage = false, 340 }; 341 wimp.on_mouse_motion(&motion); 342 } 343 if (wimp.grabbed_view) { 344 wimp.can_snap = try_snap(); 345 } 346 break; 347 348 case CURSOR_MOVE: 349 process_cursor_move(time, zoom); 350 break; 351 352 case CURSOR_RESIZE: 353 process_cursor_resize(time, zoom); 354 break; 355 } 356 } 357 358 359 static void on_cursor_motion(struct wl_listener *listener, void *data){ 360 struct wlr_event_pointer_motion *event = data; 361 if (wimp.resize_edges) { 362 if (wimp.resize_edges & WLR_EDGE_TOP) { 363 if (wimp.grabbed_view->surface->geometry.height <= CORNER && event->delta_y > 0) { 364 event->delta_y = 0; 365 } 366 } else if (wimp.resize_edges & WLR_EDGE_BOTTOM) { 367 if (wimp.grabbed_view->surface->geometry.height <= CORNER && event->delta_y < 0) { 368 event->delta_y = 0; 369 } 370 } 371 if (wimp.resize_edges & WLR_EDGE_LEFT) { 372 if (wimp.grabbed_view->surface->geometry.width <= CORNER && event->delta_x > 0) { 373 event->delta_x = 0; 374 } 375 } else if (wimp.resize_edges & WLR_EDGE_RIGHT) { 376 if (wimp.grabbed_view->surface->geometry.width <= CORNER && event->delta_x < 0) { 377 event->delta_x = 0; 378 } 379 } 380 } 381 382 double x = wimp.cursor->x; 383 double y = wimp.cursor->y; 384 wlr_cursor_move(wimp.cursor, event->device, event->delta_x, event->delta_y); 385 if (x == wimp.cursor->x) { 386 event->delta_x = 0; 387 } 388 if (y == wimp.cursor->y) { 389 event->delta_y = 0; 390 } 391 392 process_cursor_motion(event->time_msec, event->delta_x, event->delta_y); 393 } 394 395 396 static void on_cursor_motion_absolute(struct wl_listener *listener, void *data) { 397 struct wlr_event_pointer_motion_absolute *event = data; 398 double lx, ly; 399 wlr_cursor_absolute_to_layout_coords( 400 wimp.cursor, event->device, event->x, event->y, &lx, &ly 401 ); 402 403 struct wlr_event_pointer_motion relative = { 404 .device = event->device, 405 .delta_x = lx - wimp.cursor->x, 406 .delta_y = ly - wimp.cursor->y, 407 }; 408 on_cursor_motion(NULL, &relative); 409 } 410 411 412 static void on_cursor_button(struct wl_listener *listener, void *data) { 413 struct wlr_event_pointer_button *event = data; 414 wlr_seat_pointer_notify_button(wimp.seat, event->time_msec, event->button, event->state); 415 double sx, sy; 416 struct wlr_surface *surface; 417 bool is_layer; 418 419 if (event->state == WLR_BUTTON_RELEASED) { 420 if (wimp.cursor_mode == CURSOR_MOD) { 421 wimp.on_mouse_motion = NULL; 422 struct binding *kb; 423 wl_list_for_each(kb, &wimp.mouse_bindings, link) { 424 if (kb->key == MOTION) { 425 wimp.on_mouse_motion = kb->action; 426 break; 427 } 428 } 429 } else { 430 wimp.cursor_mode = CURSOR_PASSTHROUGH; 431 } 432 if (wimp.grabbed_view) { 433 if (try_snap()) { 434 int border_width = wimp.current_desk->border_width; 435 double zoom = wimp.current_desk->zoom; 436 wimp.snap_geobox.x = (wimp.snap_geobox.x + border_width) / zoom; 437 wimp.snap_geobox.y = (wimp.snap_geobox.y + border_width) / zoom; 438 wimp.snap_geobox.width = (wimp.snap_geobox.width - border_width * 2) / zoom; 439 wimp.snap_geobox.height = (wimp.snap_geobox.height - border_width * 2) / zoom; 440 view_apply_geometry(wimp.grabbed_view, &wimp.snap_geobox); 441 } 442 wimp.grabbed_view = NULL; 443 wimp.can_snap = false; 444 wimp.resize_edges = 0; 445 } 446 } 447 448 else if (wimp.cursor_mode == CURSOR_MOD) { 449 bool grab = true; 450 switch (event->button) { 451 case BTN_LEFT: 452 wimp.on_mouse_motion = wimp.on_drag1; 453 break; 454 case BTN_MIDDLE: 455 wimp.on_mouse_motion = wimp.on_drag2; 456 break; 457 case BTN_RIGHT: 458 wimp.on_mouse_motion = wimp.on_drag3; 459 break; 460 default: 461 grab = false; 462 } 463 if (grab) { 464 wimp.grabbed_view = under_pointer(&surface, &sx, &sy, &is_layer); 465 } 466 } 467 468 else { 469 struct view *view = under_pointer(&surface, &sx, &sy, &is_layer); 470 if (!is_layer) { 471 if (view) { 472 enum wlr_edges edges = pointer_in_view_corner(view); 473 if (edges) { 474 wimp.grabbed_view = view; 475 wimp.resize_edges = edges; 476 wimp.grab_x = wimp.cursor->x - view->x * wimp.current_desk->zoom; 477 wimp.grab_y = wimp.cursor->y - view->y * wimp.current_desk->zoom; 478 wlr_xdg_surface_get_geometry(view->surface, &wimp.grab_geobox); 479 wimp.grab_geobox.x = view->x; 480 wimp.grab_geobox.y = view->y; 481 wimp.cursor_mode = CURSOR_RESIZE; 482 } else { 483 focus_view(view, surface); 484 } 485 } else { 486 focus_view(view, surface); 487 } 488 } 489 } 490 } 491 492 493 static void on_cursor_axis(struct wl_listener *listener, void *data) { 494 struct wlr_event_pointer_axis *event = data; 495 if (wimp.cursor_mode == CURSOR_MOD) { 496 if (wimp.on_mouse_scroll) { 497 struct motion motion = { 498 .dx = 0, 499 .dy = 0, 500 .is_percentage = false, 501 }; 502 if (event->orientation == WLR_AXIS_ORIENTATION_VERTICAL) { 503 motion.dy = wimp.reverse_scrolling ? - event->delta : event->delta; 504 } else { 505 motion.dx = wimp.reverse_scrolling ? - event->delta : event->delta; 506 }; 507 wimp.on_mouse_scroll(&motion); 508 } 509 } else { 510 wlr_seat_pointer_notify_axis( 511 wimp.seat, event->time_msec, event->orientation, event->delta, 512 event->delta_discrete, event->source 513 ); 514 } 515 } 516 517 518 static void on_cursor_frame(struct wl_listener *listener, void *data) { 519 wlr_seat_pointer_notify_frame(wimp.seat); 520 } 521 522 523 static void on_pinch_end(struct wl_listener *listener, void *data) { 524 struct wlr_event_pointer_pinch_end *event = data; 525 if (wimp.cursor_mode == CURSOR_PASSTHROUGH) { 526 wlr_pointer_gestures_v1_send_pinch_end( 527 wimp.pointer_gestures, wimp.seat, event->time_msec, event->cancelled 528 ); 529 } 530 } 531 532 533 static void on_pinch_update(struct wl_listener *listener, void *data) { 534 struct wlr_event_pointer_pinch_update *event = data; 535 if (wimp.cursor_mode == CURSOR_MOD) { 536 if (wimp.on_pinch) { 537 wimp.on_pinch(&event->scale); 538 } 539 } else { 540 wlr_pointer_gestures_v1_send_pinch_update( 541 wimp.pointer_gestures, wimp.seat, event->time_msec, 542 event->dx, event->dy, event->scale, event->rotation 543 ); 544 } 545 } 546 547 548 static void on_pinch_begin(struct wl_listener *listener, void *data) { 549 struct wlr_event_pointer_pinch_begin *event = data; 550 if (wimp.cursor_mode == CURSOR_MOD) { 551 if (wimp.on_pinch_begin) { 552 wimp.on_pinch_begin(NULL); 553 } 554 } else { 555 wlr_pointer_gestures_v1_send_pinch_begin( 556 wimp.pointer_gestures, wimp.seat, event->time_msec, event->fingers 557 ); 558 } 559 } 560 561 562 static void on_swipe_end(struct wl_listener *listener, void *data) { 563 struct wlr_event_pointer_swipe_end *event = data; 564 wlr_pointer_gestures_v1_send_swipe_end( 565 wimp.pointer_gestures, wimp.seat, event->time_msec, event->cancelled 566 ); 567 } 568 569 570 static void on_swipe_update(struct wl_listener *listener, void *data) { 571 struct wlr_event_pointer_swipe_update *event = data; 572 wlr_pointer_gestures_v1_send_swipe_update( 573 wimp.pointer_gestures, wimp.seat, event->time_msec, event->dx, event->dy 574 ); 575 } 576 577 578 static void on_swipe_begin(struct wl_listener *listener, void *data) { 579 struct wlr_event_pointer_swipe_begin *event = data; 580 wlr_pointer_gestures_v1_send_swipe_begin( 581 wimp.pointer_gestures, wimp.seat, event->time_msec, event->fingers 582 ); 583 } 584 585 586 void set_up_cursor() { 587 wimp.cursor = wlr_cursor_create(); 588 wlr_cursor_attach_output_layout(wimp.cursor, wimp.output_layout); 589 wimp.cursor_manager = wlr_xcursor_manager_create(NULL, 24); 590 wlr_xcursor_manager_load(wimp.cursor_manager, 1); 591 592 wimp.request_cursor_listener.notify = on_request_cursor; 593 wl_signal_add(&wimp.seat->events.request_set_cursor, &wimp.request_cursor_listener); 594 595 wimp.cursor_axis_listener.notify = on_cursor_axis; 596 wimp.cursor_frame_listener.notify = on_cursor_frame; 597 wimp.cursor_button_listener.notify = on_cursor_button; 598 wimp.cursor_motion_listener.notify = on_cursor_motion; 599 wimp.cursor_motion_absolute_listener.notify = on_cursor_motion_absolute; 600 wl_signal_add(&wimp.cursor->events.axis, &wimp.cursor_axis_listener); 601 wl_signal_add(&wimp.cursor->events.frame, &wimp.cursor_frame_listener); 602 wl_signal_add(&wimp.cursor->events.button, &wimp.cursor_button_listener); 603 wl_signal_add(&wimp.cursor->events.motion, &wimp.cursor_motion_listener); 604 wl_signal_add(&wimp.cursor->events.motion_absolute, &wimp.cursor_motion_absolute_listener); 605 606 wimp.pointer_gestures = wlr_pointer_gestures_v1_create(wimp.display); 607 wimp.pinch_end_listener.notify = on_pinch_end; 608 wimp.pinch_begin_listener.notify = on_pinch_begin; 609 wimp.pinch_update_listener.notify = on_pinch_update; 610 wl_signal_add(&wimp.cursor->events.pinch_end, &wimp.pinch_end_listener); 611 wl_signal_add(&wimp.cursor->events.pinch_begin, &wimp.pinch_begin_listener); 612 wl_signal_add(&wimp.cursor->events.pinch_update, &wimp.pinch_update_listener); 613 wimp.swipe_end_listener.notify = on_swipe_end; 614 wimp.swipe_begin_listener.notify = on_swipe_begin; 615 wimp.swipe_update_listener.notify = on_swipe_update; 616 wl_signal_add(&wimp.cursor->events.swipe_end, &wimp.swipe_end_listener); 617 wl_signal_add(&wimp.cursor->events.swipe_begin, &wimp.swipe_begin_listener); 618 wl_signal_add(&wimp.cursor->events.swipe_update, &wimp.swipe_update_listener); 619 } 620