Project
Loading...
Searching...
No Matches
GPUDisplayFrontendWayland.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
17#include "GPUDisplayGUIWrapper.h" // Destructor of the GUI wrapper unique_ptr member
18#include "GPUDisplay.h"
19#include "GPULogging.h"
20#include <cstdio>
21#include <cstdlib>
22#include <cstring>
23#include <stdexcept>
24#include <cerrno>
25#include <chrono>
26#include <algorithm>
27#include <cmath>
28
29#include <vulkan/vulkan.h>
30#include <vulkan/vulkan_wayland.h>
31
32#include <unistd.h>
33#include <poll.h>
34#include <sys/mman.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>
42
43using namespace o2::gpu;
44
46
52
53void GPUDisplayFrontendWayland::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton)
54{
55}
56
57int32_t GPUDisplayFrontendWayland::GetKey(uint32_t key, uint32_t state)
58{
59 int32_t retVal = 0;
60 if (mXKBkeymap) {
61 xkb_keysym_t sym = xkb_state_key_get_one_sym(mXKBstate, key + 8);
62 if (sym == XKB_KEY_KP_Subtract) {
63 return ('-');
64 } else if (sym == XKB_KEY_KP_Add) {
65 return ('+');
66 } else if (sym == XKB_KEY_Shift_L || sym == XKB_KEY_Shift_R) {
67 return (KEY_SHIFT);
68 } else if (sym == XKB_KEY_Alt_L) {
69 return (KEY_ALT);
70 } else if (sym == XKB_KEY_ISO_Level3_Shift || sym == XKB_KEY_Alt_R) {
71 return (KEY_RALT);
72 } else if (sym == XKB_KEY_Control_L) {
73 return (KEY_CTRL);
74 } else if (sym == XKB_KEY_Control_R) {
75 return (KEY_RCTRL);
76 } else if (sym == XKB_KEY_Up) {
77 return (KEY_UP);
78 } else if (sym == XKB_KEY_Down) {
79 return (KEY_DOWN);
80 } else if (sym == XKB_KEY_Left) {
81 return (KEY_LEFT);
82 } else if (sym == XKB_KEY_Right) {
83 return (KEY_RIGHT);
84 } else if (sym == XKB_KEY_Page_Up) {
85 return (KEY_PAGEUP);
86 } else if (sym == XKB_KEY_Page_Down) {
87 return (KEY_PAGEDOWN);
88 } else if (sym == XKB_KEY_Escape) {
89 return (KEY_ESCAPE);
90 } else if (sym == XKB_KEY_Return) {
91 return (KEY_ENTER);
92 } else if (sym == XKB_KEY_End) {
93 return (KEY_END);
94 } else if (sym == XKB_KEY_Home) {
95 return (KEY_HOME);
96 } else if (sym == XKB_KEY_Insert) {
97 return (KEY_INSERT);
98 } else if (sym == XKB_KEY_F1) {
99 return (KEY_F1);
100 } else if (sym == XKB_KEY_F2) {
101 return (KEY_F2);
102 } else if (sym == XKB_KEY_F3) {
103 return (KEY_F3);
104 } else if (sym == XKB_KEY_F4) {
105 return (KEY_F4);
106 } else if (sym == XKB_KEY_F5) {
107 return (KEY_F5);
108 } else if (sym == XKB_KEY_F6) {
109 return (KEY_F6);
110 } else if (sym == XKB_KEY_F7) {
111 return (KEY_F7);
112 } else if (sym == XKB_KEY_F8) {
113 return (KEY_F8);
114 } else if (sym == XKB_KEY_F9) {
115 return (KEY_F9);
116 } else if (sym == XKB_KEY_F10) {
117 return (KEY_F10);
118 } else if (sym == XKB_KEY_F11) {
119 return (KEY_F11);
120 } else if (sym == XKB_KEY_F12) {
121 return (KEY_F12);
122 } else if (sym == 32) {
123 return (KEY_SPACE);
124 } else if (sym > 255) {
125 return (0);
126 } else {
127 retVal = xkb_keysym_to_utf32(sym);
128 }
129 }
130 if (retVal > 255) {
131 return (0);
132 }
133 return retVal;
134}
135
136int32_t GPUDisplayFrontendWayland::FrontendMain()
137{
138 if (backend()->backendType() != GPUDisplayBackend::TYPE_VULKAN) {
139 fprintf(stderr, "Only Vulkan backend supported\n");
140 return 1;
141 }
142
143 mXKBcontext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
144 if (mXKBcontext == nullptr) {
145 throw std::runtime_error("Error initializing xkb context");
146 }
147
148 mWayland = wl_display_connect(nullptr);
149 if (mWayland == nullptr) {
150 throw std::runtime_error("Could not connect to wayland display");
151 }
152 mRegistry = wl_display_get_registry(mWayland);
153 if (mRegistry == nullptr) {
154 throw std::runtime_error("Could not create wayland registry");
155 }
156
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);
161 }
162 if (me->mCursorShapeDevice) {
163 wp_cursor_shape_device_v1_set_shape(me->mCursorShapeDevice, serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
164 }
165 };
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;
169 };
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;
174 };
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) {
181 me->mMouseDn = true;
182 } else if (button == BTN_MIDDLE) {
183 me->mMouseDnM = true;
184 }
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;
194 }
195 }
196 };
197 auto pointer_frame = [](void* data, wl_pointer* wl_pointer) { ((GPUDisplayFrontendWayland*)data)->applyScroll(); };
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) {
203 me->applyScroll();
204 }
205 }
206 };
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;
216 }
217 };
218#endif
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
224#endif
225 };
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; // MAP_PRIVATE required since wl_seat version 7
230 close(fd);
231 if (keymap_string == MAP_FAILED) {
232 GPUError("Cannot read Wayland keymap");
233 return;
234 }
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;
238 if (!state) { // Keep the previous keymap
239 GPUError("Cannot compile Wayland keymap");
240 xkb_keymap_unref(keymap);
241 return;
242 }
243 if (me->mXKBkeymap) {
244 xkb_state_unref(me->mXKBstate);
245 xkb_keymap_unref(me->mXKBkeymap);
246 }
247 me->mXKBkeymap = keymap;
248 me->mXKBstate = state;
249 };
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) {
253 me->focusLost();
254 me->mRepeatKeyCode = -1;
255 };
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;
262 me->mKeysShift[keyPress] = me->mKeys[KEY_SHIFT];
263 me->HandleKey(symbol);
264 if (symbol && me->mRepeatRate > 0 && me->mXKBkeymap && xkb_keymap_key_repeats(me->mXKBkeymap, key + 8)) { // Wayland compositors do not repeat keys, the client must do it
265 me->mRepeatKeyCode = key;
266 me->mRepeatSymbol = symbol;
267 me->mRepeatNext = std::chrono::steady_clock::now() + std::chrono::milliseconds(me->mRepeatDelay);
268 }
269 } else {
270 me->mKeys[keyPress] = false;
271 me->mKeysShift[keyPress] = false;
272 if ((int64_t)key == me->mRepeatKeyCode) {
273 me->mRepeatKeyCode = -1;
274 }
275 }
276 };
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);
280 };
281 auto keyboard_repeat = [](void* data, wl_keyboard* wl_keyboard, int32_t rate, int32_t delay) {
283 me->mRepeatRate = rate;
284 me->mRepeatDelay = delay;
285 if (rate <= 0) {
286 me->mRepeatKeyCode = -1;
287 }
288 };
289 mKeyboardListener = wl_keyboard_listener{.keymap = keyboard_keymap, .enter = keyboard_enter, .leave = keyboard_leave, .key = keyboard_key, .modifiers = keyboard_modifiers, .repeat_info = keyboard_repeat};
290
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);
293 };
294 mXdgWmBaseListener = xdg_wm_base_listener{
295 .ping = xdg_wm_base_ping,
296 };
297
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);
303 }
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);
307 }
308 };
309 auto seat_name = [](void* data, struct wl_seat* seat, const char* name) {
310 if (((GPUDisplayFrontendWayland*)data)->mDisplay->GetProcessingSettings().debugLevel >= 2) {
311 GPUInfo("Wayland seat: %s", name);
312 }
313 };
314 mSeatListener = wl_seat_listener{
315 .capabilities = seat_capabilities,
316 .name = seat_name,
317 };
318
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) {};
320 auto output_mode = [](void* data, wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
322 if (auto* o = me->findOutput(output); o && (flags & WL_OUTPUT_MODE_CURRENT)) {
323 o->refresh = refresh;
324 }
325 };
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)) {
330 o->scale = factor;
331 me->updateScale();
332 }
333 };
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)) {
338 o->name = name;
339 }
340 if (me->mDisplay->GetProcessingSettings().debugLevel >= 2) {
341 GPUInfo("Wayland output: %s", name);
342 }
343 };
344 auto output_description = [](void* data, wl_output* output, const char* description) {};
345#endif
346 mOutputListener = wl_output_listener{
347 .geometry = output_geometry,
348 .mode = output_mode,
349 .done = output_done,
350 .scale = output_scale,
351#ifdef WL_OUTPUT_NAME_SINCE_VERSION
352 .name = output_name,
353 .description = output_description,
354#endif
355 };
356
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);
361 }
362 auto bindVersion = [version](const wl_interface& iface, uint32_t maxVersion) { return std::min<uint32_t>({version, (uint32_t)iface.version, maxVersion}); }; // Highest version supported by compositor, generated headers, and this code
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);
385 }
386 };
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);
394 } else {
395 wl_output_destroy(it->output);
396 }
397 me->mOutputs.erase(it);
398 me->updateScale();
399 break;
400 }
401 }
402 };
403 mRegistryListener = wl_registry_listener{.global = registry_global, .global_remove = registry_global_remove};
404
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");
409 }
410
411 if (mCompositor == nullptr || mXdgBase == nullptr || mSeat == nullptr) {
412 throw std::runtime_error("Error getting wayland objects");
413 }
414
415 mSurface = wl_compositor_create_surface(mCompositor);
416 if (mSurface == nullptr) {
417 throw std::runtime_error("Error creating wayland surface");
418 }
419
420 auto surface_enter = [](void* data, wl_surface* surface, wl_output* output) {
422 me->mSurfaceOutputs.emplace_back(output);
423 me->updateScale();
424 };
425 auto surface_leave = [](void* data, wl_surface* surface, wl_output* output) {
427 std::erase(me->mSurfaceOutputs, output);
428 me->updateScale();
429 };
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;
434 me->updateScale();
435 };
436 auto surface_preferred_buffer_transform = [](void* data, wl_surface* surface, uint32_t transform) {};
437#endif
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,
444#endif
445 };
446 wl_surface_add_listener(mSurface, &mSurfaceListener, this);
447
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;
452 me->updateScale();
453 };
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);
458 }
459
460 mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface);
461 if (mXdgSurface == nullptr) {
462 throw std::runtime_error("Error creating wayland xdg surface");
463 }
464 mXdgToplevel = xdg_surface_get_toplevel(mXdgSurface);
465
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);
470 }
471 me->mWidthRequested = width;
472 me->mHeightRequested = 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) {
478 fullScreen = true;
479 } else if (state[i] == XDG_TOPLEVEL_STATE_MAXIMIZED) {
480 maximized = true;
481#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION
482 } else if (state[i] == XDG_TOPLEVEL_STATE_SUSPENDED) {
483 me->mSuspended = true;
484#endif
485 }
486 }
487 me->setWindowState(fullScreen, maximized);
488 me->mStateFullScreenOrMaximized = fullScreen || maximized;
489 };
490
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;
500 }
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);
507 }
508 }
509 me->updateSize();
510 me->mWidthRequested = me->mHeightRequested = 0;
511 };
512
513 auto xdg_toplevel_handle_close = [](void* data, xdg_toplevel* toplevel) {
514 ((GPUDisplayFrontendWayland*)data)->mDisplayControl = 2;
515 };
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) {
519 me->mBoundsWidth = width;
520 me->mBoundsHeight = height;
521 };
522#endif
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;
531 }
532 };
533#endif
534
535 mXdgSurfaceListener = xdg_surface_listener{
536 .configure = xdg_surface_handle_configure,
537 };
538
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,
544#endif
545#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION
546 .wm_capabilities = xdg_toplevel_handle_wm_capabilities,
547#endif
548 };
549
550 xdg_surface_add_listener(mXdgSurface, &mXdgSurfaceListener, this);
551 xdg_toplevel_add_listener(mXdgToplevel, &mXdgToplevelListener, this);
552
553 xdg_toplevel_set_title(mXdgToplevel, DISPLAY_WINDOW_NAME);
554 xdg_toplevel_set_min_size(mXdgToplevel, MIN_WIDTH, MIN_HEIGHT); // Logical size, avoids degenerate swapchains / projections
555 xdg_toplevel_set_app_id(mXdgToplevel, "o2-gpu-event-display"); // Used by the compositor for window grouping, window rules, and matching a .desktop file
556
557 if (mDecManager) { // Must be requested before the first commit. Without the protocol (e.g. GNOME), the window has no decorations
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);
560 } else if (mDisplay->GetProcessingSettings().debugLevel >= 2) {
561 GPUInfo("Wayland compositor does not support server-side decorations");
562 }
563
564 const bool initialFullScreen = mDisplay->cfgR().fullScreen, initialMaximized = mDisplay->cfgR().maximized; // Read before the configure, which syncs the settings to the (windowed) state
565 wl_surface_commit(mSurface); // Initial commit without buffer, the compositor answers with the first configure
566 if (wl_display_roundtrip(mWayland) == -1) {
567 reportError("initial roundtrip");
568 throw std::runtime_error("Wayland initialization failed");
569 }
570 if (initialFullScreen || initialMaximized) { // Only after the windowed initial configure, so that the compositor has a normal (un-maximized) placement, then wait for the new configure before creating the swapchain
571 setWindowState(initialFullScreen, initialMaximized);
573 if (wl_display_roundtrip(mWayland) == -1) {
574 reportError("initial window state roundtrip");
575 throw std::runtime_error("Wayland initialization failed");
576 }
577 }
578
579 if (InitDisplay()) {
580 return (1);
581 }
582
583 {
584 std::lock_guard lock(mExitStateMutex);
585 mDisplayRunning = true;
586 }
587
588 while (mDisplayControl != 2) {
589 bool error = false;
590 while (!error && wl_display_prepare_read(mWayland) != 0) {
591 error = wl_display_dispatch_pending(mWayland) == -1;
592 }
593 if (error) {
594 reportError("dispatching events");
595 break;
596 }
597 wl_display_flush(mWayland);
598 pollfd pfd = {wl_display_get_fd(mWayland), POLLIN, 0};
599 if (poll(&pfd, 1, mSuspended ? 100 : 0) > 0) { // Block while suspended (no rendering), otherwise the loop spins at full CPU
600 if (wl_display_read_events(mWayland) == -1) {
601 reportError("reading events");
602 break;
603 }
604 } else {
605 wl_display_cancel_read(mWayland);
606 }
607 if (wl_display_dispatch_pending(mWayland) == -1) {
608 reportError("dispatching events");
609 break;
610 }
611 if (mDisplayControl == 2) {
612 break;
613 }
614 handleKeyRepeat();
616 if (!mSuspended) { // Window not visible, compositor would not release swapchain images
617 DrawGLScene();
618 }
620 }
621
622 ExitDisplay();
623
624 if (mXKBkeymap) {
625 xkb_state_unref(mXKBstate);
626 xkb_keymap_unref(mXKBkeymap);
627 mXKBkeymap = nullptr;
628 }
629 xkb_context_unref(mXKBcontext);
630
631 {
632 std::lock_guard lock(mExitStateMutex);
633 mDisplayRunning = false;
634 }
635
636 if (mCursorShapeDevice) {
637 wp_cursor_shape_device_v1_destroy(mCursorShapeDevice);
638 mCursorShapeDevice = nullptr;
639 }
640 if (mCursorShapeManager) {
641 wp_cursor_shape_manager_v1_destroy(mCursorShapeManager);
642 mCursorShapeManager = nullptr;
643 }
644 if (mPointer) {
645 mSeatVersion >= WL_POINTER_RELEASE_SINCE_VERSION ? wl_pointer_release(mPointer) : wl_pointer_destroy(mPointer);
646 mPointer = nullptr;
647 }
648 if (mKeyboard) {
649 mSeatVersion >= WL_KEYBOARD_RELEASE_SINCE_VERSION ? wl_keyboard_release(mKeyboard) : wl_keyboard_destroy(mKeyboard);
650 mKeyboard = nullptr;
651 }
652 if (mXdgDecoration) {
653 zxdg_toplevel_decoration_v1_destroy(mXdgDecoration);
654 mXdgDecoration = nullptr;
655 }
656 if (mFractionalScale) {
657 wp_fractional_scale_v1_destroy(mFractionalScale);
658 mFractionalScale = nullptr;
659 }
660 if (mViewport) {
661 wp_viewport_destroy(mViewport);
662 mViewport = nullptr;
663 }
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;
670 }
671 if (mViewporter) {
672 wp_viewporter_destroy(mViewporter);
673 mViewporter = nullptr;
674 }
675 if (mDecManager) {
676 zxdg_decoration_manager_v1_destroy(mDecManager);
677 mDecManager = nullptr;
678 }
679 for (auto& o : mOutputs) {
680 if (o.version >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
681 wl_output_release(o.output);
682 } else {
683 wl_output_destroy(o.output);
684 }
685 }
686 mOutputs.clear();
687 mSurfaceOutputs.clear();
688 if (mSeat) {
689 if (mSeatVersion >= WL_SEAT_RELEASE_SINCE_VERSION) {
690 wl_seat_release(mSeat);
691 } else {
692 wl_seat_destroy(mSeat);
693 }
694 mSeat = nullptr;
695 }
696 xdg_wm_base_destroy(mXdgBase);
697 wl_compositor_destroy(mCompositor);
698 wl_registry_destroy(mRegistry);
699 wl_display_disconnect(mWayland);
700
701 return (0);
702}
703
705{
706 if (!mCanFullscreen) {
707 GPUWarning("Wayland compositor does not support fullscreen");
708 } else if (set) {
709 xdg_toplevel_set_fullscreen(mXdgToplevel, mSurfaceOutputs.size() ? mSurfaceOutputs.back() : nullptr);
710 } else {
711 xdg_toplevel_unset_fullscreen(mXdgToplevel);
712 }
713}
714
716{
717 if (!mCanMaximize) {
718 GPUWarning("Wayland compositor does not support maximizing windows");
719 } else if (set) {
720 xdg_toplevel_set_maximized(mXdgToplevel);
721 } else {
722 xdg_toplevel_unset_maximized(mXdgToplevel);
723 }
724}
725
727{
728 auto* o = mSurfaceOutputs.size() ? findOutput(mSurfaceOutputs.back()) : nullptr;
729 return o ? o->refresh / 1000.f : 0.f;
730}
731
732GPUDisplayFrontendWayland::OutputInfo* GPUDisplayFrontendWayland::findOutput(wl_output* output)
733{
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;
736}
737
738void GPUDisplayFrontendWayland::updateScale()
739{
740 double scale;
741 if (mFractionalScale) {
742 scale = mFractionalScale120 ? mFractionalScale120 / 120. : 1.;
743 } else {
744 int32_t intScale = mPreferredScale;
745 if (intScale == 0) {
746 intScale = 1;
747 for (auto* output : mSurfaceOutputs) {
748 if (auto* o = findOutput(output)) {
749 intScale = std::max(intScale, o->scale);
750 }
751 }
752 }
753 scale = intScale;
754 }
755 if (scale != mScale && mSurface) {
756 mScale = scale;
757 if (!mFractionalScale) {
758 wl_surface_set_buffer_scale(mSurface, (int32_t)mScale); // Applied with the next commit, i.e. the next present, which happens after the swapchain was recreated with the new size by ResizeScene
759 }
760 updateSize();
761 }
762}
763
764void GPUDisplayFrontendWayland::updateSize()
765{
766 if (mViewport) {
767 wp_viewport_set_destination(mViewport, mWidthLogical, mHeightLogical); // Surface size in logical coordinates, independent of the buffer size, applied with the next present
768 }
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);
772 if (mDisplayRunning) {
773 ResizeScene(width, height); // Swapchain is recreated by the backend, Vulkan WSI commits the new buffer
774 } else {
775 mDisplayWidth = width; // Initial size, used by InitDisplay via getSize()
777 }
778 }
779}
780
781void GPUDisplayFrontendWayland::reportError(const char* where)
782{
783 const int32_t err = wl_display_get_error(mWayland);
784 if (err == EPROTO) {
785 const wl_interface* iface = nullptr;
786 uint32_t id = 0;
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);
789 } else {
790 GPUError("Wayland error while %s: %s", where, strerror(err));
791 }
792}
793
794void GPUDisplayFrontendWayland::handleKeyRepeat()
795{
796 if (mRepeatKeyCode < 0) {
797 return;
798 }
799 const auto now = std::chrono::steady_clock::now();
800 const auto period = std::chrono::microseconds(1000000 / mRepeatRate);
801 if (now >= mRepeatNext + period) { // More than one repeat behind (long frame), no burst of repeats
802 mRepeatNext = now;
803 }
804 while (now >= mRepeatNext && mRepeatKeyCode >= 0) {
805 HandleKey(mRepeatSymbol);
806 mRepeatNext += period;
807 }
808}
809
810void GPUDisplayFrontendWayland::applyScroll()
811{
812 mMouseWheel += mHasAxisValue120 ? (mAxisValue120 * -100 / 120) : (int32_t)(mAxisValue * (-100. / 15.));
813 mAxisValue = 0;
814 mAxisValue120 = 0;
815 mHasAxisValue120 = false;
816}
817
821
827
829{
830 VkWaylandSurfaceCreateInfoKHR info{};
831 info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
832 info.flags = 0;
833 info.display = mWayland;
834 info.surface = mSurface;
835 return vkCreateWaylandSurfaceKHR(*(VkInstance*)instance, &info, nullptr, (VkSurfaceKHR*)surface) != VK_SUCCESS;
836}
837
839{
840 static const char* exts[] = {"VK_KHR_surface", "VK_KHR_wayland_surface"};
841 p = exts;
842 return 2;
843}
header::DataDescription description
int16_t time
Definition RawEventData.h:4
std::vector< std::string > keys
int32_t i
int32_t retVal
char error[CURL_ERROR_SIZE]
SurfaceTrackState state
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
uint32_t version
Definition RawData.h:8
const Acts::Surface * surface
StringRef key
int32_t getVulkanSurface(void *instance, void *surface) override
void getSize(int32_t &width, int32_t &height) override
void OpenGLPrint(const char *s, float x, float y, float r, float g, float b, float a, bool fromBotton=true) override
uint32_t getReqVulkanExtensions(const char **&p) override
static constexpr int32_t KEY_F9
static constexpr int32_t KEY_F2
static constexpr int32_t KEY_ALT
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
static constexpr int32_t KEY_F10
static constexpr int32_t KEY_F4
static constexpr int32_t KEY_LEFT
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
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)
static constexpr int32_t KEY_UP
static constexpr int32_t KEY_DOWN
static constexpr int32_t KEY_F8
static constexpr int32_t MIN_WIDTH
static constexpr int32_t KEY_RIGHT
static constexpr int32_t KEY_RCTRL
static constexpr int32_t MIN_HEIGHT
static constexpr int32_t KEY_ESCAPE
const GPUSettingsDisplayRenderer & cfgR() const
Definition GPUDisplay.h:68
const GPUSettingsProcessing & GetProcessingSettings() const
Definition GPUDisplay.h:84
GLint GLenum GLint x
Definition glcorearb.h:403
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLenum * rate
Definition glcorearb.h:5735
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLsizei GLsizei height
Definition glcorearb.h:270
GLint GLsizei width
Definition glcorearb.h:270
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLboolean GLuint group
Definition glcorearb.h:3991
GLboolean GLboolean g
Definition glcorearb.h:1233
GLbitfield flags
Definition glcorearb.h:1570
GLboolean enable
Definition glcorearb.h:3991
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
GLuint id
Definition glcorearb.h:650
GLuint * states
Definition glcorearb.h:4932
uint64_t now() noexcept
Definition Clock.h:69
GPUDisplayFrontend * createGPUDisplayFrontendWayland()
constexpr auto make(F f) -> std::array< int, N >
Definition SMatrixGPU.h:293
Polygon< T > close(Polygon< T > polygon)
Definition Polygon.h:126
constexpr value_T me
Definition TrackUtils.h:128