diff --git a/.github/build-canary-v3 b/.github/build-canary-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/build-canary-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/build-nest-v3 b/.github/build-nest-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/build-nest-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/release-canary-v3 b/.github/release-canary-v3 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.github/release-canary-v3 @@ -0,0 +1 @@ +1 diff --git a/.github/release-nest-v3 b/.github/release-nest-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/release-nest-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/workflows/build-canaryv3.yml b/.github/workflows/build-canaryv3.yml new file mode 100644 index 0000000..bff3d86 --- /dev/null +++ b/.github/workflows/build-canaryv3.yml @@ -0,0 +1,37 @@ +name: PikaOS Package Build Only (Canary) (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/build-canary-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:canaryv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh diff --git a/.github/workflows/build-nestv3.yml b/.github/workflows/build-nestv3.yml new file mode 100644 index 0000000..3c9c34d --- /dev/null +++ b/.github/workflows/build-nestv3.yml @@ -0,0 +1,37 @@ +name: PikaOS Package Build Only (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/build-nest-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:nestv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh diff --git a/.github/workflows/release-canaryv3.yml b/.github/workflows/release-canaryv3.yml new file mode 100644 index 0000000..436c8ff --- /dev/null +++ b/.github/workflows/release-canaryv3.yml @@ -0,0 +1,40 @@ +name: PikaOS Package Build & Release (Canary) (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/release-canary-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:canaryv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh + + - name: Release Package + run: ./release.sh diff --git a/.github/workflows/release-nestv3.yml b/.github/workflows/release-nestv3.yml new file mode 100644 index 0000000..ed42bb7 --- /dev/null +++ b/.github/workflows/release-nestv3.yml @@ -0,0 +1,40 @@ +name: PikaOS Package Build & Release (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/release-nest-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:nestv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh + + - name: Release Package + run: ./release.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index ab7ce57..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: PikaOS Package Release - -on: - workflow_dispatch - -jobs: - build: - runs-on: self-hosted - container: - image: ghcr.io/pikaos-linux/pika-package-container:latest - volumes: - - /proc:/proc - options: --privileged -it - - steps: - - uses: actions/checkout@v3 - - - name: Import GPG key - id: import_gpg - uses: crazy-max/ghaction-import-gpg@v5 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.PASSPHRASE }} - - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.SSH_KEY }} - name: id_rsa - known_hosts: ${{ secrets.KNOWN_HOSTS }} - if_key_exists: replace - - - name: Update apt cache - run: apt update -y - - - name: Build Package - run: ./main.sh - - - name: Release Package - run: ./release.sh - - - name: Purge cache - uses: strrife/cloudflare-chunked-purge-action@master - env: - # Zone is required by both authentication methods - CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }} - - CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} - PURGE_URLS: ${{ vars.PURGE_URLS }} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..16ff30b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ +MIT License (With DPKG packaging compatibility) + +Copyright (c) 2024 PikaOS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Notes: +The files covered by this license are any files and directories in the root of this repository (including but not limited to: `main.sh`, `release.sh`, and `.github`), with the exception of the `debian` directory and its contents if `debian/copyright` exists, and declares any files or directories as a different LICENSE/COPYRIGHT. diff --git a/debian/files b/debian/files deleted file mode 100644 index 66f40cc..0000000 --- a/debian/files +++ /dev/null @@ -1 +0,0 @@ -gamescope_3.11.58.d6c1df4.openvr-99pika15_source.buildinfo games optional diff --git a/debian/gbp.conf b/debian/gbp.conf deleted file mode 100644 index 645ac76..0000000 --- a/debian/gbp.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -upstream-branch = upstream/latest -debian-branch = debian/latest -pristine-tar = True diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml deleted file mode 100644 index 8424db4..0000000 --- a/debian/salsa-ci.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -include: - - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml diff --git a/debian/upstream/metadata b/debian/upstream/metadata deleted file mode 100644 index 4bea124..0000000 --- a/debian/upstream/metadata +++ /dev/null @@ -1,4 +0,0 @@ -Bug-Database: https://github.com/Plagman/gamescope/issues -Bug-Submit: https://github.com/Plagman/gamescope/issues/new -Repository-Browse: https://github.com/Plagman/gamescope -Repository: https://github.com/Plagman/gamescope.git diff --git a/debian/watch b/debian/watch deleted file mode 100644 index 90a5e6c..0000000 --- a/debian/watch +++ /dev/null @@ -1,2 +0,0 @@ -version=4 -https://github.com/Plagman/gamescope/tags (?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@ diff --git a/main.sh b/main.sh index 3b6978b..fca6d97 100755 --- a/main.sh +++ b/main.sh @@ -1,14 +1,25 @@ +#! /bin/bash + +set -e + +VERSION="3.14.24" + +source ./pika-build-config.sh + +echo "$PIKA_BUILD_ARCH" > pika-build-arch + # Clone Upstream -git clone https://github.com/Plagman/gamescope -b 3.12.5 -cp -rvf ./debian ./gamescope +git clone --recurse-submodules https://github.com/Plagman/gamescope +cp -rvf ./debian ./gamescope/ cd ./gamescope -git submodule update --init +git reset --hard 7ae5e0d2a75de06e267c47ca3cd3cddedd1d7416 +for i in $(cat ../patches/series) ; do echo "Applying Patch: $i" && patch -Np1 -i ../patches/$i || echo "Applying Patch $i Failed!"; done + # Get build deps -apt-get update -y apt-get build-dep ./ -y # Build package -LOGNAME=root dh_make --createorig -y -l -p gamescope_3.12.5 +LOGNAME=root dh_make --createorig -y -l -p gamescope_"$VERSION" || echo "dh-make: Ignoring Last Error" dpkg-buildpackage --no-sign # Move the debs to output diff --git a/patches/1231.patch b/patches/1231.patch new file mode 100644 index 0000000..e21cb0a --- /dev/null +++ b/patches/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 + #include + #include ++#include + + 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 cached_cookies; ++ std::tuple 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 + void operator()(T* ptr) const { +- free(const_cast*>(ptr)); ++ if (m_bOwning) ++ free(const_cast*>(ptr)); + } + }; + + template + using Reply = std::unique_ptr; ++ ++ template ++ 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 operator()(XcbConn conn, auto... args) { //have to use auto for argsTwo, since otherwise there'd be a type deduction conflict ++ return Reply { m_replyFunc(conn, m_cookieFunc(conn, args...), nullptr) }; ++ } ++ }; ++ ++ template ++ concept CacheableCookie = std::is_same::value ++ || std::is_same::value; ++ ++ template ++ class XcbFetch { ++ 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 getCachedReply(xcb_connection_t* connection) { ++ if (std::get(g_cache.cached_replies) == nullptr) { ++ std::get(g_cache.cached_replies) = m_replyFunc(connection, std::get(g_cache.cached_cookies), nullptr); ++ } + ++ return Reply{std::get(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 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 { 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 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(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(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 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(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 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(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 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(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(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 GetPrefetcherIf(bool bCond, xcb_connection_t* connection, const xcb_window_t window) { ++ if (bCond) ++ return std::optional(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 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 { m_replyFunc(conn, m_cookieFunc(conn, window), nullptr) }; + + auto ret = getCachedReply(conn); diff --git a/patches/1335.patch b/patches/1335.patch new file mode 100644 index 0000000..515c829 --- /dev/null +++ b/patches/1335.patch @@ -0,0 +1,127 @@ +From 35f5ba8bb9297d06b7d8238295ae3601367d91ad Mon Sep 17 00:00:00 2001 +From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> +Date: Fri, 24 May 2024 15:43:09 -0400 +Subject: [PATCH 1/2] sdlwindow.cpp: close m_thread w/o std::terminate being + called + +--- + src/Backends/SDLBackend.cpp | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/src/Backends/SDLBackend.cpp b/src/Backends/SDLBackend.cpp +index d7456b6a4..5420683a3 100644 +--- a/src/Backends/SDLBackend.cpp ++++ b/src/Backends/SDLBackend.cpp +@@ -53,6 +53,7 @@ namespace gamescope + GAMESCOPE_SDL_EVENT_CURSOR, + + GAMESCOPE_SDL_EVENT_COUNT, ++ GAMESCOPE_SDL_EVENT_REQ_EXIT, + }; + + class CSDLConnector final : public IBackendConnector +@@ -111,6 +112,7 @@ namespace gamescope + { + public: + CSDLBackend(); ++ ~CSDLBackend(); + + ///////////// + // IBackend +@@ -548,7 +550,12 @@ namespace gamescope + { + // Do nothing. + } +- ++ ++ CSDLBackend::~CSDLBackend() { ++ PushUserEvent(GAMESCOPE_SDL_EVENT_REQ_EXIT); ++ m_SDLThread.join(); ++ } ++ + void CSDLBackend::SDLThreadFunc() + { + pthread_setname_np( pthread_self(), "gamescope-sdl" ); +@@ -944,6 +951,9 @@ namespace gamescope + + SDL_SetCursor( m_pCursor ); + } ++ else if ( event.type == GetUserEventIndex( GAMESCOPE_SDL_EVENT_REQ_EXIT ) ) { ++ return; ++ } + } + break; + } + +From 61392a9e545e0a3341c0d510aaa453c08826cc5b Mon Sep 17 00:00:00 2001 +From: sharkautarch <128002472+sharkautarch@users.noreply.github.com> +Date: Thu, 30 May 2024 14:02:00 -0400 +Subject: [PATCH 2/2] steamcompmgr, rendervulkan: prevent segfault that occured + when closing gamescope, due to a race condition between present thread @ + present_wait_thread_func & compositor thread @ steamcompmgr_exit + +--- + src/rendervulkan.cpp | 7 ++++++- + src/rendervulkan.hpp | 4 ++++ + src/steamcompmgr.cpp | 8 ++++++++ + 3 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp +index ffd0783da..094141839 100644 +--- a/src/rendervulkan.cpp ++++ b/src/rendervulkan.cpp +@@ -2669,7 +2669,8 @@ bool acquire_next_image( void ) + } + + +-static std::atomic g_currentPresentWaitId = {0u}; ++inline std::atomic g_currentPresentWaitId = {0u}; ++inline std::atomic g_presentThreadShouldExit = {false}; + static std::mutex present_wait_lock; + + extern void mangoapp_output_update( uint64_t vblanktime ); +@@ -2693,6 +2694,10 @@ static void present_wait_thread_func( void ) + uint64_t vblanktime = get_time_in_nanos(); + GetVBlankTimer().MarkVBlank( vblanktime, true ); + mangoapp_output_update( vblanktime ); ++ } else if ( g_presentThreadShouldExit.load(std::memory_order_acquire)) { ++ g_presentThreadShouldExit = 0; ++ g_presentThreadShouldExit.notify_all(); ++ return; + } + } + } +diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp +index 177468228..a2e0dd972 100644 +--- a/src/rendervulkan.hpp ++++ b/src/rendervulkan.hpp +@@ -74,6 +74,10 @@ enum EStreamColorspace : int + #include + #include + ++extern std::atomic g_currentPresentWaitId; ++ ++extern std::atomic g_presentThreadShouldExit; ++ + struct VulkanRenderer_t + { + struct wlr_renderer base; +diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp +index 910ed3912..f6539829e 100644 +--- a/src/steamcompmgr.cpp ++++ b/src/steamcompmgr.cpp +@@ -5840,6 +5840,14 @@ steamcompmgr_exit(void) + } + } + ++ //request the present_wait thread to exit ++ //needed to avoid getting a segfault at exit due to race condition: ++ g_presentThreadShouldExit.store(true, std::memory_order_release); ++ g_currentPresentWaitId = 0; //present thread will check if it should exit if this is zero ++ g_currentPresentWaitId.notify_all(); ++ g_presentThreadShouldExit.wait(true); //present thread will toggle this atomic when it sees the exit request ++ //this allows us to wait for present thread to close before deleting the backend ++ + gamescope::IBackend::Set( nullptr ); + + wlserver_lock(); diff --git a/patches/chimeraos.patch b/patches/chimeraos.patch new file mode 100644 index 0000000..e893b52 --- /dev/null +++ b/patches/chimeraos.patch @@ -0,0 +1,2027 @@ +From c06cdd847679c930ee6197514970bc21f523e853 Mon Sep 17 00:00:00 2001 +From: Matthew Anderson +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 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(std::round(tx)); ++ // Grab 2% of the display to be used for the edge range ++ uint32_t edge_range = static_cast(g_nOutputWidth * 0.02); ++ ++ // Determine if the gesture should start ++ if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) { ++ start_gesture = true; ++ } ++ ++ // 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 g_bPendingTouchMovement; ++extern gamescope::ConVar 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 +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 cv_hdr_enabled{ "hdr_enabled", false, "Whether or not HDR is enabled if it is available." }; + bool g_bHDRItmEnable = false; + int g_nCurrentRefreshRate_CachedValue = 0; ++gamescope::ConVar cv_bypass_steam_resolution{ "bypass_steam_resolution", false, "Workaround the 720p/800p limits Steam uses for games" }; ++ + + static void + update_color_mgmt() +@@ -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 +Date: Wed, 26 Jul 2023 20:46:29 -0500 +Subject: [PATCH 03/22] Add force external orientation. + +Co-authored-by: Bouke Sybren Haarsma +--- + 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 +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 +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 +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 g_customRefreshRates; ++// eg: 60,60,90,110-120 ++static std::vector parse_custom_refresh_rates( const char *str ) ++{ ++ std::vector rates; ++ char *token = strtok( strdup(str), ","); ++ while (token) ++ { ++ char *dash = strchr(token, '-'); ++ if (dash) ++ { ++ uint32_t start = atoi(token); ++ uint32_t end = atoi(dash + 1); ++ for (uint32_t i = start; i <= end; i++) ++ { ++ rates.push_back(i); ++ } ++ } ++ else ++ { ++ rates.push_back(atoi(token)); ++ } ++ token = strtok(nullptr, ","); ++ } ++ return rates; + } + + struct sigaction handle_signal_action = {}; +@@ -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 + + #include ++#include + + 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 g_customRefreshRates; + + enum class GamescopeUpscaleFilter : uint32_t + { +-- +2.45.2 + + +From db4804880ea0fdf810979aea3153e2f45dc97bfe Mon Sep 17 00:00:00 2001 +From: Matthew Anderson +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 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +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 +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 @@ + + + +- + + + +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 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_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 +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 +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 + + static LogScope wl_log("wlserver"); +- ++bool pending_gesture = false; ++bool pending_osk = false; + //#define GAMESCOPE_SWAPCHAIN_DEBUG + gamescope::ConVar cv_touch_gestures( "enable_touch_gestures", false, "Enable/Disable the usage of touch gestures" ); + extern GamescopePanelOrientation g_DesiredInternalOrientation; +@@ -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(std::round(tx)); +- // Grab 2% of the display to be used for the edge range +- uint32_t edge_range = static_cast(g_nOutputWidth * 0.02); +- +- // Determine if the gesture should start +- if (roundedCursorX <= edge_range || roundedCursorX >= g_nOutputWidth - edge_range) { +- start_gesture = true; +- } +- +- // Handle Home gesture +- if (start_gesture && roundedCursorX >= edge_range) { ++ uint32_t roundedCursorY = static_cast(std::round(ty)); ++ uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.02); ++ uint32_t edge_range_y = static_cast(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 +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 +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 + + 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 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(std::round(tx)); +- uint32_t roundedCursorY = static_cast(std::round(ty)); +- uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.02); +- uint32_t edge_range_y = static_cast(g_nOutputWidth * 0.02); ++ uint32_t rounded_tx = static_cast(std::round(tx)); ++ uint32_t rounded_ty = static_cast(std::round(ty)); ++ uint32_t edge_range_x = static_cast(g_nOutputWidth * 0.05); ++ uint32_t edge_range_y = static_cast(g_nOutputWidth * 0.05); + uint32_t gesture_limits_x = edge_range_x * 2; + uint32_t gesture_limits_y = edge_range_y * 2; ++ uint32_t threshold_distance_x = gesture_limits_x; ++ uint32_t threshold_distance_y = gesture_limits_y; + + // Left to Right and Right to Left +- if (!pending_gesture && 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 +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 g_bPendingTouchMovement; + extern gamescope::ConVar cv_touch_gestures; + + void wlserver_open_steam_menu( bool qam ); +- ++extern void drm_set_orientation( struct drm_t *drm, bool isRotated); ++extern drm_t g_DRM; + 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 +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 +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 +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 +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 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 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 +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 g_customRefreshRates; + // eg: 60,60,90,110-120 +-- +2.45.2 + + +From e528844eb590f8183fdfffaf9a7af39f33dc2213 Mon Sep 17 00:00:00 2001 +From: Bouke Sybren Haarsma +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 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& vecPossibleFocusWindows, bool globalFocus, const std::vector& 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?= + +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 cv_drm_debug_disable_blend_tf( "drm_debug_disable_blend_ + gamescope::ConVar cv_drm_debug_disable_ctm( "drm_debug_disable_ctm", false, "CTM chicken bit. (Forces CTM off, does not affect other logic)" ); + gamescope::ConVar cv_drm_debug_disable_color_encoding( "drm_debug_disable_color_encoding", false, "YUV Color Encoding chicken bit. (Forces COLOR_ENCODING to DEFAULT, does not affect other logic)" ); + gamescope::ConVar cv_drm_debug_disable_color_range( "drm_debug_disable_color_range", false, "YUV Color Range chicken bit. (Forces COLOR_RANGE to DEFAULT, does not affect other logic)" ); +-gamescope::ConVar cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", false, "Force disable explicit sync on the DRM backend." ); ++gamescope::ConVar cv_drm_debug_disable_explicit_sync( "drm_debug_disable_explicit_sync", true, "Force disable explicit sync on the DRM backend." ); + gamescope::ConVar cv_drm_debug_disable_in_fence_fd( "drm_debug_disable_in_fence_fd", false, "Force disable IN_FENCE_FD being set to avoid over-synchronization on the DRM backend." ); + + // HACK: +-- +2.45.2 + + +From 1dbcfed76f4b80d8a9f6570819b5af7917c786fc Mon Sep 17 00:00:00 2001 +From: Kyle Gospodnetich +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 +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/patches/deckhd.patch b/patches/deckhd.patch new file mode 100644 index 0000000..5140e85 --- /dev/null +++ b/patches/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/patches/disable-steam-touch-click-atom.patch b/patches/disable-steam-touch-click-atom.patch new file mode 100644 index 0000000..822b3a8 --- /dev/null +++ b/patches/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 cv_bypass_steam_resolution{ "bypass_steam_resolution", false, "Workaround the 720p/800p limits Steam uses for games" }; + ++gamescope::ConVar 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/patches/drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch b/patches/drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch new file mode 100644 index 0000000..f39afdb --- /dev/null +++ b/patches/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 +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/patches/revert-299bc34.patch b/patches/revert-299bc34.patch new file mode 100644 index 0000000..550870f --- /dev/null +++ b/patches/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/patches/series b/patches/series new file mode 100755 index 0000000..0ccd322 --- /dev/null +++ b/patches/series @@ -0,0 +1,8 @@ +chimeraos.patch +disable-steam-touch-click-atom.patch +deckhd.patch +drm-Separate-BOE-and-SDC-OLED-Deck-panel-rates.patch +revert-299bc34.patch +1335.patch +1231.patch +v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch diff --git a/patches/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch b/patches/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch new file mode 100644 index 0000000..b583936 --- /dev/null +++ b/patches/v2-0001-always-send-ctrl-1-2-to-steam-s-wayland-session.patch @@ -0,0 +1,40 @@ +From 35e001dc59a44227d670c667a85a6ef5472eee58 Mon Sep 17 00:00:00 2001 +From: antheas +Date: Sat, 20 Jul 2024 01:23:19 +0300 +Subject: [PATCH v2] always send ctrl+1/2 to steam's wayland session + +--- + src/wlserver.cpp | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/wlserver.cpp b/src/wlserver.cpp +index 1852be9..7de737d 100644 +--- a/src/wlserver.cpp ++++ b/src/wlserver.cpp +@@ -369,7 +369,12 @@ static void wlserver_handle_key(struct wl_listener *listener, void *data) + keysym == XKB_KEY_XF86AudioLowerVolume || + keysym == XKB_KEY_XF86AudioRaiseVolume || + keysym == XKB_KEY_XF86PowerOff; +- if ( ( event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->state == WL_KEYBOARD_KEY_STATE_RELEASED ) && forbidden_key ) ++ ++ // Check for steam keys (ctrl + 1/2) ++ bool is_steamshortcut = (keyboard->wlr->modifiers.depressed & WLR_MODIFIER_CTRL) && (keysym == XKB_KEY_1 || ++ keysym == XKB_KEY_2); ++ ++ if ( ( event->state == WL_KEYBOARD_KEY_STATE_PRESSED || event->state == WL_KEYBOARD_KEY_STATE_RELEASED ) && (forbidden_key || is_steamshortcut) ) + { + // Always send volume+/- to root server only, to avoid it reaching the game. + struct wlr_surface *old_kb_surf = wlserver.kb_focus_surface; +@@ -378,6 +383,9 @@ static void wlserver_handle_key(struct wl_listener *listener, void *data) + { + wlserver_keyboardfocus( new_kb_surf, false ); + wlr_seat_set_keyboard( wlserver.wlr.seat, keyboard->wlr ); ++ // Send modifiers to steam for it to work ++ if (is_steamshortcut) ++ wlr_seat_keyboard_notify_modifiers(wlserver.wlr.seat, &keyboard->wlr->modifiers); + wlr_seat_keyboard_notify_key( wlserver.wlr.seat, event->time_msec, event->keycode, event->state ); + wlserver_keyboardfocus( old_kb_surf, false ); + return; +-- +2.45.2 + diff --git a/pika-build-config/amd64-v3.sh b/pika-build-config/amd64-v3.sh new file mode 100755 index 0000000..10285b4 --- /dev/null +++ b/pika-build-config/amd64-v3.sh @@ -0,0 +1,10 @@ +#! /bin/bash +export PIKA_BUILD_ARCH="amd64-v3" +export DEBIAN_FRONTEND="noninteractive" +export DEB_BUILD_MAINT_OPTIONS="optimize=+lto -march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CPPFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CXXFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_LDFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_BUILD_OPTIONS="nocheck notest terse" +export DPKG_GENSYMBOLS_CHECK_LEVEL=0 diff --git a/pika-build-config/i386.sh b/pika-build-config/i386.sh new file mode 100755 index 0000000..7629d66 --- /dev/null +++ b/pika-build-config/i386.sh @@ -0,0 +1,5 @@ +#! /bin/bash +export PIKA_BUILD_ARCH="i386" +export DEBIAN_FRONTEND="noninteractive" +export DEB_BUILD_OPTIONS="nocheck notest terse" +export DPKG_GENSYMBOLS_CHECK_LEVEL=0 diff --git a/release.sh b/release.sh index 1575255..660f48f 100755 --- a/release.sh +++ b/release.sh @@ -1,8 +1,2 @@ # send debs to server -rsync -azP --include './' --include '*.deb' --exclude '*' ./output/ ferreo@direct.pika-os.com:/srv/www/incoming/ - -# add debs to repo -ssh ferreo@direct.pika-os.com 'aptly repo add -force-replace -remove-files pikauwu-main /srv/www/incoming/' - -# publish the repo -ssh ferreo@direct.pika-os.com 'aptly publish update -batch -skip-contents -force-overwrite pikauwu filesystem:pikarepo:' \ No newline at end of file +rsync -azP --include './' --include '*.deb' --exclude '*' ./output/ ferreo@direct.pika-os.com:/srv/www/cockatiel-incoming/