From 732629e5a0f9101142dbb9466dfd7fbf3afbe06d Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Mon, 19 Feb 2024 07:59:51 -0500 Subject: [PATCH] Implement Wayland hotkeys --- .github/workflows/ubuntu.yml | 3 +- meson.build | 2 + meson_options.txt | 2 +- src/keybinds.cpp | 8 ++- src/keybinds.h | 53 +++++++++------- src/mangohud.version | 2 + src/meson.build | 12 +++- src/vulkan.cpp | 25 ++++++++ src/wayland_hook.cpp | 59 ++++++++++++++++++ src/wayland_hook.h | 13 ++++ src/wayland_keybinds.cpp | 115 +++++++++++++++++++++++++++++++++++ 11 files changed, 268 insertions(+), 26 deletions(-) create mode 100644 src/wayland_hook.cpp create mode 100644 src/wayland_hook.h create mode 100644 src/wayland_keybinds.cpp diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index fc15f39b..04d7081f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -28,7 +28,8 @@ jobs: libglew-dev \ libglfw3-dev \ libwayland-dev \ - libxnvctrl-dev + libxnvctrl-dev \ + libxkbcommon-dev sudo pip3 install 'meson>=0.60' - name: 'Install clang' if: ${{ (matrix.compiler == 'clang') }} diff --git a/meson.build b/meson.build index b80e6619..b40132e8 100644 --- a/meson.build +++ b/meson.build @@ -89,6 +89,7 @@ if is_unixy dep_wayland_client = dependency('wayland-client', required: get_option('with_wayland'), version : '>=1.11') dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true) + dep_xkb = dependency('xkbcommon', required: get_option('with_wayland')) else dep_x11 = null_dep dep_wayland_client = null_dep @@ -102,6 +103,7 @@ endif if dep_wayland_client.found() vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR'] vulkan_wsi_deps += dep_wayland_client + vulkan_wsi_deps += dep_xkb endif if is_unixy and not dep_x11.found() and not dep_wayland_client.found() diff --git a/meson_options.txt b/meson_options.txt index c82ae158..b6f127e6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,7 +5,7 @@ option('include_doc', type : 'boolean', value : true, description: 'Include the option('with_nvml', type : 'combo', value : 'enabled', choices: ['enabled', 'system', 'disabled'], description: 'Enable NVML support') option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support') option('with_x11', type : 'feature', value : 'enabled') -option('with_wayland', type : 'feature', value : 'disabled') +option('with_wayland', type : 'feature', value : 'enabled') option('with_dbus', type : 'feature', value : 'enabled') option('loglevel', type: 'combo', choices : ['trace', 'debug', 'info', 'warn', 'err', 'critical', 'off'], value : 'info', description: 'Max log level in non-debug build') option('mangoapp', type: 'boolean', value : false) diff --git a/src/keybinds.cpp b/src/keybinds.cpp index 395eb559..84c0288f 100644 --- a/src/keybinds.cpp +++ b/src/keybinds.cpp @@ -1,10 +1,16 @@ #include +#include +#include +#include +#include #include "overlay.h" #include "timing.hpp" #include "logging.h" #include "keybinds.h" #include "fps_metrics.h" +Clock::time_point last_f2_press, toggle_fps_limit_press, toggle_preset_press, last_f12_press, reload_cfg_press, last_upload_press; + void check_keybinds(struct overlay_params& params, uint32_t vendorID){ using namespace std::chrono_literals; auto now = Clock::now(); /* us */ @@ -20,7 +26,7 @@ void check_keybinds(struct overlay_params& params, uint32_t vendorID){ return; last_check = now; - auto keyPressDelay = 400ms; + const auto keyPressDelay = 400ms; if (elapsedF2 >= keyPressDelay && keys_are_pressed(params.toggle_logging)) { diff --git a/src/keybinds.h b/src/keybinds.h index e48aafa9..62d33ac6 100644 --- a/src/keybinds.h +++ b/src/keybinds.h @@ -6,37 +6,50 @@ #include "shared_x11.h" #include "loaders/loader_x11.h" #endif +#ifdef HAVE_WAYLAND +#include "wayland_hook.h" +#endif #ifndef KeySym typedef unsigned long KeySym; #endif -Clock::time_point last_f2_press, toggle_fps_limit_press, toggle_preset_press, last_f12_press, reload_cfg_press, last_upload_press; +#if defined(HAVE_X11) || defined(HAVE_WAYLAND) +static inline bool keys_are_pressed(const std::vector& keys) +{ + #if defined(HAVE_WAYLAND) + if(wl_display_ptr && wl_handle) + { + update_wl_queue(); -#if defined(HAVE_X11) -static inline bool keys_are_pressed(const std::vector& keys) { + if(wl_pressed_keys.size() == keys.size() && wl_pressed_keys == keys) + return true; + } + #endif - if (!init_x11()) - return false; + #if defined(HAVE_X11) + if (init_x11()) + { + char keys_return[32]; + size_t pressed = 0; - char keys_return[32]; - size_t pressed = 0; + auto libx11 = get_libx11(); + libx11->XQueryKeymap(get_xdisplay(), keys_return); - auto libx11 = get_libx11(); - libx11->XQueryKeymap(get_xdisplay(), keys_return); + for (KeySym ks : keys) { + KeyCode kc2 = libx11->XKeysymToKeycode(get_xdisplay(), ks); - for (KeySym ks : keys) { - KeyCode kc2 = libx11->XKeysymToKeycode(get_xdisplay(), ks); + bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7))); - bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7))); + if (isPressed) + pressed++; + } - if (isPressed) - pressed++; - } - - if (pressed > 0 && pressed == keys.size()) { - return true; + if (pressed > 0 && pressed == keys.size()) { + return true; + } } + #endif return false; } @@ -56,10 +69,6 @@ static inline bool keys_are_pressed(const std::vector& keys) { return false; } -#else // XXX: Add wayland support -static inline bool keys_are_pressed(const std::vector& keys) { - return false; -} #endif #endif //MANGOHUD_KEYBINDS_H diff --git a/src/mangohud.version b/src/mangohud.version index 7fd09a4b..5be0a299 100644 --- a/src/mangohud.version +++ b/src/mangohud.version @@ -8,5 +8,7 @@ dlsym; mangohud_find_glx_ptr; mangohud_find_egl_ptr; + wl_display_connect; + wl_display_connect_to_fd; local: *; }; diff --git a/src/meson.build b/src/meson.build index c776ca3f..5754b72d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -59,7 +59,7 @@ vklayer_files = files( 'config.cpp', 'gpu.cpp', 'blacklist.cpp', - 'file_utils.cpp', + 'file_utils.cpp' ) opengl_files = [] @@ -150,6 +150,15 @@ if is_unixy ) endif + if get_option('with_wayland').enabled() + pre_args += '-DHAVE_WAYLAND' + + vklayer_files += files( + 'wayland_hook.cpp', + 'wayland_keybinds.cpp' + ) + endif + if dbus_dep.found() and get_option('with_dbus').enabled() pre_args += '-DHAVE_DBUS' vklayer_files += files( @@ -290,6 +299,7 @@ if get_option('mangoapp') spdlog_dep, dbus_dep, dep_x11, + dep_wayland_client, glfw3_dep, json_dep, glew_dep, diff --git a/src/vulkan.cpp b/src/vulkan.cpp index 322f01a4..f205b80a 100644 --- a/src/vulkan.cpp +++ b/src/vulkan.cpp @@ -50,8 +50,13 @@ #include "notify.h" #include "blacklist.h" #include "pci_ids.h" +#if defined(HAVE_WAYLAND) +#include "wayland_hook.h" +#endif +#include "real_dlsym.h" #include "file_utils.h" #ifdef __linux__ +#include #include "implot.h" #endif @@ -2028,6 +2033,23 @@ static void overlay_DestroyInstance( destroy_instance_data(instance_data); } +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +static VkResult overlay_CreateWaylandSurfaceKHR( + VkInstance instance, + const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface +) +{ + struct instance_data *instance_data = FIND(struct instance_data, instance); + if (!wl_handle) + wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY); + wl_display_ptr = pCreateInfo->display; + init_wayland_data(); + return instance_data->vtable.CreateWaylandSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface); +} +#endif + extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetDeviceProcAddr(VkDevice dev, const char *funcName); extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance, @@ -2048,6 +2070,9 @@ static const struct { ADD_HOOK(EndCommandBuffer), ADD_HOOK(CmdExecuteCommands), +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + ADD_HOOK(CreateWaylandSurfaceKHR), +#endif ADD_HOOK(CreateSwapchainKHR), ADD_HOOK(QueuePresentKHR), ADD_HOOK(DestroySwapchainKHR), diff --git a/src/wayland_hook.cpp b/src/wayland_hook.cpp new file mode 100644 index 00000000..c29e47a6 --- /dev/null +++ b/src/wayland_hook.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "real_dlsym.h" +#include "wayland_hook.h" + +EXPORT_C_(struct wl_display*) wl_display_connect(const char *name); +EXPORT_C_(struct wl_display*) wl_display_connect_to_fd(int fd); + +typedef struct wl_display* (*pwl_display_connect)(const char *name); +typedef struct wl_display* (*pwl_display_connect_to_fd)(int fd); + +pwl_display_connect wl_display_connect_ptr = nullptr; +pwl_display_connect_to_fd wl_display_connect_to_fd_ptr = nullptr; +void* wl_handle = nullptr; +struct wl_display* wl_display_ptr = nullptr; + +EXPORT_C_(struct wl_display*) wl_display_connect(const char *name) +{ + struct wl_display *ret = NULL; + + if(!wl_handle) + { + wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY); + wl_display_connect_ptr = (pwl_display_connect)real_dlsym(wl_handle, "wl_display_connect"); + wl_display_connect_to_fd_ptr = (pwl_display_connect_to_fd)real_dlsym(wl_handle, "wl_display_connect_to_fd"); + } + + ret = wl_display_connect_ptr(name); + + if(!wl_display_ptr) + wl_display_ptr = ret; + + init_wayland_data(); + + return ret; +} + +EXPORT_C_(struct wl_display*) wl_display_connect_to_fd(int fd) +{ + struct wl_display *ret = NULL; + + if(!wl_handle) + { + wl_handle = real_dlopen("libwayland-client.so", RTLD_LAZY); + wl_display_connect_to_fd_ptr = (pwl_display_connect_to_fd)real_dlsym(wl_handle, "wl_display_connect_to_fd"); + wl_display_connect_ptr = (pwl_display_connect)real_dlsym(wl_handle, "wl_display_connect"); + } + + ret = wl_display_connect_to_fd_ptr(fd); + + if(!wl_display_ptr) + wl_display_ptr = ret; + + init_wayland_data(); + + return ret; +} diff --git a/src/wayland_hook.h b/src/wayland_hook.h new file mode 100644 index 00000000..c637e724 --- /dev/null +++ b/src/wayland_hook.h @@ -0,0 +1,13 @@ +#include +#include + +#ifndef KeySym +typedef unsigned long KeySym; +#endif + +extern void* wl_handle; +extern struct wl_display* wl_display_ptr; +extern std::vector wl_pressed_keys; + +void init_wayland_data(); +void update_wl_queue(); diff --git a/src/wayland_keybinds.cpp b/src/wayland_keybinds.cpp new file mode 100644 index 00000000..670040b6 --- /dev/null +++ b/src/wayland_keybinds.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wayland_hook.h" +#include "timing.hpp" +#include "keybinds.h" + +struct wl_seat* seat = nullptr; +struct wl_keyboard* keyboard = nullptr; +struct xkb_context *context_xkb = nullptr; +struct xkb_keymap *keymap_xkb = nullptr; +struct xkb_state *state_xkb = nullptr; +struct wl_event_queue* queue = nullptr; +std::vector wl_pressed_keys {}; + +static void registry_handle_global(void *data, struct wl_registry* registry, uint32_t name, const char *interface, uint32_t version) +{ + if(strcmp(interface, wl_seat_interface.name) == 0) + { + seat = (struct wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 7); + } +} + +static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name){} + +static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) +{ + char* map_shm = (char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + + if(!context_xkb) + context_xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + if(keymap_xkb && state_xkb) + { + xkb_keymap_unref(keymap_xkb); + xkb_state_unref(state_xkb); + } + + keymap_xkb = xkb_keymap_new_from_string( + context_xkb, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + state_xkb = xkb_state_new(keymap_xkb); + + munmap((void*)map_shm, size); + close(fd); +} + +static void wl_keyboard_enter(void *user_data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys){} + +static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) +{ + wl_pressed_keys.clear(); +} + +static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + xkb_keycode_t keycode = key + 8; + xkb_keysym_t keysym = xkb_state_key_get_one_sym(state_xkb, keycode); + + if(state) + { + wl_pressed_keys.push_back(keysym); + } + else + { + auto it = std::find(wl_pressed_keys.begin(), wl_pressed_keys.end(), keysym); + if(it != wl_pressed_keys.end()) + wl_pressed_keys.erase(it); + } +} + +static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group){} + +static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){} + +struct wl_registry_listener registry_listener { + .global = registry_handle_global, + .global_remove = registry_handle_global_remove +}; + +struct wl_keyboard_listener keyboard_listener { + .keymap = wl_keyboard_keymap, + .enter = wl_keyboard_enter, + .leave = wl_keyboard_leave, + .key = wl_keyboard_key, + .modifiers = wl_keyboard_modifiers, + .repeat_info = wl_keyboard_repeat_info +}; + +void update_wl_queue() +{ + wl_display_roundtrip_queue(wl_display_ptr, queue); +} + +void init_wayland_data() +{ + struct wl_display *display_wrapped = (struct wl_display*)wl_proxy_create_wrapper(wl_display_ptr); + queue = wl_display_create_queue(wl_display_ptr); + wl_proxy_set_queue((struct wl_proxy*)display_wrapped, queue); + wl_registry *registry = wl_display_get_registry(display_wrapped); + wl_proxy_wrapper_destroy(display_wrapped); + wl_registry_add_listener(registry, ®istry_listener, NULL); + update_wl_queue(); + update_wl_queue(); + keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL); + update_wl_queue(); +}