diff --git a/protocol/gamescope-control.xml b/protocol/gamescope-control.xml index 012c48c..7f5578b 100644 --- a/protocol/gamescope-control.xml +++ b/protocol/gamescope-control.xml @@ -99,5 +99,22 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp index 97ef446..85e5126 100644 --- a/src/Backends/DRMBackend.cpp +++ b/src/Backends/DRMBackend.cpp @@ -56,6 +56,7 @@ static constexpr bool k_bUseCursorPlane = false; extern int g_nPreferredOutputWidth; extern int g_nPreferredOutputHeight; +bool panelTypeChanged = false; gamescope::ConVar cv_drm_single_plane_optimizations( "drm_single_plane_optimizations", true, "Whether or not to enable optimizations for single plane usage." ); @@ -67,7 +68,7 @@ gamescope::ConVar cv_drm_debug_disable_blend_tf( "drm_debug_disable_blend_ gamescope::ConVar cv_drm_debug_disable_ctm( "drm_debug_disable_ctm", false, "CTM chicken bit. (Forces CTM off, does not affect other logic)" ); gamescope::ConVar cv_drm_debug_disable_color_encoding( "drm_debug_disable_color_encoding", false, "YUV Color Encoding chicken bit. (Forces COLOR_ENCODING to DEFAULT, does not affect other logic)" ); gamescope::ConVar cv_drm_debug_disable_color_range( "drm_debug_disable_color_range", false, "YUV Color Range chicken bit. (Forces COLOR_RANGE to DEFAULT, does not affect other logic)" ); -gamescope::ConVar cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", false, "Force disable explicit sync on the DRM backend." ); +gamescope::ConVar cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", true, "Force disable explicit sync on the DRM backend." ); gamescope::ConVar cv_drm_debug_disable_in_fence_fd( "drm_debug_disable_in_fence_fd", false, "Force disable IN_FENCE_FD being set to avoid over-synchronization on the DRM backend." ); // HACK: @@ -321,8 +322,19 @@ namespace gamescope if ( m_pConnector->connector_type == DRM_MODE_CONNECTOR_eDP || m_pConnector->connector_type == DRM_MODE_CONNECTOR_LVDS || m_pConnector->connector_type == DRM_MODE_CONNECTOR_DSI ) - return GAMESCOPE_SCREEN_TYPE_INTERNAL; + { + if ( g_bExternalForced ) + { + panelTypeChanged = true; + return g_ForcedScreenType; + } + else + { + return GAMESCOPE_SCREEN_TYPE_INTERNAL; + } + } + panelTypeChanged = false; return GAMESCOPE_SCREEN_TYPE_EXTERNAL; } @@ -536,6 +548,7 @@ bool g_bSupportsSyncObjs = false; extern gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration; extern GamescopePanelOrientation g_DesiredInternalOrientation; +extern GamescopePanelOrientation g_DesiredExternalOrientation; extern bool g_bForceDisableColorMgmt; @@ -2019,14 +2032,31 @@ namespace gamescope void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode ) { - if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) - { + + if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && panelTypeChanged ) + drm_log.infof("Display is internal faked as external"); + if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && !panelTypeChanged ) + drm_log.infof("Display is real internal"); + if (panelTypeChanged){ + drm_log.infof("Panel type was changed"); + } + + if (( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) || + ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO + && panelTypeChanged)) { + + drm_log.infof("We are rotating the orientation of the internal or faked external display"); m_ChosenOrientation = g_DesiredInternalOrientation; } + else if (this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO) { + drm_log.infof("We are rotating the orientation of an external display"); + m_ChosenOrientation = g_DesiredExternalOrientation; + } else { if ( this->GetProperties().panel_orientation ) { + drm_log.infof("We are using a kernel orientation quirk to rotate the display"); switch ( this->GetProperties().panel_orientation->GetCurrentValue() ) { case DRM_MODE_PANEL_ORIENTATION_NORMAL: @@ -2048,6 +2078,7 @@ namespace gamescope if ( this->GetScreenType() == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL && pMode ) { + drm_log.infof("We are using legacy code to rotate the display"); // Auto-detect portait mode for internal displays m_ChosenOrientation = pMode->hdisplay < pMode->vdisplay ? GAMESCOPE_PANEL_ORIENTATION_270 @@ -2055,6 +2086,7 @@ namespace gamescope } else { + drm_log.infof("No orientation quirks have been applied"); m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0; } } @@ -2120,6 +2152,10 @@ namespace gamescope ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Jupiter"sv ) || ( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Galileo"sv ); + if ( g_customRefreshRates.size() > 0 && ( GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL || g_bExternalForced ) ) { + m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates); + return; + } if ( bSteamDeckDisplay ) { static constexpr uint32_t kPIDGalileoSDC = 0x3003; @@ -2926,6 +2962,25 @@ bool drm_update_color_mgmt(struct drm_t *drm) return true; } +void drm_set_orientation( struct drm_t *drm, bool isRotated) +{ + int width = g_nOutputWidth; + int height = g_nOutputHeight; + g_bRotated = isRotated; + if ( g_bRotated ) { + int tmp = width; + width = height; + height = tmp; + } + + if (!drm->pConnector || !drm->pConnector->GetModeConnector()) + return; + + drmModeConnector *connector = drm->pConnector->GetModeConnector(); + const drmModeModeInfo *mode = find_mode(connector, width, height, 0); + update_drm_effective_orientations(drm, mode); +} + static void drm_unset_mode( struct drm_t *drm ) { drm->pending.mode_id = 0; diff --git a/src/backend.h b/src/backend.h index 9c2db15..046eb10 100644 --- a/src/backend.h +++ b/src/backend.h @@ -17,6 +17,7 @@ struct wlr_buffer; struct wlr_dmabuf_attributes; struct FrameInfo_t; +extern gamescope::GamescopeScreenType g_ForcedScreenType; namespace gamescope { @@ -213,6 +214,8 @@ namespace gamescope // Dumb helper we should remove to support multi display someday. gamescope::GamescopeScreenType GetScreenType() { + if (g_ForcedScreenType != GAMESCOPE_SCREEN_TYPE_AUTO) + return g_ForcedScreenType; if ( GetCurrentConnector() ) return GetCurrentConnector()->GetScreenType(); diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h index f34174e..ed30d8c 100644 --- a/src/gamescope_shared.h +++ b/src/gamescope_shared.h @@ -22,6 +22,7 @@ namespace gamescope { GAMESCOPE_SCREEN_TYPE_INTERNAL, GAMESCOPE_SCREEN_TYPE_EXTERNAL, + GAMESCOPE_SCREEN_TYPE_AUTO, GAMESCOPE_SCREEN_TYPE_COUNT }; diff --git a/src/main.cpp b/src/main.cpp index 59dec4f..119e043 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -107,6 +107,8 @@ const struct option *gamescope_options = (struct option[]){ // wlserver options { "xwayland-count", required_argument, nullptr, 0 }, + { "touch-gestures", no_argument, nullptr, 0 }, + // steamcompmgr options { "cursor", required_argument, nullptr, 0 }, @@ -125,7 +127,12 @@ const struct option *gamescope_options = (struct option[]){ { "disable-xres", no_argument, nullptr, 'x' }, { "fade-out-duration", required_argument, nullptr, 0 }, { "force-orientation", required_argument, nullptr, 0 }, + { "force-external-orientation", required_argument, nullptr, 0 }, + { "force-panel-type", required_argument, nullptr, 0 }, { "force-windows-fullscreen", no_argument, nullptr, 0 }, + { "bypass-steam-resolution", no_argument, nullptr, 0 }, + { "custom-refresh-rates", required_argument, nullptr, 0 }, + { "disable-color-management", no_argument, nullptr, 0 }, { "sdr-gamut-wideness", required_argument, nullptr, 0 }, @@ -184,9 +191,13 @@ const char usage[] = " -T, --stats-path write statistics to path\n" " -C, --hide-cursor-delay hide cursor image after delay\n" " -e, --steam enable Steam integration\n" + " --bypass-steam-resolution bypass Steam's default 720p/800p default resolution\n" + " --touch-gestures enable touch gestures for Steam menus\n" " --xwayland-count create N xwayland servers\n" " --prefer-vk-device prefer Vulkan device for compositing (ex: 1002:7300)\n" " --force-orientation rotate the internal display (left, right, normal, upsidedown)\n" + " --force-external-orientation rotate the external display (left, right, normal, upsidedown)\n" + " --force-panel-type force gamescope to treat the display as either internal or external\n" " --force-windows-fullscreen force windows inside of gamescope to be the size of the nested display (fullscreen)\n" " --cursor-scale-height if specified, sets a base output height to linearly scale the cursor against.\n" " --hdr-enabled enable HDR output (needs Gamescope WSI layer enabled for support from clients)\n" @@ -199,6 +210,7 @@ const char usage[] = " --hdr-itm-target-nits set the target luminace of the inverse tone mapping process.\n" " Default: 1000 nits, Max: 10000 nits\n" " --framerate-limit Set a simple framerate limit. Used as a divisor of the refresh rate, rounds down eg 60 / 59 -> 60fps, 60 / 25 -> 30fps. Default: 0, disabled.\n" + " --custom-refresh-rates Set custom refresh rates for the output. eg: 60,90,110-120\n" " --mangoapp Launch with the mangoapp (mangohud) performance overlay enabled. You should use this instead of using mangohud on the game or gamescope.\n" "\n" "Nested mode options:\n" @@ -282,6 +294,8 @@ bool g_bOutputHDREnabled = false; bool g_bFullscreen = false; bool g_bForceRelativeMouse = false; +bool g_bExternalForced = false; + bool g_bGrabbed = false; float g_mouseSensitivity = 1.0; @@ -363,7 +377,37 @@ static GamescopePanelOrientation force_orientation(const char *str) } else if (strcmp(str, "upsidedown") == 0) { return GAMESCOPE_PANEL_ORIENTATION_180; } else { - fprintf( stderr, "gamescope: invalid value for --force-orientation\n" ); + fprintf( stderr, "gamescope: invalid value for given for --force-orientation\n" ); + exit(1); + } +} + +GamescopePanelOrientation g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_AUTO; +static GamescopePanelOrientation force_external_orientation(const char *str) +{ + if (strcmp(str, "normal") == 0) { + return GAMESCOPE_PANEL_ORIENTATION_0; + } else if (strcmp(str, "right") == 0) { + return GAMESCOPE_PANEL_ORIENTATION_270; + } else if (strcmp(str, "left") == 0) { + return GAMESCOPE_PANEL_ORIENTATION_90; + } else if (strcmp(str, "upsidedown") == 0) { + return GAMESCOPE_PANEL_ORIENTATION_180; + } else { + fprintf( stderr, "gamescope: invalid value for --force-external-orientation\n" ); + exit(1); + } +} +gamescope::GamescopeScreenType g_ForcedScreenType = gamescope::GAMESCOPE_SCREEN_TYPE_AUTO; +static gamescope::GamescopeScreenType force_panel_type(const char *str) +{ + if (strcmp(str, "internal") == 0) { + return gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL; + } else if (strcmp(str, "external") == 0) { + g_bExternalForced = true; + return gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL; + } else { + fprintf( stderr, "gamescope: invalid value for --force-panel-type\n" ); exit(1); } } @@ -430,6 +474,33 @@ static enum gamescope::GamescopeBackend parse_backend_name(const char *str) } } +std::vector g_customRefreshRates; +// eg: 60,60,90,110-120 +static std::vector parse_custom_refresh_rates( const char *str ) +{ + std::vector rates; + char *token = strtok( strdup(str), ","); + while (token) + { + char *dash = strchr(token, '-'); + if (dash) + { + uint32_t start = atoi(token); + uint32_t end = atoi(dash + 1); + for (uint32_t i = start; i <= end; i++) + { + rates.push_back(i); + } + } + else + { + rates.push_back(atoi(token)); + } + token = strtok(nullptr, ","); + } + return rates; +} + struct sigaction handle_signal_action = {}; extern std::mutex g_ChildPidMutex; extern std::vector g_ChildPids; @@ -766,6 +837,8 @@ int main(int argc, char **argv) g_bDebugLayers = true; } else if (strcmp(opt_name, "disable-color-management") == 0) { g_bForceDisableColorMgmt = true; + } else if (strcmp(opt_name, "touch-gestures") == 0) { + cv_touch_gestures = true; } else if (strcmp(opt_name, "xwayland-count") == 0) { g_nXWaylandCount = atoi( optarg ); } else if (strcmp(opt_name, "composite-debug") == 0) { @@ -779,6 +852,12 @@ int main(int argc, char **argv) g_eGamescopeModeGeneration = parse_gamescope_mode_generation( optarg ); } else if (strcmp(opt_name, "force-orientation") == 0) { g_DesiredInternalOrientation = force_orientation( optarg ); + } else if (strcmp(opt_name, "force-external-orientation") == 0) { + g_DesiredExternalOrientation = force_external_orientation( optarg ); + } else if (strcmp(opt_name, "force-panel-type") == 0) { + g_ForcedScreenType = force_panel_type( optarg ); + } else if (strcmp(opt_name, "custom-refresh-rates") == 0) { + g_customRefreshRates = parse_custom_refresh_rates( optarg ); } else if (strcmp(opt_name, "sharpness") == 0 || strcmp(opt_name, "fsr-sharpness") == 0) { g_upscaleFilterSharpness = atoi( optarg ); diff --git a/src/main.hpp b/src/main.hpp index 4e4e9a7..c74f40b 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -3,6 +3,7 @@ #include #include +#include extern const char *gamescope_optstring; extern const struct option *gamescope_options; @@ -28,6 +29,8 @@ extern bool g_bGrabbed; extern float g_mouseSensitivity; extern const char *g_sOutputName; +extern bool g_bExternalForced; +extern std::vector g_customRefreshRates; enum class GamescopeUpscaleFilter : uint32_t { diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 096c8a1..92bf617 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -345,6 +345,8 @@ bool g_bForceHDR10OutputDebug = false; gamescope::ConVar cv_hdr_enabled{ "hdr_enabled", false, "Whether or not HDR is enabled if it is available." }; bool g_bHDRItmEnable = false; int g_nCurrentRefreshRate_CachedValue = 0; +gamescope::ConVar cv_bypass_steam_resolution{ "bypass_steam_resolution", false, "Workaround the 720p/800p limits Steam uses for games" }; + static void update_color_mgmt() @@ -3123,8 +3125,8 @@ static bool is_good_override_candidate( steamcompmgr_win_t *override, steamcompm if ( !focus ) return false; - return override != focus && override->GetGeometry().nX >= 0 && override->GetGeometry().nY >= 0; -} + return override != focus && override->xwayland().a.x >= 0 && override->xwayland().a.y >= 0; +} static bool pick_primary_focus_and_override(focus_t *out, Window focusControlWindow, const std::vector& vecPossibleFocusWindows, bool globalFocus, const std::vector& ctxFocusControlAppIDs) @@ -5371,6 +5373,13 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) size_t server_idx = size_t{ xwayland_mode_ctl[ 0 ] }; int width = xwayland_mode_ctl[ 1 ]; int height = xwayland_mode_ctl[ 2 ]; + + if ( g_nOutputWidth != 1280 && width == 1280 && cv_bypass_steam_resolution ) + { + width = g_nOutputWidth; + height = g_nOutputHeight; + } + bool allowSuperRes = !!xwayland_mode_ctl[ 3 ]; if ( !allowSuperRes ) @@ -7268,6 +7277,8 @@ steamcompmgr_main(int argc, char **argv) bForceWindowsFullscreen = true; } else if (strcmp(opt_name, "hdr-enabled") == 0) { cv_hdr_enabled = true; + } else if (strcmp(opt_name, "bypass-steam-resolution") == 0) { + cv_bypass_steam_resolution = true; } else if (strcmp(opt_name, "hdr-debug-force-support") == 0) { g_bForceHDRSupportDebug = true; } else if (strcmp(opt_name, "hdr-debug-force-output") == 0) { diff --git a/src/wlserver.cpp b/src/wlserver.cpp index d9182aa..776e014 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -71,8 +71,13 @@ #include static LogScope wl_log("wlserver"); - +bool pending_gesture_x = false; +bool pending_gesture_y = false; +bool pending_osk = false; //#define GAMESCOPE_SWAPCHAIN_DEBUG +gamescope::ConVar cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" ); +extern GamescopePanelOrientation g_DesiredInternalOrientation; +extern GamescopePanelOrientation g_DesiredExternalOrientation; struct wlserver_t wlserver = { .touch_down_ids = {} @@ -418,6 +423,39 @@ void wlserver_open_steam_menu( bool qam ) XTestFakeKeyEvent(server->get_xdisplay(), XKeysymToKeycode( server->get_xdisplay(), XK_Control_L ), False, CurrentTime); } +void wlserver_open_steam_osk(bool osk) +{ + gamescope_xwayland_server_t *server = wlserver_get_xwayland_server( 0 ); + if (!server) + return; + + uint32_t osk_open = osk; + + if (osk_open) + { + const char *command = "xdg-open steam://open/keyboard?"; + int result = system(command); + if (result == 0) { + printf("Command executed successfully.\n"); + } else { + printf("Error executing command.\n"); + } + pending_osk = false; + } + else + { + const char *command = "xdg-open steam://close/keyboard?"; + int result = system(command); + if (result == 0) { + printf("Command executed successfully.\n"); + } else { + printf("Error executing command.\n"); + } + pending_osk = false; + } + +} + static void wlserver_handle_pointer_button(struct wl_listener *listener, void *data) { struct wlserver_pointer *pointer = wl_container_of( listener, pointer, button ); @@ -1095,6 +1133,56 @@ static void gamescope_control_take_screenshot( struct wl_client *client, struct } ); } +static void gamescope_control_rotate_display( struct wl_client *client, struct wl_resource *resource, uint32_t orientation, uint32_t target_type ) +{ + bool isRotated = false; + if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_INTERNAL ) + { + switch (orientation) { + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: + g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_0; + break; + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: + g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_90; + isRotated = true; + break; + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: + g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_270; + isRotated = true; + break; + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: + g_DesiredInternalOrientation = GAMESCOPE_PANEL_ORIENTATION_180; + break; + default: + wl_log.errorf("Invalid target orientation selected"); + } + } + else if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_EXTERNAL ) + { + switch (orientation) { + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL: + g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_0; + break; + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT: + g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_90; + isRotated = true; + break; + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT: + g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_270; + isRotated = true; + break; + case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN: + g_DesiredExternalOrientation = GAMESCOPE_PANEL_ORIENTATION_180; + break; + default: + wl_log.errorf("Invalid target orientation selected"); + } + } + drm_set_orientation(&g_DRM, isRotated); + GetBackend()->DirtyState( true, true ); + +} + static void gamescope_control_handle_destroy( struct wl_client *client, struct wl_resource *resource ) { wl_resource_destroy( resource ); @@ -1104,6 +1192,7 @@ static const struct gamescope_control_interface gamescope_control_impl = { .destroy = gamescope_control_handle_destroy, .set_app_target_refresh_cycle = gamescope_control_set_app_target_refresh_cycle, .take_screenshot = gamescope_control_take_screenshot, + .rotate_display = gamescope_control_rotate_display, }; static uint32_t get_conn_display_info_flags() @@ -2469,36 +2558,104 @@ const std::shared_ptr& wlserver_surface_swapchai /* Handle the orientation of the touch inputs */ static void apply_touchscreen_orientation(double *x, double *y ) { - double tx = 0; - double ty = 0; + double tx = 0; + double ty = 0; - // Use internal screen always for orientation purposes. - switch ( GetBackend()->GetConnector( gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL )->GetCurrentOrientation() ) - { - default: - case GAMESCOPE_PANEL_ORIENTATION_AUTO: - case GAMESCOPE_PANEL_ORIENTATION_0: - tx = *x; - ty = *y; - break; - case GAMESCOPE_PANEL_ORIENTATION_90: - tx = 1.0 - *y; - ty = *x; - break; - case GAMESCOPE_PANEL_ORIENTATION_180: - tx = 1.0 - *x; - ty = 1.0 - *y; - break; - case GAMESCOPE_PANEL_ORIENTATION_270: - tx = *y; - ty = 1.0 - *x; - break; - } + // Use internal screen always for orientation purposes. + if ( g_ForcedScreenType != gamescope::GAMESCOPE_SCREEN_TYPE_AUTO ) + { + if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL ) + { + if(panelTypeChanged) + { + switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL)->GetCurrentOrientation()) + { + default: + case GAMESCOPE_PANEL_ORIENTATION_AUTO: + case GAMESCOPE_PANEL_ORIENTATION_0: + tx = *x; + ty = *y; + break; + case GAMESCOPE_PANEL_ORIENTATION_90: + tx = 1.0 - *y; + ty = *x; + break; + case GAMESCOPE_PANEL_ORIENTATION_180: + tx = 1.0 - *x; + ty = 1.0 - *y; + break; + case GAMESCOPE_PANEL_ORIENTATION_270: + tx = *y; + ty = 1.0 - *x; + break; + } + } + } + else + { + switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL)->GetCurrentOrientation()) + { + default: + case GAMESCOPE_PANEL_ORIENTATION_AUTO: + case GAMESCOPE_PANEL_ORIENTATION_0: + tx = *x; + ty = *y; + break; + case GAMESCOPE_PANEL_ORIENTATION_90: + tx = 1.0 - *y; + ty = *x; + break; + case GAMESCOPE_PANEL_ORIENTATION_180: + tx = 1.0 - *x; + ty = 1.0 - *y; + break; + case GAMESCOPE_PANEL_ORIENTATION_270: + tx = *y; + ty = 1.0 - *x; + break; + } + } + + } + else if (g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_AUTO) + { + switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL)->GetCurrentOrientation()) + { + default: + case GAMESCOPE_PANEL_ORIENTATION_AUTO: + case GAMESCOPE_PANEL_ORIENTATION_0: + tx = *x; + ty = *y; + break; + case GAMESCOPE_PANEL_ORIENTATION_90: + tx = 1.0 - *y; + ty = *x; + break; + case GAMESCOPE_PANEL_ORIENTATION_180: + tx = 1.0 - *x; + ty = 1.0 - *y; + break; + case GAMESCOPE_PANEL_ORIENTATION_270: + tx = *y; + ty = 1.0 - *x; + break; + } + } + + *x = tx; + *y = ty; +} - *x = tx; - *y = ty; +void wlserver_gesture_flush() +{ + pending_gesture_x = false; + pending_gesture_y = false; } +// Variables to track the direction of the touch motion +uint32_t previous_tx = 0; +uint32_t previous_ty = 0; + void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool bAlwaysWarpCursor ) { assert( wlserver_is_lock_held() ); @@ -2534,6 +2691,59 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool if ( bAlwaysWarpCursor ) wlserver_mousewarp( tx, ty, time, false ); + + if ( cv_touch_gestures ) + { + uint32_t rounded_tx = static_cast(std::round(tx)); + uint32_t rounded_ty = static_cast(std::round(ty)); + uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.05); + uint32_t edge_range_y = static_cast(g_nOutputWidth * 0.05); + uint32_t gesture_limits_x = edge_range_x * 2; + uint32_t gesture_limits_y = edge_range_y * 2; + uint32_t threshold_distance_x = gesture_limits_x; + uint32_t threshold_distance_y = gesture_limits_y; + + // Left to Right and Right to Left + if (!pending_gesture_x && ((rounded_tx >= 1 && rounded_tx < edge_range_x) || (rounded_tx >= g_nOutputWidth - edge_range_x))) { + // Check if the distance moved is greater than the threshold + if (rounded_tx - previous_tx > threshold_distance_x) { + pending_gesture_x = true; + } + } + + // Top to Bottom and Bottom to Top + if (!pending_gesture_y && ((rounded_ty >= 1 && rounded_ty < edge_range_y) || (rounded_ty >= g_nOutputHeight - edge_range_y))) { + // Check if the distance moved is greater than the threshold + if (rounded_ty - previous_ty > threshold_distance_y) { + pending_gesture_y = true; + } + } + + //left + if (pending_gesture_x && previous_tx < rounded_tx && rounded_tx >= edge_range_x && rounded_tx < gesture_limits_x) { + wlserver_open_steam_menu(0); + wlserver_gesture_flush(); + } + //right + if (pending_gesture_x && previous_tx > rounded_tx && rounded_tx <= g_nOutputWidth - edge_range_x && rounded_tx > g_nOutputWidth - gesture_limits_x) { + wlserver_open_steam_menu(1); + wlserver_gesture_flush(); + } + + // Top + if (pending_gesture_y && previous_ty < rounded_ty && rounded_ty >= edge_range_y && rounded_ty < gesture_limits_y) { + wlserver_gesture_flush(); + // Top to Bottom function to add + } + // Bottom + if (pending_gesture_y && previous_ty > rounded_ty && !pending_osk && rounded_ty <= g_nOutputWidth - edge_range_y && rounded_ty > g_nOutputHeight - gesture_limits_y) { + wlserver_gesture_flush(); + pending_osk = true; + wlserver_open_steam_osk(1); + } + previous_tx = rounded_tx; + previous_ty = rounded_ty; + } } else if ( eMode == gamescope::TouchClickModes::Disabled ) { diff --git a/src/wlserver.hpp b/src/wlserver.hpp index 0d8f3ac..07675fe 100644 --- a/src/wlserver.hpp +++ b/src/wlserver.hpp @@ -287,9 +287,12 @@ void wlserver_x11_surface_info_finish( struct wlserver_x11_surface_info *surf ); void wlserver_set_xwayland_server_mode( size_t idx, int w, int h, int refresh ); extern std::atomic g_bPendingTouchMovement; +extern gamescope::ConVar cv_touch_gestures; void wlserver_open_steam_menu( bool qam ); - +extern void drm_set_orientation( struct drm_t *drm, bool isRotated); +extern drm_t g_DRM; +extern bool panelTypeChanged; uint32_t wlserver_make_new_xwayland_server(); void wlserver_destroy_xwayland_server(gamescope_xwayland_server_t *server);