MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
WaylandWindow.cpp
Go to the documentation of this file.
1#include "WaylandWindow.hpp"
2
3#ifdef MAYAFLUX_PLATFORM_LINUX
4
5#include "KeyMapping.hpp"
7
8#include <linux/input-event-codes.h>
9#include <poll.h>
10#include <sys/mman.h>
11#include <sys/timerfd.h>
12#include <unistd.h>
13
14namespace MayaFlux::Core {
15
16// ============================================================================
17// Construction / destruction
18// ============================================================================
19
20WaylandWindow::WaylandWindow(const WindowCreateInfo& create_info,
21 const GlobalGraphicsConfig& graphics_config)
22 : m_display(wl_display_connect(nullptr))
23 , m_create_info(create_info)
24 , m_key_repeat_config(graphics_config.key_repeat_config)
25{
26
27 if (!m_display) {
28 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
29 "wl_display_connect failed");
30 return;
31 }
32
33 m_xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
34
35 m_registry = wl_display_get_registry(m_display);
36 wl_registry_add_listener(m_registry, &s_registry_listener, this);
37 wl_display_roundtrip(m_display);
38
39 if (!m_compositor || !m_xdg_wm_base) {
40 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
41 "Required Wayland globals not found (compositor={}, xdg_wm_base={})",
42 static_cast<void*>(m_compositor), static_cast<void*>(m_xdg_wm_base));
43 return;
44 }
45
46 m_surface = wl_compositor_create_surface(m_compositor);
47 m_xdg_surface = xdg_wm_base_get_xdg_surface(m_xdg_wm_base, m_surface);
48 xdg_surface_add_listener(m_xdg_surface, &s_xdg_surface_listener, this);
49
50 m_xdg_toplevel = xdg_surface_get_toplevel(m_xdg_surface);
51 xdg_toplevel_add_listener(m_xdg_toplevel, &s_toplevel_listener, this);
52 xdg_toplevel_set_title(m_xdg_toplevel, create_info.title.c_str());
53 xdg_toplevel_set_app_id(m_xdg_toplevel, "mayaflux");
54
55 if (m_decoration_manager) {
56 m_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
57 m_decoration_manager, m_xdg_toplevel);
58 zxdg_toplevel_decoration_v1_set_mode(m_decoration,
59 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
60 }
61
62 wl_surface_commit(m_surface);
63 wl_display_roundtrip(m_display);
64
65 m_state.current_width = static_cast<int>(create_info.width);
66 m_state.current_height = static_cast<int>(create_info.height);
67}
68
69WaylandWindow::~WaylandWindow()
70{
71 destroy();
72 if (m_display) {
73 wl_display_disconnect(m_display);
74 m_display = nullptr;
75 }
76}
77
78// ============================================================================
79// Window interface
80// ============================================================================
81
82void WaylandWindow::show()
83{
84 if (m_surface)
85 wl_surface_commit(m_surface);
86}
87
88void WaylandWindow::hide()
89{
90 if (m_surface) {
91 wl_surface_attach(m_surface, nullptr, 0, 0);
92 wl_surface_commit(m_surface);
93 }
94}
95
96void WaylandWindow::destroy()
97{
98 if (m_repeat_fd >= 0) {
99 close(m_repeat_fd);
100 m_repeat_fd = -1;
101 }
102
103 if (m_text_repeat_fd >= 0) {
104 close(m_text_repeat_fd);
105 m_text_repeat_fd = -1;
106 }
107
108 if (m_keyboard) {
109 wl_keyboard_destroy(m_keyboard);
110 m_keyboard = nullptr;
111 }
112 if (m_pointer) {
113 wl_pointer_destroy(m_pointer);
114 m_pointer = nullptr;
115 }
116
117 if (m_xkb_state) {
118 xkb_state_unref(m_xkb_state);
119 m_xkb_state = nullptr;
120 }
121 if (m_xkb_keymap) {
122 xkb_keymap_unref(m_xkb_keymap);
123 m_xkb_keymap = nullptr;
124 }
125 if (m_xkb_context) {
126 xkb_context_unref(m_xkb_context);
127 m_xkb_context = nullptr;
128 }
129
130 if (m_decoration) {
131 zxdg_toplevel_decoration_v1_destroy(m_decoration);
132 m_decoration = nullptr;
133 }
134 if (m_decoration_manager) {
135 zxdg_decoration_manager_v1_destroy(m_decoration_manager);
136 m_decoration_manager = nullptr;
137 }
138 if (m_xdg_toplevel) {
139 xdg_toplevel_destroy(m_xdg_toplevel);
140 m_xdg_toplevel = nullptr;
141 }
142 if (m_xdg_surface) {
143 xdg_surface_destroy(m_xdg_surface);
144 m_xdg_surface = nullptr;
145 }
146 if (m_surface) {
147 wl_surface_destroy(m_surface);
148 m_surface = nullptr;
149 }
150 if (m_xdg_wm_base) {
151 xdg_wm_base_destroy(m_xdg_wm_base);
152 m_xdg_wm_base = nullptr;
153 }
154 if (m_seat) {
155 wl_seat_destroy(m_seat);
156 m_seat = nullptr;
157 }
158 if (m_compositor) {
159 wl_compositor_destroy(m_compositor);
160 m_compositor = nullptr;
161 }
162 if (m_registry) {
163 wl_registry_destroy(m_registry);
164 m_registry = nullptr;
165 }
166}
167
168bool WaylandWindow::should_close() const
169{
170 return m_should_close.load();
171}
172
173void WaylandWindow::request_close()
174{
175 m_event_source.defer([this]() {
176 if (!m_should_close.exchange(true)) {
177 WindowEvent ev;
178 ev.type = WindowEventType::WINDOW_CLOSED;
179 emit(ev);
180 }
181 });
182}
183
184void WaylandWindow::poll()
185{
186 if (!m_display)
187 return;
188
189 while (wl_display_prepare_read(m_display) != 0) {
190 if (wl_display_dispatch_pending(m_display) < 0) {
191 handle_display_error();
192 return;
193 }
194 }
195
196 if (wl_display_flush(m_display) < 0 && errno != EAGAIN) {
197 wl_display_cancel_read(m_display);
198 handle_display_error();
199 return;
200 }
201
202 pollfd pfd {};
203 pfd.fd = wl_display_get_fd(m_display);
204 pfd.events = POLLIN;
205
206 if (::poll(&pfd, 1, 0) > 0 && (pfd.revents & (POLLIN | POLLHUP | POLLERR))) {
207 if (wl_display_read_events(m_display) < 0) {
208 handle_display_error();
209 return;
210 }
211 } else {
212 wl_display_cancel_read(m_display);
213 }
214
215 if (wl_display_dispatch_pending(m_display) < 0) {
216 handle_display_error();
217 return;
218 }
219
220 if (m_repeat_fd >= 0 && !m_held_keys.empty()) {
221 uint64_t expirations = 0;
222 if (read(m_repeat_fd, &expirations, sizeof(expirations)) == sizeof(expirations)
223 && expirations > 0) {
224 for (const auto& [k, kd] : m_held_keys) {
225 WindowEvent rev;
226 rev.type = WindowEventType::KEY_REPEAT;
227 rev.data = kd;
228 emit(rev);
229 }
230 }
231 }
232
233 if (m_text_repeat_fd >= 0 && m_active_text_key) {
234 uint64_t expirations = 0;
235 if (read(m_text_repeat_fd, &expirations, sizeof(expirations)) == sizeof(expirations)
236 && expirations > 0) {
237 WindowEvent tev;
238 tev.type = WindowEventType::TEXT_INPUT;
239 tev.data = WindowEvent::TextData { .codepoint = m_active_text_key->second };
240 emit(tev);
241 }
242 }
243
244 if (m_pending_configure.exchange(false) && m_xdg_surface && m_surface) {
245 xdg_surface_set_window_geometry(m_xdg_surface, 0, 0,
246 static_cast<int32_t>(m_state.current_width),
247 static_cast<int32_t>(m_state.current_height));
248 wl_surface_commit(m_surface);
249 wl_display_flush(m_display);
250 }
251}
252
253void WaylandWindow::handle_display_error()
254{
255 const int err = wl_display_get_error(m_display);
256
257 if (err == EPROTO) {
258 const wl_interface* iface = nullptr;
259 uint32_t id = 0;
260 const uint32_t code = wl_display_get_protocol_error(m_display, &iface, &id);
261 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
262 "Wayland protocol error: interface={}, id={}, code={}",
263 iface ? iface->name : "unknown", id, code);
264 } else {
265 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
266 "Wayland display error: {}", err);
267 }
268
269 if (!m_should_close.exchange(true)) {
270 WindowEvent ev;
271 ev.type = WindowEventType::WINDOW_CLOSED;
272 emit(ev);
273 }
274}
275
276void WaylandWindow::set_event_callback(WindowEventCallback callback)
277{
278 m_event_callback = std::move(callback);
279}
280
281void* WaylandWindow::get_native_handle() const
282{
283 return m_surface;
284}
285
286void* WaylandWindow::get_native_display() const
287{
288 return m_display;
289}
290
291void WaylandWindow::set_title(const std::string& title)
292{
293 m_create_info.title = title;
294 if (m_xdg_toplevel)
295 xdg_toplevel_set_title(m_xdg_toplevel, title.c_str());
296}
297
298void WaylandWindow::set_size(uint32_t w, uint32_t h)
299{
300 m_create_info.width = w;
301 m_create_info.height = h;
302 if (m_xdg_toplevel && m_surface) {
303 xdg_toplevel_set_min_size(m_xdg_toplevel,
304 static_cast<int32_t>(w), static_cast<int32_t>(h));
305 xdg_toplevel_set_max_size(m_xdg_toplevel,
306 static_cast<int32_t>(w), static_cast<int32_t>(h));
307 wl_surface_commit(m_surface);
308 }
309}
310
311void WaylandWindow::set_position(uint32_t, uint32_t)
312{
313 // Wayland does not allow client-side window positioning.
314}
315
316void WaylandWindow::set_color(const std::array<float, 4>& color)
317{
318 m_create_info.clear_color = color;
319}
320
321// ============================================================================
322// Render tracking
323// ============================================================================
324
325void WaylandWindow::register_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
326{
327 std::lock_guard lock(m_render_tracking_mutex);
328 m_rendering_buffers.push_back(buf);
329}
330
331void WaylandWindow::unregister_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
332{
333 std::lock_guard lock(m_render_tracking_mutex);
334 std::erase_if(m_rendering_buffers, [&buf](const auto& wp) {
335 auto sp = wp.lock();
336 return !sp || sp == buf;
337 });
338}
339
340void WaylandWindow::track_frame_command(uint64_t id)
341{
342 m_frame_commands.push_back(id);
343}
344
345const std::vector<uint64_t>& WaylandWindow::get_frame_commands() const
346{
347 return m_frame_commands;
348}
349
350void WaylandWindow::clear_frame_commands()
351{
352 m_frame_commands.clear();
353}
354
355std::vector<std::shared_ptr<Buffers::VKBuffer>> WaylandWindow::get_rendering_buffers() const
356{
357 std::lock_guard lock(m_render_tracking_mutex);
358 std::vector<std::shared_ptr<Buffers::VKBuffer>> out;
359 out.reserve(m_rendering_buffers.size());
360 for (auto& wp : m_rendering_buffers) {
361 if (auto sp = wp.lock())
362 out.push_back(sp);
363 }
364 return out;
365}
366
367// ============================================================================
368// Internal helpers
369// ============================================================================
370
371void WaylandWindow::emit(const WindowEvent& ev)
372{
373 m_event_source.signal(ev);
374 if (m_event_callback)
375 m_event_callback(ev);
376}
377
378// ============================================================================
379// Registry
380// ============================================================================
381
382void WaylandWindow::on_registry_global(void* data, wl_registry* reg,
383 uint32_t name, const char* iface, uint32_t version)
384{
385 auto* self = static_cast<WaylandWindow*>(data);
386
387 if (std::string_view(iface) == wl_compositor_interface.name) {
388 self->m_compositor = static_cast<wl_compositor*>(
389 wl_registry_bind(reg, name, &wl_compositor_interface,
390 std::min(version, 4U)));
391 } else if (std::string_view(iface) == xdg_wm_base_interface.name) {
392 self->m_xdg_wm_base = static_cast<xdg_wm_base*>(
393 wl_registry_bind(reg, name, &xdg_wm_base_interface,
394 std::min(version, 6U)));
395 xdg_wm_base_add_listener(self->m_xdg_wm_base, &s_wm_base_listener, self);
396 } else if (std::string_view(iface) == wl_seat_interface.name) {
397 self->m_seat = static_cast<wl_seat*>(
398 wl_registry_bind(reg, name, &wl_seat_interface,
399 std::min(version, 7U)));
400 wl_seat_add_listener(self->m_seat, &s_seat_listener, self);
401 } else if (std::string_view(iface) == zxdg_decoration_manager_v1_interface.name) {
402 self->m_decoration_manager = static_cast<zxdg_decoration_manager_v1*>(
403 wl_registry_bind(reg, name, &zxdg_decoration_manager_v1_interface, 1U));
404 }
405}
406
407void WaylandWindow::on_registry_global_remove(void*, wl_registry*, uint32_t) { }
408
409// ============================================================================
410// xdg_wm_base
411// ============================================================================
412
413void WaylandWindow::on_wm_base_ping(void*, xdg_wm_base* base, uint32_t serial)
414{
415 xdg_wm_base_pong(base, serial);
416}
417
418// ============================================================================
419// xdg_surface
420// ============================================================================
421
422void WaylandWindow::on_xdg_surface_configure(void* data, xdg_surface* surf, uint32_t serial)
423{
424 auto* self = static_cast<WaylandWindow*>(data);
425 xdg_surface_ack_configure(surf, serial);
426 self->m_pending_configure.store(true);
427}
428
429// ============================================================================
430// xdg_toplevel
431// ============================================================================
432
433void WaylandWindow::on_toplevel_configure(void* data, xdg_toplevel*,
434 int32_t w, int32_t h, wl_array* states)
435{
436 auto* self = static_cast<WaylandWindow*>(data);
437
438 bool now_resizing = false;
439 const auto* state_begin = static_cast<const uint32_t*>(states->data);
440 const auto* state_end = reinterpret_cast<const uint32_t*>(
441 static_cast<const uint8_t*>(states->data) + states->size);
442 for (const auto* state = state_begin; state != state_end; ++state) {
443 if (*state == XDG_TOPLEVEL_STATE_RESIZING) {
444 now_resizing = true;
445 break;
446 }
447 }
448 const bool gesture_ended = self->m_resizing && !now_resizing;
449 self->m_resizing = now_resizing;
450
451 if (w <= 0 || h <= 0)
452 return;
453
454 const bool size_changed = static_cast<uint32_t>(w) != self->m_state.current_width
455 || static_cast<uint32_t>(h) != self->m_state.current_height;
456
457 if (!size_changed && !gesture_ended)
458 return;
459
460 self->m_state.current_width = static_cast<uint32_t>(w);
461 self->m_state.current_height = static_cast<uint32_t>(h);
462 self->m_create_info.width = static_cast<uint32_t>(w);
463 self->m_create_info.height = static_cast<uint32_t>(h);
464
465 WindowEvent ev;
466 ev.type = WindowEventType::WINDOW_RESIZED;
467 ev.data = WindowEvent::ResizeData {
468 .width = static_cast<uint32_t>(w),
469 .height = static_cast<uint32_t>(h)
470 };
471
472 if (self->m_event_callback)
473 self->m_event_callback(ev);
474
475 if (!now_resizing)
476 self->m_event_source.signal(ev);
477}
478
479void WaylandWindow::on_toplevel_close(void* data, xdg_toplevel*)
480{
481 auto* self = static_cast<WaylandWindow*>(data);
482 self->m_should_close.store(true);
483 WindowEvent ev;
484 ev.type = WindowEventType::WINDOW_CLOSED;
485 self->emit(ev);
486}
487
488void WaylandWindow::on_toplevel_configure_bounds(void*, xdg_toplevel*,
489 int32_t, int32_t) { }
490
491void WaylandWindow::on_toplevel_wm_capabilities(void*, xdg_toplevel*,
492 wl_array*) { }
493
494// ============================================================================
495// wl_seat
496// ============================================================================
497
498void WaylandWindow::on_seat_capabilities(void* data, wl_seat* seat, uint32_t caps)
499{
500 auto* self = static_cast<WaylandWindow*>(data);
501
502 const bool has_kb = caps & WL_SEAT_CAPABILITY_KEYBOARD;
503 const bool has_ptr = caps & WL_SEAT_CAPABILITY_POINTER;
504
505 if (has_kb && !self->m_keyboard) {
506 self->m_keyboard = wl_seat_get_keyboard(seat);
507 wl_keyboard_add_listener(self->m_keyboard, &s_keyboard_listener, self);
508 } else if (!has_kb && self->m_keyboard) {
509 wl_keyboard_destroy(self->m_keyboard);
510 self->m_keyboard = nullptr;
511 }
512
513 if (has_ptr && !self->m_pointer) {
514 self->m_pointer = wl_seat_get_pointer(seat);
515 wl_pointer_add_listener(self->m_pointer, &s_pointer_listener, self);
516 } else if (!has_ptr && self->m_pointer) {
517 wl_pointer_destroy(self->m_pointer);
518 self->m_pointer = nullptr;
519 }
520}
521
522void WaylandWindow::on_seat_name(void*, wl_seat*, const char*) { }
523
524// ============================================================================
525// wl_keyboard
526// ============================================================================
527
528void WaylandWindow::on_keyboard_keymap(void* data, wl_keyboard*,
529 uint32_t fmt, int fd, uint32_t size)
530{
531 auto* self = static_cast<WaylandWindow*>(data);
532
533 if (fmt != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
534 close(fd);
535 return;
536 }
537
538 void* buf = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
539 close(fd);
540 if (buf == MAP_FAILED)
541 return;
542
543 if (self->m_xkb_state) {
544 xkb_state_unref(self->m_xkb_state);
545 self->m_xkb_state = nullptr;
546 }
547 if (self->m_xkb_keymap) {
548 xkb_keymap_unref(self->m_xkb_keymap);
549 self->m_xkb_keymap = nullptr;
550 }
551
552 self->m_xkb_keymap = xkb_keymap_new_from_string(self->m_xkb_context,
553 static_cast<const char*>(buf),
554 XKB_KEYMAP_FORMAT_TEXT_V1,
555 XKB_KEYMAP_COMPILE_NO_FLAGS);
556
557 munmap(buf, size);
558
559 if (self->m_xkb_keymap)
560 self->m_xkb_state = xkb_state_new(self->m_xkb_keymap);
561}
562
563void WaylandWindow::on_keyboard_enter(void* data, wl_keyboard*,
564 uint32_t, wl_surface*, wl_array*)
565{
566 auto* self = static_cast<WaylandWindow*>(data);
567 self->m_state.is_focused = true;
568 WindowEvent ev;
569 ev.type = WindowEventType::WINDOW_FOCUS_GAINED;
570 self->emit(ev);
571}
572
573void WaylandWindow::on_keyboard_leave(void* data, wl_keyboard*,
574 uint32_t, wl_surface*)
575{
576 auto* self = static_cast<WaylandWindow*>(data);
577 self->m_state.is_focused = false;
578 WindowEvent ev;
579 ev.type = WindowEventType::WINDOW_FOCUS_LOST;
580 self->emit(ev);
581}
582
583void WaylandWindow::on_keyboard_key(void* data, wl_keyboard*,
584 uint32_t, uint32_t, uint32_t key, uint32_t state)
585{
586 auto* self = static_cast<WaylandWindow*>(data);
587 if (!self->m_xkb_state)
588 return;
589
590 const xkb_keycode_t xkb_key = key + 8;
591
592 IO::Keys mf_key = from_evdev_scancode(key);
593 if (mf_key == IO::Keys::Unknown) {
594 xkb_keysym_t sym = xkb_state_key_get_one_sym(self->m_xkb_state, xkb_key);
595 mf_key = from_xkb_keysym(sym);
596 }
597
598 const WindowEvent::KeyData kd {
599 .key = static_cast<int16_t>(mf_key),
600 .scancode = static_cast<int32_t>(key),
601 .mods = 0
602 };
603
604 WindowEvent ev;
605 ev.data = kd;
606
607 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
608 ev.type = WindowEventType::KEY_PRESSED;
609 self->emit(ev);
610
611 if (const uint32_t codepoint = xkb_state_key_get_utf32(self->m_xkb_state, xkb_key);
612 is_text_input_codepoint(codepoint)) {
613 WindowEvent tev;
614 tev.type = WindowEventType::TEXT_INPUT;
615 tev.data = WindowEvent::TextData { .codepoint = codepoint };
616 self->emit(tev);
617
618 self->m_active_text_key = { kd.key, codepoint };
619
620 if (self->m_text_repeat_fd < 0)
621 self->m_text_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
622
623 const long t_delay_ns = static_cast<long>(self->m_key_repeat_config.text_initial_delay_ms) * 1'000'000L;
624 const long t_repeat_ns = self->m_key_repeat_config.text_interval_ms > 0
625 ? static_cast<long>(self->m_key_repeat_config.text_interval_ms) * 1'000'000L
626 : 33'333'333L;
627
628 itimerspec ts {};
629 ts.it_value.tv_sec = 0;
630 ts.it_value.tv_nsec = t_delay_ns;
631 ts.it_interval.tv_sec = 0;
632 ts.it_interval.tv_nsec = t_repeat_ns;
633 timerfd_settime(self->m_text_repeat_fd, 0, &ts, nullptr);
634 }
635
636 self->m_held_keys[kd.key] = kd;
637
638 if (self->m_repeat_fd < 0)
639 self->m_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
640
641 const long delay_ns = static_cast<long>(self->m_key_repeat_config.initial_delay_ms) * 1'000'000L;
642 const long repeat_ns = self->m_key_repeat_config.interval_ms > 0
643 ? static_cast<long>(self->m_key_repeat_config.interval_ms) * 1'000'000L
644 : 16'666'667L;
645
646 itimerspec ts {};
647 ts.it_value.tv_sec = 0;
648 ts.it_value.tv_nsec = delay_ns;
649 ts.it_interval.tv_sec = 0;
650 ts.it_interval.tv_nsec = repeat_ns;
651 timerfd_settime(self->m_repeat_fd, 0, &ts, nullptr);
652
653 } else {
654 if (self->m_active_text_key && self->m_active_text_key->first == kd.key) {
655 self->m_active_text_key = std::nullopt;
656 if (self->m_text_repeat_fd >= 0) {
657 itimerspec ts {};
658 timerfd_settime(self->m_text_repeat_fd, 0, &ts, nullptr);
659 }
660 }
661
662 self->m_held_keys.erase(kd.key);
663
664 if (self->m_held_keys.empty() && self->m_repeat_fd >= 0) {
665 itimerspec ts {};
666 timerfd_settime(self->m_repeat_fd, 0, &ts, nullptr);
667 }
668
669 ev.type = WindowEventType::KEY_RELEASED;
670 self->emit(ev);
671 }
672}
673
674void WaylandWindow::on_keyboard_modifiers(void* data, wl_keyboard*,
675 uint32_t, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
676{
677 auto* self = static_cast<WaylandWindow*>(data);
678 if (self->m_xkb_state) {
679 xkb_state_update_mask(self->m_xkb_state,
680 depressed, latched, locked, 0, 0, group);
681 }
682}
683
684void WaylandWindow::on_keyboard_repeat_info(void* data, wl_keyboard*,
685 int32_t rate, int32_t delay)
686{
687 auto* self = static_cast<WaylandWindow*>(data);
688 if (!self->m_key_repeat_config.allow_compositor_override)
689 return;
690
691 if (rate > 0) {
692 self->m_key_repeat_config.interval_ms = 1000U / static_cast<uint32_t>(rate);
693 self->m_key_repeat_config.text_interval_ms = 1000U / static_cast<uint32_t>(rate);
694 }
695 if (delay > 0) {
696 self->m_key_repeat_config.initial_delay_ms = static_cast<uint32_t>(delay);
697 self->m_key_repeat_config.text_initial_delay_ms = static_cast<uint32_t>(delay);
698 }
699}
700
701// ============================================================================
702// wl_pointer
703// ============================================================================
704
705void WaylandWindow::on_pointer_enter(void* data, wl_pointer*,
706 uint32_t, wl_surface*, wl_fixed_t sx, wl_fixed_t sy)
707{
708 auto* self = static_cast<WaylandWindow*>(data);
709 self->m_pointer_x = wl_fixed_to_double(sx);
710 self->m_pointer_y = wl_fixed_to_double(sy);
711 WindowEvent ev;
712 ev.type = WindowEventType::MOUSE_ENTERED;
713 self->emit(ev);
714}
715
716void WaylandWindow::on_pointer_leave(void* data, wl_pointer*, uint32_t, wl_surface*)
717{
718 auto* self = static_cast<WaylandWindow*>(data);
719 WindowEvent ev;
720 ev.type = WindowEventType::MOUSE_EXITED;
721 self->emit(ev);
722}
723
724void WaylandWindow::on_pointer_motion(void* data, wl_pointer*,
725 uint32_t, wl_fixed_t sx, wl_fixed_t sy)
726{
727 auto* self = static_cast<WaylandWindow*>(data);
728 self->m_pointer_x = wl_fixed_to_double(sx);
729 self->m_pointer_y = wl_fixed_to_double(sy);
730 WindowEvent ev;
731 ev.type = WindowEventType::MOUSE_MOTION;
732 ev.data = WindowEvent::MousePosData {
733 .x = self->m_pointer_x,
734 .y = self->m_pointer_y
735 };
736 self->emit(ev);
737}
738
739void WaylandWindow::on_pointer_button(void* data, wl_pointer*,
740 uint32_t, uint32_t, uint32_t button, uint32_t state)
741{
742 auto* self = static_cast<WaylandWindow*>(data);
743
744 IO::MouseButtons btn = IO::MouseButtons::Unknown;
745 switch (button) {
746 case BTN_LEFT:
747 btn = IO::MouseButtons::Left;
748 break;
749 case BTN_RIGHT:
750 btn = IO::MouseButtons::Right;
751 break;
752 case BTN_MIDDLE:
753 btn = IO::MouseButtons::Middle;
754 break;
755 case BTN_SIDE:
756 btn = IO::MouseButtons::Button4;
757 break;
758 case BTN_EXTRA:
759 btn = IO::MouseButtons::Button5;
760 break;
761 default:
762 break;
763 }
764
765 WindowEvent ev;
766 ev.type = (state == WL_POINTER_BUTTON_STATE_PRESSED)
767 ? WindowEventType::MOUSE_BUTTON_PRESSED
769 ev.data = WindowEvent::MouseButtonData {
770 .button = static_cast<int8_t>(btn),
771 .mods = 0
772 };
773 self->emit(ev);
774}
775
776void WaylandWindow::on_pointer_axis(void* data, wl_pointer*,
777 uint32_t, uint32_t axis, wl_fixed_t value)
778{
779 auto* self = static_cast<WaylandWindow*>(data);
780 const double v = wl_fixed_to_double(value) / 10.0;
781 WindowEvent ev;
782 ev.type = WindowEventType::MOUSE_SCROLLED;
783 ev.data = WindowEvent::ScrollData {
784 .x_offset = (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) ? v : 0.0,
785 .y_offset = (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) ? -v : 0.0
786 };
787 self->emit(ev);
788}
789
790void WaylandWindow::on_pointer_frame(void*, wl_pointer*) { }
791void WaylandWindow::on_pointer_axis_source(void*, wl_pointer*, uint32_t) { }
792void WaylandWindow::on_pointer_axis_stop(void*, wl_pointer*, uint32_t, uint32_t) { }
793void WaylandWindow::on_pointer_axis_discrete(void*, wl_pointer*, uint32_t, int32_t) { }
794
795} // namespace MayaFlux::Core
796
797#endif // MAYAFLUX_PLATFORM_LINUX
#define MF_ERROR(comp, ctx,...)
glm::vec3 color
Definition Decoder.cpp:97
bool size
float rate
uint32_t h
Definition InkPress.cpp:29
PickCallback callback
Definition Picker.cpp:22
std::vector< float > * out
glm::vec3 value
std::string name
Definition VKDevice.cpp:123
uint32_t height
WindowEventType
Types of window and input events.
constexpr bool is_text_input_codepoint(uint32_t codepoint) noexcept
Check if a Unicode codepoint is valid for text input.
Definition Window.hpp:217
std::vector< double > delay(std::span< const double > data, uint32_t delay_samples, double fill_value)
Prepend delay_samples zero-valued (or fill_value) samples, returning a new vector.
void close(std::span< const float > mask, std::span< float > tmp, std::span< float > dst, uint32_t w, uint32_t h, uint32_t radius)
Morphological closing writing into dst.
void destroy(Surface &surface, uint32_t id)
Tear down an element and everything related to it.
Definition Forma.cpp:90