MayaFlux 0.5.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_keyboard) {
104 wl_keyboard_destroy(m_keyboard);
105 m_keyboard = nullptr;
106 }
107 if (m_pointer) {
108 wl_pointer_destroy(m_pointer);
109 m_pointer = nullptr;
110 }
111
112 if (m_xkb_state) {
113 xkb_state_unref(m_xkb_state);
114 m_xkb_state = nullptr;
115 }
116 if (m_xkb_keymap) {
117 xkb_keymap_unref(m_xkb_keymap);
118 m_xkb_keymap = nullptr;
119 }
120 if (m_xkb_context) {
121 xkb_context_unref(m_xkb_context);
122 m_xkb_context = nullptr;
123 }
124
125 if (m_decoration) {
126 zxdg_toplevel_decoration_v1_destroy(m_decoration);
127 m_decoration = nullptr;
128 }
129 if (m_decoration_manager) {
130 zxdg_decoration_manager_v1_destroy(m_decoration_manager);
131 m_decoration_manager = nullptr;
132 }
133 if (m_xdg_toplevel) {
134 xdg_toplevel_destroy(m_xdg_toplevel);
135 m_xdg_toplevel = nullptr;
136 }
137 if (m_xdg_surface) {
138 xdg_surface_destroy(m_xdg_surface);
139 m_xdg_surface = nullptr;
140 }
141 if (m_surface) {
142 wl_surface_destroy(m_surface);
143 m_surface = nullptr;
144 }
145 if (m_xdg_wm_base) {
146 xdg_wm_base_destroy(m_xdg_wm_base);
147 m_xdg_wm_base = nullptr;
148 }
149 if (m_seat) {
150 wl_seat_destroy(m_seat);
151 m_seat = nullptr;
152 }
153 if (m_compositor) {
154 wl_compositor_destroy(m_compositor);
155 m_compositor = nullptr;
156 }
157 if (m_registry) {
158 wl_registry_destroy(m_registry);
159 m_registry = nullptr;
160 }
161}
162
163bool WaylandWindow::should_close() const
164{
165 return m_should_close.load();
166}
167
168void WaylandWindow::poll()
169{
170 if (!m_display)
171 return;
172
173 while (wl_display_prepare_read(m_display) != 0) {
174 if (wl_display_dispatch_pending(m_display) < 0) {
175 handle_display_error();
176 return;
177 }
178 }
179
180 if (wl_display_flush(m_display) < 0 && errno != EAGAIN) {
181 wl_display_cancel_read(m_display);
182 handle_display_error();
183 return;
184 }
185
186 pollfd pfd {};
187 pfd.fd = wl_display_get_fd(m_display);
188 pfd.events = POLLIN;
189
190 if (::poll(&pfd, 1, 0) > 0 && (pfd.revents & (POLLIN | POLLHUP | POLLERR))) {
191 if (wl_display_read_events(m_display) < 0) {
192 handle_display_error();
193 return;
194 }
195 } else {
196 wl_display_cancel_read(m_display);
197 }
198
199 if (wl_display_dispatch_pending(m_display) < 0) {
200 handle_display_error();
201 return;
202 }
203
204 if (m_repeat_fd >= 0 && !m_held_keys.empty()) {
205 uint64_t expirations = 0;
206 if (read(m_repeat_fd, &expirations, sizeof(expirations)) == sizeof(expirations)
207 && expirations > 0) {
208 for (const auto& [k, kd] : m_held_keys) {
209 WindowEvent rev;
210 rev.type = WindowEventType::KEY_REPEAT;
211 rev.data = kd;
212 emit(rev);
213 }
214 }
215 }
216
217 if (m_pending_configure.exchange(false)) {
218 xdg_surface_set_window_geometry(m_xdg_surface, 0, 0,
219 static_cast<int32_t>(m_state.current_width),
220 static_cast<int32_t>(m_state.current_height));
221 wl_surface_commit(m_surface);
222 wl_display_flush(m_display);
223 }
224}
225
226void WaylandWindow::handle_display_error()
227{
228 const int err = wl_display_get_error(m_display);
229
230 if (err == EPROTO) {
231 const wl_interface* iface = nullptr;
232 uint32_t id = 0;
233 const uint32_t code = wl_display_get_protocol_error(m_display, &iface, &id);
234 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
235 "Wayland protocol error: interface={}, id={}, code={}",
236 iface ? iface->name : "unknown", id, code);
237 } else {
238 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
239 "Wayland display error: {}", err);
240 }
241
242 if (!m_should_close.exchange(true)) {
243 WindowEvent ev;
244 ev.type = WindowEventType::WINDOW_CLOSED;
245 emit(ev);
246 }
247}
248
249void WaylandWindow::set_event_callback(WindowEventCallback callback)
250{
251 m_event_callback = std::move(callback);
252}
253
254void* WaylandWindow::get_native_handle() const
255{
256 return m_surface;
257}
258
259void* WaylandWindow::get_native_display() const
260{
261 return m_display;
262}
263
264void WaylandWindow::set_title(const std::string& title)
265{
266 m_create_info.title = title;
267 if (m_xdg_toplevel)
268 xdg_toplevel_set_title(m_xdg_toplevel, title.c_str());
269}
270
271void WaylandWindow::set_size(uint32_t w, uint32_t h)
272{
273 m_create_info.width = w;
274 m_create_info.height = h;
275 if (m_xdg_toplevel && m_surface) {
276 xdg_toplevel_set_min_size(m_xdg_toplevel,
277 static_cast<int32_t>(w), static_cast<int32_t>(h));
278 xdg_toplevel_set_max_size(m_xdg_toplevel,
279 static_cast<int32_t>(w), static_cast<int32_t>(h));
280 wl_surface_commit(m_surface);
281 }
282}
283
284void WaylandWindow::set_position(uint32_t, uint32_t)
285{
286 // Wayland does not allow client-side window positioning.
287}
288
289void WaylandWindow::set_color(const std::array<float, 4>& color)
290{
291 m_create_info.clear_color = color;
292}
293
294// ============================================================================
295// Render tracking
296// ============================================================================
297
298void WaylandWindow::register_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
299{
300 std::lock_guard lock(m_render_tracking_mutex);
301 m_rendering_buffers.push_back(buf);
302}
303
304void WaylandWindow::unregister_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
305{
306 std::lock_guard lock(m_render_tracking_mutex);
307 std::erase_if(m_rendering_buffers, [&buf](const auto& wp) {
308 auto sp = wp.lock();
309 return !sp || sp == buf;
310 });
311}
312
313void WaylandWindow::track_frame_command(uint64_t id)
314{
315 m_frame_commands.push_back(id);
316}
317
318const std::vector<uint64_t>& WaylandWindow::get_frame_commands() const
319{
320 return m_frame_commands;
321}
322
323void WaylandWindow::clear_frame_commands()
324{
325 m_frame_commands.clear();
326}
327
328std::vector<std::shared_ptr<Buffers::VKBuffer>> WaylandWindow::get_rendering_buffers() const
329{
330 std::lock_guard lock(m_render_tracking_mutex);
331 std::vector<std::shared_ptr<Buffers::VKBuffer>> out;
332 out.reserve(m_rendering_buffers.size());
333 for (auto& wp : m_rendering_buffers) {
334 if (auto sp = wp.lock())
335 out.push_back(sp);
336 }
337 return out;
338}
339
340// ============================================================================
341// Internal helpers
342// ============================================================================
343
344void WaylandWindow::emit(const WindowEvent& ev)
345{
346 m_event_source.signal(ev);
347 if (m_event_callback)
348 m_event_callback(ev);
349}
350
351// ============================================================================
352// Registry
353// ============================================================================
354
355void WaylandWindow::on_registry_global(void* data, wl_registry* reg,
356 uint32_t name, const char* iface, uint32_t version)
357{
358 auto* self = static_cast<WaylandWindow*>(data);
359
360 if (std::string_view(iface) == wl_compositor_interface.name) {
361 self->m_compositor = static_cast<wl_compositor*>(
362 wl_registry_bind(reg, name, &wl_compositor_interface,
363 std::min(version, 4U)));
364 } else if (std::string_view(iface) == xdg_wm_base_interface.name) {
365 self->m_xdg_wm_base = static_cast<xdg_wm_base*>(
366 wl_registry_bind(reg, name, &xdg_wm_base_interface,
367 std::min(version, 6U)));
368 xdg_wm_base_add_listener(self->m_xdg_wm_base, &s_wm_base_listener, self);
369 } else if (std::string_view(iface) == wl_seat_interface.name) {
370 self->m_seat = static_cast<wl_seat*>(
371 wl_registry_bind(reg, name, &wl_seat_interface,
372 std::min(version, 7U)));
373 wl_seat_add_listener(self->m_seat, &s_seat_listener, self);
374 } else if (std::string_view(iface) == zxdg_decoration_manager_v1_interface.name) {
375 self->m_decoration_manager = static_cast<zxdg_decoration_manager_v1*>(
376 wl_registry_bind(reg, name, &zxdg_decoration_manager_v1_interface, 1U));
377 }
378}
379
380void WaylandWindow::on_registry_global_remove(void*, wl_registry*, uint32_t) { }
381
382// ============================================================================
383// xdg_wm_base
384// ============================================================================
385
386void WaylandWindow::on_wm_base_ping(void*, xdg_wm_base* base, uint32_t serial)
387{
388 xdg_wm_base_pong(base, serial);
389}
390
391// ============================================================================
392// xdg_surface
393// ============================================================================
394
395void WaylandWindow::on_xdg_surface_configure(void* data, xdg_surface* surf, uint32_t serial)
396{
397 auto* self = static_cast<WaylandWindow*>(data);
398 xdg_surface_ack_configure(surf, serial);
399 self->m_pending_configure.store(true);
400}
401
402// ============================================================================
403// xdg_toplevel
404// ============================================================================
405
406void WaylandWindow::on_toplevel_configure(void* data, xdg_toplevel*,
407 int32_t w, int32_t h, wl_array*)
408{
409 auto* self = static_cast<WaylandWindow*>(data);
410 if (w <= 0 || h <= 0)
411 return;
412
413 if (static_cast<uint32_t>(w) == self->m_state.current_width && static_cast<uint32_t>(h) == self->m_state.current_height)
414 return;
415
416 self->m_state.current_width = static_cast<uint32_t>(w);
417 self->m_state.current_height = static_cast<uint32_t>(h);
418 self->m_create_info.width = static_cast<uint32_t>(w);
419 self->m_create_info.height = static_cast<uint32_t>(h);
420 WindowEvent ev;
421 ev.type = WindowEventType::WINDOW_RESIZED;
422 ev.data = WindowEvent::ResizeData {
423 .width = static_cast<uint32_t>(w),
424 .height = static_cast<uint32_t>(h)
425 };
426 self->emit(ev);
427}
428
429void WaylandWindow::on_toplevel_close(void* data, xdg_toplevel*)
430{
431 auto* self = static_cast<WaylandWindow*>(data);
432 self->m_should_close.store(true);
433 WindowEvent ev;
434 ev.type = WindowEventType::WINDOW_CLOSED;
435 self->emit(ev);
436}
437
438void WaylandWindow::on_toplevel_configure_bounds(void*, xdg_toplevel*,
439 int32_t, int32_t) { }
440
441void WaylandWindow::on_toplevel_wm_capabilities(void*, xdg_toplevel*,
442 wl_array*) { }
443
444// ============================================================================
445// wl_seat
446// ============================================================================
447
448void WaylandWindow::on_seat_capabilities(void* data, wl_seat* seat, uint32_t caps)
449{
450 auto* self = static_cast<WaylandWindow*>(data);
451
452 const bool has_kb = caps & WL_SEAT_CAPABILITY_KEYBOARD;
453 const bool has_ptr = caps & WL_SEAT_CAPABILITY_POINTER;
454
455 if (has_kb && !self->m_keyboard) {
456 self->m_keyboard = wl_seat_get_keyboard(seat);
457 wl_keyboard_add_listener(self->m_keyboard, &s_keyboard_listener, self);
458 } else if (!has_kb && self->m_keyboard) {
459 wl_keyboard_destroy(self->m_keyboard);
460 self->m_keyboard = nullptr;
461 }
462
463 if (has_ptr && !self->m_pointer) {
464 self->m_pointer = wl_seat_get_pointer(seat);
465 wl_pointer_add_listener(self->m_pointer, &s_pointer_listener, self);
466 } else if (!has_ptr && self->m_pointer) {
467 wl_pointer_destroy(self->m_pointer);
468 self->m_pointer = nullptr;
469 }
470}
471
472void WaylandWindow::on_seat_name(void*, wl_seat*, const char*) { }
473
474// ============================================================================
475// wl_keyboard
476// ============================================================================
477
478void WaylandWindow::on_keyboard_keymap(void* data, wl_keyboard*,
479 uint32_t fmt, int fd, uint32_t size)
480{
481 auto* self = static_cast<WaylandWindow*>(data);
482
483 if (fmt != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
484 close(fd);
485 return;
486 }
487
488 void* buf = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
489 close(fd);
490 if (buf == MAP_FAILED)
491 return;
492
493 if (self->m_xkb_state) {
494 xkb_state_unref(self->m_xkb_state);
495 self->m_xkb_state = nullptr;
496 }
497 if (self->m_xkb_keymap) {
498 xkb_keymap_unref(self->m_xkb_keymap);
499 self->m_xkb_keymap = nullptr;
500 }
501
502 self->m_xkb_keymap = xkb_keymap_new_from_string(self->m_xkb_context,
503 static_cast<const char*>(buf),
504 XKB_KEYMAP_FORMAT_TEXT_V1,
505 XKB_KEYMAP_COMPILE_NO_FLAGS);
506
507 munmap(buf, size);
508
509 if (self->m_xkb_keymap)
510 self->m_xkb_state = xkb_state_new(self->m_xkb_keymap);
511}
512
513void WaylandWindow::on_keyboard_enter(void* data, wl_keyboard*,
514 uint32_t, wl_surface*, wl_array*)
515{
516 auto* self = static_cast<WaylandWindow*>(data);
517 self->m_state.is_focused = true;
518 WindowEvent ev;
519 ev.type = WindowEventType::WINDOW_FOCUS_GAINED;
520 self->emit(ev);
521}
522
523void WaylandWindow::on_keyboard_leave(void* data, wl_keyboard*,
524 uint32_t, wl_surface*)
525{
526 auto* self = static_cast<WaylandWindow*>(data);
527 self->m_state.is_focused = false;
528 WindowEvent ev;
529 ev.type = WindowEventType::WINDOW_FOCUS_LOST;
530 self->emit(ev);
531}
532
533void WaylandWindow::on_keyboard_key(void* data, wl_keyboard*,
534 uint32_t, uint32_t, uint32_t key, uint32_t state)
535{
536 auto* self = static_cast<WaylandWindow*>(data);
537 if (!self->m_xkb_state)
538 return;
539
540 IO::Keys mf_key = from_evdev_scancode(key);
541 if (mf_key == IO::Keys::Unknown) {
542 xkb_keycode_t xkb_key = key + 8;
543 xkb_keysym_t sym = xkb_state_key_get_one_sym(self->m_xkb_state, xkb_key);
544 mf_key = from_xkb_keysym(sym);
545 }
546
547 const WindowEvent::KeyData kd {
548 .key = static_cast<int16_t>(mf_key),
549 .scancode = static_cast<int32_t>(key),
550 .mods = 0
551 };
552
553 WindowEvent ev;
554 ev.data = kd;
555
556 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
557 ev.type = WindowEventType::KEY_PRESSED;
558 self->emit(ev);
559
560 self->m_held_keys[kd.key] = kd;
561
562 if (self->m_repeat_fd < 0)
563 self->m_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
564
565 const long delay_ns = static_cast<long>(self->m_key_repeat_config.initial_delay_ms) * 1'000'000L;
566 const long repeat_ns = self->m_key_repeat_config.interval_ms > 0
567 ? static_cast<long>(self->m_key_repeat_config.interval_ms) * 1'000'000L
568 : 16'666'667L;
569
570 itimerspec ts {};
571 ts.it_value.tv_sec = 0;
572 ts.it_value.tv_nsec = delay_ns;
573 ts.it_interval.tv_sec = 0;
574 ts.it_interval.tv_nsec = repeat_ns;
575 timerfd_settime(self->m_repeat_fd, 0, &ts, nullptr);
576
577 } else {
578 self->m_held_keys.erase(kd.key);
579
580 if (self->m_held_keys.empty() && self->m_repeat_fd >= 0) {
581 itimerspec ts {};
582 timerfd_settime(self->m_repeat_fd, 0, &ts, nullptr);
583 }
584
585 ev.type = WindowEventType::KEY_RELEASED;
586 self->emit(ev);
587 }
588}
589
590void WaylandWindow::on_keyboard_modifiers(void* data, wl_keyboard*,
591 uint32_t, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
592{
593 auto* self = static_cast<WaylandWindow*>(data);
594 if (self->m_xkb_state) {
595 xkb_state_update_mask(self->m_xkb_state,
596 depressed, latched, locked, 0, 0, group);
597 }
598}
599
600void WaylandWindow::on_keyboard_repeat_info(void* data, wl_keyboard*,
601 int32_t rate, int32_t delay)
602{
603 auto* self = static_cast<WaylandWindow*>(data);
604 if (!self->m_key_repeat_config.allow_compositor_override)
605 return;
606
607 if (rate > 0)
608 self->m_key_repeat_config.interval_ms = 1000U / static_cast<uint32_t>(rate);
609 if (delay > 0)
610 self->m_key_repeat_config.initial_delay_ms = static_cast<uint32_t>(delay);
611}
612
613// ============================================================================
614// wl_pointer
615// ============================================================================
616
617void WaylandWindow::on_pointer_enter(void* data, wl_pointer*,
618 uint32_t, wl_surface*, wl_fixed_t sx, wl_fixed_t sy)
619{
620 auto* self = static_cast<WaylandWindow*>(data);
621 self->m_pointer_x = wl_fixed_to_double(sx);
622 self->m_pointer_y = wl_fixed_to_double(sy);
623 WindowEvent ev;
624 ev.type = WindowEventType::MOUSE_ENTERED;
625 self->emit(ev);
626}
627
628void WaylandWindow::on_pointer_leave(void* data, wl_pointer*, uint32_t, wl_surface*)
629{
630 auto* self = static_cast<WaylandWindow*>(data);
631 WindowEvent ev;
632 ev.type = WindowEventType::MOUSE_EXITED;
633 self->emit(ev);
634}
635
636void WaylandWindow::on_pointer_motion(void* data, wl_pointer*,
637 uint32_t, wl_fixed_t sx, wl_fixed_t sy)
638{
639 auto* self = static_cast<WaylandWindow*>(data);
640 self->m_pointer_x = wl_fixed_to_double(sx);
641 self->m_pointer_y = wl_fixed_to_double(sy);
642 WindowEvent ev;
643 ev.type = WindowEventType::MOUSE_MOTION;
644 ev.data = WindowEvent::MousePosData {
645 .x = self->m_pointer_x,
646 .y = self->m_pointer_y
647 };
648 self->emit(ev);
649}
650
651void WaylandWindow::on_pointer_button(void* data, wl_pointer*,
652 uint32_t, uint32_t, uint32_t button, uint32_t state)
653{
654 auto* self = static_cast<WaylandWindow*>(data);
655
656 IO::MouseButtons btn = IO::MouseButtons::Unknown;
657 switch (button) {
658 case BTN_LEFT:
659 btn = IO::MouseButtons::Left;
660 break;
661 case BTN_RIGHT:
662 btn = IO::MouseButtons::Right;
663 break;
664 case BTN_MIDDLE:
665 btn = IO::MouseButtons::Middle;
666 break;
667 case BTN_SIDE:
668 btn = IO::MouseButtons::Button4;
669 break;
670 case BTN_EXTRA:
671 btn = IO::MouseButtons::Button5;
672 break;
673 default:
674 break;
675 }
676
677 WindowEvent ev;
678 ev.type = (state == WL_POINTER_BUTTON_STATE_PRESSED)
679 ? WindowEventType::MOUSE_BUTTON_PRESSED
681 ev.data = WindowEvent::MouseButtonData {
682 .button = static_cast<int8_t>(btn),
683 .mods = 0
684 };
685 self->emit(ev);
686}
687
688void WaylandWindow::on_pointer_axis(void* data, wl_pointer*,
689 uint32_t, uint32_t axis, wl_fixed_t value)
690{
691 auto* self = static_cast<WaylandWindow*>(data);
692 const double v = wl_fixed_to_double(value) / 10.0;
693 WindowEvent ev;
694 ev.type = WindowEventType::MOUSE_SCROLLED;
695 ev.data = WindowEvent::ScrollData {
696 .x_offset = (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) ? v : 0.0,
697 .y_offset = (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) ? -v : 0.0
698 };
699 self->emit(ev);
700}
701
702void WaylandWindow::on_pointer_frame(void*, wl_pointer*) { }
703void WaylandWindow::on_pointer_axis_source(void*, wl_pointer*, uint32_t) { }
704void WaylandWindow::on_pointer_axis_stop(void*, wl_pointer*, uint32_t, uint32_t) { }
705void WaylandWindow::on_pointer_axis_discrete(void*, wl_pointer*, uint32_t, int32_t) { }
706
707} // namespace MayaFlux::Core
708
709#endif // MAYAFLUX_PLATFORM_LINUX
#define MF_ERROR(comp, ctx,...)
float rate
uint32_t h
Definition InkPress.cpp:28
std::string name
Definition VKDevice.cpp:143
float value
float k
uint32_t height
WindowEventType
Types of window and input events.
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.