wio

a wayland wm stylised after plan9 rio - forked from git.sr.ht/~srcmpwn/wio
git clone git://src.gearsix.net/wio
Log | Files | Refs | Atom | Submodules | README | LICENSE

input.c (17784B)


      1 #define _POSIX_C_SOURCE 200811L
      2 #include <signal.h>
      3 #include <stdlib.h>
      4 #include <sys/wait.h>
      5 #include <linux/input-event-codes.h>
      6 #include <wlr/types/wlr_cursor.h>
      7 #include <wlr/types/wlr_seat.h>
      8 #include <wlr/types/wlr_input_device.h>
      9 #include <wlr/types/wlr_keyboard.h>
     10 #include <wlr/types/wlr_pointer.h>
     11 #include <wlr/util/log.h>
     12 #include <xkbcommon/xkbcommon.h>
     13 #include <unistd.h>
     14 #include "server.h"
     15 #include "view.h"
     16 
     17 static void keyboard_handle_modifiers(
     18 		struct wl_listener *listener, void *data) {
     19 	struct wio_keyboard *keyboard =
     20 		wl_container_of(listener, keyboard, modifiers);
     21 	wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device);
     22 	wlr_seat_keyboard_notify_modifiers(keyboard->server->seat,
     23 		&keyboard->device->keyboard->modifiers);
     24 }
     25 
     26 static void view_end_interactive(struct wio_server *server);
     27 static void keyboard_handle_key(
     28 		struct wl_listener *listener, void *data) {
     29 	struct wio_keyboard *keyboard =
     30 		wl_container_of(listener, keyboard, key);
     31 	struct wio_server *server = keyboard->server;
     32 	struct wlr_event_keyboard_key *event = data;
     33 	struct wlr_seat *seat = server->seat;
     34 	xkb_keycode_t keycode = event->keycode + 8;
     35 	const xkb_keysym_t *syms;
     36 	int nsyms = xkb_state_key_get_syms(
     37 		keyboard->device->keyboard->xkb_state,
     38 		keycode, &syms);
     39 
     40 	for (int i = 0; i < nsyms; i++) {
     41 		if (syms[i] == XKB_KEY_Escape) {
     42 			switch (server->input_state) {
     43 			case INPUT_STATE_NONE:
     44 			case INPUT_STATE_MENU:
     45 				break;
     46 			default:
     47 				view_end_interactive(server);
     48 				return;
     49 			}
     50 		}
     51 	}
     52 
     53 	wlr_seat_set_keyboard(seat, keyboard->device);
     54 	wlr_seat_keyboard_notify_key(seat, event->time_msec,
     55 		event->keycode, event->state);
     56 }
     57 
     58 static void server_new_keyboard(
     59 		struct wio_server *server, struct wlr_input_device *device) {
     60 	struct wio_keyboard *keyboard = calloc(1, sizeof(struct wio_keyboard));
     61 	keyboard->server = server;
     62 	keyboard->device = device;
     63 
     64 	struct xkb_rule_names rules = { 0 };
     65 	rules.rules = getenv("XKB_DEFAULT_RULES");
     66 	rules.model = getenv("XKB_DEFAULT_MODEL");
     67 	rules.layout = getenv("XKB_DEFAULT_LAYOUT");
     68 	rules.variant = getenv("XKB_DEFAULT_VARIANT");
     69 	rules.options = getenv("XKB_DEFAULT_OPTIONS");
     70 	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
     71 	struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
     72 		XKB_KEYMAP_COMPILE_NO_FLAGS);
     73 
     74 	wlr_keyboard_set_keymap(device->keyboard, keymap);
     75 	xkb_keymap_unref(keymap);
     76 	xkb_context_unref(context);
     77 	wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
     78 
     79 	keyboard->modifiers.notify = keyboard_handle_modifiers;
     80 	wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers);
     81 	keyboard->key.notify = keyboard_handle_key;
     82 	wl_signal_add(&device->keyboard->events.key, &keyboard->key);
     83 
     84 	wlr_seat_set_keyboard(server->seat, device);
     85 	wl_list_insert(&server->keyboards, &keyboard->link);
     86 }
     87 
     88 static void server_new_pointer(
     89 		struct wio_server *server, struct wlr_input_device *device) {
     90 	wlr_cursor_attach_input_device(server->cursor, device);
     91 }
     92 
     93 void server_new_input(struct wl_listener *listener, void *data) {
     94 	struct wio_server *server = wl_container_of(listener, server, new_input);
     95 	struct wlr_input_device *device = data;
     96 	switch (device->type) {
     97 	case WLR_INPUT_DEVICE_KEYBOARD:
     98 		server_new_keyboard(server, device);
     99 		break;
    100 	case WLR_INPUT_DEVICE_POINTER:
    101 		server_new_pointer(server, device);
    102 		break;
    103 	default:
    104 		break;
    105 	}
    106 	uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
    107 	if (!wl_list_empty(&server->keyboards)) {
    108 		caps |= WL_SEAT_CAPABILITY_KEYBOARD;
    109 	}
    110 	wlr_seat_set_capabilities(server->seat, caps);
    111 }
    112 
    113 static void process_cursor_motion(struct wio_server *server, uint32_t time) {
    114 	double sx, sy;
    115 	int view_area;
    116 	struct wlr_seat *seat = server->seat;
    117 	struct wlr_surface *surface = NULL;
    118 	struct wio_view *view = wio_view_at(
    119 			server, server->cursor->x, server->cursor->y, &surface, &sx, &sy,
    120 			&view_area);
    121 	if (!view) {
    122 		switch (server->input_state) {
    123 		case INPUT_STATE_MOVE_SELECT:
    124 		case INPUT_STATE_RESIZE_SELECT:
    125 		case INPUT_STATE_DELETE_SELECT:
    126 		case INPUT_STATE_HIDE_SELECT:
    127 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    128 					"hand1", server->cursor);
    129 			break;
    130 		case INPUT_STATE_MOVE:
    131 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    132 					"grabbing", server->cursor);
    133 			break;
    134 		case INPUT_STATE_RESIZE_START:
    135 		case INPUT_STATE_NEW_START:
    136 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    137 					"top_left_corner", server->cursor);
    138 			break;
    139 		case INPUT_STATE_BORDER_DRAG_TOP:
    140 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    141 					"top_side", server->cursor);
    142 			break;
    143 		case INPUT_STATE_BORDER_DRAG_RIGHT:
    144 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    145 					"right_side", server->cursor);
    146 			break;
    147 		case INPUT_STATE_BORDER_DRAG_BOTTOM:
    148 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    149 					"bottom_side", server->cursor);
    150 			break;
    151 		case INPUT_STATE_BORDER_DRAG_LEFT:
    152 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    153 					"left_side", server->cursor);
    154 			break;
    155 		case INPUT_STATE_RESIZE_END:
    156 		case INPUT_STATE_NEW_END:
    157 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    158 					"bottom_right_corner", server->cursor);
    159 			break;
    160 		default:
    161 			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    162 					"left_ptr", server->cursor);
    163 			break;
    164 		}
    165 	}
    166 	if (surface) {
    167 		bool focus_changed = seat->pointer_state.focused_surface != surface;
    168 		wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
    169 		if (!focus_changed) {
    170 			wlr_seat_pointer_notify_motion(seat, time, sx, sy);
    171 		}
    172 	} else {
    173 		wlr_seat_pointer_clear_focus(seat);
    174 	}
    175 }
    176 
    177 void server_cursor_motion(struct wl_listener *listener, void *data) {
    178 	struct wio_server *server =
    179 		wl_container_of(listener, server, cursor_motion);
    180 	struct wlr_event_pointer_motion *event = data;
    181 	wlr_cursor_move(server->cursor, event->device,
    182 			event->delta_x, event->delta_y);
    183 	process_cursor_motion(server, event->time_msec);
    184 }
    185 
    186 void server_cursor_motion_absolute(
    187 		struct wl_listener *listener, void *data) {
    188 	struct wio_server *server =
    189 		wl_container_of(listener, server, cursor_motion_absolute);
    190 	struct wlr_event_pointer_motion_absolute *event = data;
    191 	wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y);
    192 	process_cursor_motion(server, event->time_msec);
    193 }
    194 
    195 static void menu_handle_button(
    196 		struct wio_server *server, struct wlr_event_pointer_button *event) {
    197 	server->menu.x = server->menu.y = -1;
    198 	switch (server->menu.selected) {
    199 	case 0:
    200 		server->input_state = INPUT_STATE_NEW_START;
    201 		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    202 				"top_left_corner", server->cursor);
    203 		break;
    204 	case 1:
    205 		server->input_state = INPUT_STATE_RESIZE_SELECT;
    206 		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    207 				"hand1", server->cursor);
    208 		break;
    209 	case 2:
    210 		server->input_state = INPUT_STATE_MOVE_SELECT;
    211 		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    212 				"hand1", server->cursor);
    213 		break;
    214 	case 3:
    215 		server->input_state = INPUT_STATE_DELETE_SELECT;
    216 		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    217 				"hand1", server->cursor);
    218 		break;
    219 	default:
    220 		server->input_state = INPUT_STATE_NONE;
    221 		break;
    222 	}
    223 }
    224 
    225 static void view_begin_interactive(struct wio_view *view,
    226 		struct wlr_surface *surface, double sx, double sy,
    227 		const char *cursor, enum wio_input_state state) {
    228 	wio_view_focus(view, surface);
    229 	view->server->interactive.view = view;
    230 	view->server->interactive.sx = (int)sx;
    231 	view->server->interactive.sy = (int)sy;
    232 	view->server->input_state = state;
    233 	wlr_xcursor_manager_set_cursor_image(view->server->cursor_mgr,
    234 			cursor, view->server->cursor);
    235 }
    236 
    237 static void view_end_interactive(struct wio_server *server) {
    238 	server->input_state = INPUT_STATE_NONE;
    239 	server->interactive.view = NULL;
    240 	// TODO: Restore previous pointer?
    241 	wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
    242 			"left_ptr", server->cursor);
    243 }
    244 
    245 static void new_view(struct wio_server *server) {
    246 	int x1 = server->interactive.sx, x2 = server->cursor->x;
    247 	int y1 = server->interactive.sy, y2 = server->cursor->y;
    248 	if (x2 < x1) {
    249 		int _ = x1;
    250 		x1 = x2;
    251 		x2 = _;
    252 	}
    253 	if (y2 < y1) {
    254 		int _ = y1;
    255 		y1 = y2;
    256 		y2 = _;
    257 	}
    258 	struct wio_new_view *view = calloc(1, sizeof(struct wio_new_view));
    259 	view->box.x = x1;
    260 	view->box.y = y1;
    261 	view->box.width = x2 - x1;
    262 	view->box.height = y2 - y1;
    263 	if (view->box.width < 100){
    264 		view->box.width = 100;
    265 	}
    266 	if (view->box.height < 100){
    267 		view->box.height = 100;
    268 	}
    269 	int fd[2];
    270 	if (pipe(fd) != 0) {
    271 		wlr_log(WLR_ERROR, "Unable to create pipe for fork");
    272 		return;
    273 	}
    274 	char cmd[1024];
    275 	if (snprintf(cmd, sizeof(cmd), "%s -- %s",
    276 			server->cage, server->term) >= (int)sizeof(cmd)) {
    277 		fprintf(stderr, "New view command truncated\n");
    278 		return;
    279 	}
    280 	pid_t pid, child;
    281 	if ((pid = fork()) == 0) {
    282 		setsid();
    283 		sigset_t set;
    284 		sigemptyset(&set);
    285 		sigprocmask(SIG_SETMASK, &set, NULL);
    286 		close(fd[0]);
    287 		if ((child = fork()) == 0) {
    288 			close(fd[1]);
    289 			execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
    290 			_exit(0);
    291 		}
    292 		ssize_t s = 0;
    293 		while ((size_t)s < sizeof(pid_t)) {
    294 			s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s);
    295 		}
    296 		close(fd[1]);
    297 		_exit(0); // Close child process
    298 	} else if (pid < 0) {
    299 		close(fd[0]);
    300 		close(fd[1]);
    301 		wlr_log(WLR_ERROR, "fork failed");
    302 		return;
    303 	}
    304 	close(fd[1]); // close write
    305 	ssize_t s = 0;
    306 	while ((size_t)s < sizeof(pid_t)) {
    307 		s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s);
    308 	}
    309 	close(fd[0]);
    310 	waitpid(pid, NULL, 0);
    311 	if (child > 0) {
    312 		view->pid = child;
    313 		wl_list_insert(&server->new_views, &view->link);
    314 	}
    315 }
    316 
    317 static void handle_button_internal(
    318 		struct wio_server *server, struct wlr_event_pointer_button *event) {
    319 	// TODO: open menu if the client doesn't handle the button press
    320 	// will basically involve some serial hacking
    321 	struct wlr_box menu_box = {
    322 		.x = server->menu.x, .y = server->menu.y,
    323 		.width = server->menu.width, .height = server->menu.height,
    324 	};
    325 	int x1, x2, y1, y2;
    326 	uint32_t width, height;
    327 	switch (server->input_state) {
    328 	case INPUT_STATE_NONE:
    329 		if (event->state == WLR_BUTTON_PRESSED && event->button == BTN_RIGHT) {
    330 			// TODO: Open over the last-used menu item
    331 			server->input_state = INPUT_STATE_MENU;
    332 			server->menu.x = server->cursor->x;
    333 			server->menu.y = server->cursor->y;
    334 		}
    335 		break;
    336 	case INPUT_STATE_MENU:
    337 		if (wlr_box_contains_point(
    338 					&menu_box, server->cursor->x, server->cursor->y)) {
    339 			menu_handle_button(server, event);
    340 		} else {
    341 			if (event->state == WLR_BUTTON_PRESSED) {
    342 				server->input_state = INPUT_STATE_NONE;
    343 				server->menu.x = server->menu.y = -1;
    344 			}
    345 		}
    346 		break;
    347 	case INPUT_STATE_NEW_START:
    348 		if (event->state == WLR_BUTTON_PRESSED) {
    349 			server->interactive.sx = server->cursor->x;
    350 			server->interactive.sy = server->cursor->y;
    351 			server->input_state = INPUT_STATE_NEW_END;
    352 		}
    353 		break;
    354 	case INPUT_STATE_NEW_END:
    355 		new_view(server);
    356 		view_end_interactive(server);
    357 		break;
    358 	case INPUT_STATE_RESIZE_SELECT:
    359 		if (event->state == WLR_BUTTON_PRESSED) {
    360 			double sx, sy;
    361 			int view_area;
    362 			struct wlr_surface *surface = NULL;
    363 			struct wio_view *view = wio_view_at(server,
    364 					server->cursor->x, server->cursor->y, &surface, &sx, &sy,
    365 					&view_area);
    366 			if (view != NULL) {
    367 				view_begin_interactive(view, surface, sx, sy,
    368 						"bottom_right_corner", INPUT_STATE_RESIZE_START);
    369 			} else {
    370 				view_end_interactive(server);
    371 			}
    372 		}
    373 		break;
    374 	case INPUT_STATE_RESIZE_START:
    375 		if (event->state == WLR_BUTTON_PRESSED) {
    376 			server->interactive.sx = server->cursor->x;
    377 			server->interactive.sy = server->cursor->y;
    378 			server->input_state = INPUT_STATE_RESIZE_END;
    379 		}
    380 		break;
    381 	case INPUT_STATE_BORDER_DRAG_TOP:
    382 		y1 = server->interactive.view->y + server->interactive.view->xdg_surface->surface->current.height;
    383 		y2 = server->cursor->y;
    384 		x1 = server->interactive.view->x;
    385 		if (y2 < y1) {
    386 			int _ = y1;
    387 			y1 = y2;
    388 			y2 = _;
    389 		}
    390 		wio_view_move(server->interactive.view,
    391 				x1, y1);
    392 		width = server->interactive.view->xdg_surface->surface->current.width;
    393 		height = y2 - y1;
    394 		if (height < 100) {
    395 			height = 100;
    396 		}
    397 		wlr_xdg_toplevel_set_size(
    398 				server->interactive.view->xdg_surface, width, height);
    399 		view_end_interactive(server);
    400 		break;
    401 	case INPUT_STATE_BORDER_DRAG_LEFT:
    402 		x1 = server->interactive.view->x + server->interactive.view->xdg_surface->surface->current.width;
    403 		x2 = server->cursor->x;
    404 		y1 = server->interactive.view->y;
    405 		if (x2 < x1) {
    406 			int _ = x1;
    407 			x1 = x2;
    408 			x2 = _;
    409 		}
    410 		wio_view_move(server->interactive.view,
    411 				x1, y1);
    412 		width = x2 - x1;
    413 		height = server->interactive.view->xdg_surface->surface->current.height;
    414 		if (width < 100) {
    415 			width = 100;
    416 		}
    417 		wlr_xdg_toplevel_set_size(
    418 				server->interactive.view->xdg_surface, width, height);
    419 		view_end_interactive(server);
    420 		break;
    421 	case INPUT_STATE_BORDER_DRAG_BOTTOM:
    422 		x1 = server->interactive.view->x;
    423 		y1 = server->interactive.view->y, y2 = server->cursor->y;
    424 		if (y2 < y1) {
    425 			int _ = y1;
    426 			y1 = y2;
    427 			y2 = _;
    428 		}
    429 		wio_view_move(server->interactive.view,
    430 				x1, y1);
    431 		width = server->interactive.view->xdg_surface->surface->current.width;
    432 		height = y2 - y1;
    433 		if (width < 100) {
    434 			width = 100;
    435 		}
    436 		wlr_xdg_toplevel_set_size(
    437 				server->interactive.view->xdg_surface, width, height);
    438 		view_end_interactive(server);
    439 		break;
    440 	case INPUT_STATE_BORDER_DRAG_RIGHT:
    441 		x1 = server->interactive.view->x, x2 = server->cursor->x;
    442 		y1 = server->interactive.view->y;
    443 		if (x2 < x1) {
    444 			int _ = x1;
    445 			x1 = x2;
    446 			x2 = _;
    447 		}
    448 		wio_view_move(server->interactive.view,
    449 				x1, y1);
    450 		width = x2 - x1;
    451 		height = server->interactive.view->xdg_surface->surface->current.height;
    452 		if (width < 100) {
    453 			width = 100;
    454 		}
    455 		wlr_xdg_toplevel_set_size(
    456 				server->interactive.view->xdg_surface, width, height);
    457 		view_end_interactive(server);
    458 		break;
    459 	case INPUT_STATE_RESIZE_END:
    460 		x1 = server->interactive.sx, x2 = server->cursor->x;
    461 		y1 = server->interactive.sy, y2 = server->cursor->y;
    462 		if (x2 < x1) {
    463 			int _ = x1;
    464 			x1 = x2;
    465 			x2 = _;
    466 		}
    467 		if (y2 < y1) {
    468 			int _ = y1;
    469 			y1 = y2;
    470 			y2 = _;
    471 		}
    472 		wio_view_move(server->interactive.view,
    473 				x1, y1);
    474 		width = x2 - x1, height = y2 - y1;
    475 		if (width < 100) {
    476 			width = 100;
    477 		}
    478 		if (height < 100) {
    479 			height = 100;
    480 		}
    481 		wlr_xdg_toplevel_set_size(
    482 				server->interactive.view->xdg_surface, width, height);
    483 		view_end_interactive(server);
    484 		break;
    485 	case INPUT_STATE_MOVE_SELECT:
    486 		if (event->state == WLR_BUTTON_PRESSED) {
    487 			double sx, sy;
    488 			int view_area;
    489 			struct wlr_surface *surface = NULL;
    490 			struct wio_view *view = wio_view_at(server,
    491 					server->cursor->x, server->cursor->y, &surface, &sx, &sy,
    492 					&view_area);
    493 			if (view != NULL) {
    494 				view_begin_interactive(view, surface, sx, sy,
    495 						"grabbing", INPUT_STATE_MOVE);
    496 			} else {
    497 				view_end_interactive(server);
    498 			}
    499 		}
    500 		break;
    501 	case INPUT_STATE_MOVE:
    502 		wio_view_move(server->interactive.view,
    503 			server->cursor->x - server->interactive.sx,
    504 			server->cursor->y - server->interactive.sy);
    505 		view_end_interactive(server);
    506 		break;
    507 	case INPUT_STATE_DELETE_SELECT:
    508 		if (event->state == WLR_BUTTON_PRESSED) {
    509 			double sx, sy;
    510 			int view_area;
    511 			struct wlr_surface *surface = NULL;
    512 			struct wio_view *view = wio_view_at(server,
    513 					server->cursor->x, server->cursor->y, &surface, &sx, &sy,
    514 					&view_area);
    515 			if (view != NULL) {
    516 				wlr_xdg_toplevel_send_close(view->xdg_surface);
    517 			}
    518 			view_end_interactive(server);
    519 		}
    520 		break;
    521 	default:
    522 		// TODO
    523 		break;
    524 	}
    525 }
    526 
    527 void server_cursor_button(struct wl_listener *listener, void *data) {
    528 	struct wio_server *server =
    529 		wl_container_of(listener, server, cursor_button);
    530 	struct wlr_event_pointer_button *event = data;
    531 	double sx, sy;
    532 	struct wlr_surface *surface = NULL;
    533 	int view_area;
    534 	struct wio_view *view = wio_view_at(
    535 			server, server->cursor->x, server->cursor->y, &surface, &sx, &sy,
    536 			&view_area);
    537 	if (server->input_state == INPUT_STATE_NONE && view) {
    538 		wio_view_focus(view, surface);
    539 		switch (view_area) {
    540 		case VIEW_AREA_SURFACE:
    541 			wlr_seat_pointer_notify_button(server->seat,
    542 					event->time_msec, event->button, event->state);
    543 			break;
    544 		case VIEW_AREA_BORDER_TOP:
    545 			view_begin_interactive(view, surface, view->x, view->y,
    546 					"top_side", INPUT_STATE_BORDER_DRAG_TOP);
    547 			break;
    548 		case VIEW_AREA_BORDER_RIGHT:
    549 			view_begin_interactive(view, surface, view->x, view->y,
    550 					"right_side", INPUT_STATE_BORDER_DRAG_RIGHT);
    551 			break;
    552 		case VIEW_AREA_BORDER_BOTTOM:
    553 			view_begin_interactive(view, surface, view->x, view->y,
    554 					"bottom_side", INPUT_STATE_BORDER_DRAG_BOTTOM);
    555 			break;
    556 		case VIEW_AREA_BORDER_LEFT:
    557 			view_begin_interactive(view, surface, view->x, view->y,
    558 					"left_side", INPUT_STATE_BORDER_DRAG_LEFT);
    559 			break;
    560 		}
    561 	} else {
    562 		handle_button_internal(server, event);
    563 	}
    564 }
    565 
    566 void server_cursor_axis(struct wl_listener *listener, void *data) {
    567 	struct wio_server *server = wl_container_of(listener, server, cursor_axis);
    568 	struct wlr_event_pointer_axis *event = data;
    569 	wlr_seat_pointer_notify_axis(server->seat,
    570 			event->time_msec, event->orientation, event->delta,
    571 			event->delta_discrete, event->source);
    572 }
    573 
    574 void server_cursor_frame(struct wl_listener *listener, void *data) {
    575 	struct wio_server *server =
    576 		wl_container_of(listener, server, cursor_frame);
    577 	wlr_seat_pointer_notify_frame(server->seat);
    578 }
    579 
    580 void seat_request_cursor(struct wl_listener *listener, void *data) {
    581 	struct wio_server *server = wl_container_of(
    582 			listener, server, request_cursor);
    583 	struct wlr_seat_pointer_request_set_cursor_event *event = data;
    584 	struct wlr_seat_client *focused_client =
    585 		server->seat->pointer_state.focused_client;
    586 	if (focused_client == event->seat_client
    587 			&& server->input_state == INPUT_STATE_NONE) {
    588 		wlr_cursor_set_surface(server->cursor, event->surface,
    589 				event->hotspot_x, event->hotspot_y);
    590 	}
    591 }