From 646446c157d55d8508c734cddfc3025834f7cf50 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Fri, 17 May 2024 19:43:49 -0500
Subject: [PATCH 01/21] Add touch-gestures to open up Steam menus

---
 src/main.cpp     |  5 +++++
 src/wlserver.cpp | 28 ++++++++++++++++++++++++++++
 src/wlserver.hpp |  1 +
 3 files changed, 34 insertions(+)

diff --git a/src/main.cpp b/src/main.cpp
index ca40012..3a1b1ae 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -108,6 +108,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 },
@@ -185,6 +187,7 @@ 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"
+	"  --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"
@@ -734,6 +737,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) {
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index ccbd512..26b953a 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -81,6 +81,7 @@ using namespace std::literals;
 extern gamescope::ConVar<bool> cv_drm_debug_disable_explicit_sync;
 
 //#define GAMESCOPE_SWAPCHAIN_DEBUG
+gamescope::ConVar<bool> cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" );
 
 struct wlserver_t wlserver = {
 	.touch_down_ids = {}
@@ -2526,6 +2527,33 @@ 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;
+
+				// Round the x-coordinate to the nearest whole number
+				uint32_t roundedCursorX = static_cast<int>(std::round(tx));
+				// Grab 2% of the display to be used for the edge range
+				uint32_t edge_range = static_cast<uint32_t>(g_nOutputWidth * 0.02);
+
+				// Determine if the gesture should start
+				if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) {
+					start_gesture = true;
+				}
+
+				// Handle Home gesture
+				if (start_gesture && roundedCursorX >= edge_range) {
+					wlserver_open_steam_menu(0);
+					start_gesture = false;
+				}
+
+				// Handle QAM gesture
+				if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth) {
+					wlserver_open_steam_menu(1);
+					start_gesture = false;
+				}
+			}
+
 		}
 		else if ( eMode == gamescope::TouchClickModes::Disabled )
 		{
diff --git a/src/wlserver.hpp b/src/wlserver.hpp
index 0569472..3304c18 100644
--- a/src/wlserver.hpp
+++ b/src/wlserver.hpp
@@ -272,6 +272,7 @@ 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<bool> g_bPendingTouchMovement;
+extern gamescope::ConVar<bool> cv_touch_gestures;
 
 void wlserver_open_steam_menu( bool qam );
 
-- 
2.46.0


From 97c2ed0ea6a5cd43b450e0952db20c08d82bbe60 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Fri, 17 May 2024 20:16:20 -0500
Subject: [PATCH 02/21] Add bypass_steam_resolution to workaround the 720p/800p
 restrictions Steam has for games

---
 src/main.cpp         |  3 +++
 src/steamcompmgr.cpp | 11 +++++++++++
 2 files changed, 14 insertions(+)

diff --git a/src/main.cpp b/src/main.cpp
index 3a1b1ae..534779d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -129,6 +129,8 @@ const struct option *gamescope_options = (struct option[]){
 	{ "fade-out-duration", required_argument, nullptr, 0 },
 	{ "force-orientation", required_argument, nullptr, 0 },
 	{ "force-windows-fullscreen", no_argument, nullptr, 0 },
+	{ "bypass-steam-resolution", no_argument, nullptr, 0 },
+
 
 	{ "disable-color-management", no_argument, nullptr, 0 },
 	{ "sdr-gamut-wideness", required_argument, nullptr, 0 },
@@ -187,6 +189,7 @@ 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"
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
index e997c85..952a5f8 100644
--- a/src/steamcompmgr.cpp
+++ b/src/steamcompmgr.cpp
@@ -356,6 +356,8 @@ bool g_bForceHDR10OutputDebug = false;
 gamescope::ConVar<bool> 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<bool> cv_bypass_steam_resolution{ "bypass_steam_resolution", false, "Workaround the 720p/800p limits Steam uses for games" };
+
 
 static void
 update_color_mgmt()
@@ -5387,6 +5389,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 )
@@ -7370,6 +7379,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) {
-- 
2.46.0


From 234e3bb0369b7ed45a8e2f3c41f3c1e900be9d34 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Wed, 26 Jul 2023 20:46:29 -0500
Subject: [PATCH 03/21] Add force external orientation.

Co-authored-by: Bouke Sybren Haarsma <boukehaarsma23@gmail.com>
---
 src/Backends/DRMBackend.cpp |  5 +++++
 src/main.cpp                | 25 ++++++++++++++++++++++++-
 src/main.hpp                |  1 +
 src/wlserver.cpp            | 23 +++++++++++++++++++++++
 4 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 799fd39..3815b98 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -537,6 +537,7 @@ bool g_bSupportsSyncObjs = false;
 
 extern gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration;
 extern GamescopePanelOrientation g_DesiredInternalOrientation;
+extern GamescopePanelOrientation g_DesiredExternalOrientation;
 
 extern bool g_bForceDisableColorMgmt;
 
@@ -2031,6 +2032,10 @@ namespace gamescope
 		{
 			m_ChosenOrientation = g_DesiredInternalOrientation;
 		}
+		else if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO )
+		{
+			m_ChosenOrientation = g_DesiredExternalOrientation;
+		}
 		else
 		{
 			if ( this->GetProperties().panel_orientation )
diff --git a/src/main.cpp b/src/main.cpp
index 534779d..f9be05e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -128,6 +128,7 @@ 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-windows-fullscreen", no_argument, nullptr, 0 },
 	{ "bypass-steam-resolution", no_argument, nullptr, 0 },
 
@@ -194,6 +195,7 @@ const char usage[] =
 	"  --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-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"
@@ -289,6 +291,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;
@@ -362,7 +366,24 @@ 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);
 	}
 }
@@ -755,6 +776,8 @@ 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, "sharpness") == 0 ||
 						   strcmp(opt_name, "fsr-sharpness") == 0) {
 					g_upscaleFilterSharpness = atoi( optarg );
diff --git a/src/main.hpp b/src/main.hpp
index 2e6fb83..ebd018a 100644
--- a/src/main.hpp
+++ b/src/main.hpp
@@ -28,6 +28,7 @@ extern bool g_bGrabbed;
 
 extern float g_mouseSensitivity;
 extern const char *g_sOutputName;
+extern bool g_bExternalForced;
 
 enum class GamescopeUpscaleFilter : uint32_t
 {
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index 26b953a..837079a 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -2488,6 +2488,29 @@ static void apply_touchscreen_orientation(double *x, double *y )
 			break;
 	}
 
+	// Rotate screen if it's forced with --force-external-orientation
+	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;
+	}
+
 	*x = tx;
 	*y = ty;
 }
-- 
2.46.0


From bc0ddd46fd9db62fbc3fe14e0b1493e35ceb2be8 Mon Sep 17 00:00:00 2001
From: Bouke Sybren Haarsma <boukehaarsma23@gmail.com>
Date: Tue, 12 Mar 2024 00:07:57 +0100
Subject: [PATCH 04/21] implement force-panel-type

---
 src/backend.h          |  3 +++
 src/gamescope_shared.h |  1 +
 src/main.cpp           | 16 ++++++++++++++++
 3 files changed, 20 insertions(+)

diff --git a/src/backend.h b/src/backend.h
index 7d9fb46..08e8268 100644
--- a/src/backend.h
+++ b/src/backend.h
@@ -18,6 +18,7 @@ struct wlr_buffer;
 struct wlr_dmabuf_attributes;
 
 struct FrameInfo_t;
+extern gamescope::GamescopeScreenType g_ForcedScreenType;
 
 namespace gamescope
 {
@@ -221,6 +222,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 5ce8591..d1b7a6e 100644
--- a/src/gamescope_shared.h
+++ b/src/gamescope_shared.h
@@ -25,6 +25,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 f9be05e..dc3e9c8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -129,6 +129,7 @@ const struct option *gamescope_options = (struct option[]){
 	{ "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 },
 
@@ -196,6 +197,7 @@ const char usage[] =
 	"  --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"
@@ -387,6 +389,18 @@ static GamescopePanelOrientation force_external_orientation(const char *str)
 		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) {
+		return gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL;
+	} else {
+		fprintf( stderr, "gamescope: invalid value for --force-panel-type\n" );
+		exit(1);
+	}
+}
 
 static enum GamescopeUpscaleScaler parse_upscaler_scaler(const char *str)
 {
@@ -778,6 +792,8 @@ int main(int argc, char **argv)
 					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, "sharpness") == 0 ||
 						   strcmp(opt_name, "fsr-sharpness") == 0) {
 					g_upscaleFilterSharpness = atoi( optarg );
-- 
2.46.0


From 2a0c92febded984bc0d610eaedc9e8cc4d4ce51a Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Fri, 17 May 2024 21:11:34 -0500
Subject: [PATCH 05/21] wlserver: Fix an issue that would cause gamescope to
 crash when the touchscreen was used

---
 src/wlserver.cpp | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index 837079a..26b953a 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -2488,29 +2488,6 @@ static void apply_touchscreen_orientation(double *x, double *y )
 			break;
 	}
 
-	// Rotate screen if it's forced with --force-external-orientation
-	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;
-	}
-
 	*x = tx;
 	*y = ty;
 }
-- 
2.46.0


From 69bae3bffa954251d6a857b1b6928fc4755e40bf Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Fri, 17 May 2024 21:56:55 -0500
Subject: [PATCH 06/21] Add --custom-refresh-rates

---
 src/Backends/DRMBackend.cpp |  4 ++++
 src/main.cpp                | 30 ++++++++++++++++++++++++++++++
 src/main.hpp                |  2 ++
 3 files changed, 36 insertions(+)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 3815b98..e3512a1 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2135,6 +2135,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;
diff --git a/src/main.cpp b/src/main.cpp
index dc3e9c8..18eb399 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -132,6 +132,7 @@ const struct option *gamescope_options = (struct option[]){
 	{ "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 },
@@ -210,6 +211,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"
@@ -462,6 +464,32 @@ static enum gamescope::GamescopeBackend parse_backend_name(const char *str)
 		fprintf( stderr, "gamescope: invalid value for --backend\n" );
 		exit(1);
 	}
+
+std::vector<uint32_t> g_customRefreshRates;
+// eg: 60,60,90,110-120
+static std::vector<uint32_t> parse_custom_refresh_rates( const char *str )
+{
+	std::vector<uint32_t> 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 = {};
@@ -794,6 +822,8 @@ int main(int argc, char **argv)
 					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 ebd018a..4e09e3b 100644
--- a/src/main.hpp
+++ b/src/main.hpp
@@ -3,6 +3,7 @@
 #include <getopt.h>
 
 #include <atomic>
+#include <vector>
 
 extern const char *gamescope_optstring;
 extern const struct option *gamescope_options;
@@ -29,6 +30,7 @@ extern bool g_bGrabbed;
 extern float g_mouseSensitivity;
 extern const char *g_sOutputName;
 extern bool g_bExternalForced;
+extern std::vector<uint32_t> g_customRefreshRates;
 
 enum class GamescopeUpscaleFilter : uint32_t
 {
-- 
2.46.0


From 5125582bc6fdfc651ca5ccb58cb71d956e2c3676 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sat, 18 May 2024 08:44:38 -0500
Subject: [PATCH 07/21] Add rotation gamescope_control command

---
 protocol/gamescope-control.xml | 18 ++++++++++++
 src/Backends/DRMBackend.cpp    | 24 ++++++++++++++--
 src/gamescope_shared.h         | 18 ++++++++++++
 src/main.cpp                   |  1 +
 src/wlserver.cpp               | 50 ++++++++++++++++++++++++++++++++++
 5 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/protocol/gamescope-control.xml b/protocol/gamescope-control.xml
index 012c48c..eab8a84 100644
--- a/protocol/gamescope-control.xml
+++ b/protocol/gamescope-control.xml
@@ -99,5 +99,23 @@
       <arg name="path" type="string" summary="Path to written screenshot"></arg>
     </event>
 
+
+    <enum name="display_rotation_flag" bitfield="true" since="2">
+      <entry name="normal" value="1"/>
+      <entry name="left" value="2"/>
+      <entry name="right" value="3"/>
+      <entry name="upsidedown" value="4"/>
+    </enum>
+
+    <enum name="display_target_type" since="2">
+      <entry name="internal" value="1"/>
+      <entry name="external" value="2"/>
+    </enum>
+
+    <request name="rotate_display" since="2">
+      <arg name="orientation" type="uint" enum="display_rotation_flag" summary="Set the orientation of the display output."/>
+      <arg name="target_type" type="uint" enum="display_target_type" summary="Internal (1) or External (2) target type."/>
+    </request>
+
   </interface>
 </protocol>
diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index e3512a1..f05c2e8 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2028,7 +2028,9 @@ 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_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO )
+										|| ( GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO
+											&& g_bExternalForced ) )
 		{
 			m_ChosenOrientation = g_DesiredInternalOrientation;
 		}
@@ -2960,8 +2962,26 @@ bool drm_update_color_mgmt(struct drm_t *drm)
 	return true;
 }
 
-int g_nDynamicRefreshHz = 0;
+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);
+}
+
+int g_nDynamicRefreshHz = 0;
 static void drm_unset_mode( struct drm_t *drm )
 {
 	drm->pending.mode_id = 0;
diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h
index d1b7a6e..cffa576 100644
--- a/src/gamescope_shared.h
+++ b/src/gamescope_shared.h
@@ -65,6 +65,24 @@ enum GamescopePanelOrientation
 	GAMESCOPE_PANEL_ORIENTATION_AUTO,
 };
 
+struct GamescopeTimelinePoint
+{
+	struct wlr_drm_syncobj_timeline *pTimeline = nullptr;
+	uint64_t ulPoint = 0;
+
+	void Release();
+};
+
+enum GamescopePanelExternalOrientation
+{
+	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0,   // normal
+	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270, // right
+	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90,  // left
+	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180, // upside down
+
+	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_AUTO,
+};
+
 // Disable partial composition for now until we get
 // composite priorities working in libliftoff + also
 // use the proper libliftoff composite plane system.
diff --git a/src/main.cpp b/src/main.cpp
index 18eb399..675020f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -397,6 +397,7 @@ 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" );
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index 26b953a..c37dbc1 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -1042,6 +1042,55 @@ 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:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0;
+			break;
+		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_90;
+			isRotated = true;
+			break;
+		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_270;
+			isRotated = true;
+			break;
+		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN:
+			//m_ChosenOrientation = 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:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0;
+			break;
+		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90;
+			isRotated = true;
+			break;
+		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270;
+			isRotated = true;
+			break;
+		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN:
+			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180;
+			break;
+		default:
+			wl_log.errorf("Invalid target orientation selected");
+		}
+	}
+	//drm_set_orientation(&g_DRM, isRotated);
+	//g_DRM.out_of_date = 2;
+}
+
 static void gamescope_control_handle_destroy( struct wl_client *client, struct wl_resource *resource )
 {
 	wl_resource_destroy( resource );
@@ -1051,6 +1100,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()
-- 
2.46.0


From 304c747d5eab4b62fe38225d04529ff53142e832 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sat, 18 May 2024 11:54:50 -0500
Subject: [PATCH 08/21] Fix an issue that caused force-panel to not work

---
 protocol/gamescope-control.xml |   1 -
 src/Backends/DRMBackend.cpp    |   3 +
 src/gamescope_shared.h         |  10 ---
 src/wlserver.cpp               | 145 ++++++++++++++++++++-------------
 4 files changed, 90 insertions(+), 69 deletions(-)

diff --git a/protocol/gamescope-control.xml b/protocol/gamescope-control.xml
index eab8a84..7f5578b 100644
--- a/protocol/gamescope-control.xml
+++ b/protocol/gamescope-control.xml
@@ -99,7 +99,6 @@
       <arg name="path" type="string" summary="Path to written screenshot"></arg>
     </event>
 
-
     <enum name="display_rotation_flag" bitfield="true" since="2">
       <entry name="normal" value="1"/>
       <entry name="left" value="2"/>
diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index f05c2e8..663b1e2 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -318,6 +318,9 @@ namespace gamescope
 
 		GamescopeScreenType GetScreenType() const override
 		{
+			if ( g_ForcedScreenType != GAMESCOPE_SCREEN_TYPE_AUTO )
+				return g_ForcedScreenType;
+
 			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 )
diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h
index cffa576..a11f598 100644
--- a/src/gamescope_shared.h
+++ b/src/gamescope_shared.h
@@ -73,16 +73,6 @@ struct GamescopeTimelinePoint
 	void Release();
 };
 
-enum GamescopePanelExternalOrientation
-{
-	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0,   // normal
-	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270, // right
-	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90,  // left
-	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180, // upside down
-
-	GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_AUTO,
-};
-
 // Disable partial composition for now until we get
 // composite priorities working in libliftoff + also
 // use the proper libliftoff composite plane system.
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index c37dbc1..ff534ad 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -82,6 +82,8 @@ extern gamescope::ConVar<bool> cv_drm_debug_disable_explicit_sync;
 
 //#define GAMESCOPE_SWAPCHAIN_DEBUG
 gamescope::ConVar<bool> 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 = {}
@@ -1048,43 +1050,43 @@ static void gamescope_control_rotate_display( struct wl_client *client, struct w
 	if (target_type == GAMESCOPE_CONTROL_DISPLAY_TARGET_TYPE_INTERNAL )
 	{
 		switch (orientation) {
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_NORMAL:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0;
-			break;
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_90;
-			isRotated = true;
-			break;
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_270;
-			isRotated = true;
-			break;
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_180;
-			break;
-		default:
-			wl_log.errorf("Invalid target orientation selected");
+			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:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_0;
-			break;
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_LEFT:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_90;
-			isRotated = true;
-			break;
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_RIGHT:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_270;
-			isRotated = true;
-			break;
-		case GAMESCOPE_CONTROL_DISPLAY_ROTATION_FLAG_UPSIDEDOWN:
-			//m_ChosenOrientation = GAMESCOPE_PANEL_EXTERNAL_ORIENTATION_180;
-			break;
-		default:
-			wl_log.errorf("Invalid target orientation selected");
+			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);
@@ -2512,34 +2514,61 @@ const std::shared_ptr<wlserver_vk_swapchain_feedback>& 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_INTERNAL || g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL )
+    {
+        switch ( g_DesiredInternalOrientation )
+        {
+			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_touchmotion( double x, double y, int touch_id, uint32_t time, bool bAlwaysWarpCursor )
-- 
2.46.0


From 3efb4df00c1e88219a3ff7c47af99b7217d1fe95 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sat, 18 May 2024 13:50:57 -0500
Subject: [PATCH 09/21] Fix an arithmetic error

---
 src/Backends/DRMBackend.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 663b1e2..eafb2f9 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2031,9 +2031,9 @@ namespace gamescope
 
 	void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode )
 	{
-		if ( this->GetScreenType() == ( GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO )
-										|| ( GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO
-											&& g_bExternalForced ) )
+		if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO
+										|| GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO
+											&& g_bExternalForced )
 		{
 			m_ChosenOrientation = g_DesiredInternalOrientation;
 		}
-- 
2.46.0


From dd53e4d18b122cae5383d77fc3f6e79084d8993f Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sat, 18 May 2024 19:04:48 -0500
Subject: [PATCH 10/21] Rework the touch gestures to be more smooth

---
 src/wlserver.cpp | 89 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 69 insertions(+), 20 deletions(-)

diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index ff534ad..593a074 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -80,6 +80,8 @@ using namespace std::literals;
 
 extern gamescope::ConVar<bool> cv_drm_debug_disable_explicit_sync;
 
+bool pending_gesture = false;
+bool pending_osk = false;
 //#define GAMESCOPE_SWAPCHAIN_DEBUG
 gamescope::ConVar<bool> cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" );
 extern GamescopePanelOrientation g_DesiredInternalOrientation;
@@ -362,6 +364,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 );
@@ -2607,32 +2642,46 @@ 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;
-
-				// Round the x-coordinate to the nearest whole number
+			if ( cv_touch_gestures )
+			{
 				uint32_t roundedCursorX = static_cast<int>(std::round(tx));
-				// Grab 2% of the display to be used for the edge range
-				uint32_t edge_range = static_cast<uint32_t>(g_nOutputWidth * 0.02);
-
-				// Determine if the gesture should start
-				if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) {
-					start_gesture = true;
-				}
-
-				// Handle Home gesture
-				if (start_gesture && roundedCursorX >= edge_range) {
+				uint32_t roundedCursorY = static_cast<int>(std::round(ty));
+				uint32_t edge_range_x = static_cast<uint32_t>(g_nOutputWidth * 0.02);
+				uint32_t edge_range_y = static_cast<uint32_t>(g_nOutputWidth * 0.02);
+				uint32_t gesture_limits_x = edge_range_x * 2;
+				uint32_t gesture_limits_y = edge_range_y * 2;
+
+				// Left to Right and Right to Left
+				if (!pending_gesture && roundedCursorX >= 1 && roundedCursorX < edge_range_x ||
+						!pending_gesture && roundedCursorX >= g_nOutputWidth - edge_range_x )
+					pending_gesture = true;
+
+				//left
+				if (pending_gesture && roundedCursorX >= edge_range_x && roundedCursorX < gesture_limits_x) {
 					wlserver_open_steam_menu(0);
-					start_gesture = false;
+					pending_gesture = false;
 				}
-
-				// Handle QAM gesture
-				if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth) {
+				//right
+				if (pending_gesture && roundedCursorX <= g_nOutputWidth - edge_range_x && roundedCursorX > g_nOutputWidth - gesture_limits_x) {
 					wlserver_open_steam_menu(1);
-					start_gesture = false;
+					pending_gesture = false;
+				}
+				// Top to Bottom and Bottom to Top
+				if (!pending_gesture && roundedCursorY >= 1 && roundedCursorY < edge_range_y ||
+						!pending_gesture && roundedCursorY >= g_nOutputHeight - edge_range_y )
+					pending_gesture = true;
+				// Top
+				if (pending_gesture && roundedCursorY >= edge_range_y && roundedCursorY < gesture_limits_y) {
+					pending_gesture = false;
+					// Top to Bottom function to add
+				}
+				// Bottom
+				if (pending_gesture && !pending_osk && roundedCursorY <= g_nOutputWidth - edge_range_y && roundedCursorY > g_nOutputHeight - gesture_limits_y) {
+					pending_gesture = false;
+					pending_osk = true;
+					//wlserver_open_steam_osk(1);
 				}
 			}
-
 		}
 		else if ( eMode == gamescope::TouchClickModes::Disabled )
 		{
-- 
2.46.0


From 70d4ea8937e43752e1adb32ebbcdd3d1a0bcf23c Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sun, 19 May 2024 08:55:28 -0500
Subject: [PATCH 11/21] Fix a typo for --bypass-steam-resolution

---
 src/steamcompmgr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
index 952a5f8..6fceb1d 100644
--- a/src/steamcompmgr.cpp
+++ b/src/steamcompmgr.cpp
@@ -7379,7 +7379,7 @@ 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) {
+				} 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;
-- 
2.46.0


From be8e56d7f3bf251f75da24aa8a99d2e91aa60812 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sun, 19 May 2024 11:48:52 -0500
Subject: [PATCH 12/21] Handle gesture cases better to prevent unexpected
 behavior

---
 src/wlserver.cpp | 63 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 43 insertions(+), 20 deletions(-)

diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index 593a074..833140a 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -80,7 +80,8 @@ using namespace std::literals;
 
 extern gamescope::ConVar<bool> cv_drm_debug_disable_explicit_sync;
 
-bool pending_gesture = false;
+bool pending_gesture_x = false;
+bool pending_gesture_y = false;
 bool pending_osk = false;
 //#define GAMESCOPE_SWAPCHAIN_DEBUG
 gamescope::ConVar<bool> cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" );
@@ -2606,6 +2607,16 @@ static void apply_touchscreen_orientation(double *x, double *y )
     *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() );
@@ -2644,43 +2655,55 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time, bool
 
 			if ( cv_touch_gestures )
 			{
-				uint32_t roundedCursorX = static_cast<int>(std::round(tx));
-				uint32_t roundedCursorY = static_cast<int>(std::round(ty));
-				uint32_t edge_range_x = static_cast<uint32_t>(g_nOutputWidth * 0.02);
-				uint32_t edge_range_y = static_cast<uint32_t>(g_nOutputWidth * 0.02);
+				uint32_t rounded_tx = static_cast<int>(std::round(tx));
+				uint32_t rounded_ty = static_cast<int>(std::round(ty));
+				uint32_t edge_range_x = static_cast<uint32_t>(g_nOutputWidth * 0.05);
+				uint32_t edge_range_y = static_cast<uint32_t>(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 && roundedCursorX >= 1 && roundedCursorX < edge_range_x ||
-						!pending_gesture && roundedCursorX >= g_nOutputWidth - edge_range_x )
-					pending_gesture = true;
+				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 && roundedCursorX >= edge_range_x && roundedCursorX < gesture_limits_x) {
+				if (pending_gesture_x && previous_tx < rounded_tx && rounded_tx >= edge_range_x && rounded_tx < gesture_limits_x) {
 					wlserver_open_steam_menu(0);
-					pending_gesture = false;
+					wlserver_gesture_flush();
 				}
 				//right
-				if (pending_gesture && roundedCursorX <= g_nOutputWidth - edge_range_x && roundedCursorX > g_nOutputWidth - gesture_limits_x) {
+				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);
-					pending_gesture = false;
+					wlserver_gesture_flush();
 				}
-				// Top to Bottom and Bottom to Top
-				if (!pending_gesture && roundedCursorY >= 1 && roundedCursorY < edge_range_y ||
-						!pending_gesture && roundedCursorY >= g_nOutputHeight - edge_range_y )
-					pending_gesture = true;
+
 				// Top
-				if (pending_gesture && roundedCursorY >= edge_range_y && roundedCursorY < gesture_limits_y) {
-					pending_gesture = false;
+				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 && !pending_osk && roundedCursorY <= g_nOutputWidth - edge_range_y && roundedCursorY > g_nOutputHeight - gesture_limits_y) {
-					pending_gesture = false;
+				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 )
-- 
2.46.0


From b31ab55d57f0a8b746d4d0fa7939f6473bcad9a4 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Sun, 19 May 2024 18:14:23 -0500
Subject: [PATCH 13/21] Add references to drm_set_orientation() and g_drm in
 wlserver for rotation gamescope-control

---
 src/wlserver.cpp | 5 +++--
 src/wlserver.hpp | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index 833140a..eb3936a 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -1125,8 +1125,9 @@ static void gamescope_control_rotate_display( struct wl_client *client, struct w
 				wl_log.errorf("Invalid target orientation selected");
 		}
 	}
-	//drm_set_orientation(&g_DRM, isRotated);
-	//g_DRM.out_of_date = 2;
+	drm_set_orientation(&g_DRM, isRotated);
+	GetBackend()->DirtyState( true, true );
+
 }
 
 static void gamescope_control_handle_destroy( struct wl_client *client, struct wl_resource *resource )
diff --git a/src/wlserver.hpp b/src/wlserver.hpp
index 3304c18..0754ee5 100644
--- a/src/wlserver.hpp
+++ b/src/wlserver.hpp
@@ -275,7 +275,8 @@ extern std::atomic<bool> g_bPendingTouchMovement;
 extern gamescope::ConVar<bool> 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;
 uint32_t wlserver_make_new_xwayland_server();
 void wlserver_destroy_xwayland_server(gamescope_xwayland_server_t *server);
 
-- 
2.46.0


From 8df476a6f7d41157e847ee7c3346c796e674d9fe Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Mon, 20 May 2024 07:02:52 -0500
Subject: [PATCH 14/21] Fix an issue where forced panel type orientations
 weren't being applied

---
 src/Backends/DRMBackend.cpp | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index eafb2f9..1ea1221 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2031,20 +2031,19 @@ namespace gamescope
 
 	void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode )
 	{
-		if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO
-										|| GAMESCOPE_SCREEN_TYPE_EXTERNAL && g_DesiredExternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO
-											&& g_bExternalForced )
-		{
+		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 && g_bExternalForced)) {
+			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 )
-		{
+		} 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:
@@ -2066,6 +2065,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
@@ -2073,6 +2073,7 @@ namespace gamescope
 			}
 			else
 			{
+				drm_log.infof("No orientation quirks have been applied");
 				m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0;
 			}
 		}
-- 
2.46.0


From 5d7781dd8ca41f4d865aa336b4e925beeec9349e Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Mon, 20 May 2024 07:25:29 -0500
Subject: [PATCH 15/21] add missing curly bracket...

---
 src/Backends/DRMBackend.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 1ea1221..8d86df0 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2033,7 +2033,7 @@ namespace gamescope
 	{
 		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 && g_bExternalForced)) {
-			drm_log.infof("We are rotating the orientation of the internal or faked external display")
+			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");
-- 
2.46.0


From 0282352468bb6b99e75eb1c24cb3ed555886fff5 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Mon, 20 May 2024 10:17:55 -0500
Subject: [PATCH 16/21] Fix case where real externals were rotated with faked
 external panels

---
 src/Backends/DRMBackend.cpp | 21 +++++++----
 src/wlserver.cpp            | 72 +++++++++++++++++++++++++------------
 2 files changed, 64 insertions(+), 29 deletions(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 8d86df0..0008505 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -318,13 +318,20 @@ namespace gamescope
 
 		GamescopeScreenType GetScreenType() const override
 		{
-			if ( g_ForcedScreenType != GAMESCOPE_SCREEN_TYPE_AUTO )
-				return g_ForcedScreenType;
-
 			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 )
+				{
+					return g_ForcedScreenType;
+				}
+				else
+				{
+					return GAMESCOPE_SCREEN_TYPE_INTERNAL;
+				}
+			}
+
 
 			return GAMESCOPE_SCREEN_TYPE_EXTERNAL;
 		}
@@ -2031,11 +2038,11 @@ namespace gamescope
 
 	void CDRMConnector::UpdateEffectiveOrientation( const drmModeModeInfo *pMode )
 	{
-		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 && g_bExternalForced)) {
+		if ( this->GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL && g_DesiredInternalOrientation != GAMESCOPE_PANEL_ORIENTATION_AUTO ) {
 			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) {
+		}
+		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;
 		}
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index eb3936a..896ec04 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -2555,29 +2555,57 @@ static void apply_touchscreen_orientation(double *x, double *y )
     double ty = 0;
 
     // Use internal screen always for orientation purposes.
-    if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_INTERNAL || g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL )
+    if ( g_ForcedScreenType != gamescope::GAMESCOPE_SCREEN_TYPE_AUTO )
     {
-        switch ( g_DesiredInternalOrientation )
-        {
-			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;
-        }
+		if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL )
+		{
+			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)
     {
-- 
2.46.0


From 276c1220de3542467b6d8e2218dfa1f2ccfc5b79 Mon Sep 17 00:00:00 2001
From: Matthew Anderson <ruinairas1992@gmail.com>
Date: Mon, 20 May 2024 16:30:47 -0500
Subject: [PATCH 17/21] Add verbose panel logs and attempt to address all
 orientation issues

---
 src/Backends/DRMBackend.cpp | 18 ++++++++++++++--
 src/wlserver.cpp            | 41 ++++++++++++++++++++-----------------
 src/wlserver.hpp            |  1 +
 3 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 0008505..bd5ffec 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<bool> cv_drm_single_plane_optimizations( "drm_single_plane_optimizations", true, "Whether or not to enable optimizations for single plane usage." );
 
@@ -324,6 +325,7 @@ namespace gamescope
 			{
 				if ( g_bExternalForced )
 				{
+					panelTypeChanged = true;
 					return g_ForcedScreenType;
 				}
 				else
@@ -332,7 +334,7 @@ namespace gamescope
 				}
 			}
 
-
+			panelTypeChanged = false;
 			return GAMESCOPE_SCREEN_TYPE_EXTERNAL;
 		}
 
@@ -2038,7 +2040,19 @@ 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;
 		}
diff --git a/src/wlserver.cpp b/src/wlserver.cpp
index 896ec04..e5bb2e2 100644
--- a/src/wlserver.cpp
+++ b/src/wlserver.cpp
@@ -2559,26 +2559,29 @@ static void apply_touchscreen_orientation(double *x, double *y )
     {
 		if ( g_ForcedScreenType == gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL )
 		{
-			switch (GetBackend()->GetConnector(gamescope::GAMESCOPE_SCREEN_TYPE_EXTERNAL)->GetCurrentOrientation())
+			if(panelTypeChanged)
 			{
-				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;
+				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
diff --git a/src/wlserver.hpp b/src/wlserver.hpp
index 0754ee5..bdf3b0b 100644
--- a/src/wlserver.hpp
+++ b/src/wlserver.hpp
@@ -277,6 +277,7 @@ extern gamescope::ConVar<bool> 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);
 
-- 
2.46.0


From 11664b820a9065b597d8655e53ade8dd9a29c4bc Mon Sep 17 00:00:00 2001
From: Bouke Sybren Haarsma <boukehaarsma23@gmail.com>
Date: Tue, 28 May 2024 21:56:47 +0200
Subject: [PATCH 18/21] add closing bracket

---
 src/main.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/main.cpp b/src/main.cpp
index 675020f..315b718 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -465,6 +465,7 @@ static enum gamescope::GamescopeBackend parse_backend_name(const char *str)
 		fprintf( stderr, "gamescope: invalid value for --backend\n" );
 		exit(1);
 	}
+}
 
 std::vector<uint32_t> g_customRefreshRates;
 // eg: 60,60,90,110-120
-- 
2.46.0


From 1e5c439eb0b1e809a3ec6414e8d0536db22c9601 Mon Sep 17 00:00:00 2001
From: Bouke Sybren Haarsma <boukehaarsma23@gmail.com>
Date: Wed, 3 Jan 2024 17:03:04 +0100
Subject: [PATCH 19/21] remove hacky texture

This will use more hardware planes, causing some devices to composite yeilding lower framerates
---
 src/steamcompmgr.cpp | 62 ++++++++++++--------------------------------
 1 file changed, 17 insertions(+), 45 deletions(-)

diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
index 6fceb1d..4dbbdaf 100644
--- a/src/steamcompmgr.cpp
+++ b/src/steamcompmgr.cpp
@@ -1620,7 +1620,7 @@ bool MouseCursor::getTexture()
 				{
 					pixels[i * image->width + j] = image->pixels[i * image->width + j];
 				}
-			} 
+			}
 			std::vector<uint32_t> resizeBuffer( nDesiredWidth * nDesiredHeight );
 			stbir_resize_uint8_srgb( (unsigned char *)pixels.data(),       image->width,  image->height,  0,
 									 (unsigned char *)resizeBuffer.data(), nDesiredWidth, nDesiredHeight, 0,
@@ -2306,7 +2306,7 @@ paint_all(bool async)
 					}
 				}
 			}
-			
+
 			int nOldLayerCount = frameInfo.layerCount;
 
 			uint32_t flags = 0;
@@ -2314,7 +2314,7 @@ paint_all(bool async)
 				flags |= PaintWindowFlag::BasePlane;
 			paint_window(w, w, &frameInfo, global_focus.cursor, flags);
 			update_touch_scaling( &frameInfo );
-			
+
 			// paint UI unless it's fully hidden, which it communicates to us through opacity=0
 			// we paint it to extract scaling coefficients above, then remove the layer if one was added
 			if ( w->opacity == TRANSLUCENT && bHasVideoUnderlay && nOldLayerCount < frameInfo.layerCount )
@@ -2327,7 +2327,7 @@ paint_all(bool async)
 				float opacityScale = g_bPendingFade
 					? 0.0f
 					: ((currentTime - fadeOutStartTime) / (float)g_FadeOutDuration);
-		
+
 				paint_cached_base_layer(g_HeldCommits[HELD_COMMIT_FADE], g_CachedPlanes[HELD_COMMIT_FADE], &frameInfo, 1.0f - opacityScale, false);
 				paint_window(w, w, &frameInfo, global_focus.cursor, PaintWindowFlag::BasePlane | PaintWindowFlag::FadeTarget | PaintWindowFlag::DrawBorders, opacityScale, override);
 			}
@@ -2401,34 +2401,6 @@ paint_all(bool async)
 		if ( overlay == global_focus.inputFocusWindow )
 			update_touch_scaling( &frameInfo );
 	}
-	else if ( !GetBackend()->UsesVulkanSwapchain() && GetBackend()->IsSessionBased() )
-	{
-		auto tex = vulkan_get_hacky_blank_texture();
-		if ( tex != nullptr )
-		{
-			// HACK! HACK HACK HACK
-			// To avoid stutter when toggling the overlay on 
-			int curLayer = frameInfo.layerCount++;
-
-			FrameInfo_t::Layer_t *layer = &frameInfo.layers[ curLayer ];
-
-
-			layer->scale.x = g_nOutputWidth == tex->width() ? 1.0f : tex->width() / (float)g_nOutputWidth;
-			layer->scale.y = g_nOutputHeight == tex->height() ? 1.0f : tex->height() / (float)g_nOutputHeight;
-			layer->offset.x = 0.0f;
-			layer->offset.y = 0.0f;
-			layer->opacity = 1.0f; // BLAH
-			layer->zpos = g_zposOverlay;
-			layer->applyColorMgmt = g_ColorMgmt.pending.enabled;
-
-			layer->colorspace = GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR;
-			layer->ctm = nullptr;
-			layer->tex = tex;
-
-			layer->filter = GamescopeUpscaleFilter::NEAREST;
-			layer->blackBorder = true;
-		}
-	}
 
 	if (notification)
 	{
@@ -3014,7 +2986,7 @@ win_maybe_a_dropdown( steamcompmgr_win_t *w )
 	//
 	// TODO: Come back to me for original Age of Empires HD launcher.
 	// Does that use it? It wants blending!
-	// 
+	//
 	// Only do this if we have CONTROLPARENT right now. Some other apps, such as the
 	// Street Fighter V (310950) Splash Screen also use LAYERED and TOOLWINDOW, and we don't
 	// want that to be overlayed.
@@ -3029,12 +3001,12 @@ win_maybe_a_dropdown( steamcompmgr_win_t *w )
 
 	// Josh:
 	// The logic here is as follows. The window will be treated as a dropdown if:
-	// 
+	//
 	// If this window has a fixed position on the screen + static gravity:
 	//  - If the window has either skipPage or skipTaskbar
 	//    - If the window isn't a dialog, always treat it as a dropdown, as it's
 	//      probably meant to be some form of popup.
-	//    - If the window is a dialog 
+	//    - If the window is a dialog
 	// 		- If the window has transient for, disregard it, as it is trying to redirecting us elsewhere
 	//        ie. a settings menu dialog popup or something.
 	//      - If the window has both skip taskbar and pager, treat it as a dialog.
@@ -3126,7 +3098,7 @@ static bool is_good_override_candidate( steamcompmgr_win_t *override, steamcompm
 		return false;
 
 	return override != focus && override->GetGeometry().nX >= 0 && override->GetGeometry().nY >= 0;
-} 
+}
 
 static bool
 pick_primary_focus_and_override(focus_t *out, Window focusControlWindow, const std::vector<steamcompmgr_win_t*>& vecPossibleFocusWindows, bool globalFocus, const std::vector<uint32_t>& ctxFocusControlAppIDs)
@@ -3267,7 +3239,7 @@ found:;
 
 	if ( focus )
 	{
-		if ( window_has_commits( focus ) ) 
+		if ( window_has_commits( focus ) )
 			out->focusWindow = focus;
 		else
 			focus->outdatedInteractiveFocus = true;
@@ -3310,9 +3282,9 @@ found:;
 					override_focus = fake_override;
 					goto found2;
 				}
-			}	
+			}
 		}
-		
+
 		found2:;
 		resolveTransientOverrides( true );
 	}
@@ -4574,7 +4546,7 @@ finish_destroy_win(xwayland_ctx_t *ctx, Window id, bool gone)
 		{
 			if (gone)
 				finish_unmap_win (ctx, w);
-			
+
 			{
 				std::unique_lock lock( ctx->list_mutex );
 				*prev = w->xwayland().next;
@@ -4631,7 +4603,7 @@ destroy_win(xwayland_ctx_t *ctx, Window id, bool gone, bool fade)
 		global_focus.overrideWindow = nullptr;
 	if (x11_win(global_focus.fadeWindow) == id && gone)
 		global_focus.fadeWindow = nullptr;
-		
+
 	MakeFocusDirty();
 
 	finish_destroy_win(ctx, id, gone);
@@ -5243,7 +5215,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
 		{
 			get_win_type(ctx, w);
 			MakeFocusDirty();
-		}		
+		}
 	}
 	if (ev->atom == ctx->atoms.sizeHintsAtom)
 	{
@@ -6153,7 +6125,7 @@ void handle_done_commits_xdg( bool vblank, uint64_t vblank_idx )
 			commits_before_their_time.push_back( entry );
 			continue;
 		}
-		
+
 		if (!entry.earliestPresentTime)
 		{
 			entry.earliestPresentTime = next_refresh_time;
@@ -7143,7 +7115,7 @@ void update_mode_atoms(xwayland_ctx_t *root_ctx, bool* needs_flush = nullptr)
 	}
 	XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeDisplayModeListExternal, XA_STRING, 8, PropModeReplace,
 		(unsigned char *)modes, strlen(modes) + 1 );
-	
+
 	uint32_t one = 1;
 	XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeDisplayIsExternal, XA_CARDINAL, 32, PropModeReplace,
 		(unsigned char *)&one, 1 );
@@ -7975,7 +7947,7 @@ void steamcompmgr_send_frame_done_to_focus_window()
 	{
 		wlserver_lock();
 		wlserver_send_frame_done( global_focus.focusWindow->xwayland().surface.main_surface , &now );
-		wlserver_unlock();		
+		wlserver_unlock();
 	}
 }
 
-- 
2.46.0


From 672fd75c5ac7bb1226c418270fa134ad6fe07b30 Mon Sep 17 00:00:00 2001
From: Kyle Gospodnetich <me@kylegospodneti.ch>
Date: Tue, 2 Jul 2024 14:12:47 -0700
Subject: [PATCH 20/21] Only change refresh rates on internal displays

---
 src/Backends/DRMBackend.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index bd5ffec..03b9b4c 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2162,7 +2162,7 @@ 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 ) {
+		if ( g_customRefreshRates.size() > 0 && GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL ) {
 					m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates);
 					return;
 		}
-- 
2.46.0


From 0cada655064b2bb2c0b5540f4c249a3c6a1254ed Mon Sep 17 00:00:00 2001
From: Kyle Gospodnetich <me@kylegospodneti.ch>
Date: Tue, 2 Jul 2024 15:14:23 -0700
Subject: [PATCH 21/21] Also check g_bExternalForced

---
 src/Backends/DRMBackend.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
index 03b9b4c..24e81b1 100644
--- a/src/Backends/DRMBackend.cpp
+++ b/src/Backends/DRMBackend.cpp
@@ -2162,9 +2162,9 @@ 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 ) {
-					m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates);
-					return;
+		if ( g_customRefreshRates.size() > 0 && ( GetScreenType() == GAMESCOPE_SCREEN_TYPE_INTERNAL || g_bExternalForced ) ) {
+			m_Mutable.ValidDynamicRefreshRates = std::span(g_customRefreshRates);
+			return;
 		}
 		if ( bSteamDeckDisplay )
 		{
-- 
2.46.0