Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion protocol/gamescope-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
it.
</description>

<interface name="gamescope_control" version="6">
<interface name="gamescope_control" version="7">
<request name="destroy" type="destructor"></request>

<enum name="feature">
Expand All @@ -44,6 +44,7 @@
<entry name="mura_correction" value="5"/>
<entry name="look" value="6"/>
<entry name="perf_query" value="7"/>
<entry name="display_selection" value="8"/>
</enum>

<event name="feature_support">
Expand All @@ -59,6 +60,7 @@
<entry name="internal_display" value="0x1"/>
<entry name="supports_hdr" value="0x2"/>
<entry name="supports_vrr" value="0x4"/>
<entry name="current" value="0x8"/>
</enum>

<event name="active_display_info" since="2">
Expand Down Expand Up @@ -143,5 +145,27 @@
<arg name="frametime_ns_hi" type="uint" summary="frametime_ns high bits"></arg>
</event>

<event name="available_display_info" since="7">
<description summary="report one display the compositor can switch to"></description>
<arg name="connector_name" type="string" summary="connector name (e.g. 'DP-1') to pass back via set_display"/>
<arg name="display_make" type="string"/>
<arg name="display_model" type="string"/>
<arg name="display_flags" type="uint" enum="display_flag" summary="combination of 'display_flag' values"/>
<arg name="display_identifier" type="string" summary="opaque EDID-derived key for persisting a display preference; identical monitors get a trailing ' [...]' suffix to disambiguate, so match identity on the text before it, then set_display its connector_name"/>
</event>

<event name="available_display_info_done" since="7">
<description summary="terminator for an available_display_info batch"></description>
</event>

<request name="set_display" since="7">
<description summary="select the display to drive; ignored if connector_name is not a currently-connected output"></description>
<arg name="connector_name" type="string" summary="connector to drive, as reported by available_display_info"/>
</request>

<request name="unset_display" since="7">
<description summary="clear any runtime display selection"></description>
</request>

</interface>
</protocol>
57 changes: 53 additions & 4 deletions src/Apps/gamescopectl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ namespace gamescope
std::vector<uint32_t> ValidRefreshRates;
};

struct GamescopeAvailableDisplayInfo
{
std::string szConnectorName;
std::string szDisplayMake;
std::string szDisplayModel;
uint32_t uDisplayFlags;
std::string szDisplayIdentifier;
};

class GamescopeCtl
{
public:
Expand All @@ -44,6 +53,7 @@ namespace gamescope

std::span<GamescopeFeature> GetFeatures() { return std::span<GamescopeFeature>{ m_Features }; }
const std::optional<GamescopeActiveDisplayInfo> &GetActiveDisplayInfo() { return m_ActiveDisplayInfo; }
std::span<const GamescopeAvailableDisplayInfo> GetAvailableDisplays() { return m_AvailableDisplays; }
private:
bool m_bInitControl = false;
bool m_bInitPrivate = false;
Expand All @@ -56,13 +66,15 @@ namespace gamescope

std::vector<GamescopeFeature> m_Features;
std::optional<GamescopeActiveDisplayInfo> m_ActiveDisplayInfo;
std::vector<GamescopeAvailableDisplayInfo> m_AvailableDisplays;

void Wayland_Registry_Global( wl_registry *pRegistry, uint32_t uName, const char *pInterface, uint32_t uVersion );
static const wl_registry_listener s_RegistryListener;

void Wayland_GamescopeControl_FeatureSupport( gamescope_control *pGamescopeControl, uint32_t uFeature, uint32_t uVersion, uint32_t uFlags );
void Wayland_GamescopeControl_ActiveDisplayInfo( gamescope_control *pGamescopeControl, const char *pConnectorName, const char *pDisplayMake, const char *pDisplayModel, uint32_t uDisplayFlags, wl_array *pValidRefreshRatesArray );
void Wayland_GamescopeControl_ScreenshotTaken( gamescope_control *pGamescopeControl, const char *pPath );
void Wayland_GamescopeControl_AvailableDisplayInfo( gamescope_control *pGamescopeControl, const char *pConnectorName, const char *pDisplayMake, const char *pDisplayModel, uint32_t uDisplayFlags, const char *pDisplayIdentifier );
static const gamescope_control_listener s_GamescopeControlListener;

void Wayland_GamescopePrivate_Log( gamescope_private *pGamescopePrivate, const char *pText );
Expand Down Expand Up @@ -187,12 +199,26 @@ namespace gamescope
fprintf( stderr, "Screenshot taken to: %s\n", pPath );
}

void GamescopeCtl::Wayland_GamescopeControl_AvailableDisplayInfo( gamescope_control *pGamescopeControl, const char *pConnectorName, const char *pDisplayMake, const char *pDisplayModel, uint32_t uDisplayFlags, const char *pDisplayIdentifier )
{
m_AvailableDisplays.emplace_back( GamescopeAvailableDisplayInfo
{
.szConnectorName = pConnectorName,
.szDisplayMake = pDisplayMake,
.szDisplayModel = pDisplayModel,
.uDisplayFlags = uDisplayFlags,
.szDisplayIdentifier = pDisplayIdentifier,
} );
}

const gamescope_control_listener GamescopeCtl::s_GamescopeControlListener =
{
.feature_support = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_FeatureSupport ),
.active_display_info = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_ActiveDisplayInfo ),
.screenshot_taken = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_ScreenshotTaken ),
.app_performance_stats = WAYLAND_NULL(),
.feature_support = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_FeatureSupport ),
.active_display_info = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_ActiveDisplayInfo ),
.screenshot_taken = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_ScreenshotTaken ),
.app_performance_stats = WAYLAND_NULL(),
.available_display_info = WAYLAND_USERDATA_TO_THIS( GamescopeCtl, Wayland_GamescopeControl_AvailableDisplayInfo ),
.available_display_info_done = WAYLAND_NULL(),
};

void GamescopeCtl::Wayland_GamescopePrivate_Log( gamescope_private *pGamescopePrivate, const char *pText )
Expand Down Expand Up @@ -231,6 +257,8 @@ namespace gamescope
return "Look";
case GAMESCOPE_CONTROL_FEATURE_PERF_QUERY:
return "Performance Query";
case GAMESCOPE_CONTROL_FEATURE_DISPLAY_SELECTION:
return "Display Selection";
default:
return "Unknown";
}
Expand Down Expand Up @@ -266,6 +294,27 @@ namespace gamescope
}
fprintf( stdout, "\n" );
}
auto availableDisplays = gamescopeCtl.GetAvailableDisplays();
if ( !availableDisplays.empty() )
{
fprintf( stdout, " Available Displays:\n" );
for ( const GamescopeAvailableDisplayInfo &avail : availableDisplays )
{
fprintf( stdout, " - %s", avail.szConnectorName.c_str() );
std::string szMakeModel = avail.szDisplayMake;
if ( !szMakeModel.empty() && !avail.szDisplayModel.empty() )
szMakeModel += " ";
szMakeModel += avail.szDisplayModel;
if ( !szMakeModel.empty() )
fprintf( stdout, " (%s)", szMakeModel.c_str() );
fprintf( stdout, " - Flags: 0x%x", avail.uDisplayFlags );
if ( !avail.szDisplayIdentifier.empty() )
fprintf( stdout, " - Identifier: \"%s\"", avail.szDisplayIdentifier.c_str() );
if ( avail.uDisplayFlags & GAMESCOPE_CONTROL_DISPLAY_FLAG_CURRENT )
fprintf( stdout, " [current]" );
fprintf( stdout, "\n" );
}
}
fprintf( stdout, " Features:\n" );
for ( const GamescopeFeature &feature : gamescopeCtl.GetFeatures() )
{
Expand Down
Loading