diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp index 325f91f78..f0f34a6f8 100644 --- a/src/Backends/SDLBackend.cpp +++ b/src/Backends/SDLBackend.cpp @@ -13,6 +13,7 @@ #include "SDL_events.h" #include "gamescope_shared.h" #include "main.hpp" +#include "shortcut.h" #include "wlserver.hpp" #include #include @@ -730,9 +731,7 @@ namespace gamescope if ( event.key.keysym.mod & KMOD_LGUI ) { uint32_t key = SDLScancodeToLinuxKey( event.key.keysym.scancode ); - const uint32_t shortcutKeys[] = {KEY_F, KEY_N, KEY_B, KEY_U, KEY_Y, KEY_I, KEY_O, KEY_S, KEY_G}; - const bool isShortcutKey = std::find(std::begin(shortcutKeys), std::end(shortcutKeys), key) != std::end(shortcutKeys); - if ( isShortcutKey ) + if ( g_shortcutHandler.GetShortcut( key ) != GAMESCOPE_SHORTCUT_NONE ) { break; } @@ -745,38 +744,16 @@ namespace gamescope if ( event.type == SDL_KEYUP && ( event.key.keysym.mod & KMOD_LGUI ) ) { - bool handled = true; - switch ( key ) + auto shortcut = g_shortcutHandler.GetShortcut( key ); + g_shortcutHandler.HandleShortcut( shortcut ); + + switch ( shortcut ) { - case KEY_F: + case GAMESCOPE_SHORTCUT_FULLSCREEN: g_bFullscreen = !g_bFullscreen; SDL_SetWindowFullscreen( m_Connector.GetSDLWindow(), g_bFullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0 ); break; - case KEY_N: - g_wantedUpscaleFilter = GamescopeUpscaleFilter::PIXEL; - break; - case KEY_B: - g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; - break; - case KEY_U: - g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::FSR) ? - GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::FSR; - break; - case KEY_Y: - g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::NIS) ? - GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::NIS; - break; - case KEY_I: - g_upscaleFilterSharpness = std::min(20, g_upscaleFilterSharpness + 1); - break; - case KEY_O: - g_upscaleFilterSharpness = std::max(0, g_upscaleFilterSharpness - 1); - break; - case KEY_S: - gamescope::CScreenshotManager::Get().TakeScreenshot( true ); - break; - case KEY_G: - g_bGrabbed = !g_bGrabbed; + case GAMESCOPE_SHORTCUT_KEYBOARD_GRAB: SDL_SetWindowKeyboardGrab( m_Connector.GetSDLWindow(), g_bGrabbed ? SDL_TRUE : SDL_FALSE ); SDL_Event event; @@ -784,9 +761,9 @@ namespace gamescope SDL_PushEvent( &event ); break; default: - handled = false; + break; } - if ( handled ) + if ( shortcut != GAMESCOPE_SHORTCUT_NONE ) { break; } diff --git a/src/Backends/WaylandBackend.cpp b/src/Backends/WaylandBackend.cpp index ec28d2815..c6f62a35b 100644 --- a/src/Backends/WaylandBackend.cpp +++ b/src/Backends/WaylandBackend.cpp @@ -2544,81 +2544,16 @@ namespace gamescope void CWaylandInputThread::HandleKey( uint32_t uKey, bool bPressed ) { - if ( m_uKeyModifiers & m_uModMask[ GAMESCOPE_WAYLAND_MOD_META ] ) + if ( !bPressed && m_uKeyModifiers & m_uModMask[ GAMESCOPE_WAYLAND_MOD_META ] ) { - switch ( uKey ) - { - case KEY_F: - { - if ( !bPressed ) - { - static_cast< CWaylandConnector * >( m_pBackend->GetCurrentConnector() )->SetFullscreen( !g_bFullscreen ); - } - return; - } - - case KEY_N: - { - if ( !bPressed ) - { - g_wantedUpscaleFilter = GamescopeUpscaleFilter::PIXEL; - } - return; - } + auto shortcut = g_shortcutHandler.GetShortcut( uKey ); + g_shortcutHandler.HandleShortcut( shortcut ); - case KEY_B: - { - if ( !bPressed ) - { - g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; - } - return; - } - - case KEY_U: - { - if ( !bPressed ) - { - g_wantedUpscaleFilter = ( g_wantedUpscaleFilter == GamescopeUpscaleFilter::FSR ) ? - GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::FSR; - } - return; - } - - case KEY_Y: - { - if ( !bPressed ) - { - g_wantedUpscaleFilter = ( g_wantedUpscaleFilter == GamescopeUpscaleFilter::NIS ) ? - GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::NIS; - } - return; - } - - case KEY_I: - { - if ( !bPressed ) - { - g_upscaleFilterSharpness = std::min( 20, g_upscaleFilterSharpness + 1 ); - } - return; - } - - case KEY_O: - { - if ( !bPressed ) - { - g_upscaleFilterSharpness = std::max( 0, g_upscaleFilterSharpness - 1 ); - } - return; - } - - case KEY_S: + switch ( shortcut ) + { + case GAMESCOPE_SHORTCUT_FULLSCREEN: { - if ( !bPressed ) - { - gamescope::CScreenshotManager::Get().TakeScreenshot( true ); - } + static_cast< CWaylandConnector * >( m_pBackend->GetCurrentConnector() )->SetFullscreen( !g_bFullscreen ); return; } diff --git a/src/main.cpp b/src/main.cpp index 7bd4f6f2e..0ad7937e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -312,6 +312,8 @@ float g_flMaxWindowScale = FLT_MAX; uint32_t g_preferVendorID = 0; uint32_t g_preferDeviceID = 0; +gamescope::ShortcutHandler g_shortcutHandler = {}; + pthread_t g_mainThread; static void steamCompMgrThreadRun(int argc, char **argv); diff --git a/src/main.hpp b/src/main.hpp index 2e6fb833a..d5c1885f6 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -4,6 +4,8 @@ #include +#include "shortcut.h" + extern const char *gamescope_optstring; extern const struct option *gamescope_options; @@ -73,3 +75,4 @@ extern int g_nXWaylandCount; extern uint32_t g_preferVendorID; extern uint32_t g_preferDeviceID; +extern gamescope::ShortcutHandler g_shortcutHandler; diff --git a/src/meson.build b/src/meson.build index 63897dd2a..052ec9050 100644 --- a/src/meson.build +++ b/src/meson.build @@ -116,6 +116,7 @@ src = [ 'mangoapp.cpp', 'Timeline.cpp', 'reshade_effect_manager.cpp', + 'shortcut.cpp', 'backend.cpp', 'x11cursor.cpp', 'InputEmulation.cpp', diff --git a/src/shortcut.cpp b/src/shortcut.cpp new file mode 100644 index 000000000..ddaf69996 --- /dev/null +++ b/src/shortcut.cpp @@ -0,0 +1,183 @@ +#include + +#include "shortcut.h" +#include "convar.h" +#include "steamcompmgr_shared.hpp" +#include "main.hpp" + +namespace gamescope +{ + ShortcutHandler::ShortcutHandler() + { + // default mappings + m_binds = { + { KEY_F, GAMESCOPE_SHORTCUT_FULLSCREEN }, + { KEY_N, GAMESCOPE_SHORTCUT_FILTER_NEAREST }, + { KEY_B, GAMESCOPE_SHORTCUT_FILTER_LINEAR }, + { KEY_U, GAMESCOPE_SHORTCUT_UPSCALE_FSR }, + { KEY_Y, GAMESCOPE_SHORTCUT_UPSCALE_NIS }, + { KEY_I, GAMESCOPE_SHORTCUT_UPSCALE_SHARPNESS_UP }, + { KEY_O, GAMESCOPE_SHORTCUT_UPSCALE_SHARPNESS_DOWN }, + { KEY_S, GAMESCOPE_SHORTCUT_SCREENSHOT }, + { KEY_G, GAMESCOPE_SHORTCUT_KEYBOARD_GRAB } + }; + } + + ShortcutHandler::~ShortcutHandler() {} + + void ShortcutHandler::Bind( uint32_t key, GamescopeShortcut shortcut ) + { + Unbind( key ); + m_binds.insert( { key, shortcut } ); + } + + void ShortcutHandler::Unbind( uint32_t key ) + { + m_binds.erase( key ); + } + + void ShortcutHandler::Unbind( GamescopeShortcut shortcut ) + { + m_binds.erase( shortcut ); + } + + GamescopeShortcut ShortcutHandler::GetShortcut( uint32_t key ) + { + if (auto it = m_binds.find( key ); it != m_binds.end()) + { + return it->second; + } + + return GAMESCOPE_SHORTCUT_NONE; + } + + void ShortcutHandler::HandleShortcut( GamescopeShortcut shortcut ) + { + switch ( shortcut ) + { + case GAMESCOPE_SHORTCUT_FILTER_NEAREST: + g_wantedUpscaleFilter = GamescopeUpscaleFilter::PIXEL; + break; + case GAMESCOPE_SHORTCUT_FILTER_LINEAR: + g_wantedUpscaleFilter = GamescopeUpscaleFilter::LINEAR; + break; + case GAMESCOPE_SHORTCUT_UPSCALE_FSR: + g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::FSR) ? + GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::FSR; + break; + case GAMESCOPE_SHORTCUT_UPSCALE_NIS: + g_wantedUpscaleFilter = (g_wantedUpscaleFilter == GamescopeUpscaleFilter::NIS) ? + GamescopeUpscaleFilter::LINEAR : GamescopeUpscaleFilter::NIS; + break; + case GAMESCOPE_SHORTCUT_UPSCALE_SHARPNESS_UP: + g_upscaleFilterSharpness = std::min( 20, g_upscaleFilterSharpness + 1 ); + break; + case GAMESCOPE_SHORTCUT_UPSCALE_SHARPNESS_DOWN: + g_upscaleFilterSharpness = std::max( 0, g_upscaleFilterSharpness - 1 ); + break; + case GAMESCOPE_SHORTCUT_SCREENSHOT: + gamescope::CScreenshotManager::Get().TakeScreenshot( true ); + break; + case GAMESCOPE_SHORTCUT_KEYBOARD_GRAB: + g_bGrabbed = !g_bGrabbed; + break; + default: + break; + } + } + + static const char* ShortcutNames[GAMESCOPE_SHORTCUT_COUNT] = { + "", + "fullscreen", + "filter_nearest", + "filter_linear", + "upscale_fsr", + "upscale_nis", + "upscale_sharpness_up", + "upscale_sharpness_down", + "screenshot", + "keyboard_grab", + }; + + static GamescopeShortcut ParseShortcut( std::string_view sv ) + { + if ( sv.length() > 0 ) + { + for ( auto i = 0; i < GAMESCOPE_SHORTCUT_COUNT; i++ ) + { + if ( sv == ShortcutNames[i] ) + { + return static_cast( i ); + } + } + } + + return GAMESCOPE_SHORTCUT_NONE; + } + + static ConCommand cc_bind( "bind", + "Bind a shortcut to SUPER- combination. e.g. 'bind 33,fullscreen'", + [](std::span svArgs ) + { + if ( svArgs.size() < 2 ) + { + console_log.errorf( "Invalid number of arguments." ); + return; + } + + // Space delimited args would be nice but requires + // changes to gamescopectl / gamescope_private_execute. + auto svBinds = gamescope::Split( svArgs[1], "," ); + if ( svBinds.size() < 2 ) + { + console_log.errorf( "Invalid number of arguments." ); + return; + } + + // TODO: key names + std::optional ouKey = Parse( svBinds[0] ); + if ( !ouKey ) + { + console_log.errorf( "Failed to parse key." ); + return; + } + + uint32_t uKey = *ouKey; + auto shortcut = ParseShortcut( svBinds[1] ); + + if ( shortcut != GAMESCOPE_SHORTCUT_NONE ) + { + g_shortcutHandler.Bind( uKey, shortcut ); + } + }); + + static ConCommand cc_unbind( "unbind", "Unbind a shortcut from SUPER- combination.", + [](std::span svArgs ) + { + if ( svArgs.size() < 2 ) + { + console_log.errorf( "Invalid number of arguments." ); + return; + } + + // TODO: key names + std::optional ouKey = Parse( svArgs[1] ); + if ( !ouKey ) + { + console_log.errorf( "Failed to parse key." ); + return; + } + + g_shortcutHandler.Unbind( *ouKey ); + }); + + static ConCommand cc_shortcuts( "shortcuts", "List shortcut names for use with 'bind'", + [](std::span svArgs ) + { + console_log.infof( "Shortcut names:" ); + for ( auto i = 0; i < GAMESCOPE_SHORTCUT_COUNT; i++ ) + { + console_log.infof( " %s", ShortcutNames[i] ); + } + }); +} diff --git a/src/shortcut.h b/src/shortcut.h new file mode 100644 index 000000000..1a4236405 --- /dev/null +++ b/src/shortcut.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +namespace gamescope +{ + enum GamescopeShortcut + { + GAMESCOPE_SHORTCUT_NONE, + + GAMESCOPE_SHORTCUT_FULLSCREEN, + GAMESCOPE_SHORTCUT_FILTER_NEAREST, + GAMESCOPE_SHORTCUT_FILTER_LINEAR, + GAMESCOPE_SHORTCUT_UPSCALE_FSR, + GAMESCOPE_SHORTCUT_UPSCALE_NIS, + GAMESCOPE_SHORTCUT_UPSCALE_SHARPNESS_UP, + GAMESCOPE_SHORTCUT_UPSCALE_SHARPNESS_DOWN, + GAMESCOPE_SHORTCUT_SCREENSHOT, + GAMESCOPE_SHORTCUT_KEYBOARD_GRAB, + + GAMESCOPE_SHORTCUT_COUNT + }; + + class ShortcutHandler + { + public: + ShortcutHandler(); + ~ShortcutHandler(); + + void Bind( uint32_t key, GamescopeShortcut shortcut ); + void Unbind( uint32_t key); + void Unbind( GamescopeShortcut shortcut ); + + GamescopeShortcut GetShortcut( uint32_t key ); + + void HandleShortcut( GamescopeShortcut shortcut ); + + private: + std::map m_binds; + }; +}