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 <xkbcommon/xkbcommon.h>
39#include <linux/input-event-codes.h>
45template <
class T,
class... Args>
47 std::function<T(Args...)>
func;
51 return funcwrap->
func(std::forward<Args>(args)...);
66int32_t GPUDisplayFrontendWayland::GetKey(uint32_t
key, uint32_t state)
70 xkb_keysym_t sym = xkb_state_key_get_one_sym(mXKBstate,
key + 8);
73 }
else if (sym == 65451) {
75 }
else if (sym == XKB_KEY_Shift_L || sym == XKB_KEY_Shift_R) {
77 }
else if (sym == XKB_KEY_Alt_L) {
79 }
else if (sym == XKB_KEY_ISO_Level3_Shift || sym == XKB_KEY_Alt_R) {
81 }
else if (sym == XKB_KEY_Control_L) {
83 }
else if (sym == XKB_KEY_Control_R) {
85 }
else if (sym == XKB_KEY_Up) {
87 }
else if (sym == XKB_KEY_Down) {
89 }
else if (sym == XKB_KEY_Left) {
91 }
else if (sym == XKB_KEY_Right) {
93 }
else if (sym == XKB_KEY_Page_Up) {
95 }
else if (sym == XKB_KEY_Page_Down) {
97 }
else if (sym == XKB_KEY_Escape) {
99 }
else if (sym == XKB_KEY_Return) {
101 }
else if (sym == XKB_KEY_End) {
103 }
else if (sym == XKB_KEY_Home) {
105 }
else if (sym == XKB_KEY_Insert) {
107 }
else if (sym == XKB_KEY_F1) {
109 }
else if (sym == XKB_KEY_F2) {
111 }
else if (sym == XKB_KEY_F3) {
113 }
else if (sym == XKB_KEY_F4) {
115 }
else if (sym == XKB_KEY_F5) {
117 }
else if (sym == XKB_KEY_F6) {
119 }
else if (sym == XKB_KEY_F7) {
121 }
else if (sym == XKB_KEY_F8) {
123 }
else if (sym == XKB_KEY_F9) {
125 }
else if (sym == XKB_KEY_F10) {
127 }
else if (sym == XKB_KEY_F11) {
129 }
else if (sym == XKB_KEY_F12) {
131 }
else if (sym == 32) {
133 }
else if (sym > 255) {
136 retVal = xkb_keysym_to_utf32(sym);
145void GPUDisplayFrontendWayland::createBuffer(uint32_t
width, uint32_t
height)
149 if (ftruncate(mFd,
size) < 0) {
150 throw std::runtime_error(
"Error setting waysland shm file size");
152 void* shm_data = mmap(
nullptr,
size, PROT_READ | PROT_WRITE, MAP_SHARED, mFd, 0);
153 if (shm_data == MAP_FAILED) {
154 throw std::runtime_error(
"wayland mmap failed");
156 memset(shm_data, 0,
size);
157 munmap(shm_data,
size);
159 mPool = wl_shm_create_pool(mShm, mFd,
size);
160 mBuffer = wl_shm_pool_create_buffer(mPool, 0,
width,
height,
stride, WL_SHM_FORMAT_XRGB8888);
163 wl_surface_attach(mSurface, mBuffer, 0, 0);
164 wl_surface_commit(mSurface);
167void GPUDisplayFrontendWayland::recreateBuffer(uint32_t
width, uint32_t
height)
169 wl_surface_attach(mSurface,
nullptr, 0, 0);
170 wl_surface_commit(mSurface);
171 wl_buffer_destroy(mBuffer);
172 wl_shm_pool_destroy(mPool);
176int32_t GPUDisplayFrontendWayland::FrontendMain()
179 fprintf(stderr,
"Only Vulkan backend supported\n");
183 mXKBcontext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
184 if (mXKBcontext ==
nullptr) {
185 throw std::runtime_error(
"Error initializing xkb context");
188 mWayland = wl_display_connect(
nullptr);
189 if (mWayland ==
nullptr) {
190 throw std::runtime_error(
"Could not connect to wayland display");
192 mRegistry = wl_display_get_registry(mWayland);
193 if (mRegistry ==
nullptr) {
194 throw std::runtime_error(
"Could not create wayland registry");
197 mFd = memfd_create(
"/tmp/ca-gpu-display-wayland-memfile", 0);
199 throw std::runtime_error(
"Error creating wayland shm segment file descriptor");
202 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) {
204 auto pointer_leave = [](
void*
data, wl_pointer* wl_pointer, uint32_t serial, wl_surface* wl_surface) {
206 auto pointer_motion = [](
void*
data, wl_pointer* wl_pointer, uint32_t
time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
208 me->mMouseMvX = wl_fixed_to_double(surface_x);
209 me->mMouseMvY = wl_fixed_to_double(surface_y);
211 auto pointer_button = [](
void*
data, wl_pointer* wl_pointer, uint32_t serial, uint32_t
time, uint32_t button, uint32_t
state) {
213 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
214 if (button == BTN_RIGHT) {
215 me->mMouseDnR =
true;
216 }
else if (button == BTN_LEFT) {
219 me->mMouseDnX =
me->mMouseMvX;
220 me->mMouseDnY =
me->mMouseMvY;
221 }
else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
222 if (button == BTN_RIGHT) {
223 me->mMouseDnR =
false;
224 }
else if (button == BTN_LEFT) {
225 me->mMouseDn =
false;
229 auto pointer_axis = [](
void*
data, wl_pointer* wl_pointer, uint32_t
time, uint32_t axis, wl_fixed_t
value) {
234#pragma GCC diagnostic push
235#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
236 const wl_pointer_listener pointer_listener = {.enter = pointer_enter, .leave = pointer_leave, .motion = pointer_motion, .button = pointer_button, .axis = pointer_axis, .frame =
nullptr, .axis_source =
nullptr, .axis_stop =
nullptr, .axis_discrete =
nullptr};
237#pragma GCC diagnostic pop
239 auto keyboard_keymap = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t
format, int32_t fd, uint32_t
size) {
241 if (
me->mXKBkeymap) {
242 xkb_state_unref(
me->mXKBstate);
243 xkb_keymap_unref(
me->mXKBkeymap);
245 char* keymap_string = (
char*)mmap(
nullptr,
size, PROT_READ, MAP_SHARED, fd, 0);
246 me->mXKBkeymap = xkb_keymap_new_from_string(
me->mXKBcontext, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
247 me->mXKBstate = xkb_state_new(
me->mXKBkeymap);
248 munmap(keymap_string,
size);
251 auto keyboard_enter = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, wl_surface* surface, wl_array* keys) {};
252 auto keyboard_leave = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, wl_surface* surface) {};
253 auto keyboard_key = [](
void*
data, wl_keyboard* wl_keyboard, uint32_t serial, uint32_t
time, uint32_t
key, uint32_t
state) {
255 int32_t symbol =
me->GetKey(
key, state);
256 int32_t keyPress = (symbol >=
'a' && symbol <=
'z') ? symbol +
'A' -
'a' : symbol;
257 if (state == XKB_KEY_DOWN) {
258 me->mKeys[keyPress] =
true;
260 me->HandleKey(symbol);
262 me->mKeys[keyPress] =
false;
263 me->mKeysShift[keyPress] =
false;
266 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) {
268 xkb_state_update_mask(
me->mXKBstate, mods_depressed, mods_latched, mods_locked, 0, 0,
group);
270 auto keyboard_repat = [](
void*
data, wl_keyboard* wl_keyboard, int32_t
rate, int32_t delay) {};
271 const wl_keyboard_listener keyboard_listener = {.keymap = keyboard_keymap, .enter = keyboard_enter, .leave = keyboard_leave, .key = keyboard_key, .modifiers = keyboard_modifiers, .repeat_info = keyboard_repat};
273 auto xdg_wm_base_ping = [](
void*
data,
struct xdg_wm_base* xdg_wm_base, uint32_t serial) {
274 xdg_wm_base_pong(xdg_wm_base, serial);
276 const xdg_wm_base_listener xdg_wm_base_listener = {
277 .ping = xdg_wm_base_ping,
280 auto seat_capabilities = [&](
struct wl_seat* seat, uint32_t capabilities) {
281 if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
282 mPointer = wl_seat_get_pointer(mSeat);
283 wl_pointer_add_listener(mPointer, &pointer_listener,
this);
285 if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
286 mKeyboard = wl_seat_get_keyboard(mSeat);
287 wl_keyboard_add_listener(mKeyboard, &keyboard_listener,
this);
292 auto seat_name = [](
void*
data,
struct wl_seat* seat,
const char*
name) {
294 GPUInfo(
"Wayland seat: %s",
name);
297 const wl_seat_listener seat_listener = {
298 .capabilities = seat_capabilities_c.callback,
302 auto registry_global = [&](wl_registry* registry, uint32_t
name,
const char* interface, uint32_t
version) {
304 GPUInfo(
"Available interface %s", interface);
306 if (strcmp(interface, wl_output_interface.name) == 0) {
307 mOutput = (wl_output*)wl_registry_bind(registry,
name, &wl_output_interface, 1);
309 }
else if (strcmp(interface, wl_compositor_interface.name) == 0) {
310 mCompositor = (wl_compositor*)wl_registry_bind(registry,
name, &wl_compositor_interface, 1);
311 }
else if (strcmp(interface, wl_shm_interface.name) == 0) {
312 mShm = (wl_shm*)wl_registry_bind(registry,
name, &wl_shm_interface, 1);
313 }
else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
314 mXdgBase = (xdg_wm_base*)wl_registry_bind(registry,
name, &xdg_wm_base_interface, 1);
315 xdg_wm_base_add_listener(mXdgBase, &xdg_wm_base_listener,
this);
316 }
else if (strcmp(interface, wl_seat_interface.name) == 0) {
317 mSeat = (wl_seat*)wl_registry_bind(registry,
name, &wl_seat_interface, 1);
318 wl_seat_add_listener(mSeat, &seat_listener, &seat_capabilities_c);
319 }
else if (strcmp(interface, zxdg_toplevel_decoration_v1_interface.name) == 0) {
320 mDecManager = (zxdg_decoration_manager_v1*)wl_registry_bind(registry,
name, &zxdg_toplevel_decoration_v1_interface, 1);
324 auto registry_global_c =
internal::CCallWrapper<void, wl_registry*, uint32_t, const char*, uint32_t>{[registry_global](wl_registry* registry, uint32_t
name,
const char* interface, uint32_t
version) { registry_global(registry,
name, interface,
version); }};
325 auto registry_global_remove = [](
void*
a, wl_registry*
b, uint32_t
c) {};
326 const wl_registry_listener registry_listener = {.global = ®istry_global_c.callback, .global_remove = registry_global_remove};
328 wl_registry_add_listener(mRegistry, ®istry_listener, ®istry_global_c);
329 wl_display_roundtrip(mWayland);
331 if (mCompositor ==
nullptr || mShm ==
nullptr || mXdgBase ==
nullptr || mSeat ==
nullptr || mOutput ==
nullptr) {
332 throw std::runtime_error(
"Error getting wayland objects");
335 mSurface = wl_compositor_create_surface(mCompositor);
336 if (mSurface ==
nullptr) {
337 throw std::runtime_error(
"Error creating wayland surface");
339 mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface);
340 if (mXdgSurface ==
nullptr) {
341 throw std::runtime_error(
"Error creating wayland xdg surface");
343 mXdgToplevel = xdg_surface_get_toplevel(mXdgSurface);
345 auto xdg_toplevel_handle_configure = [](
void*
data, xdg_toplevel* toplevel, int32_t
width, int32_t
height, wl_array*
states) {
347 if (
me->mDisplay->param()->par.debugLevel >= 3) {
348 GPUInfo(
"Wayland surface resized to %d %d",
width,
height);
354 auto xdg_surface_handle_configure = [](
void*
data, xdg_surface* surface, uint32_t serial) {
356 xdg_surface_ack_configure(
me->mXdgSurface, serial);
357 if (
me->mWidthRequested &&
me->mHeightRequested && (
me->mWidthRequested !=
me->mDisplayWidth ||
me->mHeightRequested !=
me->mDisplayHeight)) {
358 me->recreateBuffer(
me->mWidthRequested,
me->mHeightRequested);
359 me->ResizeScene(
me->mDisplayWidth,
me->mDisplayHeight);
363 me->mWidthRequested =
me->mHeightRequested = 0;
366 auto xdg_toplevel_handle_close = [](
void*
data, xdg_toplevel* toplevel) {
370#pragma GCC diagnostic push
371#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
372 const xdg_surface_listener xdg_surface_listener = {
373 .configure = xdg_surface_handle_configure,
376 const xdg_toplevel_listener xdg_toplevel_listener = {
377 .configure = xdg_toplevel_handle_configure,
378 .close = xdg_toplevel_handle_close,
379 .configure_bounds =
nullptr};
380#pragma GCC diagnostic pop
382 xdg_surface_add_listener(mXdgSurface, &xdg_surface_listener,
this);
383 xdg_toplevel_add_listener(mXdgToplevel, &xdg_toplevel_listener,
this);
388 printf(
"Enabling decoration\n");
391 wl_surface_commit(mSurface);
392 wl_display_roundtrip(mWayland);
410 xkb_state_unref(mXKBstate);
411 xkb_keymap_unref(mXKBkeymap);
412 mXKBkeymap =
nullptr;
414 xkb_context_unref(mXKBcontext);
416 pthread_mutex_lock(&mSemLockExit);
417 mDisplayRunning =
true;
418 pthread_mutex_unlock(&mSemLockExit);
420 pthread_mutex_lock(&mSemLockExit);
421 mDisplayRunning =
false;
422 pthread_mutex_unlock(&mSemLockExit);
425 wl_pointer_release(mPointer);
429 wl_keyboard_release(mKeyboard);
432 xdg_toplevel_destroy(mXdgToplevel);
433 xdg_surface_destroy(mXdgSurface);
434 wl_surface_destroy(mSurface);
435 wl_buffer_destroy(mBuffer);
436 wl_shm_pool_destroy(mPool);
437 wl_registry_destroy(mRegistry);
438 wl_display_disconnect(mWayland);
446 pthread_mutex_lock(&mSemLockExit);
447 if (mDisplayRunning) {
450 pthread_mutex_unlock(&mSemLockExit);
451 while (mDisplayRunning) {
459 xdg_toplevel_set_fullscreen(mXdgToplevel, mOutput);
461 xdg_toplevel_unset_fullscreen(mXdgToplevel);
468 xdg_toplevel_set_maximized(mXdgToplevel);
470 xdg_toplevel_unset_maximized(mXdgToplevel);
482 GPUError(
"Coult not Create frontend Thread...");
496 VkWaylandSurfaceCreateInfoKHR info{};
497 info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
499 info.display = mWayland;
500 info.surface = mSurface;
501 return vkCreateWaylandSurfaceKHR(*(VkInstance*)instance, &info,
nullptr, (VkSurfaceKHR*)surface) != VK_SUCCESS;
506 static const char* exts[] = {
"VK_KHR_surface",
"VK_KHR_wayland_surface"};
void ToggleMaximized(bool set) override
void SwitchFullscreen(bool set) override
int32_t getVulkanSurface(void *instance, void *surface) override
int32_t StartDisplay() override
void getSize(int32_t &width, int32_t &height) override
void DisplayExit() 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
static constexpr int32_t KEY_F9
const char * mFrontendName
static constexpr int32_t KEY_F2
static void * FrontendThreadWrapper(void *)
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
volatile int32_t mDisplayControl
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
static constexpr const char * DISPLAY_WINDOW_NAME
static constexpr int32_t INIT_WIDTH
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
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
static constexpr int32_t INIT_HEIGHT
static constexpr int32_t KEY_RIGHT
static constexpr int32_t KEY_RCTRL
static constexpr int32_t KEY_ESCAPE
GLuint const GLchar * name
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
GLint GLenum GLboolean GLsizei stride
GLboolean GLboolean GLboolean GLboolean a
GLint GLint GLsizei GLint GLenum format
Polygon< T > close(Polygon< T > polygon)
std::function< T(Args...)> func
static T callback(void *context, Args... args)
std::vector< std::byte > createBuffer(gsl::span< std::string > data, uint32_t orbit=12345, uint16_t bc=678)