29#include <vulkan/vulkan.h>
30#include <vulkan/vulkan_wayland.h>
35#include <wayland-client.h>
36#include "xdg-shell-client-protocol.h"
37#include "xdg-decoration-client-protocol.h"
38#include "viewporter-client-protocol.h"
39#include "cursor-shape-client-protocol.h"
40#include <xkbcommon/xkbcommon.h>
41#include <linux/input-event-codes.h>
57int32_t GPUDisplayFrontendWayland::GetKey(uint32_t
key, uint32_t state)
61 xkb_keysym_t sym = xkb_state_key_get_one_sym(mXKBstate,
key + 8);
62 if (sym == XKB_KEY_KP_Subtract) {
64 }
else if (sym == XKB_KEY_KP_Add) {
66 }
else if (sym == XKB_KEY_Shift_L || sym == XKB_KEY_Shift_R) {
68 }
else if (sym == XKB_KEY_Alt_L) {
70 }
else if (sym == XKB_KEY_ISO_Level3_Shift || sym == XKB_KEY_Alt_R) {
72 }
else if (sym == XKB_KEY_Control_L) {
74 }
else if (sym == XKB_KEY_Control_R) {
76 }
else if (sym == XKB_KEY_Up) {
78 }
else if (sym == XKB_KEY_Down) {
80 }
else if (sym == XKB_KEY_Left) {
82 }
else if (sym == XKB_KEY_Right) {
84 }
else if (sym == XKB_KEY_Page_Up) {
86 }
else if (sym == XKB_KEY_Page_Down) {
88 }
else if (sym == XKB_KEY_Escape) {
90 }
else if (sym == XKB_KEY_Return) {
92 }
else if (sym == XKB_KEY_End) {
94 }
else if (sym == XKB_KEY_Home) {
96 }
else if (sym == XKB_KEY_Insert) {
98 }
else if (sym == XKB_KEY_F1) {
100 }
else if (sym == XKB_KEY_F2) {
102 }
else if (sym == XKB_KEY_F3) {
104 }
else if (sym == XKB_KEY_F4) {
106 }
else if (sym == XKB_KEY_F5) {
108 }
else if (sym == XKB_KEY_F6) {
110 }
else if (sym == XKB_KEY_F7) {
112 }
else if (sym == XKB_KEY_F8) {
114 }
else if (sym == XKB_KEY_F9) {
116 }
else if (sym == XKB_KEY_F10) {
118 }
else if (sym == XKB_KEY_F11) {
120 }
else if (sym == XKB_KEY_F12) {
122 }
else if (sym == 32) {
124 }
else if (sym > 255) {
127 retVal = xkb_keysym_to_utf32(sym);
136int32_t GPUDisplayFrontendWayland::FrontendMain()
139 fprintf(stderr,
"Only Vulkan backend supported\n");
143 mXKBcontext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
144 if (mXKBcontext ==
nullptr) {
145 throw std::runtime_error(
"Error initializing xkb context");
148 mWayland = wl_display_connect(
nullptr);
149 if (mWayland ==
nullptr) {
150 throw std::runtime_error(
"Could not connect to wayland display");
152 mRegistry = wl_display_get_registry(mWayland);
153 if (mRegistry ==
nullptr) {
154 throw std::runtime_error(
"Could not create wayland registry");
157 auto pointer_enter = [](
void*
data, wl_pointer* wl_pointer, uint32_t serial, wl_surface*
surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
159 if (!
me->mCursorShapeDevice &&
me->mCursorShapeManager) {
160 me->mCursorShapeDevice = wp_cursor_shape_manager_v1_get_pointer(
me->mCursorShapeManager, wl_pointer);
162 if (
me->mCursorShapeDevice) {
163 wp_cursor_shape_device_v1_set_shape(
me->mCursorShapeDevice, serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
166 auto pointer_leave = [](
void*
data, wl_pointer* wl_pointer, uint32_t serial, wl_surface* wl_surface) {
168 me->mMouseDn =
me->mMouseDnR =
me->mMouseDnM =
false;
170 auto pointer_motion = [](
void*
data, wl_pointer* wl_pointer, uint32_t
time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
172 me->mMouseMvX = wl_fixed_to_double(surface_x) *
me->mScale;
173 me->mMouseMvY = wl_fixed_to_double(surface_y) *
me->mScale;
175 auto pointer_button = [](
void*
data, wl_pointer* wl_pointer, uint32_t serial, uint32_t
time, uint32_t button, uint32_t
state) {
177 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
178 if (button == BTN_RIGHT) {
179 me->mMouseDnR =
true;
180 }
else if (button == BTN_LEFT) {
182 }
else if (button == BTN_MIDDLE) {
183 me->mMouseDnM =
true;
185 me->mMouseDnX =
me->mMouseMvX;
186 me->mMouseDnY =
me->mMouseMvY;
187 }
else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
188 if (button == BTN_RIGHT) {
189 me->mMouseDnR =
false;
190 }
else if (button == BTN_LEFT) {
191 me->mMouseDn =
false;
192 }
else if (button == BTN_MIDDLE) {
193 me->mMouseDnM =
false;
198 auto pointer_axis = [](
void*
data, wl_pointer* wl_pointer, uint32_t
time, uint32_t axis, wl_fixed_t
value) {
200 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
201 me->mAxisValue += wl_fixed_to_double(
value);
202 if (
me->mSeatVersion < WL_POINTER_FRAME_SINCE_VERSION) {
207 auto pointer_axis_source = [](
void*
data, wl_pointer* wl_pointer, uint32_t axis_source) {};
208 auto pointer_axis_stop = [](
void*
data, wl_pointer* wl_pointer, uint32_t
time, uint32_t axis) {};
209 auto pointer_axis_discrete = [](
void*
data, wl_pointer* wl_pointer, uint32_t axis, int32_t discrete) {};
210#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
211 auto pointer_axis_value120 = [](
void*
data, wl_pointer* wl_pointer, uint32_t axis, int32_t value120) {
213 if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
214 me->mAxisValue120 += value120;
215 me->mHasAxisValue120 =
true;
219#pragma GCC diagnostic push
220#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
221 mPointerListener = wl_pointer_listener{.enter = pointer_enter, .leave = pointer_leave, .motion = pointer_motion, .button = pointer_button, .axis = pointer_axis, .frame = pointer_frame, .axis_source = pointer_axis_source, .axis_stop = pointer_axis_stop, .axis_discrete = pointer_axis_discrete,
222#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
223 .axis_value120 = pointer_axis_value120
226#pragma GCC diagnostic pop
227 auto keyboard_keymap = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t
format, int32_t fd, uint32_t
size) {
229 char* keymap_string =
format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 ? (
char*)mmap(
nullptr,
size, PROT_READ, MAP_PRIVATE, fd, 0) : (
char*)MAP_FAILED;
231 if (keymap_string == MAP_FAILED) {
232 GPUError(
"Cannot read Wayland keymap");
235 xkb_keymap* keymap = xkb_keymap_new_from_string(
me->mXKBcontext, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
236 munmap(keymap_string,
size);
237 xkb_state*
state = keymap ? xkb_state_new(keymap) : nullptr;
239 GPUError(
"Cannot compile Wayland keymap");
240 xkb_keymap_unref(keymap);
243 if (
me->mXKBkeymap) {
244 xkb_state_unref(
me->mXKBstate);
245 xkb_keymap_unref(
me->mXKBkeymap);
247 me->mXKBkeymap = keymap;
250 auto keyboard_enter = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, wl_surface*
surface, wl_array*
keys) {};
251 auto keyboard_leave = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, wl_surface*
surface) {
254 me->mRepeatKeyCode = -1;
256 auto keyboard_key = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, uint32_t
time, uint32_t
key, uint32_t
state) {
258 int32_t symbol =
me->GetKey(
key, state);
259 int32_t keyPress = (symbol >=
'a' && symbol <=
'z') ? symbol +
'A' -
'a' : symbol;
260 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
261 me->mKeys[keyPress] =
true;
263 me->HandleKey(symbol);
264 if (symbol &&
me->mRepeatRate > 0 &&
me->mXKBkeymap && xkb_keymap_key_repeats(
me->mXKBkeymap,
key + 8)) {
265 me->mRepeatKeyCode =
key;
266 me->mRepeatSymbol = symbol;
267 me->mRepeatNext = std::chrono::steady_clock::now() + std::chrono::milliseconds(
me->mRepeatDelay);
270 me->mKeys[keyPress] =
false;
271 me->mKeysShift[keyPress] =
false;
273 me->mRepeatKeyCode = -1;
277 auto keyboard_modifiers = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t
group) {
279 xkb_state_update_mask(
me->mXKBstate, mods_depressed, mods_latched, mods_locked, 0, 0,
group);
281 auto keyboard_repeat = [](
void*
data, wl_keyboard* wl_keyboard, int32_t
rate, int32_t delay) {
284 me->mRepeatDelay = delay;
286 me->mRepeatKeyCode = -1;
289 mKeyboardListener = wl_keyboard_listener{.keymap = keyboard_keymap, .enter = keyboard_enter, .leave = keyboard_leave, .key = keyboard_key, .modifiers = keyboard_modifiers, .repeat_info = keyboard_repeat};
291 auto xdg_wm_base_ping = [](
void*
data,
struct xdg_wm_base* xdg_wm_base, uint32_t serial) {
292 xdg_wm_base_pong(xdg_wm_base, serial);
294 mXdgWmBaseListener = xdg_wm_base_listener{
295 .ping = xdg_wm_base_ping,
298 auto seat_capabilities = [](
void*
data,
struct wl_seat* seat, uint32_t capabilities) {
300 if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !
me->mPointer) {
301 me->mPointer = wl_seat_get_pointer(seat);
302 wl_pointer_add_listener(
me->mPointer, &
me->mPointerListener, me);
304 if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !
me->mKeyboard) {
305 me->mKeyboard = wl_seat_get_keyboard(seat);
306 wl_keyboard_add_listener(
me->mKeyboard, &
me->mKeyboardListener, me);
309 auto seat_name = [](
void*
data,
struct wl_seat* seat,
const char*
name) {
311 GPUInfo(
"Wayland seat: %s",
name);
314 mSeatListener = wl_seat_listener{
315 .capabilities = seat_capabilities,
319 auto output_geometry = [](
void*
data, wl_output*
output, int32_t
x, int32_t
y, int32_t physical_width, int32_t physical_height, int32_t subpixel,
const char*
make,
const char* model, int32_t transform) {};
322 if (
auto* o =
me->findOutput(
output); o && (
flags & WL_OUTPUT_MODE_CURRENT)) {
323 o->refresh = refresh;
326 auto output_done = [](
void*
data, wl_output*
output) {};
327 auto output_scale = [](
void*
data, wl_output*
output, int32_t factor) {
329 if (
auto* o =
me->findOutput(
output)) {
334#ifdef WL_OUTPUT_NAME_SINCE_VERSION
335 auto output_name = [](
void*
data, wl_output*
output,
const char*
name) {
337 if (
auto* o =
me->findOutput(
output)) {
340 if (
me->mDisplay->GetProcessingSettings().debugLevel >= 2) {
341 GPUInfo(
"Wayland output: %s",
name);
346 mOutputListener = wl_output_listener{
347 .geometry = output_geometry,
350 .scale = output_scale,
351#ifdef WL_OUTPUT_NAME_SINCE_VERSION
353 .description = output_description,
357 auto registry_global = [](
void*
data, wl_registry* registry, uint32_t
name,
const char* interface, uint32_t
version) {
359 if (
me->mDisplay->GetProcessingSettings().debugLevel >= 3) {
360 GPUInfo(
"Available interface %s (version %u)", interface,
version);
362 auto bindVersion = [
version](
const wl_interface& iface, uint32_t maxVersion) {
return std::min<uint32_t>({
version, (uint32_t)iface.version, maxVersion}); };
363 if (strcmp(interface, wl_output_interface.name) == 0) {
364 uint32_t
v = bindVersion(wl_output_interface, 4);
365 wl_output*
output = (wl_output*)wl_registry_bind(registry,
name, &wl_output_interface,
v);
366 me->mOutputs.emplace_back(OutputInfo{
output,
name,
v, 1,
""});
367 wl_output_add_listener(
output, &
me->mOutputListener, me);
368 }
else if (strcmp(interface, wl_compositor_interface.name) == 0) {
369 me->mCompositor = (wl_compositor*)wl_registry_bind(registry,
name, &wl_compositor_interface, bindVersion(wl_compositor_interface, 6));
370 }
else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
371 me->mXdgBase = (xdg_wm_base*)wl_registry_bind(registry,
name, &xdg_wm_base_interface, bindVersion(xdg_wm_base_interface, 6));
372 xdg_wm_base_add_listener(
me->mXdgBase, &
me->mXdgWmBaseListener, me);
373 }
else if (strcmp(interface, wl_seat_interface.name) == 0 && !
me->mSeat) {
374 me->mSeatVersion = bindVersion(wl_seat_interface, 8);
375 me->mSeat = (wl_seat*)wl_registry_bind(registry,
name, &wl_seat_interface,
me->mSeatVersion);
376 wl_seat_add_listener(
me->mSeat, &
me->mSeatListener, me);
377 }
else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
378 me->mDecManager = (zxdg_decoration_manager_v1*)wl_registry_bind(registry,
name, &zxdg_decoration_manager_v1_interface, 1);
379 }
else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
380 me->mCursorShapeManager = (wp_cursor_shape_manager_v1*)wl_registry_bind(registry,
name, &wp_cursor_shape_manager_v1_interface, 1);
381 }
else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
382 me->mViewporter = (wp_viewporter*)wl_registry_bind(registry,
name, &wp_viewporter_interface, 1);
383 }
else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
384 me->mFractionalScaleManager = (wp_fractional_scale_manager_v1*)wl_registry_bind(registry,
name, &wp_fractional_scale_manager_v1_interface, 1);
387 auto registry_global_remove = [](
void*
data, wl_registry* registry, uint32_t
name) {
389 for (
auto it =
me->mOutputs.begin(); it !=
me->mOutputs.end(); it++) {
390 if (it->globalName ==
name) {
391 std::erase(
me->mSurfaceOutputs, it->output);
392 if (it->version >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
393 wl_output_release(it->output);
395 wl_output_destroy(it->output);
397 me->mOutputs.erase(it);
403 mRegistryListener = wl_registry_listener{.global = registry_global, .global_remove = registry_global_remove};
405 wl_registry_add_listener(mRegistry, &mRegistryListener,
this);
406 if (wl_display_roundtrip(mWayland) == -1) {
407 reportError(
"initial roundtrip");
408 throw std::runtime_error(
"Wayland initialization failed");
411 if (mCompositor ==
nullptr || mXdgBase ==
nullptr || mSeat ==
nullptr) {
412 throw std::runtime_error(
"Error getting wayland objects");
415 mSurface = wl_compositor_create_surface(mCompositor);
416 if (mSurface ==
nullptr) {
417 throw std::runtime_error(
"Error creating wayland surface");
422 me->mSurfaceOutputs.emplace_back(
output);
427 std::erase(
me->mSurfaceOutputs,
output);
430#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION
431 auto surface_preferred_buffer_scale = [](
void*
data, wl_surface*
surface, int32_t factor) {
433 me->mPreferredScale = factor;
436 auto surface_preferred_buffer_transform = [](
void*
data, wl_surface*
surface, uint32_t transform) {};
438 mSurfaceListener = wl_surface_listener{
439 .enter = surface_enter,
440 .leave = surface_leave,
441#ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION
442 .preferred_buffer_scale = surface_preferred_buffer_scale,
443 .preferred_buffer_transform = surface_preferred_buffer_transform,
446 wl_surface_add_listener(mSurface, &mSurfaceListener,
this);
448 if (mViewporter && mFractionalScaleManager) {
449 auto fractional_scale_preferred_scale = [](
void*
data, wp_fractional_scale_v1* fractional_scale, uint32_t scale) {
451 me->mFractionalScale120 = scale;
454 mFractionalScaleListener = wp_fractional_scale_v1_listener{.preferred_scale = fractional_scale_preferred_scale};
455 mViewport = wp_viewporter_get_viewport(mViewporter, mSurface);
456 mFractionalScale = wp_fractional_scale_manager_v1_get_fractional_scale(mFractionalScaleManager, mSurface);
457 wp_fractional_scale_v1_add_listener(mFractionalScale, &mFractionalScaleListener,
this);
460 mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface);
461 if (mXdgSurface ==
nullptr) {
462 throw std::runtime_error(
"Error creating wayland xdg surface");
464 mXdgToplevel = xdg_surface_get_toplevel(mXdgSurface);
466 auto xdg_toplevel_handle_configure = [](
void*
data, xdg_toplevel* toplevel, int32_t
width, int32_t
height, wl_array*
states) {
468 if (
me->mDisplay->GetProcessingSettings().debugLevel >= 3) {
469 GPUInfo(
"Wayland surface resized to %d %d",
width,
height);
473 bool fullScreen =
false, maximized =
false;
474 me->mSuspended =
false;
475 const uint32_t*
state = (
const uint32_t*)
states->data;
476 for (
size_t i = 0;
i <
states->size /
sizeof(uint32_t);
i++) {
477 if (
state[
i] == XDG_TOPLEVEL_STATE_FULLSCREEN) {
479 }
else if (
state[
i] == XDG_TOPLEVEL_STATE_MAXIMIZED) {
481#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION
482 }
else if (
state[
i] == XDG_TOPLEVEL_STATE_SUSPENDED) {
483 me->mSuspended =
true;
487 me->setWindowState(fullScreen, maximized);
488 me->mStateFullScreenOrMaximized = fullScreen || maximized;
491 auto xdg_surface_handle_configure = [](
void*
data, xdg_surface*
surface, uint32_t serial) {
493 xdg_surface_ack_configure(
me->mXdgSurface, serial);
494 if (
me->mWidthRequested &&
me->mHeightRequested) {
495 me->mWidthLogical =
me->mWidthRequested;
496 me->mHeightLogical =
me->mHeightRequested;
497 if (!
me->mStateFullScreenOrMaximized) {
498 me->mWidthWindowed =
me->mWidthRequested;
499 me->mHeightWindowed =
me->mHeightRequested;
501 }
else if (!
me->mStateFullScreenOrMaximized) {
502 me->mWidthLogical =
me->mWidthWindowed;
503 me->mHeightLogical =
me->mHeightWindowed;
504 if (
me->mBoundsWidth &&
me->mBoundsHeight) {
505 me->mWidthLogical = std::min(
me->mWidthLogical,
me->mBoundsWidth);
506 me->mHeightLogical = std::min(
me->mHeightLogical,
me->mBoundsHeight);
510 me->mWidthRequested =
me->mHeightRequested = 0;
513 auto xdg_toplevel_handle_close = [](
void*
data, xdg_toplevel* toplevel) {
516#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
517 auto xdg_toplevel_handle_configure_bounds = [](
void*
data, xdg_toplevel* toplevel, int32_t
width, int32_t
height) {
523#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
524 auto xdg_toplevel_handle_wm_capabilities = [](
void*
data, xdg_toplevel* toplevel, wl_array* capabilities) {
526 me->mCanFullscreen =
me->mCanMaximize =
false;
527 const uint32_t* cap = (
const uint32_t*)capabilities->data;
528 for (
size_t i = 0;
i < capabilities->size /
sizeof(uint32_t);
i++) {
529 me->mCanFullscreen |= cap[
i] == XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN;
530 me->mCanMaximize |= cap[
i] == XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE;
535 mXdgSurfaceListener = xdg_surface_listener{
536 .configure = xdg_surface_handle_configure,
539 mXdgToplevelListener = xdg_toplevel_listener{
540 .configure = xdg_toplevel_handle_configure,
541 .close = xdg_toplevel_handle_close,
542#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION
543 .configure_bounds = xdg_toplevel_handle_configure_bounds,
545#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
546 .wm_capabilities = xdg_toplevel_handle_wm_capabilities,
550 xdg_surface_add_listener(mXdgSurface, &mXdgSurfaceListener,
this);
551 xdg_toplevel_add_listener(mXdgToplevel, &mXdgToplevelListener,
this);
555 xdg_toplevel_set_app_id(mXdgToplevel,
"o2-gpu-event-display");
558 mXdgDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(mDecManager, mXdgToplevel);
559 zxdg_toplevel_decoration_v1_set_mode(mXdgDecoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
561 GPUInfo(
"Wayland compositor does not support server-side decorations");
565 wl_surface_commit(mSurface);
566 if (wl_display_roundtrip(mWayland) == -1) {
567 reportError(
"initial roundtrip");
568 throw std::runtime_error(
"Wayland initialization failed");
570 if (initialFullScreen || initialMaximized) {
573 if (wl_display_roundtrip(mWayland) == -1) {
574 reportError(
"initial window state roundtrip");
575 throw std::runtime_error(
"Wayland initialization failed");
590 while (!
error && wl_display_prepare_read(mWayland) != 0) {
591 error = wl_display_dispatch_pending(mWayland) == -1;
594 reportError(
"dispatching events");
597 wl_display_flush(mWayland);
598 pollfd pfd = {wl_display_get_fd(mWayland), POLLIN, 0};
599 if (poll(&pfd, 1, mSuspended ? 100 : 0) > 0) {
600 if (wl_display_read_events(mWayland) == -1) {
601 reportError(
"reading events");
605 wl_display_cancel_read(mWayland);
607 if (wl_display_dispatch_pending(mWayland) == -1) {
608 reportError(
"dispatching events");
625 xkb_state_unref(mXKBstate);
626 xkb_keymap_unref(mXKBkeymap);
627 mXKBkeymap =
nullptr;
629 xkb_context_unref(mXKBcontext);
636 if (mCursorShapeDevice) {
637 wp_cursor_shape_device_v1_destroy(mCursorShapeDevice);
638 mCursorShapeDevice =
nullptr;
640 if (mCursorShapeManager) {
641 wp_cursor_shape_manager_v1_destroy(mCursorShapeManager);
642 mCursorShapeManager =
nullptr;
645 mSeatVersion >= WL_POINTER_RELEASE_SINCE_VERSION ? wl_pointer_release(mPointer) : wl_pointer_destroy(mPointer);
649 mSeatVersion >= WL_KEYBOARD_RELEASE_SINCE_VERSION ? wl_keyboard_release(mKeyboard) : wl_keyboard_destroy(mKeyboard);
652 if (mXdgDecoration) {
653 zxdg_toplevel_decoration_v1_destroy(mXdgDecoration);
654 mXdgDecoration =
nullptr;
656 if (mFractionalScale) {
657 wp_fractional_scale_v1_destroy(mFractionalScale);
658 mFractionalScale =
nullptr;
661 wp_viewport_destroy(mViewport);
664 xdg_toplevel_destroy(mXdgToplevel);
665 xdg_surface_destroy(mXdgSurface);
666 wl_surface_destroy(mSurface);
667 if (mFractionalScaleManager) {
668 wp_fractional_scale_manager_v1_destroy(mFractionalScaleManager);
669 mFractionalScaleManager =
nullptr;
672 wp_viewporter_destroy(mViewporter);
673 mViewporter =
nullptr;
676 zxdg_decoration_manager_v1_destroy(mDecManager);
677 mDecManager =
nullptr;
679 for (
auto& o : mOutputs) {
680 if (o.version >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
681 wl_output_release(o.output);
683 wl_output_destroy(o.output);
687 mSurfaceOutputs.clear();
689 if (mSeatVersion >= WL_SEAT_RELEASE_SINCE_VERSION) {
690 wl_seat_release(mSeat);
692 wl_seat_destroy(mSeat);
696 xdg_wm_base_destroy(mXdgBase);
697 wl_compositor_destroy(mCompositor);
698 wl_registry_destroy(mRegistry);
699 wl_display_disconnect(mWayland);
706 if (!mCanFullscreen) {
707 GPUWarning(
"Wayland compositor does not support fullscreen");
709 xdg_toplevel_set_fullscreen(mXdgToplevel, mSurfaceOutputs.size() ? mSurfaceOutputs.back() :
nullptr);
711 xdg_toplevel_unset_fullscreen(mXdgToplevel);
718 GPUWarning(
"Wayland compositor does not support maximizing windows");
720 xdg_toplevel_set_maximized(mXdgToplevel);
722 xdg_toplevel_unset_maximized(mXdgToplevel);
728 auto* o = mSurfaceOutputs.size() ? findOutput(mSurfaceOutputs.back()) :
nullptr;
729 return o ? o->refresh / 1000.f : 0.f;
732GPUDisplayFrontendWayland::OutputInfo* GPUDisplayFrontendWayland::findOutput(wl_output*
output)
734 auto it = std::find_if(mOutputs.begin(), mOutputs.end(), [
output](
const auto& o) { return o.output == output; });
735 return it == mOutputs.end() ? nullptr : &*it;
738void GPUDisplayFrontendWayland::updateScale()
741 if (mFractionalScale) {
742 scale = mFractionalScale120 ? mFractionalScale120 / 120. : 1.;
744 int32_t intScale = mPreferredScale;
747 for (
auto*
output : mSurfaceOutputs) {
748 if (
auto* o = findOutput(
output)) {
749 intScale = std::max(intScale, o->scale);
755 if (scale != mScale && mSurface) {
757 if (!mFractionalScale) {
758 wl_surface_set_buffer_scale(mSurface, (int32_t)mScale);
764void GPUDisplayFrontendWayland::updateSize()
767 wp_viewport_set_destination(mViewport, mWidthLogical, mHeightLogical);
769 const int32_t
width = mFractionalScale ? (int32_t)std::lround(mWidthLogical * mScale) : (mWidthLogical * (int32_t)mScale);
770 const int32_t
height = mFractionalScale ? (int32_t)std::lround(mHeightLogical * mScale) : (mHeightLogical * (int32_t)mScale);
781void GPUDisplayFrontendWayland::reportError(
const char* where)
783 const int32_t err = wl_display_get_error(mWayland);
785 const wl_interface* iface =
nullptr;
787 const uint32_t code = wl_display_get_protocol_error(mWayland, &iface, &
id);
788 GPUError(
"Wayland protocol error %u on %s@%u while %s", code, iface ? iface->name :
"unknown interface",
id, where);
790 GPUError(
"Wayland error while %s: %s", where, strerror(err));
794void GPUDisplayFrontendWayland::handleKeyRepeat()
796 if (mRepeatKeyCode < 0) {
799 const auto now = std::chrono::steady_clock::now();
800 const auto period = std::chrono::microseconds(1000000 / mRepeatRate);
801 if (now >= mRepeatNext + period) {
804 while (now >= mRepeatNext && mRepeatKeyCode >= 0) {
806 mRepeatNext += period;
810void GPUDisplayFrontendWayland::applyScroll()
812 mMouseWheel += mHasAxisValue120 ? (mAxisValue120 * -100 / 120) : (int32_t)(mAxisValue * (-100. / 15.));
815 mHasAxisValue120 =
false;
830 VkWaylandSurfaceCreateInfoKHR info{};
831 info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
833 info.display = mWayland;
834 info.surface = mSurface;
835 return vkCreateWaylandSurfaceKHR(*(VkInstance*)instance, &info,
nullptr, (VkSurfaceKHR*)
surface) != VK_SUCCESS;
840 static const char* exts[] = {
"VK_KHR_surface",
"VK_KHR_wayland_surface"};
header::DataDescription description
std::vector< std::string > keys
char error[CURL_ERROR_SIZE]
const Acts::Surface * surface
void ToggleMaximized(bool set) override
void SwitchFullscreen(bool set) override
int32_t getVulkanSurface(void *instance, void *surface) override
void getSize(int32_t &width, int32_t &height) override
float getRefreshRate() override
void OpenGLPrint(const char *s, float x, float y, float r, float g, float b, float a, bool fromBotton=true) override
GPUDisplayFrontendWayland()
uint32_t getReqVulkanExtensions(const char **&p) override
void SetVSync(bool enable) override
void HandleExternalGUISignals()
static constexpr int32_t KEY_F9
const char * mFrontendName
static constexpr int32_t KEY_F2
static constexpr int32_t KEY_ALT
void HandleKey(uint8_t key)
static constexpr int32_t KEY_F12
static constexpr int32_t KEY_F7
static constexpr int32_t KEY_END
static constexpr int32_t KEY_PAGEDOWN
static constexpr int32_t KEY_RALT
volatile int32_t mDisplayControl
static constexpr int32_t KEY_F10
static constexpr int32_t KEY_F4
static constexpr int32_t KEY_LEFT
std::mutex mExitStateMutex
static constexpr int32_t KEY_ENTER
static constexpr int32_t KEY_F6
static constexpr int32_t KEY_CTRL
static constexpr int32_t KEY_F1
void ResizeScene(int32_t width, int32_t height)
static constexpr const char * DISPLAY_WINDOW_NAME
frontendTypes mFrontendType
static constexpr int32_t KEY_SHIFT
static constexpr int32_t KEY_F3
static constexpr int32_t KEY_INSERT
static constexpr int32_t KEY_F11
void setWindowState(bool fullScreen, bool maximized)
static constexpr int32_t KEY_SPACE
static constexpr int32_t KEY_F5
static constexpr int32_t KEY_PAGEUP
static constexpr int32_t KEY_HOME
int32_t InitDisplay(bool initFailure=false)
GPUDisplayBackend * backend()
static constexpr int32_t KEY_UP
static constexpr int32_t KEY_DOWN
static constexpr int32_t KEY_F8
void applyInitialWindowState()
static constexpr int32_t MIN_WIDTH
static constexpr int32_t KEY_RIGHT
volatile bool mDisplayRunning
static constexpr int32_t KEY_RCTRL
static constexpr int32_t MIN_HEIGHT
static constexpr int32_t KEY_ESCAPE
const GPUSettingsDisplayRenderer & cfgR() const
const GPUSettingsProcessing & GetProcessingSettings() const
GLuint const GLchar * name
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
GLboolean GLboolean GLboolean GLboolean a
GLint GLint GLsizei GLint GLenum format
GPUDisplayFrontend * createGPUDisplayFrontendWayland()
constexpr auto make(F f) -> std::array< int, N >
Polygon< T > close(Polygon< T > polygon)