1#define _POSIX_C_SOURCE 200809L
2
3#include <fcntl.h>
4#include <poll.h>
5#include <stdlib.h>
6#include <string.h>
7#include <sys/mman.h>
8#include <unistd.h>
9
10#include <wayland-client.h>
11#include <wayland-cursor.h>
12#include <xdg-shell-client-protocol.h>
13#include <pointer-constraints-unstable-v1-client-protocol.h>
14#include <relative-pointer-unstable-v1-client-protocol.h>
15#include <xkbcommon/xkbcommon.h>
16
17#include "maus.h"
18#include "maus_wayland.h"
19
20static void registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name);
21static void xdg_surface_configure(void* data, struct xdg_surface* xdg_surface, uint32_t serial);
22static void xdg_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height, struct wl_array* states);
23static void xdg_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel);
24static void xdg_toplevel_configure_bounds(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height);
25static void xdg_toplevel_wm_capabilities(void* data, struct xdg_toplevel* xdg_toplevel, struct wl_array* capabilities);
26static void wm_base_ping(void* data, struct xdg_wm_base* wm_base, uint32_t serial);
27static void seat_capabilities(void* data, struct wl_seat* seat, uint32_t capabilities);
28static void seat_name(void* data, struct wl_seat* seat, const char* name);
29static void pointer_enter(void* data, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy);
30static void pointer_leave(void* data, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface);
31static void pointer_motion(void* data, struct wl_pointer* pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
32static void pointer_button(void* data, struct wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
33static void pointer_axis(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value);
34static void pointer_frame(void* data, struct wl_pointer* pointer);
35static void pointer_axis_source(void* data, struct wl_pointer* pointer, uint32_t axis_source);
36static void pointer_axis_stop(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis);
37static void pointer_axis_discrete(void* data, struct wl_pointer* pointer, uint32_t axis, int32_t discrete);
38static void pointer_axis_value120(void* data, struct wl_pointer* pointer, uint32_t axis, int32_t value120);
39static void pointer_axis_relative_direction(void* data, struct wl_pointer* pointer, uint32_t axis, uint32_t direction);
40static void relative_pointer_motion(void* data, struct zwp_relative_pointer_v1* relative_pointer, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel);
41static void keyboard_keymap(void* data, struct wl_keyboard* keyboard, uint32_t format, int32_t fd, uint32_t size);
42static void keyboard_enter(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface, struct wl_array* keys);
43static void keyboard_leave(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface);
44static void keyboard_key(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
45static void keyboard_modifiers(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group);
46static void keyboard_repeat_info(void* data, struct wl_keyboard* keyboard, int32_t rate, int32_t delay);
47static void registry_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
48
49static int8_t cursor_init(Maus* mw);
50static void cursor_destroy(Maus* mw);
51static void cursor_lock(Maus* mw);
52static void cursor_unlock(Maus* mw);
53static void cursor_show(Maus* mw);
54static void cursor_hide(Maus* mw);
55static void cursor_relative(Maus* mw);
56static void cursor_absolute(Maus* mw);
57static void event_push_pending(Maus* mw);
58static int8_t event_pop(Maus* mw, MausEventPending* ev);
59static void event_push_key(Maus* mw, uint32_t code, uint32_t sym, char text, uint8_t pressed);
60static int8_t repeat_push_due(Maus* mw);
61static int32_t repeat_timeout_ms(Maus* mw);
62static int8_t display_read(Maus* mw, int timeout_ms);
63static MausKey keysym_to_mauskey(xkb_keysym_t sym);
64static MausMouseButton wl_button_to_maus(uint32_t button);
65static void wl_buffer_release(void* data, struct wl_buffer* buffer);
66
67static struct wl_registry_listener registry_listener = { registry_global, registry_global_remove };
68static struct xdg_surface_listener xdg_surface_listener = { xdg_surface_configure };
69static struct xdg_toplevel_listener xdg_toplevel_listener = {
70 xdg_toplevel_configure, xdg_toplevel_close,
71 xdg_toplevel_configure_bounds, xdg_toplevel_wm_capabilities
72};
73static struct xdg_wm_base_listener xdg_wm_base_listener = { wm_base_ping };
74static struct wl_seat_listener seat_listener = { seat_capabilities, seat_name };
75static struct wl_pointer_listener pointer_listener = {
76 pointer_enter, pointer_leave, pointer_motion, pointer_button,
77 pointer_axis, pointer_frame, pointer_axis_source, pointer_axis_stop,
78 pointer_axis_discrete, pointer_axis_value120, pointer_axis_relative_direction
79};
80static struct wl_keyboard_listener keyboard_listener = {
81 keyboard_keymap, keyboard_enter, keyboard_leave, keyboard_key,
82 keyboard_modifiers, keyboard_repeat_info
83};
84static struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
85 relative_pointer_motion
86};
87static struct wl_buffer_listener wl_buffer_listener = { wl_buffer_release };
88
89static void wl_buffer_release(void* data, struct wl_buffer* buffer)
90{
91 MausWLBuffer* wb = data;
92
93 (void)buffer;
94
95 wb->busy = 0;
96}
97
98static void event_push_pending(Maus* mw)
99{
100 MausBackend* be = &mw->backend;
101
102 if (!be->pending.pending)
103 return;
104
105 if (be->evq_count == MAUS_EVQ_MAX) {
106 be->evq_head = (be->evq_head + 1) % MAUS_EVQ_MAX;
107 be->evq_count--;
108 }
109
110 be->evq[be->evq_tail] = be->pending;
111 be->evq_tail = (be->evq_tail + 1) % MAUS_EVQ_MAX;
112 be->evq_count++;
113 be->pending.pending = 0;
114}
115
116static int8_t event_pop(Maus* mw, MausEventPending* ev)
117{
118 MausBackend* be = &mw->backend;
119
120 if (be->evq_count == 0)
121 return 0;
122
123 *ev = be->evq[be->evq_head];
124 be->evq_head = (be->evq_head + 1) % MAUS_EVQ_MAX;
125 be->evq_count--;
126 return 1;
127}
128
129static void event_push_key(Maus* mw, uint32_t code, uint32_t sym, char text, uint8_t pressed)
130{
131 MausBackend* be = &mw->backend;
132
133 be->pending.pending = 1;
134 be->pending.type = MAUS_EV_KEY;
135 be->pending.key_code = code;
136 be->pending.key_sym = sym;
137 be->pending.key_text = text;
138 be->pending.key_pressed = pressed;
139 event_push_pending(mw);
140}
141
142static void registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
143{
144 (void)data;
145 (void)wl_registry;
146 (void)name;
147}
148
149static void xdg_surface_configure(void* data, struct xdg_surface* xdg_surface, uint32_t serial)
150{
151 Maus* mw = data;
152 xdg_surface_ack_configure(xdg_surface, serial);
153 mw->backend.configured = 1;
154 if (!mw->backend.pending.pending) {
155 mw->backend.pending.pending = 1;
156 mw->backend.pending.type = MAUS_EV_REDRAW;
157 }
158 event_push_pending(mw);
159}
160
161static void xdg_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel,
162 int32_t width, int32_t height, struct wl_array* states)
163{
164 Maus* mw = data;
165
166 (void)xdg_toplevel;
167 (void)states;
168
169 if (width <= 0 || height <= 0)
170 return;
171
172 mw->backend.pending.pending = 1;
173 mw->backend.pending.type = MAUS_EV_RESIZE;
174 mw->backend.pending.width = width;
175 mw->backend.pending.height = height;
176 event_push_pending(mw);
177}
178
179static void xdg_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel)
180{
181 Maus* mw = data;
182
183 (void)xdg_toplevel;
184
185 mw->backend.pending.pending = 1;
186 mw->backend.pending.type = MAUS_EV_CLOSE;
187 event_push_pending(mw);
188}
189
190static void xdg_toplevel_configure_bounds(void* data, struct xdg_toplevel* xdg_toplevel,
191 int32_t width, int32_t height)
192{
193 (void)data;
194 (void)xdg_toplevel;
195 (void)width;
196 (void)height;
197}
198
199static void xdg_toplevel_wm_capabilities(void* data, struct xdg_toplevel* xdg_toplevel,
200 struct wl_array* capabilities)
201{
202 (void)data;
203 (void)xdg_toplevel;
204 (void)capabilities;
205}
206
207static void wm_base_ping(void* data, struct xdg_wm_base* wm_base, uint32_t serial)
208{
209 (void)data;
210 xdg_wm_base_pong(wm_base, serial);
211}
212
213static void registry_global(void* data, struct wl_registry* registry,
214 uint32_t name, const char* interface,
215 uint32_t version)
216{
217 Maus* mw = data;
218 MausBackend* be = &mw->backend;
219
220 (void) version;
221
222 if (strcmp(interface, wl_compositor_interface.name) == 0)
223 be->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
224 else if (strcmp(interface, wl_shm_interface.name) == 0)
225 be->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
226 else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
227 be->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
228 xdg_wm_base_add_listener(be->wm_base, &xdg_wm_base_listener, mw);
229 }
230 else if (strcmp(interface, wl_seat_interface.name) == 0) {
231 be->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);
232 wl_seat_add_listener(be->seat, &seat_listener, mw);
233 }
234 else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0)
235 be->pointer_constraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1);
236 else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0)
237 be->relative_pointer_manager = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1);
238}
239
240static void seat_capabilities(void* data, struct wl_seat* seat, uint32_t capabilities)
241{
242 Maus* mw = data;
243 MausBackend* be = &mw->backend;
244
245 if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !be->pointer) {
246 be->pointer = wl_seat_get_pointer(seat);
247 wl_pointer_add_listener(be->pointer, &pointer_listener, mw);
248 }
249 else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && be->pointer) {
250 cursor_absolute(mw);
251 wl_pointer_destroy(be->pointer);
252 be->pointer = NULL;
253 }
254
255 if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !be->keyboard) {
256 be->keyboard = wl_seat_get_keyboard(seat);
257 wl_keyboard_add_listener(be->keyboard, &keyboard_listener, mw);
258 }
259 else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && be->keyboard) {
260 wl_keyboard_destroy(be->keyboard);
261 be->keyboard = NULL;
262 }
263}
264
265static void seat_name(void* data, struct wl_seat* seat, const char* name)
266{
267 (void)data;
268 (void)seat;
269 (void)name;
270}
271
272static void pointer_enter(void* data, struct wl_pointer* pointer, uint32_t serial,
273 struct wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy)
274{
275 Maus* mw = data;
276
277 (void)pointer;
278 (void)surface;
279 (void)sx;
280 (void)sy;
281
282 mw->backend.pointer_enter_serial = serial;
283 if (mw->backend.cursor_state == MAUS_CURSOR_STATE_HIDDEN)
284 cursor_hide(mw);
285 else
286 cursor_show(mw);
287}
288
289static void pointer_leave(void* data, struct wl_pointer* pointer, uint32_t serial,
290 struct wl_surface* surface)
291{
292 (void)data;
293 (void)pointer;
294 (void)serial;
295 (void)surface;
296}
297
298static void pointer_motion(void* data, struct wl_pointer* pointer, uint32_t time,
299 wl_fixed_t sx, wl_fixed_t sy)
300{
301 Maus* mw = data;
302 MausBackend* be = &mw->backend;
303 int32_t x;
304 int32_t y;
305
306 (void)pointer;
307 (void)time;
308
309 if (be->relative_pointer)
310 return;
311
312 x = wl_fixed_to_int(sx);
313 y = wl_fixed_to_int(sy);
314
315 be->pending.pending = 1;
316 be->pending.type = MAUS_EV_MOUSE_MOTION;
317 be->pending.mouse_x = x;
318 be->pending.mouse_y = y;
319 be->pending.mouse_dx = be->mouse_pos_set ? x - be->mouse_x : 0;
320 be->pending.mouse_dy = be->mouse_pos_set ? y - be->mouse_y : 0;
321 be->mouse_x = x;
322 be->mouse_y = y;
323 be->mouse_pos_set = 1;
324 event_push_pending(mw);
325}
326
327static void pointer_button(void* data, struct wl_pointer* pointer, uint32_t serial,
328 uint32_t time, uint32_t button, uint32_t state)
329{
330 Maus* mw = data;
331 MausMouseButton mb;
332
333 (void)pointer;
334 (void)serial;
335 (void)time;
336
337 mb = wl_button_to_maus(button);
338 mw->backend.pending.pending = 1;
339 mw->backend.pending.type = MAUS_EV_MOUSE_BUTTON;
340 mw->backend.pending.mouse_button = mb;
341 mw->backend.pending.mouse_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
342
343 if (mb != MAUS_MOUSE_BUTTON_NONE)
344 mw->mouse_buttons[mb] = mw->backend.pending.mouse_pressed;
345 event_push_pending(mw);
346}
347
348static void pointer_axis(void* data, struct wl_pointer* pointer, uint32_t time,
349 uint32_t axis, wl_fixed_t value)
350{
351 Maus* mw = data;
352
353 (void)pointer;
354 (void)time;
355
356 if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
357 return;
358
359 mw->backend.pending.pending = 1;
360 mw->backend.pending.type = MAUS_EV_MOUSE_BUTTON;
361 mw->backend.pending.mouse_button = wl_fixed_to_int(value) > 0
362 ? MAUS_MOUSE_BUTTON_SCROLL_DOWN
363 : MAUS_MOUSE_BUTTON_SCROLL_UP;
364 mw->backend.pending.mouse_pressed = 1;
365 event_push_pending(mw);
366}
367
368static void pointer_frame(void* data, struct wl_pointer* pointer)
369{
370 (void)data;
371 (void)pointer;
372}
373
374static void pointer_axis_source(void* data, struct wl_pointer* pointer, uint32_t axis_source)
375{
376 (void)data;
377 (void)pointer;
378 (void)axis_source;
379}
380
381static void pointer_axis_stop(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis) {
382 (void)data;
383 (void)pointer;
384 (void)time;
385 (void)axis;
386}
387
388static void pointer_axis_discrete(void* data, struct wl_pointer* pointer, uint32_t axis,
389 int32_t discrete)
390{
391 (void)data;
392 (void)pointer;
393 (void)axis;
394 (void)discrete;
395}
396
397static void pointer_axis_value120(void* data, struct wl_pointer* pointer, uint32_t axis,
398 int32_t value120)
399{
400 (void)data;
401 (void)pointer;
402 (void)axis;
403 (void)value120;
404}
405
406static void pointer_axis_relative_direction(void* data, struct wl_pointer* pointer,
407 uint32_t axis, uint32_t direction)
408{
409 (void)data;
410 (void)pointer;
411 (void)axis;
412 (void)direction;
413}
414
415static void relative_pointer_motion(void* data, struct zwp_relative_pointer_v1* relative_pointer,
416 uint32_t utime_hi, uint32_t utime_lo,
417 wl_fixed_t dx, wl_fixed_t dy,
418 wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel)
419{
420 Maus* mw = data;
421
422 (void)relative_pointer;
423 (void)utime_hi;
424 (void)utime_lo;
425 (void)dx_unaccel;
426 (void)dy_unaccel;
427
428 mw->backend.pending.pending = 1;
429 mw->backend.pending.type = MAUS_EV_MOUSE_MOTION;
430 mw->backend.pending.mouse_x = wl_fixed_to_int(dx);
431 mw->backend.pending.mouse_y = wl_fixed_to_int(dy);
432 mw->backend.pending.mouse_dx = wl_fixed_to_int(dx);
433 mw->backend.pending.mouse_dy = wl_fixed_to_int(dy);
434 event_push_pending(mw);
435}
436
437static void keyboard_keymap(void* data, struct wl_keyboard* keyboard, uint32_t format,
438 int32_t fd, uint32_t size)
439{
440 Maus* mw = data;
441 MausBackend* be = &mw->backend;
442 char* map;
443
444 (void)keyboard;
445
446 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
447 close(fd);
448 return;
449 }
450
451 map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
452 if (map == MAP_FAILED) {
453 close(fd);
454 return;
455 }
456
457 if (be->xkb_state)
458 xkb_state_unref(be->xkb_state);
459 if (be->xkb_keymap)
460 xkb_keymap_unref(be->xkb_keymap);
461
462 be->xkb_keymap = xkb_keymap_new_from_string(
463 be->xkb_context, map,
464 XKB_KEYMAP_FORMAT_TEXT_V1,
465 XKB_KEYMAP_COMPILE_NO_FLAGS
466 );
467
468 munmap(map, size);
469 close(fd);
470
471 if (!be->xkb_keymap)
472 return;
473
474 be->xkb_state = xkb_state_new(be->xkb_keymap);
475}
476
477static void keyboard_enter(void* data, struct wl_keyboard* keyboard, uint32_t serial,
478 struct wl_surface* surface, struct wl_array* keys)
479{
480 (void)data;
481 (void)keyboard;
482 (void)serial;
483 (void)surface;
484 (void)keys;
485}
486
487static void keyboard_leave(void* data, struct wl_keyboard* keyboard, uint32_t serial,
488 struct wl_surface* surface)
489{
490 Maus* mw = data;
491
492 (void)keyboard;
493 (void)serial;
494 (void)surface;
495
496 mw->backend.repeat_key_code = 0;
497}
498
499static void keyboard_key(void* data, struct wl_keyboard* keyboard, uint32_t serial,
500 uint32_t time, uint32_t key, uint32_t state)
501{
502 Maus* mw = data;
503 MausBackend* be = &mw->backend;
504 uint32_t code;
505 xkb_keysym_t sym;
506 MausKey maus_key;
507 char text[8];
508 int len;
509 uint8_t pressed;
510
511 (void)keyboard;
512 (void)serial;
513 (void)time;
514
515 code = key + 8;
516 sym = XKB_KEY_NoSymbol;
517 text[0] = 0;
518
519 if (be->xkb_state) {
520 sym = xkb_state_key_get_one_sym(be->xkb_state, code);
521 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
522 len = xkb_state_key_get_utf8(be->xkb_state, code, text, sizeof(text));
523 if (len <= 0)
524 text[0] = 0;
525 }
526 }
527
528 maus_key = keysym_to_mauskey(sym);
529 if (maus_key == MAUS_KEY_ENTER)
530 text[0] = '\n';
531 pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED;
532 event_push_key(mw, code, maus_key, text[0], pressed);
533
534 if (code < MAUS_KEYCODE_LAST)
535 mw->key_codes[code] = pressed;
536 if (maus_key != MAUS_KEY_NONE)
537 mw->key_syms[maus_key] = pressed;
538
539 if (!pressed) {
540 if (be->repeat_key_code == code)
541 be->repeat_key_code = 0;
542 return;
543 }
544
545 if (be->repeat_rate > 0 && be->xkb_keymap &&
546 xkb_keymap_key_repeats(be->xkb_keymap, code)) {
547 be->repeat_key_code = code;
548 be->repeat_key_sym = maus_key;
549 be->repeat_key_text = text[0];
550 be->repeat_next_ns = maus_get_time_ns() + be->repeat_delay * 1000000;
551 }
552 else
553 be->repeat_key_code = 0;
554}
555
556static void keyboard_modifiers(void* data, struct wl_keyboard* keyboard, uint32_t serial,
557 uint32_t mods_depressed, uint32_t mods_latched,
558 uint32_t mods_locked, uint32_t group)
559{
560 Maus* mw = data;
561
562 (void)keyboard;
563 (void)serial;
564
565 if (!mw->backend.xkb_state)
566 return;
567
568 xkb_state_update_mask(
569 mw->backend.xkb_state, mods_depressed,
570 mods_latched, mods_locked,
571 0, 0, group
572 );
573}
574
575static void keyboard_repeat_info(void* data, struct wl_keyboard* keyboard,
576 int32_t rate, int32_t delay)
577{
578 Maus* mw = data;
579
580 (void)keyboard;
581
582 mw->backend.repeat_rate = rate;
583 mw->backend.repeat_delay = delay;
584 if (rate <= 0)
585 mw->backend.repeat_key_code = 0;
586}
587
588static int8_t repeat_push_due(Maus* mw)
589{
590 MausBackend* be = &mw->backend;
591 uint64_t now;
592 uint64_t interval;
593
594 if (be->repeat_key_code == 0 || be->repeat_rate <= 0)
595 return 0;
596
597 now = maus_get_time_ns();
598 if (now < be->repeat_next_ns)
599 return 0;
600
601 event_push_key(mw, be->repeat_key_code, be->repeat_key_sym, be->repeat_key_text, 1);
602 interval = 1000000000 / be->repeat_rate;
603 do {
604 be->repeat_next_ns += interval;
605 } while (be->repeat_next_ns <= now);
606
607 return 1;
608}
609
610static int32_t repeat_timeout_ms(Maus* mw)
611{
612 MausBackend* be = &mw->backend;
613 uint64_t now;
614 uint64_t diff;
615
616 if (be->repeat_key_code == 0 || be->repeat_rate <= 0)
617 return -1;
618
619 now = maus_get_time_ns();
620 if (now >= be->repeat_next_ns)
621 return 0;
622
623 diff = be->repeat_next_ns - now;
624 /* round xto nearest ms */
625 return (int32_t)((diff + 999999) / 1000000);
626}
627
628static int8_t display_read(Maus* mw, int timeout_ms)
629{
630 MausBackend* be = &mw->backend;
631 struct pollfd pfd;
632 int ret;
633
634 if (wl_display_prepare_read(be->display) != 0) {
635 wl_display_dispatch_pending(be->display);
636 return 1;
637 }
638
639 wl_display_flush(be->display);
640
641 pfd.fd = wl_display_get_fd(be->display);
642 pfd.events = POLLIN;
643 pfd.revents = 0;
644
645 ret = poll(&pfd, 1, timeout_ms);
646 if (ret > 0 && (pfd.revents & POLLIN)) {
647 if (wl_display_read_events(be->display) == -1) {
648 wl_display_cancel_read(be->display);
649 return -1;
650 }
651 wl_display_dispatch_pending(be->display);
652 return 1;
653 }
654
655 wl_display_cancel_read(be->display);
656 return 0;
657}
658
659
660static int8_t cursor_init(Maus* mw)
661{
662 MausBackend* be = &mw->backend;
663
664 be->cursor_theme = wl_cursor_theme_load(NULL, 24, be->shm);
665 if (!be->cursor_theme)
666 return 0;
667
668 be->cursor = wl_cursor_theme_get_cursor(be->cursor_theme, "left_ptr");
669 if (!be->cursor)
670 return 0;
671
672 be->cursor_surface = wl_compositor_create_surface(be->compositor);
673 if (!be->cursor_surface)
674 return 0;
675
676 return 1;
677}
678
679static void cursor_destroy(Maus* mw)
680{
681 MausBackend* be = &mw->backend;
682
683 if (be->cursor_surface)
684 wl_surface_destroy(be->cursor_surface);
685 if (be->cursor_theme)
686 wl_cursor_theme_destroy(be->cursor_theme);
687
688 be->cursor_surface = NULL;
689 be->cursor_theme = NULL;
690 be->cursor = NULL;
691}
692
693static void cursor_hide(Maus* mw)
694{
695 MausBackend* be = &mw->backend;
696
697 if (!be->pointer)
698 return;
699
700 wl_pointer_set_cursor(be->pointer, be->pointer_enter_serial, NULL, 0, 0);
701}
702
703static void cursor_lock(Maus* mw)
704{
705 MausBackend* be = &mw->backend;
706
707 if (be->locked_pointer)
708 return;
709 if (!be->pointer_constraints || !be->surface || !be->pointer)
710 return;
711
712 be->locked_pointer = zwp_pointer_constraints_v1_confine_pointer(
713 be->pointer_constraints, be->surface, be->pointer, NULL,
714 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT
715 );
716}
717
718static void cursor_unlock(Maus* mw)
719{
720 MausBackend* be = &mw->backend;
721
722 if (be->locked_pointer) {
723 zwp_confined_pointer_v1_destroy(be->locked_pointer);
724 be->locked_pointer = NULL;
725 }
726
727 if (be->cursor_state == MAUS_CURSOR_STATE_VISIBLE)
728 cursor_show(mw);
729}
730
731static void cursor_show(Maus* mw)
732{
733 MausBackend* be = &mw->backend;
734 struct wl_cursor_image* image;
735 struct wl_buffer* buffer;
736
737 if (!be->pointer || !be->cursor || !be->cursor_surface)
738 return;
739
740 image = be->cursor->images[0];
741 buffer = wl_cursor_image_get_buffer(image);
742 if (!buffer)
743 return;
744
745 wl_pointer_set_cursor(
746 be->pointer, be->pointer_enter_serial,
747 be->cursor_surface, image->hotspot_x, image->hotspot_y
748 );
749 wl_surface_attach(be->cursor_surface, buffer, 0, 0);
750 wl_surface_damage_buffer(be->cursor_surface, 0, 0, image->width, image->height);
751 wl_surface_commit(be->cursor_surface);
752}
753
754static void cursor_relative(Maus* mw)
755{
756 MausBackend* be = &mw->backend;
757 if (be->relative_pointer)
758 return;
759 if (!be->relative_pointer_manager || !be->pointer)
760 return;
761
762 cursor_lock(mw);
763 be->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
764 be->relative_pointer_manager, be->pointer
765 );
766 zwp_relative_pointer_v1_add_listener(
767 be->relative_pointer, &relative_pointer_listener, mw
768 );
769}
770
771static void cursor_absolute(Maus* mw)
772{
773 MausBackend* be = &mw->backend;
774
775 if (be->relative_pointer) {
776 zwp_relative_pointer_v1_destroy(be->relative_pointer);
777 be->relative_pointer = NULL;
778 }
779
780 cursor_unlock(mw);
781}
782
783static int8_t fb_create(Maus* mw);
784static int8_t fb_create_buffer(Maus* mw, MausWLBuffer* wb);
785static int8_t fb_create_shm(size_t size);
786static void fb_destroy(Maus* mw);
787static int8_t handle_event(Maus* mw, MausEvent* ev);
788
789static int8_t fb_create(Maus* mw)
790{
791 MausBackend* be = &mw->backend;
792
793 if (!fb_create_buffer(mw, &be->buffers[0])) {
794 fb_destroy(mw);
795 return 0;
796 }
797 if (!fb_create_buffer(mw, &be->buffers[1])) {
798 fb_destroy(mw);
799 return 0;
800 }
801
802 mw->fb = be->buffers[0].data;
803 mw->stride = mw->width;
804 return 1;
805}
806
807static int8_t fb_create_buffer(Maus* mw, MausWLBuffer* wb)
808{
809 MausBackend* be = &mw->backend;
810 struct wl_shm_pool* pool;
811
812 int fd;
813 int stride;
814 int size;
815
816 stride = mw->width * 4;
817 size = stride * mw->height;
818
819 fd = fb_create_shm(size);
820 if (fd < 0)
821 return 0;
822
823 wb->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
824 if (wb->data == MAP_FAILED) {
825 wb->data = NULL;
826 close(fd);
827 return 0;
828 }
829
830 pool = wl_shm_create_pool(be->shm, fd, size);
831 wb->buffer = wl_shm_pool_create_buffer(
832 pool, 0, mw->width, mw->height,
833 stride, WL_SHM_FORMAT_XRGB8888
834 );
835
836 wl_shm_pool_destroy(pool);
837 close(fd);
838
839 if (!wb->buffer) {
840 munmap(wb->data, size);
841 wb->data = NULL;
842 return 0;
843 }
844
845 wl_buffer_add_listener(wb->buffer, &wl_buffer_listener, wb);
846 wb->size = size;
847 wb->busy = 0;
848
849 return 1;
850}
851
852static int8_t fb_create_shm(size_t size)
853{
854 char name[64];
855 int fd;
856
857 int i;
858
859 for (i = 0; i < 100; i++) {
860 snprintf(name, sizeof(name), "/maus-%ld-%d", (long)getpid(), i);
861
862 fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
863 if (fd >= 0) {
864 shm_unlink(name);
865
866 if (ftruncate(fd, size) < 0) {
867 close(fd);
868 return -1;
869 }
870
871 return fd;
872 }
873 }
874
875 return -1;
876}
877
878static void fb_destroy(Maus* mw)
879{
880 MausBackend* be = &mw->backend;
881
882 uint32_t i;
883
884 for (i = 0; i < 2; i++) {
885 if (be->buffers[i].buffer)
886 wl_buffer_destroy(be->buffers[i].buffer);
887
888 if (be->buffers[i].data && be->buffers[i].size > 0)
889 munmap(be->buffers[i].data, be->buffers[i].size);
890
891 be->buffers[i].buffer = NULL;
892 be->buffers[i].data = NULL;
893 be->buffers[i].size = 0;
894 be->buffers[i].busy = 0;
895 }
896 mw->fb = NULL;
897}
898
899static int8_t handle_event(Maus* mw, MausEvent* ev)
900{
901 MausEventPending pending;
902
903 if (!event_pop(mw, &pending))
904 return 0;
905
906 ev->type = pending.type;
907
908 switch (ev->type) {
909 case MAUS_EV_CLOSE:
910 return 1;
911
912 case MAUS_EV_RESIZE:
913 ev->resize.width = pending.width;
914 ev->resize.height = pending.height;
915 return 1;
916
917 case MAUS_EV_MOUSE_MOTION:
918 ev->mouse.motion.x = pending.mouse_x;
919 ev->mouse.motion.y = pending.mouse_y;
920 ev->mouse.motion.dx = pending.mouse_dx;
921 ev->mouse.motion.dy = pending.mouse_dy;
922 return 1;
923
924 case MAUS_EV_MOUSE_BUTTON:
925 ev->mouse.button.button = pending.mouse_button;
926 ev->mouse.button.pressed = pending.mouse_pressed;
927 return 1;
928
929 case MAUS_EV_KEY:
930 ev->key.code = pending.key_code;
931 ev->key.pressed = pending.key_pressed;
932 ev->key.key = pending.key_sym;
933 ev->key.text = pending.key_text;
934 return 1;
935
936 default:
937 return 1;
938 }
939}
940
941static MausMouseButton wl_button_to_maus(uint32_t button)
942{
943 switch (button) {
944 case 0x110: return MAUS_MOUSE_BUTTON_LEFT;
945 case 0x111: return MAUS_MOUSE_BUTTON_RIGHT;
946 case 0x112: return MAUS_MOUSE_BUTTON_MIDDLE;
947 default: return MAUS_MOUSE_BUTTON_NONE;
948 }
949}
950
951static MausKey keysym_to_mauskey(xkb_keysym_t sym)
952{
953 if (sym >= 0x20 && sym <= 0x7E)
954 return (MausKey)sym;
955
956 if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F12)
957 return MAUS_KEY_F1 + (sym - XKB_KEY_F1);
958 if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9)
959 return MAUS_KEY_KP_0 + (sym - XKB_KEY_KP_0);
960
961 switch (sym) {
962 case XKB_KEY_BackSpace: return MAUS_KEY_BACKSPACE;
963 case XKB_KEY_Tab: return MAUS_KEY_TAB;
964 case XKB_KEY_Return: return MAUS_KEY_ENTER;
965 case XKB_KEY_Escape: return MAUS_KEY_ESCAPE;
966 case XKB_KEY_Delete: return MAUS_KEY_DELETE;
967 case XKB_KEY_Left: return MAUS_KEY_LEFT;
968 case XKB_KEY_Right: return MAUS_KEY_RIGHT;
969 case XKB_KEY_Up: return MAUS_KEY_UP;
970 case XKB_KEY_Down: return MAUS_KEY_DOWN;
971 case XKB_KEY_Home: return MAUS_KEY_HOME;
972 case XKB_KEY_End: return MAUS_KEY_END;
973 case XKB_KEY_Page_Up: return MAUS_KEY_PAGE_UP;
974 case XKB_KEY_Page_Down: return MAUS_KEY_PAGE_DOWN;
975 case XKB_KEY_Insert: return MAUS_KEY_INSERT;
976 case XKB_KEY_Shift_L: return MAUS_KEY_SHIFT_L;
977 case XKB_KEY_Shift_R: return MAUS_KEY_SHIFT_R;
978 case XKB_KEY_Control_L: return MAUS_KEY_CONTROL_L;
979 case XKB_KEY_Control_R: return MAUS_KEY_CONTROL_R;
980 case XKB_KEY_Alt_L: return MAUS_KEY_ALT_L;
981 case XKB_KEY_Alt_R: return MAUS_KEY_ALT_R;
982 case XKB_KEY_Super_L: return MAUS_KEY_SUPER_L;
983 case XKB_KEY_Super_R: return MAUS_KEY_SUPER_R;
984 case XKB_KEY_Caps_Lock: return MAUS_KEY_CAPS_LOCK;
985 case XKB_KEY_Num_Lock: return MAUS_KEY_NUM_LOCK;
986 case XKB_KEY_Scroll_Lock: return MAUS_KEY_SCROLL_LOCK;
987 case XKB_KEY_Print: return MAUS_KEY_PRINT_SCREEN;
988 case XKB_KEY_Pause: return MAUS_KEY_PAUSE;
989 case XKB_KEY_Menu: return MAUS_KEY_MENU;
990 case XKB_KEY_KP_Decimal: return MAUS_KEY_KP_DECIMAL;
991 case XKB_KEY_KP_Divide: return MAUS_KEY_KP_DIVIDE;
992 case XKB_KEY_KP_Multiply: return MAUS_KEY_KP_MULTIPLY;
993 case XKB_KEY_KP_Subtract: return MAUS_KEY_KP_SUBTRACT;
994 case XKB_KEY_KP_Add: return MAUS_KEY_KP_ADD;
995 case XKB_KEY_KP_Enter: return MAUS_KEY_KP_ENTER;
996 case XKB_KEY_KP_Equal: return MAUS_KEY_KP_EQUAL;
997 default: return MAUS_KEY_NONE;
998 }
999}
1000
1001void maus_clear(Maus* mw, MausColor col)
1002{
1003 uint32_t up = MAUS_UNPACK_COL(col);
1004 uint32_t pxs = mw->height * mw->width;
1005
1006 uint32_t i;
1007
1008 for (i = 0; i < pxs; i++)
1009 mw->bfb[i] = up;
1010}
1011
1012/* TODO */
1013void maus_clipboard_set_text(Maus* mw, const char* text)
1014{
1015 (void)mw;
1016 (void)text;
1017}
1018
1019/* TODO */
1020char* maus_clipboard_get_text(Maus* mw)
1021{
1022 (void)mw;
1023 return NULL;
1024}
1025
1026void maus_close(Maus* mw)
1027{
1028 MausBackend* be;
1029
1030 if (!mw)
1031 return;
1032
1033 be = &mw->backend;
1034
1035 fb_destroy(mw);
1036 cursor_absolute(mw);
1037
1038 if (be->xdg_toplevel)
1039 xdg_toplevel_destroy(be->xdg_toplevel);
1040
1041 if (be->xdg_surface)
1042 xdg_surface_destroy(be->xdg_surface);
1043
1044 if (be->surface)
1045 wl_surface_destroy(be->surface);
1046
1047 if (be->keyboard)
1048 wl_keyboard_destroy(be->keyboard);
1049
1050 if (be->pointer)
1051 wl_pointer_destroy(be->pointer);
1052
1053 if (be->seat)
1054 wl_seat_destroy(be->seat);
1055
1056 if (be->pointer_constraints)
1057 zwp_pointer_constraints_v1_destroy(be->pointer_constraints);
1058
1059 if (be->relative_pointer_manager)
1060 zwp_relative_pointer_manager_v1_destroy(be->relative_pointer_manager);
1061
1062 if (be->xkb_state)
1063 xkb_state_unref(be->xkb_state);
1064
1065 if (be->xkb_keymap)
1066 xkb_keymap_unref(be->xkb_keymap);
1067
1068 if (be->xkb_context)
1069 xkb_context_unref(be->xkb_context);
1070
1071 cursor_destroy(mw);
1072
1073 if (be->wm_base)
1074 xdg_wm_base_destroy(be->wm_base);
1075
1076 if (be->shm)
1077 wl_shm_destroy(be->shm);
1078
1079 if (be->compositor)
1080 wl_compositor_destroy(be->compositor);
1081
1082 if (be->registry)
1083 wl_registry_destroy(be->registry);
1084
1085 if (be->display) {
1086 wl_display_flush(be->display);
1087 wl_display_disconnect(be->display);
1088 }
1089
1090 free(mw->bfb);
1091 free(mw);
1092}
1093
1094int8_t maus_close_window(Maus* mw)
1095{
1096 MausBackend* be = &mw->backend;
1097
1098 cursor_unlock(mw);
1099 fb_destroy(mw);
1100
1101 if (be->xdg_toplevel)
1102 xdg_toplevel_destroy(be->xdg_toplevel);
1103 if (be->xdg_surface)
1104 xdg_surface_destroy(be->xdg_surface);
1105 if (be->surface)
1106 wl_surface_destroy(be->surface);
1107
1108 be->xdg_toplevel = NULL;
1109 be->xdg_surface = NULL;
1110 be->surface = NULL;
1111 be->configured = 0;
1112
1113 return 1;
1114}
1115
1116int8_t maus_create_window(Maus* mw)
1117{
1118 MausBackend* be = &mw->backend;
1119
1120 be->surface = wl_compositor_create_surface(be->compositor);
1121 if (!be->surface)
1122 return 0;
1123
1124 be->xdg_surface = xdg_wm_base_get_xdg_surface(be->wm_base, be->surface);
1125 if (!be->xdg_surface)
1126 return 0;
1127
1128 xdg_surface_add_listener(be->xdg_surface, &xdg_surface_listener, mw);
1129
1130 be->xdg_toplevel = xdg_surface_get_toplevel(be->xdg_surface);
1131 if (!be->xdg_toplevel)
1132 return 0;
1133
1134 xdg_toplevel_add_listener(be->xdg_toplevel, &xdg_toplevel_listener, mw);
1135 xdg_toplevel_set_title(be->xdg_toplevel, mw->title);
1136
1137 if (!fb_create(mw))
1138 return 0;
1139
1140 wl_surface_commit(be->surface);
1141
1142 while (!be->configured) {
1143 if (wl_display_dispatch(be->display) == -1)
1144 return 0;
1145 }
1146
1147 return 1;
1148}
1149
1150Maus* maus_init(const char* title, int x, int y, int width, int height)
1151{
1152 Maus* mw = calloc(1, sizeof(Maus));
1153 MausBackend* be = &mw->backend;
1154
1155 if (!mw)
1156 return NULL;
1157
1158 be->display = wl_display_connect(NULL);
1159 if (!be->display) {
1160 free(mw);
1161 return NULL;
1162 }
1163
1164 be->registry = wl_display_get_registry(be->display);
1165 wl_registry_add_listener(be->registry, ®istry_listener, mw);
1166 wl_display_roundtrip(be->display);
1167
1168 be->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1169 if (!be->xkb_context)
1170 goto close;
1171
1172 if (!be->compositor || !be->shm || !be->wm_base) {
1173 maus_close(mw);
1174 free(mw);
1175 return NULL;
1176 }
1177
1178 if (!cursor_init(mw))
1179 goto close;
1180
1181 mw->frame_time_last = maus_get_time_ns();
1182 mw->title = title;
1183 mw->x = x;
1184 mw->y = y;
1185 mw->width = width;
1186 mw->height = height;
1187 mw->stride = width;
1188 be->cursor_state = MAUS_CURSOR_STATE_VISIBLE;
1189
1190 mw->bfb = calloc(mw->stride * mw->height, sizeof(uint32_t));
1191 if (!mw->bfb)
1192 goto close;
1193
1194 return mw;
1195close:
1196 maus_close(mw);
1197 free(mw);
1198 return NULL;
1199}
1200
1201int8_t maus_event_poll(Maus* mw, MausEvent* ev)
1202{
1203 MausBackend* be = &mw->backend;
1204 int8_t read;
1205
1206 ev->type = MAUS_EV_NONE;
1207
1208 if (repeat_push_due(mw) && handle_event(mw, ev))
1209 return 1;
1210
1211 if (handle_event(mw, ev))
1212 return 1;
1213
1214 wl_display_dispatch_pending(be->display);
1215 if (handle_event(mw, ev))
1216 return 1;
1217
1218 read = display_read(mw, 0);
1219 if (read == -1) {
1220 ev->type = MAUS_EV_CLOSE;
1221 return 1;
1222 }
1223 if (read == 0)
1224 return 0;
1225 if (repeat_push_due(mw) && handle_event(mw, ev))
1226 return 1;
1227
1228 return handle_event(mw, ev);
1229}
1230
1231void maus_event_wait(Maus* mw, MausEvent* ev)
1232{
1233 MausBackend* be = &mw->backend;
1234 int32_t timeout;
1235 int8_t read;
1236
1237 ev->type = MAUS_EV_NONE;
1238
1239 for (;;) {
1240 if (repeat_push_due(mw) && handle_event(mw, ev))
1241 return;
1242
1243 if (handle_event(mw, ev))
1244 return;
1245
1246 timeout = repeat_timeout_ms(mw);
1247 if (timeout >= 0) {
1248 read = display_read(mw, timeout);
1249 if (read == -1) {
1250 ev->type = MAUS_EV_CLOSE;
1251 return;
1252 }
1253 continue;
1254 }
1255
1256 if (wl_display_dispatch(be->display) == -1) {
1257 ev->type = MAUS_EV_CLOSE;
1258 return;
1259 }
1260 }
1261}
1262
1263void maus_present(Maus* mw)
1264{
1265 MausBackend* be = &mw->backend;
1266 MausWLBuffer* wb = NULL;
1267 size_t bytes;
1268
1269 uint32_t i;
1270
1271 for (i = 0; i < 2; i++) {
1272 if (be->buffers[i].buffer && !be->buffers[i].busy) {
1273 wb = &be->buffers[i];
1274 break;
1275 }
1276 }
1277
1278 if (!wb)
1279 return;
1280
1281 bytes = mw->stride * mw->height * sizeof(uint32_t);
1282 memcpy(wb->data, mw->bfb, bytes);
1283 mw->fb = wb->data;
1284 wb->busy = 1;
1285
1286 wl_surface_attach(be->surface, wb->buffer, 0, 0);
1287 wl_surface_damage_buffer(be->surface, 0, 0, mw->width, mw->height);
1288 wl_surface_commit(be->surface);
1289 wl_display_flush(be->display);
1290}
1291
1292int8_t maus_resize(Maus* mw, uint32_t width, uint32_t height)
1293{
1294 uint32_t* bfb;
1295
1296 if (width == 0 || height == 0)
1297 return 0;
1298
1299 bfb = calloc(width * height, sizeof(uint32_t));
1300 if (!bfb)
1301 return 0;
1302
1303 fb_destroy(mw);
1304 free(mw->bfb);
1305
1306 mw->width = width;
1307 mw->height = height;
1308 mw->stride = width;
1309 mw->bfb = bfb;
1310
1311 if (!fb_create(mw)) {
1312 free(mw->bfb);
1313 mw->bfb = NULL;
1314 return 0;
1315 }
1316
1317 return 1;
1318}
1319
1320void maus_cur_set_mode(Maus* mw, MausCursorState state)
1321{
1322 switch (state) {
1323 case MAUS_CURSOR_STATE_VISIBLE:
1324 cursor_show(mw);
1325 mw->backend.cursor_state = state;
1326 break;
1327 case MAUS_CURSOR_STATE_HIDDEN:
1328 cursor_hide(mw);
1329 mw->backend.cursor_state = state;
1330 break;
1331 case MAUS_CURSOR_STATE_LOCKED:
1332 mw->backend.cursor_state = state;
1333 cursor_lock(mw);
1334 break;
1335 case MAUS_CURSOR_STATE_FREE:
1336 cursor_unlock(mw);
1337 mw->backend.cursor_state = state;
1338 break;
1339 case MAUS_CURSOR_STATE_RELATIVE:
1340 cursor_relative(mw);
1341 mw->backend.cursor_state = state;
1342 break;
1343 case MAUS_CURSOR_STATE_ABSOLUTE:
1344 cursor_absolute(mw);
1345 mw->backend.cursor_state = state;
1346 break;
1347 default:
1348 break;
1349 }
1350}
1351