From 02554be16a3b7dcbc74a08968c1cc12e23091bc0 Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Fri, 6 Oct 2023 17:22:41 -0500 Subject: [PATCH 01/14] Add initial rotation atom controls --- src/drm.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ src/drm.hpp | 11 +++++++++++ src/steamcompmgr.cpp | 32 ++++++++++++++++++++++++++++++++ src/xwayland_ctx.hpp | 1 + 4 files changed, 86 insertions(+) diff --git a/src/drm.cpp b/src/drm.cpp index 400109ac16..8e921f9cd2 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -45,6 +45,7 @@ struct drm_t g_DRM = {}; uint32_t g_nDRMFormat = DRM_FORMAT_INVALID; uint32_t g_nDRMFormatOverlay = DRM_FORMAT_INVALID; // for partial composition, we may have more limited formats than base planes + alpha. bool g_bRotated = false; +bool g_rotate_ctl_enable = false; bool g_bUseLayers = true; bool g_bDebugLayers = false; const char *g_sOutputName = nullptr; @@ -65,6 +66,7 @@ bool g_bSupportsAsyncFlips = false; enum drm_mode_generation g_drmModeGeneration = DRM_MODE_GENERATE_CVT; enum g_panel_orientation g_drmModeOrientation = PANEL_ORIENTATION_AUTO; +enum g_rotate_ctl g_drmRotateCTL; std::atomic g_drmEffectiveOrientation[DRM_SCREEN_TYPE_COUNT]{ {DRM_MODE_ROTATE_0}, {DRM_MODE_ROTATE_0} }; bool g_bForceDisableColorMgmt = false; @@ -2001,6 +2003,27 @@ static void update_drm_effective_orientation(struct drm_t *drm, struct connector static void update_drm_effective_orientations(struct drm_t *drm, struct connector *conn, const drmModeModeInfo *mode) { drm_screen_type screenType = drm_get_connector_type(conn->connector); + + if (g_rotate_ctl_enable) + { + switch (g_drmRotateCTL) + { + default: + case NORMAL: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_0; + break; + case LEFT_UP: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_90; + break; + case UPSIDEDOWN: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_180; + break; + case RIGHT_UP: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_270; + break; + } + return; + } if (screenType == DRM_SCREEN_TYPE_INTERNAL) { update_drm_effective_orientation(drm, conn, mode); @@ -3074,6 +3097,25 @@ bool drm_set_refresh( struct drm_t *drm, int refresh ) return drm_set_mode(drm, &mode); } +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->connector || !drm->connector->connector) + return; + + drmModeConnector *connector = drm->connector->connector; + const drmModeModeInfo *mode = find_mode(connector, width, height, 0); + update_drm_effective_orientations(drm, drm->connector, mode); +} + bool drm_set_resolution( struct drm_t *drm, int width, int height ) { if (!drm->connector || !drm->connector->connector) diff --git a/src/drm.hpp b/src/drm.hpp index 4840e2b8d5..b6d88494a3 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -335,13 +335,24 @@ enum g_panel_orientation { PANEL_ORIENTATION_AUTO, }; +enum g_rotate_ctl{ + NORMAL, + LEFT_UP, + UPSIDEDOWN, + RIGHT_UP, +}; + extern enum drm_mode_generation g_drmModeGeneration; extern enum g_panel_orientation g_drmModeOrientation; +extern enum g_rotate_ctl g_drmRotateCTL; +extern bool g_rotate_ctl_enable; extern std::atomic g_drmEffectiveOrientation[DRM_SCREEN_TYPE_COUNT]; // DRM_MODE_ROTATE_* extern bool g_bForceDisableColorMgmt; +void drm_set_orientation( struct drm_t *drm, bool isRotated ); + bool init_drm(struct drm_t *drm, int width, int height, int refresh, bool wants_adaptive_sync); void finish_drm(struct drm_t *drm); int drm_commit(struct drm_t *drm, const struct FrameInfo_t *frameInfo ); diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 2042b91258..5837bc60af 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5644,6 +5644,37 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) if ( g_upscaleFilter == GamescopeUpscaleFilter::FSR || g_upscaleFilter == GamescopeUpscaleFilter::NIS ) hasRepaint = true; } + if ( ev->atom == ctx->atoms.gamescopeRotateControl ) + { + std::vector< uint32_t > drm_rot_ctl; + bool rotate = get_prop( ctx, ctx->root, ctx->atoms.gamescopeRotateControl, drm_rot_ctl ); + bool rotated = false; + if ( rotate && drm_rot_ctl.size() == 1 ) + { + xwm_log.debugf("drm_rot_ctl %d", drm_rot_ctl[0]); + g_rotate_ctl_enable = true; + switch ( drm_rot_ctl[0] ) + { + case 0: + g_drmRotateCTL = NORMAL; + rotated = false; + break; + case 1: + g_drmRotateCTL = LEFT_UP; + rotated = true; + break; + case 2: + g_drmRotateCTL = UPSIDEDOWN; + rotated = false; + break; + case 3: + g_drmRotateCTL = RIGHT_UP; + rotated = true; + break; + } + drm_set_orientation(&g_DRM, rotated); + } + } if ( ev->atom == ctx->atoms.gamescopeXWaylandModeControl ) { std::vector< uint32_t > xwayland_mode_ctl; @@ -7248,6 +7279,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ ctx->atoms.gamescopeFSRSharpness = XInternAtom( ctx->dpy, "GAMESCOPE_FSR_SHARPNESS", false ); ctx->atoms.gamescopeSharpness = XInternAtom( ctx->dpy, "GAMESCOPE_SHARPNESS", false ); + ctx->atoms.gamescopeRotateControl = XInternAtom( ctx->dpy, "GAMESCOPE_ROTATE_CONTROL", false ); ctx->atoms.gamescopeXWaylandModeControl = XInternAtom( ctx->dpy, "GAMESCOPE_XWAYLAND_MODE_CONTROL", false ); ctx->atoms.gamescopeFPSLimit = XInternAtom( ctx->dpy, "GAMESCOPE_FPS_LIMIT", false ); ctx->atoms.gamescopeDynamicRefresh[DRM_SCREEN_TYPE_INTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_DYNAMIC_REFRESH", false ); diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index 5764c4b7bf..6231007482 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -146,6 +146,7 @@ struct xwayland_ctx_t final : public gamescope::IWaitable Atom gamescopeFSRSharpness; Atom gamescopeSharpness; + Atom gamescopeRotateControl; Atom gamescopeXWaylandModeControl; Atom gamescopeFPSLimit; From b5be70c5cf0578e54ca10cd704dd0b92ce6f9029 Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Sat, 7 Oct 2023 10:38:09 -0500 Subject: [PATCH 02/14] Flag drm_out_of_date to ensure rotation logic gets reset --- src/steamcompmgr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 5837bc60af..3e6dfd8404 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5673,6 +5673,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) break; } drm_set_orientation(&g_DRM, rotated); + g_DRM.out_of_date = 2; } } if ( ev->atom == ctx->atoms.gamescopeXWaylandModeControl ) From fbdf922912918717afc9d2e92505ace7e0fb8ea2 Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Tue, 25 Apr 2023 06:45:01 -0500 Subject: [PATCH 03/14] Add --force-panel-type and --force-external-orientation arguments (#2) * Add --force-panel-type and --force-external-orientation arguments. * Rotate only the internal display when faked as "external" * Try to prevent the external display from being rotated when --force-panel-type external is used. * Fixed docking issue when --force-panel-type external is used and you dock/undock the handheld. --- src/drm.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++------- src/drm.hpp | 15 +++++++++++++++ src/main.cpp | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/drm.cpp b/src/drm.cpp index 8e921f9cd2..703cf2a695 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -46,6 +46,7 @@ uint32_t g_nDRMFormat = DRM_FORMAT_INVALID; uint32_t g_nDRMFormatOverlay = DRM_FORMAT_INVALID; // for partial composition, we may have more limited formats than base planes + alpha. bool g_bRotated = false; bool g_rotate_ctl_enable = false; +bool g_bDisplayTypeInternal = false; bool g_bUseLayers = true; bool g_bDebugLayers = false; const char *g_sOutputName = nullptr; @@ -68,6 +69,8 @@ enum drm_mode_generation g_drmModeGeneration = DRM_MODE_GENERATE_CVT; enum g_panel_orientation g_drmModeOrientation = PANEL_ORIENTATION_AUTO; enum g_rotate_ctl g_drmRotateCTL; std::atomic g_drmEffectiveOrientation[DRM_SCREEN_TYPE_COUNT]{ {DRM_MODE_ROTATE_0}, {DRM_MODE_ROTATE_0} }; +enum g_panel_external_orientation g_drmModeExternalOrientation = PANEL_EXTERNAL_ORIENTATION_AUTO; +enum g_panel_type g_drmPanelType = PANEL_TYPE_AUTO; bool g_bForceDisableColorMgmt = false; @@ -1972,8 +1975,29 @@ static uint64_t determine_drm_orientation(struct drm_t *drm, struct connector *c static void update_drm_effective_orientation(struct drm_t *drm, struct connector *conn, const drmModeModeInfo *mode) { drm_screen_type screenType = drm_get_connector_type(conn->connector); - - if (screenType == DRM_SCREEN_TYPE_INTERNAL) + if ( screenType == DRM_SCREEN_TYPE_EXTERNAL && g_bDisplayTypeInternal == true ) + { + switch ( g_drmModeExternalOrientation ) + { + case PANEL_EXTERNAL_ORIENTATION_0: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_0; + break; + case PANEL_EXTERNAL_ORIENTATION_90: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_90; + break; + case PANEL_EXTERNAL_ORIENTATION_180: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_180; + break; + case PANEL_EXTERNAL_ORIENTATION_270: + g_drmEffectiveOrientation[screenType] = DRM_MODE_ROTATE_270; + break; + case PANEL_EXTERNAL_ORIENTATION_AUTO: + g_drmEffectiveOrientation[screenType] = determine_drm_orientation(drm, conn, mode); + break; + } + return; + } + else if ( screenType == DRM_SCREEN_TYPE_INTERNAL ) { switch ( g_drmModeOrientation ) { @@ -2924,11 +2948,27 @@ bool drm_get_vrr_in_use(struct drm_t *drm) drm_screen_type drm_get_connector_type(drmModeConnector *connector) { - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || - connector->connector_type == DRM_MODE_CONNECTOR_LVDS || - connector->connector_type == DRM_MODE_CONNECTOR_DSI) - return DRM_SCREEN_TYPE_INTERNAL; - + // Set to the default state of false to make sure the external display isn't rotated when a system is docked + g_bDisplayTypeInternal = false; + switch ( g_drmPanelType ) + { + case PANEL_TYPE_INTERNAL: + return DRM_SCREEN_TYPE_INTERNAL; + break; + case PANEL_TYPE_EXTERNAL: + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS || + connector->connector_type == DRM_MODE_CONNECTOR_DSI) + g_bDisplayTypeInternal = true; + return DRM_SCREEN_TYPE_EXTERNAL; + break; + case PANEL_TYPE_AUTO: + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP || + connector->connector_type == DRM_MODE_CONNECTOR_LVDS || + connector->connector_type == DRM_MODE_CONNECTOR_DSI) + return DRM_SCREEN_TYPE_INTERNAL; + break; + } return DRM_SCREEN_TYPE_EXTERNAL; } diff --git a/src/drm.hpp b/src/drm.hpp index b6d88494a3..f542e70c23 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -341,11 +341,26 @@ enum g_rotate_ctl{ UPSIDEDOWN, RIGHT_UP, }; +enum g_panel_external_orientation { + PANEL_EXTERNAL_ORIENTATION_0, /* NORMAL */ + PANEL_EXTERNAL_ORIENTATION_270, /* RIGHT */ + PANEL_EXTERNAL_ORIENTATION_90, /* LEFT */ + PANEL_EXTERNAL_ORIENTATION_180, /* UPSIDE DOWN */ + PANEL_EXTERNAL_ORIENTATION_AUTO, +}; + +enum g_panel_type { + PANEL_TYPE_INTERNAL, + PANEL_TYPE_EXTERNAL, + PANEL_TYPE_AUTO, +}; extern enum drm_mode_generation g_drmModeGeneration; extern enum g_panel_orientation g_drmModeOrientation; extern enum g_rotate_ctl g_drmRotateCTL; extern bool g_rotate_ctl_enable; +extern enum g_panel_external_orientation g_drmModeExternalOrientation; +extern enum g_panel_type g_drmPanelType; extern std::atomic g_drmEffectiveOrientation[DRM_SCREEN_TYPE_COUNT]; // DRM_MODE_ROTATE_* diff --git a/src/main.cpp b/src/main.cpp index 76721d63d2..f6ba34f2f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -118,6 +118,8 @@ const struct option *gamescope_options = (struct option[]){ { "disable-xres", no_argument, nullptr, 'x' }, { "fade-out-duration", required_argument, nullptr, 0 }, { "force-orientation", required_argument, nullptr, 0 }, + { "force-external-orientation", required_argument, nullptr, 0 }, + { "force-panel-type", required_argument, nullptr, 0 }, { "force-windows-fullscreen", no_argument, nullptr, 0 }, { "disable-color-management", no_argument, nullptr, 0 }, @@ -167,6 +169,8 @@ 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-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" @@ -353,6 +357,18 @@ static enum drm_mode_generation parse_drm_mode_generation(const char *str) } } +static enum g_panel_type force_panel_type(const char *str) +{ + if (strcmp(str, "internal") == 0) { + return PANEL_TYPE_INTERNAL; + } else if (strcmp(str, "external") == 0) { + return PANEL_TYPE_EXTERNAL; + } else { + fprintf( stderr, "gamescope: invalid value for --force-panel-type\n" ); + exit(1); + } +} + static enum g_panel_orientation force_orientation(const char *str) { if (strcmp(str, "normal") == 0) { @@ -408,6 +424,22 @@ static enum GamescopeUpscaleFilter parse_upscaler_filter(const char *str) struct sigaction handle_signal_action = {}; extern pid_t child_pid; +static enum g_panel_external_orientation force_external_orientation(const char *str) +{ + if (strcmp(str, "normal") == 0) { + return PANEL_EXTERNAL_ORIENTATION_0; + } else if (strcmp(str, "right") == 0) { + return PANEL_EXTERNAL_ORIENTATION_270; + } else if (strcmp(str, "left") == 0) { + return PANEL_EXTERNAL_ORIENTATION_90; + } else if (strcmp(str, "upsidedown") == 0) { + return PANEL_EXTERNAL_ORIENTATION_180; + } else { + fprintf( stderr, "gamescope: invalid value for --force-external-orientation\n" ); + exit(1); + } +} + static void handle_signal( int sig ) { switch ( sig ) { @@ -614,6 +646,10 @@ int main(int argc, char **argv) g_drmModeGeneration = parse_drm_mode_generation( optarg ); } else if (strcmp(opt_name, "force-orientation") == 0) { g_drmModeOrientation = force_orientation( optarg ); + } else if (strcmp(opt_name, "force-external-orientation") == 0) { + g_drmModeExternalOrientation = force_external_orientation( optarg ); + } else if (strcmp(opt_name, "force-panel-type") == 0) { + g_drmPanelType = force_panel_type( optarg ); } else if (strcmp(opt_name, "sharpness") == 0 || strcmp(opt_name, "fsr-sharpness") == 0) { g_upscaleFilterSharpness = atoi( optarg ); From 6f3cb530995b98784ece3956f85dbfe98e2e4040 Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Tue, 25 Jul 2023 18:05:05 -0500 Subject: [PATCH 04/14] Set default to native resolution of display if Steam tries to force 720p/800p You can select 720p/800p still in game or via Steam's resolution setting Steam > Settings > Display > Resolution This effectively reverts the changes Valve made a year ago forcing us to 720p. --- src/steamcompmgr.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 3e6dfd8404..e86c4d840a 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5685,6 +5685,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 ) + { + width = g_nOutputWidth; + height = g_nOutputHeight; + } + bool allowSuperRes = !!xwayland_mode_ctl[ 3 ]; if ( !allowSuperRes ) From 7f6668422d70da3d7286da0c569966d433bc7a61 Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Wed, 26 Jul 2023 20:46:29 -0500 Subject: [PATCH 05/14] Fix internal display touchscreen orientation when it's forced --- src/main.cpp | 6 ++++++ src/main.hpp | 2 ++ src/wlserver.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index f6ba34f2f6..17409b5dae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -269,6 +269,8 @@ bool g_bHeadless = false; bool g_bGrabbed = false; +bool g_bExternalForced = false; + GamescopeUpscaleFilter g_upscaleFilter = GamescopeUpscaleFilter::LINEAR; GamescopeUpscaleScaler g_upscaleScaler = GamescopeUpscaleScaler::AUTO; @@ -427,12 +429,16 @@ extern pid_t child_pid; static enum g_panel_external_orientation force_external_orientation(const char *str) { if (strcmp(str, "normal") == 0) { + g_bExternalForced = true; return PANEL_EXTERNAL_ORIENTATION_0; } else if (strcmp(str, "right") == 0) { + g_bExternalForced = true; return PANEL_EXTERNAL_ORIENTATION_270; } else if (strcmp(str, "left") == 0) { + g_bExternalForced = true; return PANEL_EXTERNAL_ORIENTATION_90; } else if (strcmp(str, "upsidedown") == 0) { + g_bExternalForced = true; return PANEL_EXTERNAL_ORIENTATION_180; } else { fprintf( stderr, "gamescope: invalid value for --force-external-orientation\n" ); diff --git a/src/main.hpp b/src/main.hpp index 7d8e9f1bee..97ec0a8114 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -23,6 +23,8 @@ extern bool g_bFullscreen; extern bool g_bGrabbed; +extern bool g_bExternalForced; + enum class GamescopeUpscaleFilter : uint32_t { LINEAR = 0, diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 3fbc4ff581..dc37f977eb 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -1976,6 +1976,31 @@ static void apply_touchscreen_orientation(double *x, double *y ) ty = 1.0 - *x; break; } + // Rotate screen if it's forced with --force-external-orientation + + if ( g_bExternalForced == true) + { + switch ( g_drmEffectiveOrientation[DRM_SCREEN_TYPE_EXTERNAL] ) + { + default: + case DRM_MODE_ROTATE_0: + tx = *x; + ty = *y; + break; + case DRM_MODE_ROTATE_90: + tx = 1.0 - *y; + ty = *x; + break; + case DRM_MODE_ROTATE_180: + tx = 1.0 - *x; + ty = 1.0 - *y; + break; + case DRM_MODE_ROTATE_270: + tx = *y; + ty = 1.0 - *x; + break; + } + } *x = tx; *y = ty; From f8a54aedfd750c7825f9e807f0292eba7069385c Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Fri, 6 Oct 2023 23:58:17 -0500 Subject: [PATCH 06/14] Add initial display selection atom --- src/drm.cpp | 20 +++++++++++++ src/drm.hpp | 1 + src/steamcompmgr.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/xwayland_ctx.hpp | 1 + 4 files changed, 91 insertions(+) diff --git a/src/drm.cpp b/src/drm.cpp index 703cf2a695..d53e0ae339 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -50,6 +50,7 @@ bool g_bDisplayTypeInternal = false; bool g_bUseLayers = true; bool g_bDebugLayers = false; const char *g_sOutputName = nullptr; +char* targetConnector = (char*)"eDP-1"; #ifndef DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP #define DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP 0x15 @@ -1245,6 +1246,19 @@ static bool setup_best_connector(struct drm_t *drm, bool force, bool initial) } } + for (auto &kv : drm->connectors) { + struct connector *conn = &kv.second; + drm_log.debugf("force set adapter"); + drm_log.debugf("conn->name: %s", conn->name); + drm_log.debugf("targetConnector: %s", targetConnector); + if (strcmp(conn->name, targetConnector) == 0) + { + drm_log.debugf("target was found!!!"); + drm_log.infof(" %s (%s)", conn->name, targetConnector); + best = conn; + } + } + if (!force) { if ((!best && drm->connector) || (best && best == drm->connector)) { // Let's keep our current connector @@ -2898,6 +2912,12 @@ static bool drm_set_crtc( struct drm_t *drm, struct crtc *crtc ) return true; } +void drm_set_prefered_connector( struct drm_t *drm, char* name ) +{ + drm_log.infof("selecting prefered connector %s", name); + targetConnector = name; +} + bool drm_set_connector( struct drm_t *drm, struct connector *conn ) { drm_log.infof("selecting connector %s", conn->name); diff --git a/src/drm.hpp b/src/drm.hpp index f542e70c23..0da6880f2a 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -378,6 +378,7 @@ uint32_t drm_fbid_from_dmabuf( struct drm_t *drm, struct wlr_buffer *buf, struct void drm_lock_fbid( struct drm_t *drm, uint32_t fbid ); void drm_unlock_fbid( struct drm_t *drm, uint32_t fbid ); void drm_drop_fbid( struct drm_t *drm, uint32_t fbid ); +void drm_set_prefered_connector( struct drm_t *drm, char* name ); bool drm_set_connector( struct drm_t *drm, struct connector *conn ); bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ); bool drm_set_refresh( struct drm_t *drm, int refresh ); diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index e86c4d840a..7f0e4a00dd 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5719,6 +5719,74 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) } } } + if ( ev->atom == ctx->atoms.gamescopeConnectorControl ) + { + std::vector< uint32_t > connector_ctl; + bool hasConnectorCtrl = get_prop( ctx, ctx->root, ctx->atoms.gamescopeConnectorControl, connector_ctl ); + char* adapter_type; + if ( hasConnectorCtrl && connector_ctl.size() == 1 ) + { + switch (connector_ctl[0]) + { + case 0: + adapter_type = (char*)"eDP-1"; + break; + case 1: + adapter_type = (char*)"eDP-2"; + break; + case 2: + adapter_type = (char*)"eDP-3"; + break; + case 3: + adapter_type = (char*)"DP-1"; + break; + case 4: + adapter_type = (char*)"DP-2"; + break; + case 5: + adapter_type = (char*)"DP-3"; + break; + case 6: + adapter_type = (char*)"HDMI-A-1"; + break; + case 7: + adapter_type = (char*)"HDMI-A-2"; + break; + case 8: + adapter_type = (char*)"HDMI-A-3"; + break; + case 9: + adapter_type = (char*)"HDMI-B-1"; + break; + case 10: + adapter_type = (char*)"HDMI-B-2"; + break; + case 11: + adapter_type = (char*)"HDMI-B-3"; + break; + case 12: + adapter_type = (char*)"HDMI-C-1"; + break; + case 13: + adapter_type = (char*)"HDMI-C-2"; + break; + case 14: + adapter_type = (char*)"HDMI-C-3"; + break; + case 15: + adapter_type = (char*)"DSI-1"; + break; + case 16: + adapter_type = (char*)"DSI-2"; + break; + case 17: + adapter_type = (char*)"DSI-3"; + break; + } + g_DRM.out_of_date = 2; + drm_set_prefered_connector(&g_DRM, adapter_type); + } + } if ( ev->atom == ctx->atoms.gamescopeFPSLimit ) { g_nSteamCompMgrTargetFPS = get_prop( ctx, ctx->root, ctx->atoms.gamescopeFPSLimit, 0 ); @@ -7289,6 +7357,7 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ ctx->atoms.gamescopeRotateControl = XInternAtom( ctx->dpy, "GAMESCOPE_ROTATE_CONTROL", false ); ctx->atoms.gamescopeXWaylandModeControl = XInternAtom( ctx->dpy, "GAMESCOPE_XWAYLAND_MODE_CONTROL", false ); + ctx->atoms.gamescopeConnectorControl = XInternAtom(ctx->dpy, "GAMESCOPE_CONNECTOR_CONTROL", false ); ctx->atoms.gamescopeFPSLimit = XInternAtom( ctx->dpy, "GAMESCOPE_FPS_LIMIT", false ); ctx->atoms.gamescopeDynamicRefresh[DRM_SCREEN_TYPE_INTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_DYNAMIC_REFRESH", false ); ctx->atoms.gamescopeDynamicRefresh[DRM_SCREEN_TYPE_EXTERNAL] = XInternAtom( ctx->dpy, "GAMESCOPE_DYNAMIC_REFRESH_EXTERNAL", false ); diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index 6231007482..9dbc544acf 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -149,6 +149,7 @@ struct xwayland_ctx_t final : public gamescope::IWaitable Atom gamescopeRotateControl; Atom gamescopeXWaylandModeControl; + Atom gamescopeConnectorControl; Atom gamescopeFPSLimit; Atom gamescopeDynamicRefresh[DRM_SCREEN_TYPE_COUNT]; Atom gamescopeLowLatency; From 65e67653f2410b4ffc78d0b8055db92ec95fc146 Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Mon, 9 Oct 2023 11:21:11 -0500 Subject: [PATCH 07/14] Use sysfs connector_ids for target device selection. --- src/drm.cpp | 14 +++-------- src/drm.hpp | 2 +- src/steamcompmgr.cpp | 60 +------------------------------------------- 3 files changed, 6 insertions(+), 70 deletions(-) diff --git a/src/drm.cpp b/src/drm.cpp index d53e0ae339..acff5e5378 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -50,7 +50,7 @@ bool g_bDisplayTypeInternal = false; bool g_bUseLayers = true; bool g_bDebugLayers = false; const char *g_sOutputName = nullptr; -char* targetConnector = (char*)"eDP-1"; +uint32_t targetConnector; #ifndef DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP #define DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP 0x15 @@ -1248,13 +1248,8 @@ static bool setup_best_connector(struct drm_t *drm, bool force, bool initial) for (auto &kv : drm->connectors) { struct connector *conn = &kv.second; - drm_log.debugf("force set adapter"); - drm_log.debugf("conn->name: %s", conn->name); - drm_log.debugf("targetConnector: %s", targetConnector); - if (strcmp(conn->name, targetConnector) == 0) + if ( conn->id == targetConnector) { - drm_log.debugf("target was found!!!"); - drm_log.infof(" %s (%s)", conn->name, targetConnector); best = conn; } } @@ -2912,10 +2907,9 @@ static bool drm_set_crtc( struct drm_t *drm, struct crtc *crtc ) return true; } -void drm_set_prefered_connector( struct drm_t *drm, char* name ) +void drm_set_prefered_connector( struct drm_t *drm, uint32_t connector_type_id ) { - drm_log.infof("selecting prefered connector %s", name); - targetConnector = name; + targetConnector = connector_type_id; } bool drm_set_connector( struct drm_t *drm, struct connector *conn ) diff --git a/src/drm.hpp b/src/drm.hpp index 0da6880f2a..6033eba559 100644 --- a/src/drm.hpp +++ b/src/drm.hpp @@ -378,7 +378,7 @@ uint32_t drm_fbid_from_dmabuf( struct drm_t *drm, struct wlr_buffer *buf, struct void drm_lock_fbid( struct drm_t *drm, uint32_t fbid ); void drm_unlock_fbid( struct drm_t *drm, uint32_t fbid ); void drm_drop_fbid( struct drm_t *drm, uint32_t fbid ); -void drm_set_prefered_connector( struct drm_t *drm, char* name ); +void drm_set_prefered_connector( struct drm_t *drm, uint32_t connector_type_id ); bool drm_set_connector( struct drm_t *drm, struct connector *conn ); bool drm_set_mode( struct drm_t *drm, const drmModeModeInfo *mode ); bool drm_set_refresh( struct drm_t *drm, int refresh ); diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 7f0e4a00dd..a544f27769 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -5723,68 +5723,10 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) { std::vector< uint32_t > connector_ctl; bool hasConnectorCtrl = get_prop( ctx, ctx->root, ctx->atoms.gamescopeConnectorControl, connector_ctl ); - char* adapter_type; if ( hasConnectorCtrl && connector_ctl.size() == 1 ) { - switch (connector_ctl[0]) - { - case 0: - adapter_type = (char*)"eDP-1"; - break; - case 1: - adapter_type = (char*)"eDP-2"; - break; - case 2: - adapter_type = (char*)"eDP-3"; - break; - case 3: - adapter_type = (char*)"DP-1"; - break; - case 4: - adapter_type = (char*)"DP-2"; - break; - case 5: - adapter_type = (char*)"DP-3"; - break; - case 6: - adapter_type = (char*)"HDMI-A-1"; - break; - case 7: - adapter_type = (char*)"HDMI-A-2"; - break; - case 8: - adapter_type = (char*)"HDMI-A-3"; - break; - case 9: - adapter_type = (char*)"HDMI-B-1"; - break; - case 10: - adapter_type = (char*)"HDMI-B-2"; - break; - case 11: - adapter_type = (char*)"HDMI-B-3"; - break; - case 12: - adapter_type = (char*)"HDMI-C-1"; - break; - case 13: - adapter_type = (char*)"HDMI-C-2"; - break; - case 14: - adapter_type = (char*)"HDMI-C-3"; - break; - case 15: - adapter_type = (char*)"DSI-1"; - break; - case 16: - adapter_type = (char*)"DSI-2"; - break; - case 17: - adapter_type = (char*)"DSI-3"; - break; - } g_DRM.out_of_date = 2; - drm_set_prefered_connector(&g_DRM, adapter_type); + drm_set_prefered_connector(&g_DRM, connector_ctl[0]); } } if ( ev->atom == ctx->atoms.gamescopeFPSLimit ) From 67567c9c3121de1e9189343aeeab94b7228a1b1f Mon Sep 17 00:00:00 2001 From: Matthew Anderson Date: Tue, 16 Jan 2024 13:57:50 -0600 Subject: [PATCH 08/14] Add edge gesture support to open Home and QAM --- src/wlserver.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/wlserver.cpp b/src/wlserver.cpp index dc37f977eb..e7fb7c9eb5 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -2048,8 +2048,34 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time ) if ( get_effective_touch_mode() == WLSERVER_TOUCH_CLICK_PASSTHROUGH ) { + bool start_gesture = false; wlr_seat_touch_notify_motion( wlserver.wlr.seat, time, touch_id, wlserver.mouse_surface_cursorx, wlserver.mouse_surface_cursory ); - } + + // Round the x-coordinate to the nearest whole number + uint32_t roundedCursorX = static_cast(std::round(wlserver.mouse_surface_cursorx)); + // Grab 2% of the display to be used for the edge range + double edge_range = g_nOutputWidth * 0.02; + + // if the touch cursor x position is less or equal to the range then start the gesture for left to right + if (roundedCursorX <= edge_range) { + start_gesture = true; + } + // if the touch cursor x position is the output width minus the edge range value then we are doing right to left + if (roundedCursorX >= g_nOutputWidth - edge_range) { + start_gesture = true; + } + // when the gesture is started and we are moving to the end of the edge range open home + if (start_gesture && roundedCursorX >= 1 && roundedCursorX <= edge_range) { + wl_log.infof("Detected Home gesture"); + wlserver_open_steam_menu(0); + start_gesture = false; + } + // when the gesture is started and we are moving from the output width minus the edge range to the output width open QAM + if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth ) { + wl_log.infof("Detected QAM gesture"); + wlserver_open_steam_menu(1); + start_gesture = false; + } } else if ( get_effective_touch_mode() == WLSERVER_TOUCH_CLICK_DISABLED ) { return; From 7f112d556430e1f814c4646b427bf71ae5b0de77 Mon Sep 17 00:00:00 2001 From: Bouke Sybren Haarsma Date: Wed, 3 Jan 2024 17:03:04 +0100 Subject: [PATCH 09/14] remove hacky texture This will use more hardware planes, causing some devices to composite yeilding lower framerates --- src/steamcompmgr.cpp | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index a544f27769..00c00e94d2 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2540,35 +2540,6 @@ paint_all(bool async) if ( overlay == global_focus.inputFocusWindow ) update_touch_scaling( &frameInfo ); } - else - { - auto tex = vulkan_get_hacky_blank_texture(); - if ( !BIsNested() && 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->fbid = tex->fbid(); - - layer->filter = GamescopeUpscaleFilter::NEAREST; - layer->blackBorder = true; - } - } if (notification) { From 0e7329bd8275e46705a97a584c548a3998d08a25 Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Fri, 9 Feb 2024 16:55:04 -0800 Subject: [PATCH 10/14] Add environment variable to disable touch gestures Requested by some of our users due to them accidentally firing them off. Defaults to gestures enabled --- src/wlserver.cpp | 54 ++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/wlserver.cpp b/src/wlserver.cpp index e7fb7c9eb5..31e0998a83 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -2048,34 +2048,38 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time ) if ( get_effective_touch_mode() == WLSERVER_TOUCH_CLICK_PASSTHROUGH ) { - bool start_gesture = false; wlr_seat_touch_notify_motion( wlserver.wlr.seat, time, touch_id, wlserver.mouse_surface_cursorx, wlserver.mouse_surface_cursory ); - // Round the x-coordinate to the nearest whole number - uint32_t roundedCursorX = static_cast(std::round(wlserver.mouse_surface_cursorx)); - // Grab 2% of the display to be used for the edge range - double edge_range = g_nOutputWidth * 0.02; - - // if the touch cursor x position is less or equal to the range then start the gesture for left to right - if (roundedCursorX <= edge_range) { - start_gesture = true; - } - // if the touch cursor x position is the output width minus the edge range value then we are doing right to left - if (roundedCursorX >= g_nOutputWidth - edge_range) { - start_gesture = true; - } - // when the gesture is started and we are moving to the end of the edge range open home - if (start_gesture && roundedCursorX >= 1 && roundedCursorX <= edge_range) { - wl_log.infof("Detected Home gesture"); - wlserver_open_steam_menu(0); - start_gesture = false; + if(!getenv("GAMESCOPE_DISABLE_TOUCH_GESTURES")) { + bool start_gesture = false; + + // Round the x-coordinate to the nearest whole number + uint32_t roundedCursorX = static_cast(std::round(wlserver.mouse_surface_cursorx)); + // Grab 2% of the display to be used for the edge range + double edge_range = g_nOutputWidth * 0.02; + + // if the touch cursor x position is less or equal to the range then start the gesture for left to right + if (roundedCursorX <= edge_range) { + start_gesture = true; + } + // if the touch cursor x position is the output width minus the edge range value then we are doing right to left + if (roundedCursorX >= g_nOutputWidth - edge_range) { + start_gesture = true; + } + // when the gesture is started and we are moving to the end of the edge range open home + if (start_gesture && roundedCursorX >= 1 && roundedCursorX <= edge_range) { + wl_log.infof("Detected Home gesture"); + wlserver_open_steam_menu(0); + start_gesture = false; + } + // when the gesture is started and we are moving from the output width minus the edge range to the output width open QAM + if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth ) { + wl_log.infof("Detected QAM gesture"); + wlserver_open_steam_menu(1); + start_gesture = false; + } } - // when the gesture is started and we are moving from the output width minus the edge range to the output width open QAM - if (start_gesture && roundedCursorX >= g_nOutputWidth - edge_range && roundedCursorX <= g_nOutputWidth ) { - wl_log.infof("Detected QAM gesture"); - wlserver_open_steam_menu(1); - start_gesture = false; - } } + } else if ( get_effective_touch_mode() == WLSERVER_TOUCH_CLICK_DISABLED ) { return; From b89d8a71c173668c742cc2d735d35c202009361c Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Sat, 10 Feb 2024 10:25:46 -0800 Subject: [PATCH 11/14] Fix refresh rate control on the Legion Go Forces the Legion Go to only use 60 or 144, and none of the invalid steps between. Allows both combined and separated frame controls in Steam Game mode to work. --- src/drm.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/drm.cpp b/src/drm.cpp index acff5e5378..fdf58eefc3 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -101,6 +101,12 @@ static uint32_t galileo_display_rates[] = 90, }; +static uint32_t legion_go_display_rates[] = +{ + 60, + 144, +}; + static uint32_t get_conn_display_info_flags(struct drm_t *drm, struct connector *connector) { if (!connector) @@ -911,8 +917,11 @@ static void parse_edid( drm_t *drm, struct connector *conn) conn->valid_display_rates = std::span(galileo_display_rates); } else { conn->is_galileo_display = 0; - if ( conn->is_steam_deck_display ) + if ( conn->is_steam_deck_display ) { conn->valid_display_rates = std::span(steam_deck_display_rates); + } else if ( strcmp(conn->make_pnp, "LEN") == 0 && strcmp(conn->model, "Go Display") == 0 ) { + conn->valid_display_rates = std::span(legion_go_display_rates); + } } drm_hdr_parse_edid(drm, conn, edid); From b68fd83b32f7be04059e626b6b42a4371b318b21 Mon Sep 17 00:00:00 2001 From: Sterophonick Date: Sat, 10 Feb 2024 22:00:36 -0700 Subject: [PATCH 12/14] steamcompmgr: add env var to enable/disable 720p restriction --- src/steamcompmgr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 00c00e94d2..795898c456 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -137,6 +137,9 @@ extern float g_flInternalDisplayBrightnessNits; extern float g_flHDRItmSdrNits; extern float g_flHDRItmTargetNits; +// define env_to_bool to point to the function in drm: remove in later patches pl0x +extern bool env_to_bool(const char *env); + uint64_t g_lastWinSeq = 0; static std::shared_ptr s_scRGB709To2020Matrix; @@ -5657,7 +5660,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev) int width = xwayland_mode_ctl[ 1 ]; int height = xwayland_mode_ctl[ 2 ]; - if ( g_nOutputWidth != 1280 && width == 1280 ) + if ( g_nOutputWidth != 1280 && width == 1280 && !env_to_bool( getenv("GAMESCOPE_ENABLE_720P_RESTRICT") ) ) { width = g_nOutputWidth; height = g_nOutputHeight; From e265f2d52303e0264e47dde0a221d16a8ae53474 Mon Sep 17 00:00:00 2001 From: Kyle Gospodnetich Date: Sun, 11 Feb 2024 15:51:11 -0800 Subject: [PATCH 13/14] Use env_to_bool for check --- src/wlserver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 31e0998a83..53f9abd237 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -66,6 +66,8 @@ extern "C" { static LogScope wl_log("wlserver"); +extern bool env_to_bool(const char *env); + struct wlserver_t wlserver = { .touch_down_ids = {} }; @@ -2050,7 +2052,7 @@ void wlserver_touchmotion( double x, double y, int touch_id, uint32_t time ) { wlr_seat_touch_notify_motion( wlserver.wlr.seat, time, touch_id, wlserver.mouse_surface_cursorx, wlserver.mouse_surface_cursory ); - if(!getenv("GAMESCOPE_DISABLE_TOUCH_GESTURES")) { + if (!env_to_bool(getenv("GAMESCOPE_DISABLE_TOUCH_GESTURES"))) { bool start_gesture = false; // Round the x-coordinate to the nearest whole number From d6312db1d8dbc7cc7f2a17890e9c0d7b7b886ef8 Mon Sep 17 00:00:00 2001 From: Christophe Branchereau Date: Fri, 5 Apr 2024 17:13:47 +0200 Subject: [PATCH 14/14] Add Ayn Loki panel refresh rates in gamescope Signed-off-by: Christophe Branchereau --- src/drm.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/drm.cpp b/src/drm.cpp index fdf58eefc3..b2671171e6 100644 --- a/src/drm.cpp +++ b/src/drm.cpp @@ -107,6 +107,13 @@ static uint32_t legion_go_display_rates[] = 144, }; +static uint32_t loki_display_rates[] = +{ + 40, + 50, + 60, +}; + static uint32_t get_conn_display_info_flags(struct drm_t *drm, struct connector *connector) { if (!connector) @@ -921,6 +928,8 @@ static void parse_edid( drm_t *drm, struct connector *conn) conn->valid_display_rates = std::span(steam_deck_display_rates); } else if ( strcmp(conn->make_pnp, "LEN") == 0 && strcmp(conn->model, "Go Display") == 0 ) { conn->valid_display_rates = std::span(legion_go_display_rates); + } else if ( strcmp(conn->make_pnp, "AYN") == 0 && strcmp(conn->model, "LK-GOLDSPV58") == 0 ) { + conn->valid_display_rates = std::span(loki_display_rates); } }