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_confine(Maus* mw);
52static void cursor_unconfine(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_visible)
284 cursor_show(mw);
285 else
286 cursor_hide(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_confine(Maus* mw)
704{
705 MausBackend* be = &mw->backend;
706
707 be->cursor_confined = 1;
708 if (be->relative_pointer || be->confined_pointer)
709 return;
710 if (!be->pointer_constraints || !be->surface || !be->pointer)
711 return;
712
713 be->confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
714 be->pointer_constraints, be->surface, be->pointer, NULL,
715 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT
716 );
717}
718
719static void cursor_unconfine(Maus* mw)
720{
721 MausBackend* be = &mw->backend;
722
723 be->cursor_confined = 0;
724 if (be->confined_pointer) {
725 zwp_confined_pointer_v1_destroy(be->confined_pointer);
726 be->confined_pointer = NULL;
727 }
728}
729
730static void cursor_show(Maus* mw)
731{
732 MausBackend* be = &mw->backend;
733 struct wl_cursor_image* image;
734 struct wl_buffer* buffer;
735
736 if (!be->pointer || !be->cursor || !be->cursor_surface)
737 return;
738
739 image = be->cursor->images[0];
740 buffer = wl_cursor_image_get_buffer(image);
741 if (!buffer)
742 return;
743
744 wl_pointer_set_cursor(
745 be->pointer, be->pointer_enter_serial,
746 be->cursor_surface, image->hotspot_x, image->hotspot_y
747 );
748 wl_surface_attach(be->cursor_surface, buffer, 0, 0);
749 wl_surface_damage_buffer(be->cursor_surface, 0, 0, image->width, image->height);
750 wl_surface_commit(be->cursor_surface);
751}
752
753static void cursor_relative(Maus* mw)
754{
755 MausBackend* be = &mw->backend;
756
757 if (be->relative_pointer)
758 return;
759 if (!be->relative_pointer_manager || !be->pointer_constraints ||
760 !be->surface || !be->pointer)
761 return;
762
763 if (be->confined_pointer) {
764 zwp_confined_pointer_v1_destroy(be->confined_pointer);
765 be->confined_pointer = NULL;
766 }
767
768 be->locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
769 be->pointer_constraints, be->surface, be->pointer, NULL,
770 ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT
771 );
772 be->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
773 be->relative_pointer_manager, be->pointer
774 );
775 zwp_relative_pointer_v1_add_listener(
776 be->relative_pointer, &relative_pointer_listener, mw
777 );
778}
779
780static void cursor_absolute(Maus* mw)
781{
782 MausBackend* be = &mw->backend;
783
784 if (be->relative_pointer) {
785 zwp_relative_pointer_v1_destroy(be->relative_pointer);
786 be->relative_pointer = NULL;
787 }
788 if (be->locked_pointer) {
789 zwp_locked_pointer_v1_destroy(be->locked_pointer);
790 be->locked_pointer = NULL;
791 }
792
793 if (be->cursor_confined)
794 cursor_confine(mw);
795}
796
797static int8_t fb_create(Maus* mw);
798static int8_t fb_create_buffer(Maus* mw, MausWLBuffer* wb);
799static int8_t fb_create_shm(size_t size);
800static void fb_destroy(Maus* mw);
801static int8_t handle_event(Maus* mw, MausEvent* ev);
802
803static int8_t fb_create(Maus* mw)
804{
805 MausBackend* be = &mw->backend;
806
807 if (!fb_create_buffer(mw, &be->buffers[0])) {
808 fb_destroy(mw);
809 return 0;
810 }
811 if (!fb_create_buffer(mw, &be->buffers[1])) {
812 fb_destroy(mw);
813 return 0;
814 }
815
816 mw->fb = be->buffers[0].data;
817 mw->stride = mw->width;
818 return 1;
819}
820
821static int8_t fb_create_buffer(Maus* mw, MausWLBuffer* wb)
822{
823 MausBackend* be = &mw->backend;
824 struct wl_shm_pool* pool;
825
826 int fd;
827 int stride;
828 int size;
829
830 stride = mw->width * 4;
831 size = stride * mw->height;
832
833 fd = fb_create_shm(size);
834 if (fd < 0)
835 return 0;
836
837 wb->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
838 if (wb->data == MAP_FAILED) {
839 wb->data = NULL;
840 close(fd);
841 return 0;
842 }
843
844 pool = wl_shm_create_pool(be->shm, fd, size);
845 wb->buffer = wl_shm_pool_create_buffer(
846 pool, 0, mw->width, mw->height,
847 stride, WL_SHM_FORMAT_XRGB8888
848 );
849
850 wl_shm_pool_destroy(pool);
851 close(fd);
852
853 if (!wb->buffer) {
854 munmap(wb->data, size);
855 wb->data = NULL;
856 return 0;
857 }
858
859 wl_buffer_add_listener(wb->buffer, &wl_buffer_listener, wb);
860 wb->size = size;
861 wb->busy = 0;
862
863 return 1;
864}
865
866static int8_t fb_create_shm(size_t size)
867{
868 char name[64];
869 int fd;
870
871 int i;
872
873 for (i = 0; i < 100; i++) {
874 snprintf(name, sizeof(name), "/maus-%ld-%d", (long)getpid(), i);
875
876 fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
877 if (fd >= 0) {
878 shm_unlink(name);
879
880 if (ftruncate(fd, size) < 0) {
881 close(fd);
882 return -1;
883 }
884
885 return fd;
886 }
887 }
888
889 return -1;
890}
891
892static void fb_destroy(Maus* mw)
893{
894 MausBackend* be = &mw->backend;
895
896 uint32_t i;
897
898 for (i = 0; i < 2; i++) {
899 if (be->buffers[i].buffer)
900 wl_buffer_destroy(be->buffers[i].buffer);
901
902 if (be->buffers[i].data && be->buffers[i].size > 0)
903 munmap(be->buffers[i].data, be->buffers[i].size);
904
905 be->buffers[i].buffer = NULL;
906 be->buffers[i].data = NULL;
907 be->buffers[i].size = 0;
908 be->buffers[i].busy = 0;
909 }
910 mw->fb = NULL;
911}
912
913static int8_t handle_event(Maus* mw, MausEvent* ev)
914{
915 MausEventPending pending;
916
917 if (!event_pop(mw, &pending))
918 return 0;
919
920 ev->type = pending.type;
921
922 switch (ev->type) {
923 case MAUS_EV_CLOSE:
924 return 1;
925
926 case MAUS_EV_RESIZE:
927 ev->resize.width = pending.width;
928 ev->resize.height = pending.height;
929 return 1;
930
931 case MAUS_EV_MOUSE_MOTION:
932 ev->mouse.motion.x = pending.mouse_x;
933 ev->mouse.motion.y = pending.mouse_y;
934 ev->mouse.motion.dx = pending.mouse_dx;
935 ev->mouse.motion.dy = pending.mouse_dy;
936 return 1;
937
938 case MAUS_EV_MOUSE_BUTTON:
939 ev->mouse.button.button = pending.mouse_button;
940 ev->mouse.button.pressed = pending.mouse_pressed;
941 return 1;
942
943 case MAUS_EV_KEY:
944 ev->key.code = pending.key_code;
945 ev->key.pressed = pending.key_pressed;
946 ev->key.key = pending.key_sym;
947 ev->key.text = pending.key_text;
948 return 1;
949
950 default:
951 return 1;
952 }
953}
954
955static MausMouseButton wl_button_to_maus(uint32_t button)
956{
957 switch (button) {
958 case 0x110: return MAUS_MOUSE_BUTTON_LEFT;
959 case 0x111: return MAUS_MOUSE_BUTTON_RIGHT;
960 case 0x112: return MAUS_MOUSE_BUTTON_MIDDLE;
961 default: return MAUS_MOUSE_BUTTON_NONE;
962 }
963}
964
965static MausKey keysym_to_mauskey(xkb_keysym_t sym)
966{
967 if (sym >= 0x20 && sym <= 0x7E)
968 return (MausKey)sym;
969
970 if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F12)
971 return MAUS_KEY_F1 + (sym - XKB_KEY_F1);
972 if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9)
973 return MAUS_KEY_KP_0 + (sym - XKB_KEY_KP_0);
974
975 switch (sym) {
976 case XKB_KEY_BackSpace: return MAUS_KEY_BACKSPACE;
977 case XKB_KEY_Tab: return MAUS_KEY_TAB;
978 case XKB_KEY_Return: return MAUS_KEY_ENTER;
979 case XKB_KEY_Escape: return MAUS_KEY_ESCAPE;
980 case XKB_KEY_Delete: return MAUS_KEY_DELETE;
981 case XKB_KEY_Left: return MAUS_KEY_LEFT;
982 case XKB_KEY_Right: return MAUS_KEY_RIGHT;
983 case XKB_KEY_Up: return MAUS_KEY_UP;
984 case XKB_KEY_Down: return MAUS_KEY_DOWN;
985 case XKB_KEY_Home: return MAUS_KEY_HOME;
986 case XKB_KEY_End: return MAUS_KEY_END;
987 case XKB_KEY_Page_Up: return MAUS_KEY_PAGE_UP;
988 case XKB_KEY_Page_Down: return MAUS_KEY_PAGE_DOWN;
989 case XKB_KEY_Insert: return MAUS_KEY_INSERT;
990 case XKB_KEY_Shift_L: return MAUS_KEY_SHIFT_L;
991 case XKB_KEY_Shift_R: return MAUS_KEY_SHIFT_R;
992 case XKB_KEY_Control_L: return MAUS_KEY_CONTROL_L;
993 case XKB_KEY_Control_R: return MAUS_KEY_CONTROL_R;
994 case XKB_KEY_Alt_L: return MAUS_KEY_ALT_L;
995 case XKB_KEY_Alt_R: return MAUS_KEY_ALT_R;
996 case XKB_KEY_Super_L: return MAUS_KEY_SUPER_L;
997 case XKB_KEY_Super_R: return MAUS_KEY_SUPER_R;
998 case XKB_KEY_Caps_Lock: return MAUS_KEY_CAPS_LOCK;
999 case XKB_KEY_Num_Lock: return MAUS_KEY_NUM_LOCK;
1000 case XKB_KEY_Scroll_Lock: return MAUS_KEY_SCROLL_LOCK;
1001 case XKB_KEY_Print: return MAUS_KEY_PRINT_SCREEN;
1002 case XKB_KEY_Pause: return MAUS_KEY_PAUSE;
1003 case XKB_KEY_Menu: return MAUS_KEY_MENU;
1004 case XKB_KEY_KP_Decimal: return MAUS_KEY_KP_DECIMAL;
1005 case XKB_KEY_KP_Divide: return MAUS_KEY_KP_DIVIDE;
1006 case XKB_KEY_KP_Multiply: return MAUS_KEY_KP_MULTIPLY;
1007 case XKB_KEY_KP_Subtract: return MAUS_KEY_KP_SUBTRACT;
1008 case XKB_KEY_KP_Add: return MAUS_KEY_KP_ADD;
1009 case XKB_KEY_KP_Enter: return MAUS_KEY_KP_ENTER;
1010 case XKB_KEY_KP_Equal: return MAUS_KEY_KP_EQUAL;
1011 default: return MAUS_KEY_NONE;
1012 }
1013}
1014
1015void maus_clear(Maus* mw, MausColor col)
1016{
1017 uint32_t up = MAUS_UNPACK_COL(col);
1018 uint32_t pxs = mw->height * mw->width;
1019
1020 uint32_t i;
1021
1022 for (i = 0; i < pxs; i++)
1023 mw->bfb[i] = up;
1024}
1025
1026/* TODO */
1027void maus_clipboard_set_text(Maus* mw, const char* text)
1028{
1029 (void)mw;
1030 (void)text;
1031}
1032
1033/* TODO */
1034char* maus_clipboard_get_text(Maus* mw)
1035{
1036 (void)mw;
1037 return NULL;
1038}
1039
1040void maus_close(Maus* mw)
1041{
1042 MausBackend* be;
1043
1044 if (!mw)
1045 return;
1046
1047 be = &mw->backend;
1048
1049 fb_destroy(mw);
1050 cursor_absolute(mw);
1051 cursor_unconfine(mw);
1052
1053 if (be->xdg_toplevel)
1054 xdg_toplevel_destroy(be->xdg_toplevel);
1055
1056 if (be->xdg_surface)
1057 xdg_surface_destroy(be->xdg_surface);
1058
1059 if (be->surface)
1060 wl_surface_destroy(be->surface);
1061
1062 if (be->keyboard)
1063 wl_keyboard_destroy(be->keyboard);
1064
1065 if (be->pointer)
1066 wl_pointer_destroy(be->pointer);
1067
1068 if (be->seat)
1069 wl_seat_destroy(be->seat);
1070
1071 if (be->pointer_constraints)
1072 zwp_pointer_constraints_v1_destroy(be->pointer_constraints);
1073
1074 if (be->relative_pointer_manager)
1075 zwp_relative_pointer_manager_v1_destroy(be->relative_pointer_manager);
1076
1077 if (be->xkb_state)
1078 xkb_state_unref(be->xkb_state);
1079
1080 if (be->xkb_keymap)
1081 xkb_keymap_unref(be->xkb_keymap);
1082
1083 if (be->xkb_context)
1084 xkb_context_unref(be->xkb_context);
1085
1086 cursor_destroy(mw);
1087
1088 if (be->wm_base)
1089 xdg_wm_base_destroy(be->wm_base);
1090
1091 if (be->shm)
1092 wl_shm_destroy(be->shm);
1093
1094 if (be->compositor)
1095 wl_compositor_destroy(be->compositor);
1096
1097 if (be->registry)
1098 wl_registry_destroy(be->registry);
1099
1100 if (be->display) {
1101 wl_display_flush(be->display);
1102 wl_display_disconnect(be->display);
1103 }
1104
1105 free(mw->bfb);
1106 free(mw);
1107}
1108
1109int8_t maus_close_window(Maus* mw)
1110{
1111 MausBackend* be = &mw->backend;
1112
1113 cursor_absolute(mw);
1114 cursor_unconfine(mw);
1115 fb_destroy(mw);
1116
1117 if (be->xdg_toplevel)
1118 xdg_toplevel_destroy(be->xdg_toplevel);
1119 if (be->xdg_surface)
1120 xdg_surface_destroy(be->xdg_surface);
1121 if (be->surface)
1122 wl_surface_destroy(be->surface);
1123
1124 be->xdg_toplevel = NULL;
1125 be->xdg_surface = NULL;
1126 be->surface = NULL;
1127 be->configured = 0;
1128
1129 return 1;
1130}
1131
1132int8_t maus_create_window(Maus* mw)
1133{
1134 MausBackend* be = &mw->backend;
1135
1136 be->surface = wl_compositor_create_surface(be->compositor);
1137 if (!be->surface)
1138 return 0;
1139
1140 be->xdg_surface = xdg_wm_base_get_xdg_surface(be->wm_base, be->surface);
1141 if (!be->xdg_surface)
1142 return 0;
1143
1144 xdg_surface_add_listener(be->xdg_surface, &xdg_surface_listener, mw);
1145
1146 be->xdg_toplevel = xdg_surface_get_toplevel(be->xdg_surface);
1147 if (!be->xdg_toplevel)
1148 return 0;
1149
1150 xdg_toplevel_add_listener(be->xdg_toplevel, &xdg_toplevel_listener, mw);
1151 xdg_toplevel_set_title(be->xdg_toplevel, mw->title);
1152
1153 if (!fb_create(mw))
1154 return 0;
1155
1156 wl_surface_commit(be->surface);
1157
1158 while (!be->configured) {
1159 if (wl_display_dispatch(be->display) == -1)
1160 return 0;
1161 }
1162
1163 return 1;
1164}
1165
1166Maus* maus_init(const char* title, int x, int y, int width, int height)
1167{
1168 Maus* mw = calloc(1, sizeof(Maus));
1169 MausBackend* be = &mw->backend;
1170
1171 if (!mw)
1172 return NULL;
1173
1174 be->display = wl_display_connect(NULL);
1175 if (!be->display) {
1176 free(mw);
1177 return NULL;
1178 }
1179
1180 be->registry = wl_display_get_registry(be->display);
1181 wl_registry_add_listener(be->registry, ®istry_listener, mw);
1182 wl_display_roundtrip(be->display);
1183
1184 be->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1185 if (!be->xkb_context)
1186 goto close;
1187
1188 if (!be->compositor || !be->shm || !be->wm_base) {
1189 maus_close(mw);
1190 free(mw);
1191 return NULL;
1192 }
1193
1194 if (!cursor_init(mw))
1195 goto close;
1196
1197 mw->frame_time_last = maus_get_time_ns();
1198 mw->title = title;
1199 mw->x = x;
1200 mw->y = y;
1201 mw->width = width;
1202 mw->height = height;
1203 mw->stride = width;
1204 be->cursor_visible = 1;
1205 be->cursor_confined = 0;
1206
1207 mw->bfb = calloc(mw->stride * mw->height, sizeof(uint32_t));
1208 if (!mw->bfb)
1209 goto close;
1210
1211 return mw;
1212close:
1213 maus_close(mw);
1214 free(mw);
1215 return NULL;
1216}
1217
1218int8_t maus_event_poll(Maus* mw, MausEvent* ev)
1219{
1220 MausBackend* be = &mw->backend;
1221 int8_t read;
1222
1223 ev->type = MAUS_EV_NONE;
1224
1225 if (repeat_push_due(mw) && handle_event(mw, ev))
1226 return 1;
1227
1228 if (handle_event(mw, ev))
1229 return 1;
1230
1231 wl_display_dispatch_pending(be->display);
1232 if (handle_event(mw, ev))
1233 return 1;
1234
1235 read = display_read(mw, 0);
1236 if (read == -1) {
1237 ev->type = MAUS_EV_CLOSE;
1238 return 1;
1239 }
1240 if (read == 0)
1241 return 0;
1242 if (repeat_push_due(mw) && handle_event(mw, ev))
1243 return 1;
1244
1245 return handle_event(mw, ev);
1246}
1247
1248void maus_event_wait(Maus* mw, MausEvent* ev)
1249{
1250 MausBackend* be = &mw->backend;
1251 int32_t timeout;
1252 int8_t read;
1253
1254 ev->type = MAUS_EV_NONE;
1255
1256 for (;;) {
1257 if (repeat_push_due(mw) && handle_event(mw, ev))
1258 return;
1259
1260 if (handle_event(mw, ev))
1261 return;
1262
1263 timeout = repeat_timeout_ms(mw);
1264 if (timeout >= 0) {
1265 read = display_read(mw, timeout);
1266 if (read == -1) {
1267 ev->type = MAUS_EV_CLOSE;
1268 return;
1269 }
1270 continue;
1271 }
1272
1273 if (wl_display_dispatch(be->display) == -1) {
1274 ev->type = MAUS_EV_CLOSE;
1275 return;
1276 }
1277 }
1278}
1279
1280void maus_present(Maus* mw)
1281{
1282 MausBackend* be = &mw->backend;
1283 MausWLBuffer* wb = NULL;
1284 size_t bytes;
1285
1286 uint32_t i;
1287
1288 for (i = 0; i < 2; i++) {
1289 if (be->buffers[i].buffer && !be->buffers[i].busy) {
1290 wb = &be->buffers[i];
1291 break;
1292 }
1293 }
1294
1295 if (!wb)
1296 return;
1297
1298 bytes = mw->stride * mw->height * sizeof(uint32_t);
1299 memcpy(wb->data, mw->bfb, bytes);
1300 mw->fb = wb->data;
1301 wb->busy = 1;
1302
1303 wl_surface_attach(be->surface, wb->buffer, 0, 0);
1304 wl_surface_damage_buffer(be->surface, 0, 0, mw->width, mw->height);
1305 wl_surface_commit(be->surface);
1306 wl_display_flush(be->display);
1307}
1308
1309int8_t maus_resize(Maus* mw, uint32_t width, uint32_t height)
1310{
1311 uint32_t* bfb;
1312
1313 if (width == 0 || height == 0)
1314 return 0;
1315
1316 bfb = calloc(width * height, sizeof(uint32_t));
1317 if (!bfb)
1318 return 0;
1319
1320 fb_destroy(mw);
1321 free(mw->bfb);
1322
1323 mw->width = width;
1324 mw->height = height;
1325 mw->stride = width;
1326 mw->bfb = bfb;
1327
1328 if (!fb_create(mw)) {
1329 free(mw->bfb);
1330 mw->bfb = NULL;
1331 return 0;
1332 }
1333
1334 return 1;
1335}
1336
1337void maus_cur_set_mode(Maus* mw, MausCursorState state)
1338{
1339 switch (state) {
1340 case MAUS_CURSOR_STATE_VISIBLE:
1341 mw->backend.cursor_visible = 1;
1342 cursor_show(mw);
1343 break;
1344 case MAUS_CURSOR_STATE_HIDDEN:
1345 mw->backend.cursor_visible = 0;
1346 cursor_hide(mw);
1347 break;
1348 case MAUS_CURSOR_STATE_LOCKED:
1349 cursor_confine(mw);
1350 break;
1351 case MAUS_CURSOR_STATE_FREE:
1352 cursor_unconfine(mw);
1353 break;
1354 case MAUS_CURSOR_STATE_RELATIVE:
1355 cursor_relative(mw);
1356 break;
1357 case MAUS_CURSOR_STATE_ABSOLUTE:
1358 cursor_absolute(mw);
1359 break;
1360 default:
1361 break;
1362 }
1363}
1364