gimp but for windows: a wayland compositor
git clone https://github.com/m-col/wimp
Files | Refs | Readme

-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