diff --git a/spec_files/gamescope/chimeraos.patch b/spec_files/gamescope/chimeraos.patch index 3beb4230..ade6f559 100644 --- a/spec_files/gamescope/chimeraos.patch +++ b/spec_files/gamescope/chimeraos.patch @@ -1,5 +1,202 @@ +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 ) { ++ 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..037a22f 100644 +index 59dec4f..119e043 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -107,6 +107,8 @@ const struct option *gamescope_options = (struct option[]){ @@ -11,15 +208,124 @@ index 59dec4f..037a22f 100644 // steamcompmgr options { "cursor", required_argument, nullptr, 0 }, -@@ -184,6 +186,7 @@ const char usage[] = +@@ -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" -@@ -766,6 +769,8 @@ int main(int argc, char **argv) ++ " --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; @@ -28,61 +334,417 @@ index 59dec4f..037a22f 100644 } 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..e4ea445 100644 +index d9182aa..776e014 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp -@@ -73,6 +73,7 @@ - static LogScope wl_log("wlserver"); +@@ -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 = {} -@@ -2534,6 +2535,33 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool +@@ -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) { -+ bool start_gesture = 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; + -+ // Round the x-coordinate to the nearest whole number -+ uint32_t roundedCursorX = static_cast(std::round(tx)); -+ // Grab 2% of the display to be used for the edge range -+ uint32_t edge_range = static_cast(g_nOutputWidth * 0.02); -+ -+ // Determine if the gesture should start -+ if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) { -+ start_gesture = true; ++ // 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; ++ } + } + -+ // Handle Home gesture -+ if (start_gesture && roundedCursorX >= edge_range) { ++ // 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); -+ start_gesture = false; ++ wlserver_gesture_flush(); + } -+ -+ // Handle QAM gesture -+ if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth) { ++ //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); -+ start_gesture = false; ++ 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..0d7759e 100644 +index 0d8f3ac..07675fe 100644 --- a/src/wlserver.hpp +++ b/src/wlserver.hpp -@@ -287,6 +287,7 @@ void wlserver_x11_surface_info_finish( struct wlserver_x11_surface_info *surf ); +@@ -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);