diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index abdf7820b0..e757572e1e 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -11,6 +11,7 @@ #include "waitable.h" #include "Utils/TempFiles.h" +#include #include #include #include @@ -521,6 +522,8 @@ namespace gamescope void SetRelativePointer( bool bRelative ); + ::xkb_keymap *Wayland_GetParentKeymap() const; + private: void HandleKey( uint32_t uKey, bool bPressed ); @@ -548,7 +551,7 @@ namespace gamescope uint32_t m_uFakeTimestamp = 0; xkb_context *m_pXkbContext = nullptr; - xkb_keymap *m_pXkbKeymap = nullptr; + ::xkb_keymap *m_pXkbKeymap = nullptr; uint32_t m_uKeyModifiers = 0; uint32_t m_uModMask[ GAMESCOPE_WAYLAND_MOD_COUNT ]; @@ -626,6 +629,7 @@ namespace gamescope .modifiers = WAYLAND_USERDATA_TO_THIS( CWaylandInputThread, Wayland_Keyboard_Modifiers ), .repeat_info = WAYLAND_USERDATA_TO_THIS( CWaylandInputThread, Wayland_Keyboard_RepeatInfo ), }; + const zwp_relative_pointer_v1_listener CWaylandInputThread::s_RelativePointerListener = { .relative_motion = WAYLAND_USERDATA_TO_THIS( CWaylandInputThread, Wayland_RelativePointer_RelativeMotion ), @@ -676,6 +680,8 @@ namespace gamescope virtual bool UsesVirtualConnectors() override; virtual std::shared_ptr CreateVirtualConnector( uint64_t ulVirtualConnectorKey ) override; + + ::xkb_keymap *GetParentKeymap() const override; protected: virtual void OnBackendBlobDestroyed( BackendBlob *pBlob ) override; @@ -2349,6 +2355,11 @@ namespace gamescope return pConnector; } + ::xkb_keymap *CWaylandBackend::GetParentKeymap() const + { + return m_InputThread.Wayland_GetParentKeymap(); + } + /////////////////// // INestedHints /////////////////// @@ -2891,6 +2902,10 @@ namespace gamescope } } + ::xkb_keymap *CWaylandInputThread::Wayland_GetParentKeymap() const{ + return m_pXkbKeymap; + } + void CWaylandInputThread::HandleKey( uint32_t uKey, bool bPressed ) { if ( m_uKeyModifiers & m_uModMask[ GAMESCOPE_WAYLAND_MOD_META ] ) @@ -3148,9 +3163,6 @@ namespace gamescope void CWaylandInputThread::Wayland_Keyboard_Keymap( wl_keyboard *pKeyboard, uint32_t uFormat, int32_t nFd, uint32_t uSize ) { - // We are not doing much with the keymap, we pass keycodes thru. - // Ideally we'd use this to influence our keymap to clients, eg. x server. - defer( close( nFd ) ); if ( uFormat != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 ) return; @@ -3163,7 +3175,12 @@ namespace gamescope } defer( munmap( pMap, uSize ) ); - xkb_keymap *pKeymap = xkb_keymap_new_from_string( m_pXkbContext, pMap, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS ); + ::xkb_keymap *pKeymap = xkb_keymap_new_from_string( + m_pXkbContext, + pMap, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS + ); if ( !pKeymap ) { xdg_log.errorf( "Failed to create xkb_keymap" ); diff --git a/src/backend.h b/src/backend.h index 880aa97240..394ac69311 100644 --- a/src/backend.h +++ b/src/backend.h @@ -18,6 +18,8 @@ #include #include +#include + struct wlr_buffer; struct wlr_dmabuf_attributes; @@ -404,6 +406,8 @@ namespace gamescope virtual void OnEndFrame() = 0; + virtual ::xkb_keymap *GetParentKeymap() const { return nullptr; } + static IBackend *Get(); template static bool Set(); diff --git a/src/main.cpp b/src/main.cpp index 446dacb81b..4543273032 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1033,9 +1033,11 @@ int main(int argc, char **argv) if ( g_nNestedWidth == 0 ) g_nNestedWidth = g_nNestedHeight * 16 / 9; - if ( !wlserver_init() ) + ::xkb_keymap *parent_keymap = GetBackend()->GetParentKeymap(); + + if (!wlserver_init(parent_keymap)) { - fprintf( stderr, "Failed to initialize wlserver\n" ); + fprintf(stderr, "Failed to initialize wlserver\n"); return 1; } diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index 6734c15ace..72b1fe3765 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include "wlr_begin.hpp" diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 2675583d15..ae2512d122 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -101,7 +101,6 @@ struct wlserver_content_override { std::mutex g_wlserver_xdg_shell_windows_lock; static struct wl_list pending_surfaces = {0}; - static std::atomic g_bShutdownWLServer{ false }; static void wlserver_x11_surface_info_set_wlr( struct wlserver_x11_surface_info *surf, struct wlr_surface *wlr_surf, bool override ); @@ -484,7 +483,7 @@ static void wlserver_new_input(struct wl_listener *listener, void *data) case WLR_INPUT_DEVICE_KEYBOARD: { struct wlr_keyboard *keyboard = wlr_keyboard_from_input_device(device); - wlr_keyboard_set_keymap(keyboard, wlserver.keyboard_group->keyboard.keymap); + if (!wlr_keyboard_group_add_keyboard(wlserver.keyboard_group, keyboard)) { wl_log.errorf("failed to add physical keyboard %s", device->name); break; @@ -1936,7 +1935,7 @@ static gamescope::CAsyncWaiter g_LibEisWaiter( "gamescope-eis" ); static std::unique_ptr g_InputServer; #endif -bool wlserver_init( void ) { +bool wlserver_init( ::xkb_keymap *p_keymap ) { assert( wlserver.display != nullptr ); wl_list_init(&pending_surfaces); @@ -1967,21 +1966,15 @@ bool wlserver_init( void ) { wlserver.wlr.virtual_keyboard_device = kbd; // Create a keyboard group to keep all externally connected keyboards - // in sync (one single layout and a shared state) - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_rule_names rules = { 0 }; - rules.rules = getenv("XKB_DEFAULT_RULES"); - rules.model = getenv("XKB_DEFAULT_MODEL"); - rules.layout = getenv("XKB_DEFAULT_LAYOUT"); - rules.variant = getenv("XKB_DEFAULT_VARIANT"); - rules.options = getenv("XKB_DEFAULT_OPTIONS"); - struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - wlserver.keyboard_group = wlr_keyboard_group_create(); + // in sync (one single layout and a shared state) - defer keymap to later + wlserver.keyboard_group = wlr_keyboard_group_create(); struct wlr_keyboard *keyboard = &wlserver.keyboard_group->keyboard; wlr_keyboard_set_repeat_info(keyboard, 25, 600); - wlr_keyboard_set_keymap(keyboard, keymap); wlserver.keyboard_group_modifiers.notify = wlserver_handle_modifiers; - wl_signal_add(&keyboard->events.modifiers, &wlserver.keyboard_group_modifiers); + wl_signal_add( + &keyboard->events.modifiers, + &wlserver.keyboard_group_modifiers + ); wlserver.keyboard_group_key.notify = wlserver_handle_key; wl_signal_add(&keyboard->events.key, &wlserver.keyboard_group_key); @@ -2088,6 +2081,80 @@ bool wlserver_init( void ) { return false; } + // Backend initialized, input_devices populated. Now we can extract keymap + // from parent, then override with any set env vars + ::xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + ::xkb_rule_names rules = { 0 }; + ::xkb_keymap *keymap = nullptr; + + // Fetch xkb env vars from session + const char *env_rules = getenv("XKB_DEFAULT_RULES"); + const char *env_model = getenv("XKB_DEFAULT_MODEL"); + const char *env_layout = getenv("XKB_DEFAULT_LAYOUT"); + const char *env_variant = getenv("XKB_DEFAULT_VARIANT"); + const char *env_options = getenv("XKB_DEFAULT_OPTIONS"); + + // priority: env overrides parent keymap overrides default + if ( + env_rules + || env_model + || env_layout + || env_options + ) + { + // Build keymap from env + rules.rules = env_rules ? : "evdev"; + rules.model = env_model ? : "pc105"; + rules.layout = env_layout ? : "us"; + rules.variant = (env_layout && env_variant) ? env_variant : NULL; + rules.options = env_options; + keymap = xkb_keymap_new_from_names( + context, + &rules, + XKB_KEYMAP_COMPILE_NO_FLAGS + ); + } + + // Note: xkb_keymap_new_from_names can fail and leave keymap UB. This is + // why we check. + if (keymap) + { + wl_log.infof("Using xkb_keymap from environment variables"); + } + else if (p_keymap) + { + // Ref parent session's keymap + keymap = xkb_keymap_ref(p_keymap); + wl_log.infof("Using parent session xkb_keymap"); + } + + // Build default keymap as fallback + if (!keymap) + { + rules.rules = "evdev"; + rules.model = "pc105"; + rules.layout = "us"; + rules.variant = NULL; + rules.options = NULL; + keymap = xkb_keymap_new_from_names( + context, + &rules, + XKB_KEYMAP_COMPILE_NO_FLAGS + ); + + if (keymap) + { + wl_log.infof("Using default xkb_keymap."); + } + else + { + wl_log.errorf("Failed to create any xkb_keymap."); + return false; + } + } + + wlr_keyboard_set_keymap(kbd, keymap); + wl_signal_emit( &wlserver.wlr.multi_backend->events.new_input, kbd ); #if HAVE_LIBEIS diff --git a/src/wlserver.hpp b/src/wlserver.hpp index eb1270806d..554bae8614 100644 --- a/src/wlserver.hpp +++ b/src/wlserver.hpp @@ -228,7 +228,7 @@ bool wlsession_init( void ); int wlsession_open_kms( const char *device_name ); void wlsession_close_kms(); -bool wlserver_init( void ); +bool wlserver_init(::xkb_keymap *p_keymap = nullptr); void wlserver_run(void);