#!/usr/bin/env python3 # Copyright © 2019 Collabora Ltd. # # SPDX-License-Identifier: MIT # # 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. import logging import os import sys try: import typing except ImportError: pass else: typing # silence pyflakes import gi gi.require_version('Gtk', '3.0') from gi.repository import GLib from gi.repository import Gtk logger = logging.getLogger('pressure-vessel-test-ui') class Gui: def __init__(self): # type: (...) -> None self.home = GLib.get_home_dir() self.runtimes = {} # type: typing.Dict[str, str] for search in ( '..', '../..', os.path.expanduser( '~/.steam/root/steamapps/common/Steam Linux Runtime' ), ): source_of_runtimes = os.path.join( os.path.dirname(sys.argv[0]), search, ) for member in os.listdir(source_of_runtimes): path = os.path.realpath( os.path.join(source_of_runtimes, member) ) files = os.path.join(path, 'files') metadata = os.path.join(path, 'metadata') if os.path.isdir(files) and os.path.exists(metadata): description = self._describe_runtime(path) self.runtimes[path] = description self.window = Gtk.Window() self.window.set_default_size(600, 300) self.window.connect('delete-event', Gtk.main_quit) self.window.set_title('Choose pressure-vessel runtime') self.grid = Gtk.Grid( row_spacing=6, column_spacing=6, margin_top=12, margin_bottom=12, margin_start=12, margin_end=12, ) self.window.add(self.grid) row = 0 runtime_label = Gtk.Label.new('Runtime') self.grid.attach(runtime_label, 0, row, 1, 1) self.runtime_combo = Gtk.ComboBoxText.new() self.runtime_combo.append('/', 'None (use host system)') for path, description in sorted(self.runtimes.items()): self.runtime_combo.append(path, description) if self.runtimes: self.runtime_combo.set_active(1) else: self.runtime_combo.set_active(0) self.grid.attach(self.runtime_combo, 1, row, 1, 1) row += 1 self.script_check = Gtk.CheckButton.new_with_label( 'Use old shell script version of pressure-vessel-wrap' ) self.script_check.set_active(False) self.grid.attach(self.script_check, 1, row, 1, 1) row += 1 self.share_home_check = Gtk.CheckButton.new_with_label( 'Share real home directory' ) self.share_home_check.set_active(True) self.grid.attach(self.share_home_check, 1, row, 1, 1) row += 1 self.xterm_check = Gtk.CheckButton.new_with_label('Run in an xterm') self.xterm_check.set_active(True) self.grid.attach(self.xterm_check, 1, row, 1, 1) row += 1 buttons_grid = Gtk.Grid( column_spacing=6, column_homogeneous=True, halign=Gtk.Align.END, ) cancel_button = Gtk.Button.new_with_label('Cancel') cancel_button.connect('clicked', Gtk.main_quit) buttons_grid.attach(cancel_button, 0, 0, 1, 1) run_button = Gtk.Button.new_with_label('Run') run_button.connect('clicked', self.run_cb) buttons_grid.attach(run_button, 1, 0, 1, 1) self.grid.attach(buttons_grid, 0, row, 2, 1) row += 1 def _describe_runtime( self, path # type: str ): # type: (...) -> str description = path files = os.path.join(path, 'files') metadata = os.path.join(path, 'metadata') if description.startswith(self.home + '/'): description = '~' + description[len(self.home):] name = None # type: typing.Optional[str] pretty_name = None # type: typing.Optional[str] build_id = None # type: typing.Optional[str] try: keyfile = GLib.KeyFile.new() keyfile.load_from_file( metadata, GLib.KeyFileFlags.NONE) try: build_id = keyfile.get_string('Runtime', 'x-flatdeb-build-id') except GLib.Error: pass try: name = keyfile.get_string('Runtime', 'runtime') except GLib.Error: pass except GLib.Error: pass try: with open( os.path.join(files, 'lib', 'os-release') ) as reader: for line in reader: if line.startswith('PRETTY_NAME='): pretty_name = line.split('=', 1)[1].strip() pretty_name = GLib.shell_unquote( pretty_name ) except (GLib.Error, EnvironmentError): pass if pretty_name is None: pretty_name = name if pretty_name is None: pretty_name = os.path.basename(path) if build_id is None: build_id = '' else: build_id = ' build {}'.format(build_id) description = '{}{}\n({})'.format( pretty_name, build_id, description, ) return description def run_cb(self, _ignored=None): # type: (typing.Any) -> None if self.script_check.get_active(): wrap = 'pressure-vessel-wrap.sh' else: wrap = 'pressure-vessel-wrap' argv = [ 'env', 'G_MESSAGES_DEBUG=all', os.path.join( os.path.dirname(sys.argv[0]), wrap, ), ] id = self.runtime_combo.get_active_id() if id is None: pass elif id == '/': pass else: argv.append('--runtime') argv.append(os.path.join(id, 'files')) if self.share_home_check.get_active(): argv.append('--share-home') else: argv.append('--unshare-home') if self.xterm_check.get_active(): argv.append('--xterm') argv.append('--verbose') argv.extend(sys.argv[1:]) os.execvp(argv[0], argv) def run(self): # type: (...) -> None self.window.show_all() Gtk.main() if __name__ == '__main__': Gui().run()