3#ifdef MAYAFLUX_PLATFORM_LINUX
5#include "KeyMapping.hpp"
8#include <linux/input-event-codes.h>
11#include <sys/timerfd.h>
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)
28 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
29 "wl_display_connect failed");
33 m_xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
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);
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));
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);
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");
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);
62 wl_surface_commit(m_surface);
63 wl_display_roundtrip(m_display);
65 m_state.current_width =
static_cast<int>(create_info.width);
66 m_state.current_height =
static_cast<int>(create_info.height);
69WaylandWindow::~WaylandWindow()
73 wl_display_disconnect(m_display);
82void WaylandWindow::show()
85 wl_surface_commit(m_surface);
88void WaylandWindow::hide()
91 wl_surface_attach(m_surface,
nullptr, 0, 0);
92 wl_surface_commit(m_surface);
96void WaylandWindow::destroy()
98 if (m_repeat_fd >= 0) {
104 wl_keyboard_destroy(m_keyboard);
105 m_keyboard =
nullptr;
108 wl_pointer_destroy(m_pointer);
113 xkb_state_unref(m_xkb_state);
114 m_xkb_state =
nullptr;
117 xkb_keymap_unref(m_xkb_keymap);
118 m_xkb_keymap =
nullptr;
121 xkb_context_unref(m_xkb_context);
122 m_xkb_context =
nullptr;
126 zxdg_toplevel_decoration_v1_destroy(m_decoration);
127 m_decoration =
nullptr;
129 if (m_decoration_manager) {
130 zxdg_decoration_manager_v1_destroy(m_decoration_manager);
131 m_decoration_manager =
nullptr;
133 if (m_xdg_toplevel) {
134 xdg_toplevel_destroy(m_xdg_toplevel);
135 m_xdg_toplevel =
nullptr;
138 xdg_surface_destroy(m_xdg_surface);
139 m_xdg_surface =
nullptr;
142 wl_surface_destroy(m_surface);
146 xdg_wm_base_destroy(m_xdg_wm_base);
147 m_xdg_wm_base =
nullptr;
150 wl_seat_destroy(m_seat);
154 wl_compositor_destroy(m_compositor);
155 m_compositor =
nullptr;
158 wl_registry_destroy(m_registry);
159 m_registry =
nullptr;
163bool WaylandWindow::should_close()
const
165 return m_should_close.load();
168void WaylandWindow::poll()
173 while (wl_display_prepare_read(m_display) != 0) {
174 if (wl_display_dispatch_pending(m_display) < 0) {
175 handle_display_error();
180 if (wl_display_flush(m_display) < 0 && errno != EAGAIN) {
181 wl_display_cancel_read(m_display);
182 handle_display_error();
187 pfd.fd = wl_display_get_fd(m_display);
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();
196 wl_display_cancel_read(m_display);
199 if (wl_display_dispatch_pending(m_display) < 0) {
200 handle_display_error();
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) {
210 rev.type = WindowEventType::KEY_REPEAT;
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);
226void WaylandWindow::handle_display_error()
228 const int err = wl_display_get_error(m_display);
231 const wl_interface* iface =
nullptr;
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);
238 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
239 "Wayland display error: {}", err);
242 if (!m_should_close.exchange(
true)) {
244 ev.type = WindowEventType::WINDOW_CLOSED;
249void WaylandWindow::set_event_callback(WindowEventCallback callback)
251 m_event_callback = std::move(callback);
254void* WaylandWindow::get_native_handle()
const
259void* WaylandWindow::get_native_display()
const
264void WaylandWindow::set_title(
const std::string& title)
266 m_create_info.title = title;
268 xdg_toplevel_set_title(m_xdg_toplevel, title.c_str());
271void WaylandWindow::set_size(uint32_t w, uint32_t
h)
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);
284void WaylandWindow::set_position(uint32_t, uint32_t)
289void WaylandWindow::set_color(
const std::array<float, 4>& color)
291 m_create_info.clear_color = color;
298void WaylandWindow::register_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
300 std::lock_guard lock(m_render_tracking_mutex);
301 m_rendering_buffers.push_back(buf);
304void WaylandWindow::unregister_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
306 std::lock_guard lock(m_render_tracking_mutex);
307 std::erase_if(m_rendering_buffers, [&buf](
const auto& wp) {
309 return !sp || sp == buf;
313void WaylandWindow::track_frame_command(uint64_t
id)
315 m_frame_commands.push_back(
id);
318const std::vector<uint64_t>& WaylandWindow::get_frame_commands()
const
320 return m_frame_commands;
323void WaylandWindow::clear_frame_commands()
325 m_frame_commands.clear();
328std::vector<std::shared_ptr<Buffers::VKBuffer>> WaylandWindow::get_rendering_buffers()
const
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())
344void WaylandWindow::emit(
const WindowEvent& ev)
346 m_event_source.signal(ev);
347 if (m_event_callback)
348 m_event_callback(ev);
355void WaylandWindow::on_registry_global(
void* data, wl_registry* reg,
356 uint32_t
name,
const char* iface, uint32_t version)
358 auto* self =
static_cast<WaylandWindow*
>(data);
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));
380void WaylandWindow::on_registry_global_remove(
void*, wl_registry*, uint32_t) { }
386void WaylandWindow::on_wm_base_ping(
void*, xdg_wm_base* base, uint32_t serial)
388 xdg_wm_base_pong(base, serial);
395void WaylandWindow::on_xdg_surface_configure(
void* data, xdg_surface* surf, uint32_t serial)
397 auto* self =
static_cast<WaylandWindow*
>(data);
398 xdg_surface_ack_configure(surf, serial);
399 self->m_pending_configure.store(
true);
406void WaylandWindow::on_toplevel_configure(
void* data, xdg_toplevel*,
407 int32_t w, int32_t
h, wl_array*)
409 auto* self =
static_cast<WaylandWindow*
>(data);
410 if (w <= 0 ||
h <= 0)
413 if (
static_cast<uint32_t
>(w) == self->m_state.current_width &&
static_cast<uint32_t
>(
h) == self->m_state.current_height)
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);
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)
429void WaylandWindow::on_toplevel_close(
void* data, xdg_toplevel*)
431 auto* self =
static_cast<WaylandWindow*
>(data);
432 self->m_should_close.store(
true);
434 ev.type = WindowEventType::WINDOW_CLOSED;
438void WaylandWindow::on_toplevel_configure_bounds(
void*, xdg_toplevel*,
439 int32_t, int32_t) { }
441void WaylandWindow::on_toplevel_wm_capabilities(
void*, xdg_toplevel*,
448void WaylandWindow::on_seat_capabilities(
void* data, wl_seat* seat, uint32_t caps)
450 auto* self =
static_cast<WaylandWindow*
>(data);
452 const bool has_kb = caps & WL_SEAT_CAPABILITY_KEYBOARD;
453 const bool has_ptr = caps & WL_SEAT_CAPABILITY_POINTER;
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;
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;
472void WaylandWindow::on_seat_name(
void*, wl_seat*,
const char*) { }
478void WaylandWindow::on_keyboard_keymap(
void* data, wl_keyboard*,
479 uint32_t fmt,
int fd, uint32_t size)
481 auto* self =
static_cast<WaylandWindow*
>(data);
483 if (fmt != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
488 void* buf = mmap(
nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
490 if (buf == MAP_FAILED)
493 if (self->m_xkb_state) {
494 xkb_state_unref(self->m_xkb_state);
495 self->m_xkb_state =
nullptr;
497 if (self->m_xkb_keymap) {
498 xkb_keymap_unref(self->m_xkb_keymap);
499 self->m_xkb_keymap =
nullptr;
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);
509 if (self->m_xkb_keymap)
510 self->m_xkb_state = xkb_state_new(self->m_xkb_keymap);
513void WaylandWindow::on_keyboard_enter(
void* data, wl_keyboard*,
514 uint32_t, wl_surface*, wl_array*)
516 auto* self =
static_cast<WaylandWindow*
>(data);
517 self->m_state.is_focused =
true;
519 ev.type = WindowEventType::WINDOW_FOCUS_GAINED;
523void WaylandWindow::on_keyboard_leave(
void* data, wl_keyboard*,
524 uint32_t, wl_surface*)
526 auto* self =
static_cast<WaylandWindow*
>(data);
527 self->m_state.is_focused =
false;
529 ev.type = WindowEventType::WINDOW_FOCUS_LOST;
533void WaylandWindow::on_keyboard_key(
void* data, wl_keyboard*,
534 uint32_t, uint32_t, uint32_t key, uint32_t state)
536 auto* self =
static_cast<WaylandWindow*
>(data);
537 if (!self->m_xkb_state)
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);
547 const WindowEvent::KeyData kd {
548 .key =
static_cast<int16_t
>(mf_key),
549 .scancode =
static_cast<int32_t
>(key),
556 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
557 ev.type = WindowEventType::KEY_PRESSED;
560 self->m_held_keys[kd.key] = kd;
562 if (self->m_repeat_fd < 0)
563 self->m_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
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
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);
578 self->m_held_keys.erase(kd.key);
580 if (self->m_held_keys.empty() && self->m_repeat_fd >= 0) {
582 timerfd_settime(self->m_repeat_fd, 0, &ts,
nullptr);
585 ev.type = WindowEventType::KEY_RELEASED;
590void WaylandWindow::on_keyboard_modifiers(
void* data, wl_keyboard*,
591 uint32_t, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
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);
600void WaylandWindow::on_keyboard_repeat_info(
void* data, wl_keyboard*,
601 int32_t
rate, int32_t delay)
603 auto* self =
static_cast<WaylandWindow*
>(data);
604 if (!self->m_key_repeat_config.allow_compositor_override)
608 self->m_key_repeat_config.interval_ms = 1000U /
static_cast<uint32_t
>(
rate);
610 self->m_key_repeat_config.initial_delay_ms =
static_cast<uint32_t
>(
delay);
617void WaylandWindow::on_pointer_enter(
void* data, wl_pointer*,
618 uint32_t, wl_surface*, wl_fixed_t sx, wl_fixed_t sy)
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);
624 ev.type = WindowEventType::MOUSE_ENTERED;
628void WaylandWindow::on_pointer_leave(
void* data, wl_pointer*, uint32_t, wl_surface*)
630 auto* self =
static_cast<WaylandWindow*
>(data);
632 ev.type = WindowEventType::MOUSE_EXITED;
636void WaylandWindow::on_pointer_motion(
void* data, wl_pointer*,
637 uint32_t, wl_fixed_t sx, wl_fixed_t sy)
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);
643 ev.type = WindowEventType::MOUSE_MOTION;
644 ev.data = WindowEvent::MousePosData {
645 .x = self->m_pointer_x,
646 .y = self->m_pointer_y
651void WaylandWindow::on_pointer_button(
void* data, wl_pointer*,
652 uint32_t, uint32_t, uint32_t button, uint32_t state)
654 auto* self =
static_cast<WaylandWindow*
>(data);
656 IO::MouseButtons btn = IO::MouseButtons::Unknown;
659 btn = IO::MouseButtons::Left;
662 btn = IO::MouseButtons::Right;
665 btn = IO::MouseButtons::Middle;
668 btn = IO::MouseButtons::Button4;
671 btn = IO::MouseButtons::Button5;
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),
688void WaylandWindow::on_pointer_axis(
void* data, wl_pointer*,
689 uint32_t, uint32_t axis, wl_fixed_t
value)
691 auto* self =
static_cast<WaylandWindow*
>(data);
692 const double v = wl_fixed_to_double(
value) / 10.0;
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
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) { }
#define MF_ERROR(comp, ctx,...)
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.