diff --git a/anda/apps/envision/anda.hcl b/anda/apps/envision/anda.hcl
index f32f6eeee0..63688a4d13 100644
--- a/anda/apps/envision/anda.hcl
+++ b/anda/apps/envision/anda.hcl
@@ -2,4 +2,7 @@ project pkg {
     rpm {
         spec = "envision.spec"
     }
+    labels {
+        nightly = 1
+    }
 }
diff --git a/anda/apps/envision/envision.spec b/anda/apps/envision/envision.spec
index 32c807a611..95a27412e5 100644
--- a/anda/apps/envision/envision.spec
+++ b/anda/apps/envision/envision.spec
@@ -1,5 +1,5 @@
-%global commit 56f2e0b0d6378fcf31d43d349c1e5ff57b6eb985
-%global commit_date 20240729
+%global commit 5eb42335e0ccbf3558b1d285348d7c02906c931d
+%global commit_date 20240801
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
 
 Name:           envision
@@ -17,6 +17,7 @@ BuildRequires:  pkgconfig(vte-2.91-gtk4) >= 0.72.0
 BuildRequires:  pkgconfig(libadwaita-1)
 BuildRequires:  pkgconfig(libusb-1.0)
 BuildRequires:  openssl-devel
+BuildRequires:  openxr-devel
 BuildRequires:  libappstream-glib
 BuildRequires:  desktop-file-utils
 BuildRequires:  glib2-devel
diff --git a/anda/apps/komikku/anda.hcl b/anda/apps/komikku/anda.hcl
new file mode 100644
index 0000000000..1c08b38522
--- /dev/null
+++ b/anda/apps/komikku/anda.hcl
@@ -0,0 +1,5 @@
+project pkg {
+  rpm {
+	  spec = "komikku.spec"
+  }
+}
diff --git a/anda/apps/komikku/komikku.spec b/anda/apps/komikku/komikku.spec
new file mode 100644
index 0000000000..f8b70c5901
--- /dev/null
+++ b/anda/apps/komikku/komikku.spec
@@ -0,0 +1,109 @@
+%global appname Komikku
+%global uuid    info.febvre.%{appname}
+%global gtk4_version        4.14.4
+%global libadwaita_version  1.5.1
+%global pure_protobuf_version 2.0.0
+
+Name:           komikku
+Version:        1.51.1
+%forgemeta
+Release:        %autorelease
+Summary:        A manga reader for GNOME
+
+BuildArch:      noarch
+
+License:        GPL-3.0-or-later
+URL:            https://valos.gitlab.io/Komikku
+Source0:        https://codeberg.org/valos/%{appname}/archive/v%{version}.tar.gz#/%{name}-v%{version}.tar.gz
+
+BuildRequires:  desktop-file-utils
+BuildRequires:  intltool
+BuildRequires:  libappstream-glib
+BuildRequires:  meson >= 0.59.0
+BuildRequires:  python3-devel >= 3.8
+BuildRequires:  blueprint-compiler
+
+BuildRequires:  pkgconfig(gobject-introspection-1.0) >= 1.35.9
+BuildRequires:  pkgconfig(gtk4) >= %{gtk4_version}
+BuildRequires:  pkgconfig(libadwaita-1) >= %{libadwaita_version}
+
+Requires:       hicolor-icon-theme
+Requires:       gtk4 >= %{gtk4_version}
+Requires:       libadwaita >= %{libadwaita_version}
+Requires:       libnotify
+Requires:       webkitgtk6.0
+Requires:       python3-beautifulsoup4
+Requires:       python3-brotli
+Requires:       python3-colorthief
+Requires:       python3-dateparser  %dnl >= 1.1.4 | https://bugzilla.redhat.com/show_bug.cgi?id=2115204
+Requires:       python3-emoji
+Requires:       python3-gobject
+Requires:       python3-keyring >= 21.6.0
+Requires:       python3-lxml
+Requires:       python3-natsort
+Requires:       python3-file-magic
+Requires:       python3-piexif
+Requires:       python3-pillow
+Requires:       python3-pillow-heif
+Requires:       python3-pure-protobuf >= %{pure_protobuf_version}
+Requires:       python3-rarfile
+Requires:       python3-requests
+Requires:       python3-unidecode
+
+%description
+Komikku is a manga reader for GNOME. It focuses on providing a clean, intuitive
+and adaptive interface.
+
+Keys features
+
+* Online reading from dozens of servers
+* Offline reading of downloaded comics
+* Categories to organize your library
+* RTL, LTR, Vertical and Webtoon reading modes
+* Several types of navigation:
+  * Keyboard arrow keys
+  * Right and left navigation layout via mouse click or tapping
+    (touchpad/touch screen)
+  * Mouse wheel
+  * 2-fingers swipe gesture (touchpad)
+  * Swipe gesture (touch screen)
+* Automatic update of comics
+* Automatic download of new chapters
+* Reading history
+* Light and dark themes
+
+%prep
+%autosetup -n %{name} -p1
+
+
+%build
+%meson
+%meson_build
+
+
+%install
+%meson_install
+%find_lang %{name}
+
+
+%check
+appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/*.xml
+desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
+
+
+%files -f %{name}.lang
+%license LICENSE
+%doc README.md
+%{_bindir}/%{name}
+%{_datadir}/%{name}/
+%{_datadir}/applications/*.desktop
+%{_datadir}/glib-2.0/schemas/*.gschema.xml
+%{_datadir}/icons/hicolor/scalable/*/*.svg
+%{_datadir}/icons/hicolor/symbolic/*/*.svg
+%{_metainfodir}/*.xml
+%{python3_sitelib}/%{name}/
+
+
+%changelog
+* Thu Jul 11 2024 Trung LĂȘ <8@tle.id.au> - 1.51.1-0
+- Initial RPM package
diff --git a/anda/apps/komikku/update.rhai b/anda/apps/komikku/update.rhai
new file mode 100644
index 0000000000..c0a1b21997
--- /dev/null
+++ b/anda/apps/komikku/update.rhai
@@ -0,0 +1,3 @@
+let latest_tag = get("https://codeberg.org/api/v1/repos/valos/Komikku/tags").json_arr()[0].name;
+let new_version = find("([\\.\\d]+)", latest_tag, 1);
+rpm.version(new_version);
diff --git a/anda/apps/mpv/mpv-nightly.spec b/anda/apps/mpv/mpv-nightly.spec
index 6f7cc821d8..df573ede97 100644
--- a/anda/apps/mpv/mpv-nightly.spec
+++ b/anda/apps/mpv/mpv-nightly.spec
@@ -1,7 +1,7 @@
-%global commit e509ec0aaffce74e520702e16e3e21ea0f168940
+%global commit dcb4b491b2077be370cf5295ff4021d1e6182817
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
-%global commit_date 20240720
-%global ver v0.38.0
+%global commit_date 20240801
+%global ver 0.38.0
 
 Name:           mpv-nightly
 Version:        %ver^%commit_date.%shortcommit
diff --git a/anda/apps/mpv/update.rhai b/anda/apps/mpv/update.rhai
index 09c2703292..46267aad43 100644
--- a/anda/apps/mpv/update.rhai
+++ b/anda/apps/mpv/update.rhai
@@ -1,7 +1,11 @@
 if filters.contains("nightly") {
 	rpm.global("commit", gh_commit("mpv-player/mpv"));
 	if rpm.changed() {
-		rpm.global("ver", gh("mpv-player/mpv"));
+		let v = gh("mpv-player/mpv");
+		if v.starts_with("v") {
+			v.crop(1);
+		}
+		rpm.global("ver", v);
 		rpm.global("commit_date", date());
 		rpm.release();
 	}
diff --git a/anda/devs/flow/anda.hcl b/anda/devs/flow/anda.hcl
new file mode 100644
index 0000000000..7542fe48ea
--- /dev/null
+++ b/anda/devs/flow/anda.hcl
@@ -0,0 +1,8 @@
+project pkg {
+    rpm {
+        spec = "flow-control-nightly.spec"
+    }
+    labels {
+        nightly = 1
+    }
+}
diff --git a/anda/devs/flow/flow-control-nightly.spec b/anda/devs/flow/flow-control-nightly.spec
new file mode 100644
index 0000000000..57626d7368
--- /dev/null
+++ b/anda/devs/flow/flow-control-nightly.spec
@@ -0,0 +1,30 @@
+%global commit 3400554f57b770760cb4cb58f15963399d13848e
+%global shortcommit %(c=%{commit}; echo ${c:0:7})
+%global commit_date 20240801
+
+Name:           flow-control-nightly
+Version:        %commit_date.%shortcommit
+Release:        1%?dist
+Summary:        A programmer's text editor 
+License:        MIT
+URL:            https://github.com/neurocyte/flow
+Source0:        %url/archive/%commit.tar.gz
+BuildRequires:  zig
+Provides:       flow = %version-%release
+
+%description
+%summary.
+
+%prep
+%autosetup -n flow-%commit
+
+%build
+zig build -Doptimize=ReleaseFast --release=fast
+
+%install
+install -Dpm755 zig-out/bin/flow %buildroot%_bindir/flow
+
+%files
+%doc README.md help.md
+%license LICENSE
+%_bindir/flow
diff --git a/anda/devs/flow/update.rhai b/anda/devs/flow/update.rhai
new file mode 100644
index 0000000000..31f698e529
--- /dev/null
+++ b/anda/devs/flow/update.rhai
@@ -0,0 +1,7 @@
+if filters.contains("nightly") {
+    rpm.global("commit", gh_commit("neurocyte/flow"));
+    if rpm.changed() {
+        rpm.release();
+        rpm.global("commit_date", date());
+    }
+}
diff --git a/anda/devs/tracy/anda.hcl b/anda/devs/tracy/anda.hcl
new file mode 100644
index 0000000000..e5feed24c8
--- /dev/null
+++ b/anda/devs/tracy/anda.hcl
@@ -0,0 +1,5 @@
+project pkg {
+    rpm {
+        spec = "tracy.spec"
+    }
+}
\ No newline at end of file
diff --git a/anda/devs/tracy/tracy.spec b/anda/devs/tracy/tracy.spec
new file mode 100644
index 0000000000..85d6005b0a
--- /dev/null
+++ b/anda/devs/tracy/tracy.spec
@@ -0,0 +1,77 @@
+%global _desc Tracy is a real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.
+
+Name:			tracy
+Version:		0.11.0
+Release:		1%?dist
+Summary:		A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.
+License:		BSD-3-Clause
+URL:			https://github.com/wolfpld/tracy
+Source0:		https://github.com/wolfpld/tracy/archive/refs/tags/v%version.tar.gz
+BuildRequires:  pkgconfig(egl) pkgconfig(glfw3) pkgconfig(freetype2) pkgconfig(dbus-1) pkgconfig(libunwind) pkgconfig(libdebuginfod) pkgconfig(tbb) pkgconfig(wayland-client) pkgconfig(wayland-protocols) pkgconfig(xkbcommon) pkgconfig(capstone)
+BuildRequires:  cmake gcc gcc-c++ meson
+
+%description
+%_desc
+
+%package devel
+Summary: Development files for the tracy package
+
+%description devel
+%_desc
+This package contains the development files for the tracy package.
+
+%prep
+%autosetup -p1
+
+%build
+%meson
+%meson_build
+
+for project in capture csvexport import-chrome import-fuchsia update profiler
+do
+    pushd $project
+    %cmake -DDOWNLOAD_CAPSTONE=0
+    %cmake_build
+    popd
+done
+
+%install
+%meson_install
+
+# NOTE: the subprojects don't have install targets so we do it manually
+install -Dm755 capture/%__cmake_builddir/tracy-capture %buildroot%_bindir/tracy-capture
+install -Dm755 csvexport/%__cmake_builddir/tracy-csvexport %buildroot%_bindir/tracy-csvexport
+install -Dm755 import-chrome/%__cmake_builddir/tracy-import-chrome %buildroot%_bindir/tracy-import-chrome
+install -Dm755 import-fuchsia/%__cmake_builddir/tracy-import-fuchsia %buildroot%_bindir/tracy-import-fuchsia
+install -Dm755 update/%__cmake_builddir/tracy-update %buildroot%_bindir/tracy-update
+install -Dm755 profiler/%__cmake_builddir/tracy-profiler %buildroot%_bindir/tracy
+
+install -Dm644 extra/desktop/tracy.desktop %buildroot%_datadir/applications/tracy.desktop
+install -Dm644 icon/icon.svg %buildroot%_iconsdir/hicolor/scalable/apps/tracy.svg
+install -Dm644 extra/desktop/application-tracy.xml %buildroot%_datadir/mime/packages/application-tracy.xml
+install -Dm644 icon/application-tracy.svg %buildroot%_iconsdir/hicolor/scalable/apps/application-tracy.svg
+
+%files
+%license LICENSE
+%doc README.*
+%_bindir/tracy
+%_bindir/tracy-capture
+%_bindir/tracy-csvexport
+%_bindir/tracy-import-chrome
+%_bindir/tracy-import-fuchsia
+%_bindir/tracy-update
+%_libdir/libtracy.so
+%_datadir/applications/tracy.desktop
+%_datadir/mime/packages/application-tracy.xml
+%_iconsdir/hicolor/scalable/apps/tracy.svg
+%_iconsdir/hicolor/scalable/apps/application-tracy.svg
+
+%files devel
+%_libdir/pkgconfig/tracy.pc
+%_includedir/common
+%_includedir/tracy
+%_includedir/client
+
+%changelog
+* Wed Jul 24 2024 Owen Zimmerman <owen@fyralabs.com> - 0.11-1
+- Initial package.
diff --git a/anda/devs/tracy/update.rhai b/anda/devs/tracy/update.rhai
new file mode 100644
index 0000000000..335ed1a232
--- /dev/null
+++ b/anda/devs/tracy/update.rhai
@@ -0,0 +1 @@
+rpm.version(gh("wolfpld/tracy"));
\ No newline at end of file
diff --git a/anda/devs/zed/nightly/zed-nightly.spec b/anda/devs/zed/nightly/zed-nightly.spec
index 2fb284ce42..82f235ab58 100644
--- a/anda/devs/zed/nightly/zed-nightly.spec
+++ b/anda/devs/zed/nightly/zed-nightly.spec
@@ -1,7 +1,7 @@
-%global commit 0129d4e2506d5ec5e50ef0968382770b9abec390
+%global commit 33d06c4d9603b37885e7ddcc82fa06f4dcd83943
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
-%global commit_date 20240619
-%global ver 0.142.0
+%global commit_date 20240801
+%global ver 0.148.0
 
 %bcond_with check
 
diff --git a/anda/devs/zed/preview/update.rhai b/anda/devs/zed/preview/update.rhai
index a17dfec8da..9a764d5fbc 100644
--- a/anda/devs/zed/preview/update.rhai
+++ b/anda/devs/zed/preview/update.rhai
@@ -1,8 +1,5 @@
 let releases = "https://api.github.com/repos/zed-industries/zed/releases".get().json_arr();
 for release in releases {
-  if !release.prerelease {
-    continue;
-  }
   let tag = release.tag_name;
   tag.pop(4); // remove the "-pre" suffix
   tag.crop(1); // remove "v"
diff --git a/anda/devs/zed/preview/zed-preview.spec b/anda/devs/zed/preview/zed-preview.spec
index 4e25f4cd61..1248f34b94 100644
--- a/anda/devs/zed/preview/zed-preview.spec
+++ b/anda/devs/zed/preview/zed-preview.spec
@@ -1,6 +1,6 @@
 %bcond_with check
 
-%global ver 0.146.2
+%global ver 0.147.1
 # Exclude input files from mangling
 %global __brp_mangle_shebangs_exclude_from ^/usr/src/.*$
 # Use Mold as the linker
diff --git a/anda/devs/zed/stable/zed.spec b/anda/devs/zed/stable/zed.spec
index dca527aca2..4f2d412e79 100644
--- a/anda/devs/zed/stable/zed.spec
+++ b/anda/devs/zed/stable/zed.spec
@@ -9,7 +9,7 @@
 %global app_id dev.zed.Zed
 
 Name:           zed
-Version:        0.145.1
+Version:        0.146.4
 Release:        1%?dist
 Summary:        Zed is a high-performance, multiplayer code editor
 
diff --git a/anda/games/gamescope-session/gamescope-session.spec b/anda/games/gamescope-session/gamescope-session.spec
index 9e9926d115..29e0da1820 100644
--- a/anda/games/gamescope-session/gamescope-session.spec
+++ b/anda/games/gamescope-session/gamescope-session.spec
@@ -1,8 +1,8 @@
 %define debug_package %nil
 
-%global commit 4aa204e6ef332457d277488ffa61959f2dcde470
+%global commit 26818c6cd574c74ab9dfd2c89f081f30430e212b
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
-%global commit_date 20240719
+%global commit_date 20240801
 
 Name:           gamescope-session
 Version:        %commit_date.%shortcommit
diff --git a/anda/games/gamescope/1231.patch b/anda/games/gamescope/1231.patch
new file mode 100644
index 0000000000..e21cb0a8ea
--- /dev/null
+++ b/anda/games/gamescope/1231.patch
@@ -0,0 +1,239 @@
+From ab115896be1a448bde0eb7673c26300ea4ca5040 Mon Sep 17 00:00:00 2001
+From: sharkautarch <128002472+sharkautarch@users.noreply.github.com>
+Date: Sun, 19 May 2024 20:15:36 -0400
+Subject: [PATCH 1/2] QueuePresent: canBypassXWayland(): fetch multiple xcb
+ cookies initially before waiting on any of them
+
+---
+ layer/VkLayer_FROG_gamescope_wsi.cpp |   1 +
+ layer/xcb_helpers.hpp                | 105 +++++++++++++++++++++++----
+ 2 files changed, 93 insertions(+), 13 deletions(-)
+
+diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp
+index 5844c2a63..ca44849f2 100644
+--- a/layer/VkLayer_FROG_gamescope_wsi.cpp
++++ b/layer/VkLayer_FROG_gamescope_wsi.cpp
+@@ -975,6 +975,7 @@ namespace GamescopeWSILayer {
+             continue;
+           }
+ 
++          xcb::Prefetcher prefetcher(gamescopeSurface->connection, gamescopeSurface->window);
+           const bool canBypass = gamescopeSurface->canBypassXWayland();
+           if (canBypass != gamescopeSwapchain->isBypassingXWayland)
+             UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR);
+diff --git a/layer/xcb_helpers.hpp b/layer/xcb_helpers.hpp
+index 8fac5635b..72d0ec092 100644
+--- a/layer/xcb_helpers.hpp
++++ b/layer/xcb_helpers.hpp
+@@ -4,22 +4,106 @@
+ #include <xcb/composite.h>
+ #include <cstdio>
+ #include <optional>
++#include <pthread.h>
+ 
+ namespace xcb {
++  inline static constinit pthread_t g_cache_tid; //incase g_cache could otherwise be accessed by one thread, while it is being deleted by another thread
++  inline static constinit struct cookie_cache_t {
++    xcb_window_t window;
++    std::tuple<xcb_get_geometry_cookie_t, xcb_query_tree_cookie_t> cached_cookies;
++    std::tuple<xcb_get_geometry_reply_t*, xcb_query_tree_reply_t*> cached_replies;
++  } g_cache = {};
++  
++  //Note: this class is currently only meant to be used within GamescopeWSILayer::VkDeviceOverrides::QueuePresentKHR:
++  struct Prefetcher {
++    explicit Prefetcher(xcb_connection_t* connection, const xcb_window_t window) {
++        g_cache = {
++            .window = window,
++            .cached_cookies = {
++                xcb_get_geometry(connection, window),
++                xcb_query_tree(connection, window)
++            }
++        };
++        g_cache_tid = pthread_self();
++    }
+ 
++    ~Prefetcher() {
++        g_cache_tid = {};
++        free(std::get<0>(g_cache.cached_replies));
++        free(std::get<1>(g_cache.cached_replies));
++        g_cache.cached_replies = {nullptr,nullptr};
++    }
++  };
++  
+   struct ReplyDeleter {
++    const bool m_bOwning = true;
++    consteval ReplyDeleter(bool bOwning = true) : m_bOwning{bOwning} {}
+     template <typename T>
+     void operator()(T* ptr) const {
+-      free(const_cast<std::remove_const_t<T>*>(ptr));
++      if (m_bOwning)
++        free(const_cast<std::remove_const_t<T>*>(ptr));
+     }
+   };
+ 
+   template <typename T>
+   using Reply = std::unique_ptr<T, ReplyDeleter>;
++  
++  template <typename Cookie_RetType, typename Reply_RetType, typename XcbConn=xcb_connection_t*, typename... Args>
++  class XcbFetch {
++    using cookie_f_ptr_t = Cookie_RetType (*)(XcbConn, Args...);
++    using reply_f_ptr_t = Reply_RetType* (*)(XcbConn, Cookie_RetType, xcb_generic_error_t**);
++    
++    const cookie_f_ptr_t m_cookieFunc;
++    const reply_f_ptr_t m_replyFunc;
++    
++    public:
++        consteval XcbFetch(cookie_f_ptr_t cookieFunc, reply_f_ptr_t replyFunc) : m_cookieFunc{cookieFunc}, m_replyFunc{replyFunc} {}
++        
++        inline Reply<Reply_RetType> operator()(XcbConn conn, auto... args) { //have to use auto for argsTwo, since otherwise there'd be a type deduction conflict
++            return Reply<Reply_RetType> { m_replyFunc(conn, m_cookieFunc(conn, args...), nullptr) };
++        }
++  };
++  
++  template <typename CookieType>
++  concept CacheableCookie = std::is_same<CookieType, xcb_get_geometry_cookie_t>::value 
++                            || std::is_same<CookieType, xcb_query_tree_cookie_t>::value;
++  
++  template <CacheableCookie Cookie_RetType, typename Reply_RetType>
++  class XcbFetch<Cookie_RetType, Reply_RetType, xcb_connection_t*, xcb_window_t> {
++    using cookie_f_ptr_t = Cookie_RetType (*)(xcb_connection_t*, xcb_window_t);
++    using reply_f_ptr_t = Reply_RetType* (*)(xcb_connection_t*, Cookie_RetType, xcb_generic_error_t**);
++    
++    const cookie_f_ptr_t m_cookieFunc;
++    const reply_f_ptr_t m_replyFunc;
++    
++    inline Reply<Reply_RetType> getCachedReply(xcb_connection_t* connection) {
++        if (std::get<Reply_RetType*>(g_cache.cached_replies) == nullptr) {
++            std::get<Reply_RetType*>(g_cache.cached_replies) = m_replyFunc(connection, std::get<Cookie_RetType>(g_cache.cached_cookies), nullptr);
++        }
+ 
++        return Reply<Reply_RetType>{std::get<Reply_RetType*>(g_cache.cached_replies), ReplyDeleter{false}}; // return 'non-owning' unique_ptr
++    }
++    
++    public:
++        consteval XcbFetch(cookie_f_ptr_t cookieFunc, reply_f_ptr_t replyFunc) : m_cookieFunc{cookieFunc}, m_replyFunc{replyFunc} {}
++        
++        inline Reply<Reply_RetType> operator()(xcb_connection_t* conn, xcb_window_t window) {
++            const bool tryCached = pthread_equal(g_cache_tid, pthread_self())
++                                   && g_cache.window == window;
++            if (!tryCached) [[unlikely]]
++                return Reply<Reply_RetType> { m_replyFunc(conn, m_cookieFunc(conn, window), nullptr) };
++            
++            auto ret = getCachedReply(conn);
++            #if !defined(NDEBUG) || NDEBUG == 0
++            if (!ret)
++                fprintf(stderr, "[Gamescope WSI] getCachedReply() failed.\n");
++            #endif
++            return ret;
++        }
++  };
++ 
+   static std::optional<xcb_atom_t> getAtom(xcb_connection_t* connection, std::string_view name) {
+-    xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, false, name.length(), name.data());
+-    auto reply = Reply<xcb_intern_atom_reply_t>{ xcb_intern_atom_reply(connection, cookie, nullptr) };
++    auto reply = XcbFetch{xcb_intern_atom, xcb_intern_atom_reply}(connection, false, name.length(), name.data());
+     if (!reply) {
+       fprintf(stderr, "[Gamescope WSI] Failed to get xcb atom.\n");
+       return std::nullopt;
+@@ -34,8 +118,7 @@ namespace xcb {
+ 
+     xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
+ 
+-    xcb_get_property_cookie_t cookie = xcb_get_property(connection, false, screen->root, atom, XCB_ATOM_CARDINAL, 0, sizeof(T) / sizeof(uint32_t));
+-    auto reply = Reply<xcb_get_property_reply_t>{ xcb_get_property_reply(connection, cookie, nullptr) };
++    auto reply = XcbFetch{xcb_get_property, xcb_get_property_reply}(connection, false, screen->root, atom, XCB_ATOM_CARDINAL, 0, sizeof(T) / sizeof(uint32_t));
+     if (!reply) {
+       fprintf(stderr, "[Gamescope WSI] Failed to read T root window property.\n");
+       return std::nullopt;
+@@ -61,8 +144,7 @@ namespace xcb {
+ 
+   static std::optional<xcb_window_t> getToplevelWindow(xcb_connection_t* connection, xcb_window_t window) {
+     for (;;) {
+-      xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, window);
+-      auto reply = Reply<xcb_query_tree_reply_t>{ xcb_query_tree_reply(connection, cookie, nullptr) };
++      auto reply = XcbFetch{xcb_query_tree, xcb_query_tree_reply}(connection, window);
+ 
+       if (!reply) {
+         fprintf(stderr, "[Gamescope WSI] getToplevelWindow: xcb_query_tree failed for window 0x%x.\n", window);
+@@ -77,8 +159,7 @@ namespace xcb {
+   }
+ 
+   static std::optional<VkRect2D> getWindowRect(xcb_connection_t* connection, xcb_window_t window) {
+-    xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connection, window);
+-    auto reply = Reply<xcb_get_geometry_reply_t>{ xcb_get_geometry_reply(connection, cookie, nullptr) };
++    auto reply = XcbFetch{xcb_get_geometry, xcb_get_geometry_reply}(connection, window);
+     if (!reply) {
+       fprintf(stderr, "[Gamescope WSI] getWindowRect: xcb_get_geometry failed for window 0x%x.\n", window);
+       return std::nullopt;
+@@ -112,8 +193,7 @@ namespace xcb {
+   static std::optional<VkExtent2D> getLargestObscuringChildWindowSize(xcb_connection_t* connection, xcb_window_t window) {
+     VkExtent2D largestExtent = {};
+ 
+-    xcb_query_tree_cookie_t cookie = xcb_query_tree(connection, window);
+-    auto reply = Reply<xcb_query_tree_reply_t>{ xcb_query_tree_reply(connection, cookie, nullptr) };
++    auto reply = XcbFetch{xcb_query_tree, xcb_query_tree_reply}(connection, window);
+ 
+     if (!reply) {
+       fprintf(stderr, "[Gamescope WSI] getLargestObscuringWindowSize: xcb_query_tree failed for window 0x%x.\n", window);
+@@ -130,8 +210,7 @@ namespace xcb {
+     for (uint32_t i = 0; i < reply->children_len; i++) {
+       xcb_window_t child = children[i];
+ 
+-      xcb_get_window_attributes_cookie_t attributeCookie = xcb_get_window_attributes(connection, child);
+-      auto attributeReply = Reply<xcb_get_window_attributes_reply_t>{ xcb_get_window_attributes_reply(connection, attributeCookie, nullptr) };
++      auto attributeReply = XcbFetch{xcb_get_window_attributes, xcb_get_window_attributes_reply}(connection, child);
+ 
+       const bool obscuring =
+         attributeReply &&
+
+From 1b59621f4de5c05096d1f279cba2e04264124154 Mon Sep 17 00:00:00 2001
+From: sharkautarch <128002472+sharkautarch@users.noreply.github.com>
+Date: Tue, 18 Jun 2024 22:21:23 -0400
+Subject: [PATCH 2/2] WSI: prefetcher: fix issue w/ attempting to prefetch xcb
+ stuff for pure wayland surfaces
+
+---
+ layer/VkLayer_FROG_gamescope_wsi.cpp | 2 +-
+ layer/xcb_helpers.hpp                | 9 ++++++++-
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/layer/VkLayer_FROG_gamescope_wsi.cpp b/layer/VkLayer_FROG_gamescope_wsi.cpp
+index f26819a60..ce011dcd7 100644
+--- a/layer/VkLayer_FROG_gamescope_wsi.cpp
++++ b/layer/VkLayer_FROG_gamescope_wsi.cpp
+@@ -1234,7 +1234,7 @@ namespace GamescopeWSILayer {
+             continue;
+           }
+ 
+-          xcb::Prefetcher prefetcher(gamescopeSurface->connection, gamescopeSurface->window);
++          auto prefetcher = xcb::Prefetcher::GetPrefetcherIf(!gamescopeSurface->isWayland(), gamescopeSurface->connection, gamescopeSurface->window);
+           const bool canBypass = gamescopeSurface->canBypassXWayland();
+           if (canBypass != gamescopeSwapchain->isBypassingXWayland)
+             UpdateSwapchainResult(canBypass ? VK_SUBOPTIMAL_KHR : VK_ERROR_OUT_OF_DATE_KHR);
+diff --git a/layer/xcb_helpers.hpp b/layer/xcb_helpers.hpp
+index 72d0ec092..f26aef38b 100644
+--- a/layer/xcb_helpers.hpp
++++ b/layer/xcb_helpers.hpp
+@@ -16,6 +16,13 @@ namespace xcb {
+   
+   //Note: this class is currently only meant to be used within GamescopeWSILayer::VkDeviceOverrides::QueuePresentKHR:
+   struct Prefetcher {
++    static std::optional<Prefetcher> GetPrefetcherIf(bool bCond, xcb_connection_t* connection, const xcb_window_t window) {
++       if (bCond)
++         return std::optional<Prefetcher>(std::in_place_t{}, connection, window);
++       
++       return std::nullopt;
++    }
++    
+     explicit Prefetcher(xcb_connection_t* connection, const xcb_window_t window) {
+         g_cache = {
+             .window = window,
+@@ -90,7 +97,7 @@ namespace xcb {
+         inline Reply<Reply_RetType> operator()(xcb_connection_t* conn, xcb_window_t window) {
+             const bool tryCached = pthread_equal(g_cache_tid, pthread_self())
+                                    && g_cache.window == window;
+-            if (!tryCached) [[unlikely]]
++            if (!tryCached)
+                 return Reply<Reply_RetType> { m_replyFunc(conn, m_cookieFunc(conn, window), nullptr) };
+             
+             auto ret = getCachedReply(conn);
diff --git a/anda/games/gamescope/anda.hcl b/anda/games/gamescope/anda.hcl
new file mode 100644
index 0000000000..cd18050ddc
--- /dev/null
+++ b/anda/games/gamescope/anda.hcl
@@ -0,0 +1,8 @@
+project pkg {
+    rpm {
+        spec = "terra-gamescope.spec"
+    }
+    labels {
+        multilib = 1
+    }
+}
diff --git a/anda/games/gamescope/chimeraos.patch b/anda/games/gamescope/chimeraos.patch
new file mode 100644
index 0000000000..80cc642b3d
--- /dev/null
+++ b/anda/games/gamescope/chimeraos.patch
@@ -0,0 +1,2028 @@
+From c06cdd847679c930ee6197514970bc21f523e853 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/22] 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 cd4aeca..4b91c97 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 ee6891d..62da656 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -73,6 +73,7 @@
+ static LogScope wl_log("wlserver");
+ 
+ //#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 = {}
+@@ -2568,6 +2569,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 db7d491..da67bf7 100644
+--- a/src/wlserver.hpp
++++ b/src/wlserver.hpp
+@@ -291,6 +291,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.45.2
+
+
+From 34f22c6a52dbed8662132e4bdfd8a29dd1b7825c 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/22] 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 4b91c97..3d1057a 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 9ee265d..bec4268 100644
+--- a/src/steamcompmgr.cpp
++++ b/src/steamcompmgr.cpp
+@@ -349,6 +349,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()
+@@ -5320,6 +5322,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 )
+@@ -7166,6 +7175,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.45.2
+
+
+From 4c5657cca9a37fee0eee1d86ef3c2a6e5acef09c 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/22] 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 92f01ca..4c72dc1 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -536,6 +536,7 @@ bool g_bSupportsSyncObjs = false;
+ 
+ extern gamescope::GamescopeModeGeneration g_eGamescopeModeGeneration;
+ extern GamescopePanelOrientation g_DesiredInternalOrientation;
++extern GamescopePanelOrientation g_DesiredExternalOrientation;
+ 
+ extern bool g_bForceDisableColorMgmt;
+ 
+@@ -2023,6 +2024,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 3d1057a..fac3df2 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 8cfe629..5492cae 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 62da656..2998aed 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -2530,6 +2530,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.45.2
+
+
+From 897032bb4b8d8d5c11d67f9844a09a604b2c4333 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/22] 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 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 fac3df2..8bea38c 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.45.2
+
+
+From 2178f85676ba25434e8c39adc3848e3f7355acec 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/22] 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 2998aed..62da656 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -2530,29 +2530,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.45.2
+
+
+From 261124a5a85ad80b0d7b15926f230bacbca21b22 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/22] 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 4c72dc1..e4caebb 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2125,6 +2125,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 8bea38c..a9e1969 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 5492cae..0207a51 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.45.2
+
+
+From db4804880ea0fdf810979aea3153e2f45dc97bfe 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/22] Add rotation gamescope_control command
+
+---
+ protocol/gamescope-control.xml | 18 ++++++++++++
+ src/Backends/DRMBackend.cpp    | 23 +++++++++++++++-
+ src/gamescope_shared.h         | 10 +++++++
+ src/main.cpp                   |  1 +
+ src/wlserver.cpp               | 50 ++++++++++++++++++++++++++++++++++
+ 5 files changed, 101 insertions(+), 1 deletion(-)
+
+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 e4caebb..70d5cdf 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2020,7 +2020,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;
+ 		}
+@@ -2935,6 +2937,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/gamescope_shared.h b/src/gamescope_shared.h
+index ed30d8c..d04a907 100644
+--- a/src/gamescope_shared.h
++++ b/src/gamescope_shared.h
+@@ -62,6 +62,16 @@ enum GamescopePanelOrientation
+ 	GAMESCOPE_PANEL_ORIENTATION_AUTO,
+ };
+ 
++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 a9e1969..4469ac1 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 62da656..96bb7fc 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -1101,6 +1101,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 );
+@@ -1110,6 +1159,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.45.2
+
+
+From ef9f07b932c8be1d391b9688b1b31edbb73e86ce 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/22] 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 70d5cdf..7af994e 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 d04a907..ed30d8c 100644
+--- a/src/gamescope_shared.h
++++ b/src/gamescope_shared.h
+@@ -62,16 +62,6 @@ enum GamescopePanelOrientation
+ 	GAMESCOPE_PANEL_ORIENTATION_AUTO,
+ };
+ 
+-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 96bb7fc..959f63b 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -74,6 +74,8 @@ static LogScope wl_log("wlserver");
+ 
+ //#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 = {}
+@@ -1107,43 +1109,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);
+@@ -2554,34 +2556,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.45.2
+
+
+From 8950969d8c62982f03bf8452a503276960f2fa33 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/22] 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 7af994e..30150fb 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2023,9 +2023,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.45.2
+
+
+From 3e51d6f0ae7603e322024783143329dc46835901 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/22] Rework the touch gestures to be more smooth
+
+---
+ src/wlserver.cpp | 90 +++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 69 insertions(+), 21 deletions(-)
+
+diff --git a/src/wlserver.cpp b/src/wlserver.cpp
+index 959f63b..131cd72 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -71,7 +71,8 @@
+ #include <set>
+ 
+ static LogScope wl_log("wlserver");
+-
++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;
+@@ -421,6 +422,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 );
+@@ -2649,32 +2683,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.45.2
+
+
+From d25a36dabc8e1d8144413980d915d0e71d313434 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/22] 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 bec4268..10c0a75 100644
+--- a/src/steamcompmgr.cpp
++++ b/src/steamcompmgr.cpp
+@@ -7175,7 +7175,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.45.2
+
+
+From 6d6afedd56a3f7b578cac1af7eaf93af931e0500 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/22] 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 131cd72..9afe488 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -71,7 +71,8 @@
+ #include <set>
+ 
+ static LogScope wl_log("wlserver");
+-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" );
+@@ -2647,6 +2648,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() );
+@@ -2685,43 +2696,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.45.2
+
+
+From 58e24b2f2ac33eb2388ef734835c6b02e437e563 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/22] 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 9afe488..e963bea 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -1183,8 +1183,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 da67bf7..688d05c 100644
+--- a/src/wlserver.hpp
++++ b/src/wlserver.hpp
+@@ -294,7 +294,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.45.2
+
+
+From 768e5689e12c2f57766d27d72b4fad03d54fc01c 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/22] 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 30150fb..1a2668f 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2023,20 +2023,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:
+@@ -2058,6 +2057,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
+@@ -2065,6 +2065,7 @@ namespace gamescope
+ 			}
+ 			else
+ 			{
++				drm_log.infof("No orientation quirks have been applied");
+ 				m_ChosenOrientation = GAMESCOPE_PANEL_ORIENTATION_0;
+ 			}
+ 		}
+-- 
+2.45.2
+
+
+From d6e4cf239f0f2113d1f3071a8d4766a810497617 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/22] 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 1a2668f..825812e 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2025,7 +2025,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.45.2
+
+
+From 8983b3621dd81d4633a50a798b7794c6ae9d693b 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/22] 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 825812e..2668ad7 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;
+ 		}
+@@ -2023,11 +2030,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 e963bea..74d8209 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -2596,29 +2596,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.45.2
+
+
+From 57057c9e5dc4ac026259726145d5b6e480368699 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/22] 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 2668ad7..9df20ae 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;
+ 		}
+ 
+@@ -2030,7 +2032,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 74d8209..3d4b239 100644
+--- a/src/wlserver.cpp
++++ b/src/wlserver.cpp
+@@ -2600,26 +2600,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 688d05c..ae55963 100644
+--- a/src/wlserver.hpp
++++ b/src/wlserver.hpp
+@@ -296,6 +296,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.45.2
+
+
+From 41242b5ee7fa33cae22f30f5bcf5e27169bd8145 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/22] add closing bracket
+
+---
+ src/main.cpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/main.cpp b/src/main.cpp
+index 4469ac1..e455225 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.45.2
+
+
+From e528844eb590f8183fdfffaf9a7af39f33dc2213 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/22] 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 10c0a75..1bc9008 100644
+--- a/src/steamcompmgr.cpp
++++ b/src/steamcompmgr.cpp
+@@ -1579,7 +1579,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,
+@@ -2257,7 +2257,7 @@ paint_all(bool async)
+ 					}
+ 				}
+ 			}
+-			
++
+ 			int nOldLayerCount = frameInfo.layerCount;
+ 
+ 			uint32_t flags = 0;
+@@ -2265,7 +2265,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 )
+@@ -2278,7 +2278,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);
+ 			}
+@@ -2352,34 +2352,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)
+ 	{
+@@ -2957,7 +2929,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.
+@@ -2972,12 +2944,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.
+@@ -3069,7 +3041,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)
+@@ -3210,7 +3182,7 @@ found:;
+ 
+ 	if ( focus )
+ 	{
+-		if ( window_has_commits( focus ) ) 
++		if ( window_has_commits( focus ) )
+ 			out->focusWindow = focus;
+ 		else
+ 			focus->outdatedInteractiveFocus = true;
+@@ -3253,9 +3225,9 @@ found:;
+ 					override_focus = fake_override;
+ 					goto found2;
+ 				}
+-			}	
++			}
+ 		}
+-		
++
+ 		found2:;
+ 		resolveTransientOverrides( true );
+ 	}
+@@ -4514,7 +4486,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;
+@@ -4571,7 +4543,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);
+@@ -5176,7 +5148,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
+ 		{
+ 			get_win_type(ctx, w);
+ 			MakeFocusDirty();
+-		}		
++		}
+ 	}
+ 	if (ev->atom == ctx->atoms.sizeHintsAtom)
+ 	{
+@@ -6084,7 +6056,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;
+@@ -6938,7 +6910,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 );
+@@ -7696,7 +7668,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.45.2
+
+
+From 7865b34c5cd61fa5cc5428ace614e4551fabb6ec Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Joaqu=C3=ADn=20Ignacio=20Aramend=C3=ADa?=
+ <samsagax@gmail.com>
+Date: Tue, 9 Jul 2024 18:29:16 -0300
+Subject: [PATCH 20/22] disable explicit sync to avoid graphical artifacts
+
+---
+ 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 9df20ae..ff1858b 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -68,7 +68,7 @@ gamescope::ConVar<bool> cv_drm_debug_disable_blend_tf( "drm_debug_disable_blend_
+ gamescope::ConVar<bool> cv_drm_debug_disable_ctm( "drm_debug_disable_ctm", false, "CTM chicken bit. (Forces CTM off, does not affect other logic)" );
+ gamescope::ConVar<bool> 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<bool> 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<bool> cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", false, "Force disable explicit sync on the DRM backend." );
++gamescope::ConVar<bool> cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", true, "Force disable explicit sync on the DRM backend." );
+ gamescope::ConVar<bool> 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:
+-- 
+2.45.2
+
+
+From 1dbcfed76f4b80d8a9f6570819b5af7917c786fc Mon Sep 17 00:00:00 2001
+From: Kyle Gospodnetich <me@kylegospodneti.ch>
+Date: Tue, 2 Jul 2024 14:12:47 -0700
+Subject: [PATCH 21/22] 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 ff1858b..f5a452e 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2152,7 +2152,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.45.2
+
+
+From 3b0408f6ec7307e7a38f2c795b932d0dc05f2d72 Mon Sep 17 00:00:00 2001
+From: Kyle Gospodnetich <me@kylegospodneti.ch>
+Date: Tue, 2 Jul 2024 15:14:23 -0700
+Subject: [PATCH 22/22] 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 f5a452e..d7f935d 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2152,9 +2152,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.45.2
+
diff --git a/anda/games/gamescope/deckhd.patch b/anda/games/gamescope/deckhd.patch
new file mode 100644
index 0000000000..5140e85b0f
--- /dev/null
+++ b/anda/games/gamescope/deckhd.patch
@@ -0,0 +1,136 @@
+diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
+index 85e5126..be418b4 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -2149,6 +2149,7 @@ namespace gamescope
+ 			( m_Mutable.szMakePNP == "WLC"sv && m_Mutable.szModel == "ANX7530 U"sv ) ||
+ 			( m_Mutable.szMakePNP == "ANX"sv && m_Mutable.szModel == "ANX7530 U"sv ) ||
+ 			( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "ANX7530 U"sv ) ||
++			( m_Mutable.szMakePNP == "DHD"sv && m_Mutable.szModel == "DeckHD-1200p"sv ) ||
+ 			( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Jupiter"sv ) ||
+ 			( m_Mutable.szMakePNP == "VLV"sv && m_Mutable.szModel == "Galileo"sv );
+ 
+@@ -2160,6 +2161,7 @@ namespace gamescope
+ 		{
+ 			static constexpr uint32_t kPIDGalileoSDC = 0x3003;
+ 			static constexpr uint32_t kPIDGalileoBOE = 0x3004;
++			static constexpr uint32_t kPIDJupiterDHD = 0x4001;
+ 
+ 			if ( pProduct->product == kPIDGalileoSDC )
+ 			{
+@@ -2171,6 +2173,10 @@ namespace gamescope
+ 				m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE;
+ 				m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDRates );
+ 			}
++			else if (pProduct-> product == kPIDJupiterDHD ) {
++				m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD;
++				m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckLCDRates );
++			}
+ 			else
+ 			{
+ 				m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD;
+@@ -2200,7 +2206,8 @@ namespace gamescope
+ 			drm_log.infof( "[colorimetry]: Steam Deck LCD detected. Using known colorimetry" );
+ 			m_Mutable.DisplayColorimetry = displaycolorimetry_steamdeck_measured;
+ 		}
+-		else
++		else if (m_Mutable.eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE ||
++				 m_Mutable.eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC )
+ 		{
+ 			// Steam Deck OLED has calibrated chromaticity coordinates in the EDID
+ 			// for each unit.
+@@ -2330,7 +2337,7 @@ namespace gamescope
+ 				.uMinContentLightLevel = nits_to_u16_dark( 0 ),
+ 			};
+ 		}
+-		else if ( eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD )
++		else if ( eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD || eKnownDisplay == GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD )
+ 		{
+ 			// Set up some HDR fallbacks for undocking
+ 			return BackendConnectorHDRInfo
+diff --git a/src/gamescope_shared.h b/src/gamescope_shared.h
+index ed30d8c..3b60774 100644
+--- a/src/gamescope_shared.h
++++ b/src/gamescope_shared.h
+@@ -8,6 +8,7 @@ namespace gamescope
+ 	{
+ 		GAMESCOPE_KNOWN_DISPLAY_UNKNOWN,
+ 		GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD,      // Jupiter
++		GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD,  // Jupiter Deck HD
+ 		GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC, // Galileo SDC
+ 		GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE, // Galileo BOE
+ 	};
+diff --git a/src/modegen.cpp b/src/modegen.cpp
+index d174c2d..5dd1136 100644
+--- a/src/modegen.cpp
++++ b/src/modegen.cpp
+@@ -293,13 +293,32 @@ unsigned int galileo_boe_vfp[] =
+ 	172,152,136,120,100,84,68,52,36,20,8
+ };
+ 
+-#define GALILEO_MIN_REFRESH 45
++//SD LCD Stock Timings
++#define JUPITER_BOE_PID     0x3001
++#define JUPITER_B_PID       0x3002
++#define JUPITER_HFP         40
++#define JUPITER_HSYNC       4
++#define JUPITER_HBP         40
++#define JUPITER_VFP         30
++#define JUPITER_VSYNC       4
++#define JUPITER_VBP         8
++//SD LCD DeckHD Timings
++#define JUPITER_DHD_PID     0x4001
++#define JUPITER_DHD_HFP     40
++#define JUPITER_DHD_HSYNC   20
++#define JUPITER_DHD_HBP     40
++#define JUPITER_DHD_VFP     18
++#define JUPITER_DHD_VSYNC   2
++#define JUPITER_DHD_VBP     20
++//SD OLED SDC Timings
+ #define GALILEO_SDC_PID     0x3003
+ #define GALILEO_SDC_VSYNC   1
+ #define GALILEO_SDC_VBP     22
++//SD OLED BOE Timings
+ #define GALILEO_BOE_PID     0x3004
+ #define GALILEO_BOE_VSYNC   2
+ #define GALILEO_BOE_VBP     30
++#define GALILEO_MIN_REFRESH 45
+ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+ 
+ unsigned int get_galileo_vfp( int vrefresh, unsigned int * vfp_array, unsigned int num_rates )
+@@ -344,17 +363,28 @@ void generate_fixed_mode(drmModeModeInfo *mode, const drmModeModeInfo *base, int
+ 		mode->vsync_end = mode->vsync_start + vsync;
+ 		mode->vtotal = mode->vsync_end + vbp;
+ 	} else {
+-		if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD )
++		if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD_DHD ) {
++			mode->hdisplay = 1200;
++			mode->hsync_start = mode->hdisplay + JUPITER_DHD_HFP;
++			mode->hsync_end = mode->hsync_start + JUPITER_DHD_HSYNC;
++			mode->htotal = mode->hsync_end + JUPITER_DHD_HBP;
++
++			mode->vdisplay = 1920;
++			mode->vsync_start = mode->vdisplay + JUPITER_DHD_VFP;
++			mode->vsync_end = mode->vsync_start + JUPITER_DHD_VSYNC;
++			mode->vtotal = mode->vsync_end + JUPITER_DHD_VBP;
++		}
++		else if ( eKnownDisplay == gamescope::GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_LCD )
+ 		{
+ 			mode->hdisplay = 800;
+-			mode->hsync_start = 840;
+-			mode->hsync_end = 844;
+-			mode->htotal = 884;
++			mode->hsync_start = mode->hdisplay + JUPITER_HFP;
++			mode->hsync_end = mode->hsync_start + JUPITER_HSYNC;
++			mode->htotal = mode->hsync_end + JUPITER_HBP;
+ 
+ 			mode->vdisplay = 1280;
+-			mode->vsync_start = 1310;
+-			mode->vsync_end = 1314;
+-			mode->vtotal = 1322;
++			mode->vsync_start = mode->vdisplay + JUPITER_VFP;
++			mode->vsync_end = mode->vsync_start + JUPITER_VSYNC;
++			mode->vtotal = mode->vsync_end + JUPITER_VBP;
+ 		}
+ 
+ 		mode->clock = ( ( mode->htotal * mode->vtotal * vrefresh ) + 999 ) / 1000;
diff --git a/anda/games/gamescope/disable-steam-touch-click-atom.patch b/anda/games/gamescope/disable-steam-touch-click-atom.patch
new file mode 100644
index 0000000000..822b3a8e7e
--- /dev/null
+++ b/anda/games/gamescope/disable-steam-touch-click-atom.patch
@@ -0,0 +1,51 @@
+diff --git a/src/main.cpp b/src/main.cpp
+index 119e043..6c46d97 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -148,6 +148,8 @@ const struct option *gamescope_options = (struct option[]){
+ 	{ "reshade-effect", required_argument, nullptr, 0 },
+ 	{ "reshade-technique-idx", required_argument, nullptr, 0 },
+ 
++	{ "disable-touch-click", no_argument, nullptr, 0 },
++
+ 	// Steam Deck options
+ 	{ "mura-map", required_argument, nullptr, 0 },
+ 
+@@ -193,6 +195,7 @@ const char usage[] =
+ 	"  -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"
++	"  --disable-touch-click          disable touchscreen tap acting as a click\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"
+diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
+index 92bf617..d7498e5 100644
+--- a/src/steamcompmgr.cpp
++++ b/src/steamcompmgr.cpp
+@@ -347,6 +347,7 @@ 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" };
+ 
++gamescope::ConVar<bool> cv_disable_touch_click{ "disable_touch_click", false, "Prevents touchscreen taps acting as clicks" };
+ 
+ static void
+ update_color_mgmt()
+@@ -5128,7 +5129,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
+ 			MakeFocusDirty();
+ 		}
+ 	}
+-	if (ev->atom == ctx->atoms.steamTouchClickModeAtom )
++	if (ev->atom == ctx->atoms.steamTouchClickModeAtom && !cv_disable_touch_click)
+ 	{
+ 		gamescope::cv_touch_click_mode = (gamescope::TouchClickMode) get_prop(ctx, ctx->root, ctx->atoms.steamTouchClickModeAtom, 0u );
+ 	}
+@@ -7301,6 +7302,8 @@ steamcompmgr_main(int argc, char **argv)
+ 					g_reshade_technique_idx = atoi(optarg);
+ 				} else if (strcmp(opt_name, "mura-map") == 0) {
+ 					set_mura_overlay(optarg);
++				} else if (strcmp(opt_name, "disable-touch-click") == 0) {
++					cv_disable_touch_click = true;
+ 				}
+ 				break;
+ 			case '?':
diff --git a/anda/games/gamescope/drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch b/anda/games/gamescope/drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch
new file mode 100644
index 0000000000..f39afdbc4b
--- /dev/null
+++ b/anda/games/gamescope/drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch
@@ -0,0 +1,63 @@
+From 2e4d7ad1bf2cb98eb67ff8f9385cf6657cf2e912 Mon Sep 17 00:00:00 2001
+From: Matthew Schwartz <njtransit215@gmail.com>
+Date: Wed, 3 Jul 2024 15:20:08 -0700
+Subject: [PATCH] drm: Separate BOE and SDC OLED Deck panel valid refresh rates
+
+OLED Decks with BOE panels seem to struggle with a few different
+specific modesets (51hz/55hz/65hz) that SDC panels have no issues with.
+To work around this, let's make use of Gamescope recognizing each
+display manufacturer to correct the bad modesets while leaving
+SDC panel units alone. This can be reverted if an underlying cause can
+be found in the kernel in the future.
+---
+ src/Backends/DRMBackend.cpp | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/src/Backends/DRMBackend.cpp b/src/Backends/DRMBackend.cpp
+index 97ef446..9f93c7f 100644
+--- a/src/Backends/DRMBackend.cpp
++++ b/src/Backends/DRMBackend.cpp
+@@ -554,7 +554,7 @@ static constexpr uint32_t s_kSteamDeckLCDRates[] =
+ 	60,
+ };
+ 
+-static constexpr uint32_t s_kSteamDeckOLEDRates[] =
++static constexpr uint32_t s_kSteamDeckOLEDSDCRates[] =
+ {
+ 	45, 47, 48, 49, 
+ 	50, 51, 53, 55, 56, 59, 
+@@ -564,6 +564,16 @@ static constexpr uint32_t s_kSteamDeckOLEDRates[] =
+ 	90, 
+ };
+ 
++static constexpr uint32_t s_kSteamDeckOLEDBOERates[] =
++{
++	45, 47, 48, 49, 
++	50, 53, 56, 59, 
++	60, 62, 64, 66, 68, 
++	72, 73, 76, 77, 78, 
++	80, 81, 82, 84, 85, 86, 87, 88, 
++	90, 
++};
++
+ static void update_connector_display_info_wl(struct drm_t *drm)
+ {
+ 	wlserver_lock();
+@@ -2128,12 +2138,12 @@ namespace gamescope
+ 			if ( pProduct->product == kPIDGalileoSDC )
+ 			{
+ 				m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_SDC;
+-				m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDRates );
++				m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDSDCRates );
+ 			}
+ 			else if ( pProduct->product == kPIDGalileoBOE )
+ 			{
+ 				m_Mutable.eKnownDisplay = GAMESCOPE_KNOWN_DISPLAY_STEAM_DECK_OLED_BOE;
+-				m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDRates );
++				m_Mutable.ValidDynamicRefreshRates = std::span( s_kSteamDeckOLEDBOERates );
+ 			}
+ 			else
+ 			{
+-- 
+2.45.2
+
diff --git a/anda/games/gamescope/revert-299bc34.patch b/anda/games/gamescope/revert-299bc34.patch
new file mode 100644
index 0000000000..550870fe82
--- /dev/null
+++ b/anda/games/gamescope/revert-299bc34.patch
@@ -0,0 +1,65 @@
+diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
+index d7498e5..d1800a8 100644
+--- a/src/steamcompmgr.cpp
++++ b/src/steamcompmgr.cpp
+@@ -3271,7 +3271,7 @@ found:;
+ 		if ( window_has_commits( focus ) ) 
+ 			out->focusWindow = focus;
+ 		else
+-			focus->outdatedInteractiveFocus = true;
++			out->outdatedInteractiveFocus = true;
+ 
+ 		// Always update X's idea of focus, but still dirty
+ 		// the it being outdated so we can resolve that globally later.
+@@ -5995,28 +5995,37 @@ bool handle_done_commit( steamcompmgr_win_t *w, xwayland_ctx_t *ctx, uint64_t co
+ 			// Window just got a new available commit, determine if that's worth a repaint
+ 
+ 			// If this is an overlay that we're presenting, repaint
+-			if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT )
++			if ( gameFocused )
+ 			{
+-				hasRepaintNonBasePlane = true;
+-			}
++				if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT )
++				{
++					hasRepaintNonBasePlane = true;
++				}
+ 
+-			if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT )
+-			{
+-				hasRepaintNonBasePlane = true;
++				if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT )
++				{
++					hasRepaintNonBasePlane = true;
++				}
+ 			}
+-
+-			// If this is an external overlay, repaint
+-			if ( w == global_focus.externalOverlayWindow && w->opacity != TRANSLUCENT )
++			if ( ctx )
+ 			{
+-				hasRepaintNonBasePlane = true;
++				if ( ctx->focus.outdatedInteractiveFocus )
++				{
++					MakeFocusDirty();
++					ctx->focus.outdatedInteractiveFocus = false;
++				}
+ 			}
+-
+-			if ( w->outdatedInteractiveFocus )
++			if ( global_focus.outdatedInteractiveFocus )
+ 			{
+ 				MakeFocusDirty();
+-				w->outdatedInteractiveFocus = false;
+-			}
++				global_focus.outdatedInteractiveFocus = false;
+ 
++				// If this is an external overlay, repaint
++				if ( w == global_focus.externalOverlayWindow && w->opacity != TRANSLUCENT )
++				{
++					hasRepaintNonBasePlane = true;
++				}
++			}
+ 			// If this is the main plane, repaint
+ 			if ( w == global_focus.focusWindow && !w->isSteamStreamingClient )
+ 			{
diff --git a/anda/games/gamescope/stb.pc b/anda/games/gamescope/stb.pc
new file mode 100644
index 0000000000..02c304a9fa
--- /dev/null
+++ b/anda/games/gamescope/stb.pc
@@ -0,0 +1,7 @@
+prefix=/usr
+includedir=${prefix}/include/stb
+
+Name: stb
+Description: Single-file public domain libraries for C/C++
+Version: 0.1.0
+Cflags: -I${includedir}
diff --git a/anda/games/gamescope/terra-gamescope.spec b/anda/games/gamescope/terra-gamescope.spec
new file mode 100644
index 0000000000..b919da5b29
--- /dev/null
+++ b/anda/games/gamescope/terra-gamescope.spec
@@ -0,0 +1,140 @@
+%global libliftoff_minver 0.4.1
+
+%global _default_patch_fuzz 2
+%global gamescope_tag 3.14.26
+
+Name:           terra-gamescope
+Version:        100.%{gamescope_tag}
+Release:        1%?dist
+Summary:        Micro-compositor for video games on Wayland
+
+License:        BSD
+URL:            https://github.com/ValveSoftware/gamescope
+
+# Create stb.pc to satisfy dependency('stb')
+Source0:        stb.pc
+
+# https://github.com/ChimeraOS/gamescope
+Patch0:         chimeraos.patch
+# https://hhd.dev/
+Patch1:         disable-steam-touch-click-atom.patch
+# https://github.com/ValveSoftware/gamescope/pull/1281
+# Patch2:         deckhd.patch
+# https://github.com/ValveSoftware/gamescope/issues/1398
+Patch3:         drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch
+# https://github.com/ValveSoftware/gamescope/issues/1369
+Patch4:         revert-299bc34.patch
+# https://github.com/ValveSoftware/gamescope/pull/1231
+Patch5:         1231.patch
+
+BuildRequires:  meson >= 0.54.0
+BuildRequires:  ninja-build
+BuildRequires:  cmake
+BuildRequires:  gcc
+BuildRequires:  gcc-c++
+BuildRequires:  glm-devel
+BuildRequires:  google-benchmark-devel
+BuildRequires:  libXmu-devel
+BuildRequires:  libXcursor-devel
+BuildRequires:  libeis-devel
+BuildRequires:  pixman-devel
+BuildRequires:  pkgconfig(libdisplay-info)
+BuildRequires:  pkgconfig(pixman-1)
+BuildRequires:  pkgconfig(x11)
+BuildRequires:  pkgconfig(xdamage)
+BuildRequires:  pkgconfig(xcomposite)
+BuildRequires:  pkgconfig(xrender)
+BuildRequires:  pkgconfig(xext)
+BuildRequires:  pkgconfig(xfixes)
+BuildRequires:  pkgconfig(xxf86vm)
+BuildRequires:  pkgconfig(xtst)
+BuildRequires:  pkgconfig(xres)
+BuildRequires:  pkgconfig(libdrm)
+BuildRequires:  pkgconfig(vulkan)
+BuildRequires:  pkgconfig(wayland-scanner)
+BuildRequires:  pkgconfig(wayland-server)
+BuildRequires:  pkgconfig(wayland-protocols) >= 1.17
+BuildRequires:  pkgconfig(xkbcommon)
+BuildRequires:  pkgconfig(sdl2)
+BuildRequires:  pkgconfig(libpipewire-0.3)
+BuildRequires:  pkgconfig(libavif)
+#BuildRequires:  (pkgconfig(wlroots) >= 0.18.0 with pkgconfig(wlroots) < 0.19.0)
+#BuildRequires:  (pkgconfig(libliftoff) >= 0.4.1 with pkgconfig(libliftoff) < 0.5)
+BuildRequires:  pkgconfig(libliftoff)
+BuildRequires:  pkgconfig(libcap)
+BuildRequires:  pkgconfig(hwdata)
+BuildRequires:  pkgconfig(libudev)
+BuildRequires:  pkgconfig(libseat)
+BuildRequires:  pkgconfig(libinput)
+BuildRequires:  xcb-util-wm-devel
+BuildRequires:  pkgconfig(xcb-errors)
+BuildRequires:  pkgconfig(lcms2)
+BuildRequires:  spirv-headers-devel
+# Enforce the the minimum EVR to contain fixes for all of:
+# CVE-2021-28021 CVE-2021-42715 CVE-2021-42716 CVE-2022-28041 CVE-2023-43898
+# CVE-2023-45661 CVE-2023-45662 CVE-2023-45663 CVE-2023-45664 CVE-2023-45666
+# CVE-2023-45667
+BuildRequires:  stb_image-devel >= 2.28^20231011gitbeebb24-12
+# Header-only library: -static is for tracking per guidelines
+BuildRequires:  stb_image-static
+BuildRequires:  stb_image_resize-devel
+BuildRequires:  stb_image_resize-static
+BuildRequires:  stb_image_write-devel
+BuildRequires:  stb_image_write-static
+BuildRequires:  /usr/bin/glslangValidator
+BuildRequires:  libdecor-devel
+BuildRequires:  libXdamage-devel
+BuildRequires:  xorg-x11-server-Xwayland-devel
+BuildRequires:  git
+
+# libliftoff hasn't bumped soname, but API/ABI has changed for 0.2.0 release
+Requires:       libliftoff%{?_isa} >= %{libliftoff_minver}
+Requires:       xorg-x11-server-Xwayland
+Requires:       %{name}-libs = %{version}-%{release}
+%ifarch %{ix86}
+Requires:       %{name}-libs(x86-32) = %{version}-%{release}
+%endif
+Recommends:     mesa-dri-drivers
+Recommends:     mesa-vulkan-drivers
+
+%description
+Gamescope is the micro-compositor optimized for running video games on Wayland.
+
+%package libs
+Summary:	libs for Gamescope
+%description libs
+%summary
+
+%prep
+git clone --depth 1 --branch %{gamescope_tag} %{url}.git
+cd gamescope
+git submodule update --init --recursive
+mkdir -p pkgconfig
+cp %{SOURCE0} pkgconfig/stb.pc
+
+# Replace spirv-headers include with the system directory
+sed -i 's^../thirdparty/SPIRV-Headers/include/spirv/^/usr/include/spirv/^' src/meson.build
+
+%autopatch -p1
+
+%build
+cd gamescope
+export PKG_CONFIG_PATH=pkgconfig
+%meson -Dpipewire=enabled -Dinput_emulation=enabled -Ddrm_backend=enabled -Drt_cap=enabled -Davif_screenshots=enabled -Dsdl2_backend=enabled
+%meson_build
+
+%install
+cd gamescope
+%meson_install --skip-subprojects
+
+%files
+%license gamescope/LICENSE
+%doc gamescope/README.md
+%caps(cap_sys_nice=eip) %{_bindir}/gamescope
+%{_bindir}/gamescopectl
+%{_bindir}/gamescopestream
+%{_bindir}/gamescopereaper
+
+%files libs
+%{_libdir}/libVkLayer_FROG_gamescope_wsi_*.so
+%{_datadir}/vulkan/implicit_layer.d/VkLayer_FROG_gamescope_wsi.*.json
diff --git a/anda/games/gamescope/update.rhai b/anda/games/gamescope/update.rhai
new file mode 100644
index 0000000000..74a18ee692
--- /dev/null
+++ b/anda/games/gamescope/update.rhai
@@ -0,0 +1 @@
+rpm.global("gamescope_tag", gh_tag("ValveSoftware/gamescope"));
diff --git a/anda/langs/go/albius/albius.spec b/anda/langs/go/albius/albius.spec
index d382a6ad9b..1ce72ac161 100644
--- a/anda/langs/go/albius/albius.spec
+++ b/anda/langs/go/albius/albius.spec
@@ -1,6 +1,6 @@
 %define debug_package %nil
-%global commit 688ca6ae29de89174794a48be61ecd0fb1111c96
-%global commit_date 20240430
+%global commit cee36c6db378c380900fc7b901e2f7d0ea596cc0
+%global commit_date 20240724
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
 
 Name:           albius
diff --git a/anda/langs/nim/nim-nightly/nim-nightly.spec b/anda/langs/nim/nim-nightly/nim-nightly.spec
index 5f2b762e16..dc56a121fe 100644
--- a/anda/langs/nim/nim-nightly/nim-nightly.spec
+++ b/anda/langs/nim/nim-nightly/nim-nightly.spec
@@ -1,8 +1,8 @@
 %global csrc_commit 561b417c65791cd8356b5f73620914ceff845d10
-%global commit 881fbb8f81599c6f633158618f05fa05439816ca
+%global commit cb156648d69fd0c21ee5d041a98ddc69294eaa96
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
 %global ver 2.1.9
-%global commit_date 20240723
+%global commit_date 20240730
 %global debug_package %nil
 
 Name:			nim-nightly
diff --git a/anda/langs/rust/rnote/rnote.spec b/anda/langs/rust/rnote/rnote.spec
index 1f908389a7..1a0893708c 100644
--- a/anda/langs/rust/rnote/rnote.spec
+++ b/anda/langs/rust/rnote/rnote.spec
@@ -1,3 +1,5 @@
+%global build_rustflags %build_rustflags -C link-arg=-fuse-ld=mold
+
 Name:           rnote
 Version:        0.11.0
 Release:        1%?dist
@@ -6,7 +8,7 @@ License:        GPL-3.0
 URL:            https://github.com/flxzt/rnote
 Source0:        %{url}/archive/refs/tags/v%version.tar.gz
 Requires:       gtk4
-BuildRequires:  cargo meson cmake libappstream-glib gcc-c++ pkgconfig(alsa) alsa-lib clang-devel python desktop-file-utils
+BuildRequires:  cargo meson cmake libappstream-glib gcc-c++ pkgconfig(alsa) alsa-lib clang-devel python desktop-file-utils mold
 BuildRequires:  pkgconfig(glib-2.0) >= 2.66
 BuildRequires:  pkgconfig(gtk4) >= 4.7
 BuildRequires:  pkgconfig(libadwaita-1) >= 1.2
diff --git a/anda/lib/tdlib/tdlib-nightly.spec b/anda/lib/tdlib/tdlib-nightly.spec
index 9010b99461..7167a8a03a 100644
--- a/anda/lib/tdlib/tdlib-nightly.spec
+++ b/anda/lib/tdlib/tdlib-nightly.spec
@@ -1,5 +1,5 @@
-%global commit cb164927417f22811c74cd8678ed4a5ab7cb80ba
-%global ver 1.8.33
+%global commit a24af0992245f838f2b4b418a0a2d5fa9caa27b5
+%global ver 1.8.34
 %global commit_date 20240219
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
 
diff --git a/anda/system/cros-keyboard-map/cros-keyboard-map.spec b/anda/system/cros-keyboard-map/cros-keyboard-map.spec
index 4b8c2f7801..8f6fa88ea4 100644
--- a/anda/system/cros-keyboard-map/cros-keyboard-map.spec
+++ b/anda/system/cros-keyboard-map/cros-keyboard-map.spec
@@ -1,5 +1,5 @@
-%global commit 5f56dd60b256061a036e5f5b494da570a299cdee
-%global commit_date 20240708
+%global commit 3e3c31c227b3b91572ce4a2591281d16a9753f33
+%global commit_date 20240801
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
 %global debug_package %{nil}
 %define __os_install_post %{nil}
diff --git a/anda/tools/sbctl/sbctl.spec b/anda/tools/sbctl/sbctl.spec
index d18b2389de..8fc570ba1c 100644
--- a/anda/tools/sbctl/sbctl.spec
+++ b/anda/tools/sbctl/sbctl.spec
@@ -54,6 +54,7 @@ fi
 %{_bindir}/sbctl
 %{_prefix}/lib/kernel/install.d/91-sbctl.install
 %{_mandir}/man8/sbctl.8*
+%{_mandir}/man5/sbctl.conf.5*
 %{_datadir}/bash-completion/completions/sbctl
 %{_datadir}/fish/vendor_completions.d/sbctl.fish
 %{_datadir}/zsh/site-functions/_sbctl
diff --git a/anda/tools/yt-dlp/yt-dlp-nightly.spec b/anda/tools/yt-dlp/yt-dlp-nightly.spec
index 5a52cfa364..6e61f92a1f 100644
--- a/anda/tools/yt-dlp/yt-dlp-nightly.spec
+++ b/anda/tools/yt-dlp/yt-dlp-nightly.spec
@@ -1,8 +1,8 @@
 #bcond_without tests
-%global commit a3bab4752a2b3d56e5a59b4e0411bb8f695c010b
+%global commit bb3936ae2b3ce96d0b53f9e17cad1082058f032b
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
-%global commit_date 20240719
-%global ver 2024.07.16
+%global commit_date 20240801
+%global ver 2024.07.25
 
 Name:           yt-dlp-nightly
 Version:        %ver^%commit_date.%shortcommit