From 2fd6ac7ea1508860fe7659b041f05da88a3487c3 Mon Sep 17 00:00:00 2001 From: Timothee 'TTimo' Besset <ttimo@valvesoftware.com> Date: Wed, 8 Nov 2023 10:26:52 -0600 Subject: [PATCH] add support for taking screenshots using the xprop interface to gamescope, expose additional screenshot mode settings. enabled based on gamescope version detection, force with --screenshot-force-xprop --- client/devkit-utils/steamos-get-status | 19 ++++++++++++ client/devkit_client/__init__.py | 7 +++-- client/devkit_client/gui2/gui2.py | 42 +++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/client/devkit-utils/steamos-get-status b/client/devkit-utils/steamos-get-status index 0169a0a..b2d00aa 100755 --- a/client/devkit-utils/steamos-get-status +++ b/client/devkit-utils/steamos-get-status @@ -347,6 +347,23 @@ if __name__ == '__main__': _user_password_is_set = user_password_is_set() + # optimistic - if the test fails assume this has evolved beyond our ability to check and it's fine + gamescope_supports_xprop = True + try: + output = subprocess.check_output('pacman -Q gamescope', + shell=True, + universal_newlines=True) + logger.debug(output) + (_, major, minor, _) = re.split('gamescope (.*?)\.(.*?)\..*', output) + major = int(major) + minor = int(minor) + if major < 3: + gamescope_supports_xprop = False + elif major == 3 and minor <= 12: + gamescope_supports_xprop = False + except Exception as e: + logger.warning(f'could not check gamescope version for xprop: {e!r}') + if conf.json: ret = { 'steamdeck': steamdeck, @@ -368,6 +385,7 @@ if __name__ == '__main__': 'foxnet_hostname': foxnet_hostname, 'user_password_is_set': _user_password_is_set, 'steam_launch_flags': _steam_launch_flags, + 'gamescope_supports_xprop': gamescope_supports_xprop, } json.dump(ret, sys.stdout, sort_keys=True, indent=4) else: @@ -386,4 +404,5 @@ if __name__ == '__main__': logger.info(f"Foxnet hostname : {foxnet_hostname}") logger.info(f"User password is set : {'yes' if _user_password_is_set else 'no'}") logger.info(f"Steam launch flags : {_steam_launch_flags}") + logger.info(f"Screenshot with xprop : {gamescope_supports_xprop}") diff --git a/client/devkit_client/__init__.py b/client/devkit_client/__init__.py index 0b163a2..bb689d6 100644 --- a/client/devkit_client/__init__.py +++ b/client/devkit_client/__init__.py @@ -1568,8 +1568,11 @@ def screenshot(args): # - so they don't accumulate in tmpfs # - so we can wait and confirm a new screenshot was delivered _simple_ssh(ssh, 'rm /tmp/gamescope_*.png', silent=True) - _simple_ssh(ssh, 'kill -USR2 `pidof gamescope`', silent=True, check_status=True) - attempts = 50 + if args.xprop is None: + _simple_ssh(ssh, 'kill -USR2 `pidof gamescope`', silent=True, check_status=True) + else: + _simple_ssh(ssh, f'DISPLAY=:0 xprop -root -f GAMESCOPECTRL_DEBUG_REQUEST_SCREENSHOT 32c -set GAMESCOPECTRL_DEBUG_REQUEST_SCREENSHOT {args.xprop}', silent=True, check_status=True) + attempts = 100 while attempts > 0: time.sleep(.1) attempts -= 1 diff --git a/client/devkit_client/gui2/gui2.py b/client/devkit_client/gui2/gui2.py index bcab326..e837e49 100644 --- a/client/devkit_client/gui2/gui2.py +++ b/client/devkit_client/gui2/gui2.py @@ -351,13 +351,14 @@ class DevkitCommands: def _screenshot(self, *args): class ScreenshotArgs: - def __init__(self, devkit, folder, filename, do_timestamp): + def __init__(self, devkit, folder, filename, do_timestamp, xprop): self.machine, self.machine_name_type = devkit.machine_command_args self.http_port = devkit.http_port self.login = None self.folder = folder self.filename = filename self.do_timestamp = do_timestamp + self.xprop = xprop screenshot_args = ScreenshotArgs(*args) devkit_client.screenshot(screenshot_args) @@ -719,6 +720,10 @@ class Devkit: elif 'PROTON_LOG' in status_flags: del status_flags['PROTON_LOG'] + @property + def gamescope_supports_xprop(self): + return self.steamos_status.get('gamescope_supports_xprop', False) + def has_mdns_service(self): return self.service_name is not None @@ -2377,17 +2382,24 @@ class Screenshot(SubTool): FOLDER_KEY = 'Screenshot.folder' FILENAME_KEY = 'Screenshot.filename' TIMESTAMP_KEY = 'Screenshot.timestamp' + MODE_KEY = 'Screenshot.mode' - def setup(self): + def setup(self, force_xprop): + self.force_xprop = force_xprop if not self.FOLDER_KEY in self.settings: self.settings[self.FOLDER_KEY] = str(pathlib.Path(os.path.expanduser('~/Pictures'))) if not self.FILENAME_KEY in self.settings: self.settings[self.FILENAME_KEY] = '' if not self.TIMESTAMP_KEY in self.settings: self.settings[self.TIMESTAMP_KEY] = True + if not self.MODE_KEY in self.settings: + self.settings[self.MODE_KEY] = 1 super(Screenshot, self).setup() def devkits_window_draw(self, selected_devkit): + xprop = None + use_xprop = selected_devkit.gamescope_supports_xprop or self.force_xprop + imgui.text('Folder:') imgui.same_line() imgui.set_next_item_width(48*CHARACTER_WIDTH) @@ -2398,7 +2410,7 @@ class Screenshot(SubTool): imgui.same_line() imgui.text('Filename (optional):') imgui.same_line() - imgui.set_next_item_width(32*CHARACTER_WIDTH) + imgui.set_next_item_width(18*CHARACTER_WIDTH) changed, s = imgui.input_text('##screenshot_filename', self.settings[self.FILENAME_KEY], 128) if changed: self.settings[self.FILENAME_KEY] = s @@ -2408,6 +2420,23 @@ class Screenshot(SubTool): if clicked: self.settings[self.TIMESTAMP_KEY] = v + if use_xprop: + imgui.same_line() + mode_choices = [ + 'baseplane only', + 'all real layers', + 'full composition', + 'screen buffer' + ] + imgui.push_item_width(16*CHARACTER_WIDTH) + clicked, selected_index = imgui.combo( + '##ScreenshotMode', self.settings[self.MODE_KEY] - 1, mode_choices + ) + imgui.pop_item_width() + if clicked: + self.settings[self.MODE_KEY] = selected_index + 1 + xprop = self.settings[self.MODE_KEY] + imgui.same_line() # gross - plz halp with layout imgui.set_cursor_pos_x(1100) @@ -2417,6 +2446,7 @@ class Screenshot(SubTool): self.settings[self.FOLDER_KEY], self.settings[self.FILENAME_KEY], self.settings[self.TIMESTAMP_KEY], + xprop ) self.modal_wait = ModalWait( self.viewport, @@ -3325,6 +3355,10 @@ def main(): '--with-cmder', required=False, action='store', help='Configure run in terminal to use given exe with cmder syntax. Set to blank to clear.' ) + parser.add_argument( + '--screenshot-force-xprop', required=False, action='store_true', + help='Force using xprop for screenshots, overriding the gamescope version check.' + ) conf = parser.parse_args() @@ -3372,7 +3406,7 @@ def main(): toolbar.setup() screenshot = Screenshot(devkit_commands, viewport, toolbar, settings) - screenshot.setup() + screenshot.setup(conf.screenshot_force_xprop) perf_overlay = PerfOverlay(devkit_commands, viewport, toolbar, settings) perf_overlay.setup() gpu_trace = GPUTrace(devkit_commands, viewport, toolbar, settings) -- GitLab