3#ifdef MAYAFLUX_PLATFORM_LINUX
5#include "KeyMapping.hpp"
8#include <linux/input-event-codes.h>
10#include <sys/timerfd.h>
19WaylandWindow::WaylandWindow(
const WindowCreateInfo& create_info,
20 const GlobalGraphicsConfig& graphics_config)
21 : m_display(wl_display_connect(nullptr))
22 , m_create_info(create_info)
23 , m_key_repeat_config(graphics_config.key_repeat_config)
27 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
28 "wl_display_connect failed");
32 m_xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
34 m_registry = wl_display_get_registry(m_display);
35 wl_registry_add_listener(m_registry, &s_registry_listener,
this);
36 wl_display_roundtrip(m_display);
38 if (!m_compositor || !m_xdg_wm_base) {
39 MF_ERROR(Journal::Component::Core, Journal::Context::WindowingSubsystem,
40 "Required Wayland globals not found (compositor={}, xdg_wm_base={})",
41 static_cast<void*
>(m_compositor),
static_cast<void*
>(m_xdg_wm_base));
45 m_surface = wl_compositor_create_surface(m_compositor);
46 m_xdg_surface = xdg_wm_base_get_xdg_surface(m_xdg_wm_base, m_surface);
47 xdg_surface_add_listener(m_xdg_surface, &s_xdg_surface_listener,
this);
49 m_xdg_toplevel = xdg_surface_get_toplevel(m_xdg_surface);
50 xdg_toplevel_add_listener(m_xdg_toplevel, &s_toplevel_listener,
this);
51 xdg_toplevel_set_title(m_xdg_toplevel, create_info.title.c_str());
52 xdg_toplevel_set_app_id(m_xdg_toplevel,
"mayaflux");
54 if (m_decoration_manager) {
55 m_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
56 m_decoration_manager, m_xdg_toplevel);
57 zxdg_toplevel_decoration_v1_set_mode(m_decoration,
58 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
61 wl_surface_commit(m_surface);
62 wl_display_roundtrip(m_display);
64 m_state.current_width =
static_cast<int>(create_info.width);
65 m_state.current_height =
static_cast<int>(create_info.height);
68WaylandWindow::~WaylandWindow()
72 wl_display_disconnect(m_display);
81void WaylandWindow::show()
84 wl_surface_commit(m_surface);
87void WaylandWindow::hide()
90 wl_surface_attach(m_surface,
nullptr, 0, 0);
91 wl_surface_commit(m_surface);
95void WaylandWindow::destroy()
97 if (m_repeat_fd >= 0) {
103 wl_keyboard_destroy(m_keyboard);
104 m_keyboard =
nullptr;
107 wl_pointer_destroy(m_pointer);
112 xkb_state_unref(m_xkb_state);
113 m_xkb_state =
nullptr;
116 xkb_keymap_unref(m_xkb_keymap);
117 m_xkb_keymap =
nullptr;
120 xkb_context_unref(m_xkb_context);
121 m_xkb_context =
nullptr;
125 zxdg_toplevel_decoration_v1_destroy(m_decoration);
126 m_decoration =
nullptr;
128 if (m_decoration_manager) {
129 zxdg_decoration_manager_v1_destroy(m_decoration_manager);
130 m_decoration_manager =
nullptr;
132 if (m_xdg_toplevel) {
133 xdg_toplevel_destroy(m_xdg_toplevel);
134 m_xdg_toplevel =
nullptr;
137 xdg_surface_destroy(m_xdg_surface);
138 m_xdg_surface =
nullptr;
141 wl_surface_destroy(m_surface);
145 xdg_wm_base_destroy(m_xdg_wm_base);
146 m_xdg_wm_base =
nullptr;
149 wl_seat_destroy(m_seat);
153 wl_compositor_destroy(m_compositor);
154 m_compositor =
nullptr;
157 wl_registry_destroy(m_registry);
158 m_registry =
nullptr;
162bool WaylandWindow::should_close()
const
164 return m_should_close.load();
167void WaylandWindow::poll()
169 wl_display_dispatch_pending(m_display);
170 wl_display_flush(m_display);
172 if (m_repeat_fd >= 0 && !m_held_keys.empty()) {
173 uint64_t expirations = 0;
174 if (read(m_repeat_fd, &expirations,
sizeof(expirations)) ==
sizeof(expirations)
175 && expirations > 0) {
176 for (
const auto& [k, kd] : m_held_keys) {
178 rev.type = WindowEventType::KEY_REPEAT;
185 if (m_pending_configure.exchange(
false)) {
186 xdg_surface_set_window_geometry(m_xdg_surface, 0, 0,
187 static_cast<int32_t
>(m_state.current_width),
188 static_cast<int32_t
>(m_state.current_height));
189 wl_surface_commit(m_surface);
190 wl_display_flush(m_display);
194void WaylandWindow::set_event_callback(WindowEventCallback callback)
196 m_event_callback = std::move(callback);
199void* WaylandWindow::get_native_handle()
const
204void* WaylandWindow::get_native_display()
const
209void WaylandWindow::set_title(
const std::string& title)
211 m_create_info.title = title;
213 xdg_toplevel_set_title(m_xdg_toplevel, title.c_str());
216void WaylandWindow::set_size(uint32_t w, uint32_t
h)
218 m_create_info.width = w;
219 m_create_info.height =
h;
220 if (m_xdg_toplevel && m_surface) {
221 xdg_toplevel_set_min_size(m_xdg_toplevel,
222 static_cast<int32_t
>(w),
static_cast<int32_t
>(
h));
223 xdg_toplevel_set_max_size(m_xdg_toplevel,
224 static_cast<int32_t
>(w),
static_cast<int32_t
>(
h));
225 wl_surface_commit(m_surface);
229void WaylandWindow::set_position(uint32_t, uint32_t)
234void WaylandWindow::set_color(
const std::array<float, 4>& color)
236 m_create_info.clear_color = color;
243void WaylandWindow::register_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
245 std::lock_guard lock(m_render_tracking_mutex);
246 m_rendering_buffers.push_back(buf);
249void WaylandWindow::unregister_rendering_buffer(std::shared_ptr<Buffers::VKBuffer> buf)
251 std::lock_guard lock(m_render_tracking_mutex);
252 std::erase_if(m_rendering_buffers, [&buf](
const auto& wp) {
254 return !sp || sp == buf;
258void WaylandWindow::track_frame_command(uint64_t
id)
260 m_frame_commands.push_back(
id);
263const std::vector<uint64_t>& WaylandWindow::get_frame_commands()
const
265 return m_frame_commands;
268void WaylandWindow::clear_frame_commands()
270 m_frame_commands.clear();
273std::vector<std::shared_ptr<Buffers::VKBuffer>> WaylandWindow::get_rendering_buffers()
const
275 std::lock_guard lock(m_render_tracking_mutex);
276 std::vector<std::shared_ptr<Buffers::VKBuffer>> out;
277 out.reserve(m_rendering_buffers.size());
278 for (
auto& wp : m_rendering_buffers) {
279 if (
auto sp = wp.lock())
289void WaylandWindow::emit(
const WindowEvent& ev)
291 m_event_source.signal(ev);
292 if (m_event_callback)
293 m_event_callback(ev);
300void WaylandWindow::on_registry_global(
void* data, wl_registry* reg,
301 uint32_t name,
const char* iface, uint32_t version)
303 auto* self =
static_cast<WaylandWindow*
>(data);
305 if (std::string_view(iface) == wl_compositor_interface.name) {
306 self->m_compositor =
static_cast<wl_compositor*
>(
307 wl_registry_bind(reg, name, &wl_compositor_interface,
308 std::min(version, 4U)));
309 }
else if (std::string_view(iface) == xdg_wm_base_interface.name) {
310 self->m_xdg_wm_base =
static_cast<xdg_wm_base*
>(
311 wl_registry_bind(reg, name, &xdg_wm_base_interface,
312 std::min(version, 6U)));
313 xdg_wm_base_add_listener(self->m_xdg_wm_base, &s_wm_base_listener, self);
314 }
else if (std::string_view(iface) == wl_seat_interface.name) {
315 self->m_seat =
static_cast<wl_seat*
>(
316 wl_registry_bind(reg, name, &wl_seat_interface,
317 std::min(version, 7U)));
318 wl_seat_add_listener(self->m_seat, &s_seat_listener, self);
319 }
else if (std::string_view(iface) == zxdg_decoration_manager_v1_interface.name) {
320 self->m_decoration_manager =
static_cast<zxdg_decoration_manager_v1*
>(
321 wl_registry_bind(reg, name, &zxdg_decoration_manager_v1_interface, 1U));
325void WaylandWindow::on_registry_global_remove(
void*, wl_registry*, uint32_t) { }
331void WaylandWindow::on_wm_base_ping(
void*, xdg_wm_base* base, uint32_t serial)
333 xdg_wm_base_pong(base, serial);
340void WaylandWindow::on_xdg_surface_configure(
void* data, xdg_surface* surf, uint32_t serial)
342 auto* self =
static_cast<WaylandWindow*
>(data);
343 xdg_surface_ack_configure(surf, serial);
344 self->m_pending_configure.store(
true);
351void WaylandWindow::on_toplevel_configure(
void* data, xdg_toplevel*,
352 int32_t w, int32_t
h, wl_array*)
354 auto* self =
static_cast<WaylandWindow*
>(data);
355 if (w <= 0 ||
h <= 0)
358 if (
static_cast<uint32_t
>(w) == self->m_state.current_width &&
static_cast<uint32_t
>(
h) == self->m_state.current_height)
361 self->m_state.current_width =
static_cast<uint32_t
>(w);
362 self->m_state.current_height =
static_cast<uint32_t
>(
h);
363 self->m_create_info.width =
static_cast<uint32_t
>(w);
364 self->m_create_info.height =
static_cast<uint32_t
>(
h);
366 ev.type = WindowEventType::WINDOW_RESIZED;
367 ev.data = WindowEvent::ResizeData {
368 .width =
static_cast<uint32_t
>(w),
369 .height =
static_cast<uint32_t
>(
h)
374void WaylandWindow::on_toplevel_close(
void* data, xdg_toplevel*)
376 auto* self =
static_cast<WaylandWindow*
>(data);
377 self->m_should_close.store(
true);
379 ev.type = WindowEventType::WINDOW_CLOSED;
383void WaylandWindow::on_toplevel_configure_bounds(
void*, xdg_toplevel*,
384 int32_t, int32_t) { }
386void WaylandWindow::on_toplevel_wm_capabilities(
void*, xdg_toplevel*,
393void WaylandWindow::on_seat_capabilities(
void* data, wl_seat* seat, uint32_t caps)
395 auto* self =
static_cast<WaylandWindow*
>(data);
397 const bool has_kb = caps & WL_SEAT_CAPABILITY_KEYBOARD;
398 const bool has_ptr = caps & WL_SEAT_CAPABILITY_POINTER;
400 if (has_kb && !self->m_keyboard) {
401 self->m_keyboard = wl_seat_get_keyboard(seat);
402 wl_keyboard_add_listener(self->m_keyboard, &s_keyboard_listener, self);
403 }
else if (!has_kb && self->m_keyboard) {
404 wl_keyboard_destroy(self->m_keyboard);
405 self->m_keyboard =
nullptr;
408 if (has_ptr && !self->m_pointer) {
409 self->m_pointer = wl_seat_get_pointer(seat);
410 wl_pointer_add_listener(self->m_pointer, &s_pointer_listener, self);
411 }
else if (!has_ptr && self->m_pointer) {
412 wl_pointer_destroy(self->m_pointer);
413 self->m_pointer =
nullptr;
417void WaylandWindow::on_seat_name(
void*, wl_seat*,
const char*) { }
423void WaylandWindow::on_keyboard_keymap(
void* data, wl_keyboard*,
424 uint32_t fmt,
int fd, uint32_t size)
426 auto* self =
static_cast<WaylandWindow*
>(data);
428 if (fmt != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
433 void* buf = mmap(
nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
435 if (buf == MAP_FAILED)
438 if (self->m_xkb_state) {
439 xkb_state_unref(self->m_xkb_state);
440 self->m_xkb_state =
nullptr;
442 if (self->m_xkb_keymap) {
443 xkb_keymap_unref(self->m_xkb_keymap);
444 self->m_xkb_keymap =
nullptr;
447 self->m_xkb_keymap = xkb_keymap_new_from_string(self->m_xkb_context,
448 static_cast<const char*
>(buf),
449 XKB_KEYMAP_FORMAT_TEXT_V1,
450 XKB_KEYMAP_COMPILE_NO_FLAGS);
454 if (self->m_xkb_keymap)
455 self->m_xkb_state = xkb_state_new(self->m_xkb_keymap);
458void WaylandWindow::on_keyboard_enter(
void* data, wl_keyboard*,
459 uint32_t, wl_surface*, wl_array*)
461 auto* self =
static_cast<WaylandWindow*
>(data);
462 self->m_state.is_focused =
true;
464 ev.type = WindowEventType::WINDOW_FOCUS_GAINED;
468void WaylandWindow::on_keyboard_leave(
void* data, wl_keyboard*,
469 uint32_t, wl_surface*)
471 auto* self =
static_cast<WaylandWindow*
>(data);
472 self->m_state.is_focused =
false;
474 ev.type = WindowEventType::WINDOW_FOCUS_LOST;
478void WaylandWindow::on_keyboard_key(
void* data, wl_keyboard*,
479 uint32_t, uint32_t, uint32_t key, uint32_t state)
481 auto* self =
static_cast<WaylandWindow*
>(data);
482 if (!self->m_xkb_state)
485 IO::Keys mf_key = from_evdev_scancode(key);
486 if (mf_key == IO::Keys::Unknown) {
487 xkb_keycode_t xkb_key = key + 8;
488 xkb_keysym_t sym = xkb_state_key_get_one_sym(self->m_xkb_state, xkb_key);
489 mf_key = from_xkb_keysym(sym);
492 const WindowEvent::KeyData kd {
493 .key =
static_cast<int16_t
>(mf_key),
494 .scancode =
static_cast<int32_t
>(key),
501 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
502 ev.type = WindowEventType::KEY_PRESSED;
505 self->m_held_keys[kd.key] = kd;
507 if (self->m_repeat_fd < 0)
508 self->m_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
510 const long delay_ns =
static_cast<long>(self->m_key_repeat_config.initial_delay_ms) * 1'000'000L;
511 const long repeat_ns = self->m_key_repeat_config.interval_ms > 0
512 ?
static_cast<long>(self->m_key_repeat_config.interval_ms) * 1'000'000L
516 ts.it_value.tv_sec = 0;
517 ts.it_value.tv_nsec = delay_ns;
518 ts.it_interval.tv_sec = 0;
519 ts.it_interval.tv_nsec = repeat_ns;
520 timerfd_settime(self->m_repeat_fd, 0, &ts,
nullptr);
523 self->m_held_keys.erase(kd.key);
525 if (self->m_held_keys.empty() && self->m_repeat_fd >= 0) {
527 timerfd_settime(self->m_repeat_fd, 0, &ts,
nullptr);
530 ev.type = WindowEventType::KEY_RELEASED;
535void WaylandWindow::on_keyboard_modifiers(
void* data, wl_keyboard*,
536 uint32_t, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group)
538 auto* self =
static_cast<WaylandWindow*
>(data);
539 if (self->m_xkb_state) {
540 xkb_state_update_mask(self->m_xkb_state,
541 depressed, latched, locked, 0, 0, group);
545void WaylandWindow::on_keyboard_repeat_info(
void* data, wl_keyboard*,
546 int32_t rate, int32_t delay)
548 auto* self =
static_cast<WaylandWindow*
>(data);
549 if (!self->m_key_repeat_config.allow_compositor_override)
553 self->m_key_repeat_config.interval_ms = 1000u /
static_cast<uint32_t
>(rate);
555 self->m_key_repeat_config.initial_delay_ms =
static_cast<uint32_t
>(
delay);
562void WaylandWindow::on_pointer_enter(
void* data, wl_pointer*,
563 uint32_t, wl_surface*, wl_fixed_t sx, wl_fixed_t sy)
565 auto* self =
static_cast<WaylandWindow*
>(data);
566 self->m_pointer_x = wl_fixed_to_double(sx);
567 self->m_pointer_y = wl_fixed_to_double(sy);
569 ev.type = WindowEventType::MOUSE_ENTERED;
573void WaylandWindow::on_pointer_leave(
void* data, wl_pointer*, uint32_t, wl_surface*)
575 auto* self =
static_cast<WaylandWindow*
>(data);
577 ev.type = WindowEventType::MOUSE_EXITED;
581void WaylandWindow::on_pointer_motion(
void* data, wl_pointer*,
582 uint32_t, wl_fixed_t sx, wl_fixed_t sy)
584 auto* self =
static_cast<WaylandWindow*
>(data);
585 self->m_pointer_x = wl_fixed_to_double(sx);
586 self->m_pointer_y = wl_fixed_to_double(sy);
588 ev.type = WindowEventType::MOUSE_MOTION;
589 ev.data = WindowEvent::MousePosData {
590 .x = self->m_pointer_x,
591 .y = self->m_pointer_y
596void WaylandWindow::on_pointer_button(
void* data, wl_pointer*,
597 uint32_t, uint32_t, uint32_t button, uint32_t state)
599 auto* self =
static_cast<WaylandWindow*
>(data);
601 IO::MouseButtons btn = IO::MouseButtons::Unknown;
604 btn = IO::MouseButtons::Left;
607 btn = IO::MouseButtons::Right;
610 btn = IO::MouseButtons::Middle;
613 btn = IO::MouseButtons::Button4;
616 btn = IO::MouseButtons::Button5;
623 ev.type = (state == WL_POINTER_BUTTON_STATE_PRESSED)
624 ? WindowEventType::MOUSE_BUTTON_PRESSED
626 ev.data = WindowEvent::MouseButtonData {
627 .button =
static_cast<int8_t
>(btn),
633void WaylandWindow::on_pointer_axis(
void* data, wl_pointer*,
634 uint32_t, uint32_t axis, wl_fixed_t value)
636 auto* self =
static_cast<WaylandWindow*
>(data);
637 const double v = wl_fixed_to_double(value) / 10.0;
639 ev.type = WindowEventType::MOUSE_SCROLLED;
640 ev.data = WindowEvent::ScrollData {
641 .x_offset = (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) ? v : 0.0,
642 .y_offset = (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) ? -v : 0.0
647void WaylandWindow::on_pointer_frame(
void*, wl_pointer*) { }
648void WaylandWindow::on_pointer_axis_source(
void*, wl_pointer*, uint32_t) { }
649void WaylandWindow::on_pointer_axis_stop(
void*, wl_pointer*, uint32_t, uint32_t) { }
650void 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.