[game-data-packager] 04/10: Launcher: split into GUI and non-GUI parts
Simon McVittie
smcv at debian.org
Fri Oct 14 22:12:17 UTC 2016
This is an automated email from the git hooks/post-receive script.
smcv pushed a commit to branch master
in repository game-data-packager.
commit da495b4a5d15d1b4ccfaa996d27b4192e04545f6
Author: Simon McVittie <smcv at debian.org>
Date: Fri Oct 14 19:44:40 2016 +0100
Launcher: split into GUI and non-GUI parts
The non-GUI part is *almost* a single self-contained file that can be
used for servers, although it does import the version number.
---
Makefile | 6 +-
runtime/gdp-launcher.py | 304 ++++++++++++++++++++++++
runtime/{launcher.py => gdp_launcher_base.py} | 319 +++-----------------------
runtime/gdp_launcher_version.py | 12 +
4 files changed, 351 insertions(+), 290 deletions(-)
diff --git a/Makefile b/Makefile
index eb4707d..5ffdfd2 100644
--- a/Makefile
+++ b/Makefile
@@ -430,9 +430,9 @@ install:
install -m0644 out/vfs.zip $(DESTDIR)$(pkgdatadir)/
install -d $(DESTDIR)$(runtimedir)/
- sed -e '/^#__insert_version_here__/ r out/installed-version.py' \
- < runtime/launcher.py > $(DESTDIR)$(runtimedir)/gdp-launcher
- chmod 0755 $(DESTDIR)$(runtimedir)/gdp-launcher
+ install runtime/gdp_launcher_base.py $(DESTDIR)$(runtimedir)/
+ install -m0644 out/installed-version.py $(DESTDIR)$(runtimedir)/gdp_launcher_version.py
+ install runtime/gdp-launcher.py $(DESTDIR)$(runtimedir)/gdp-launcher
install runtime/openurl.py $(DESTDIR)$(runtimedir)/gdp-openurl
install -m0644 $(launcher_desktops) $(DESTDIR)$(runtimedir)/
install -m0644 runtime/confirm-binary-only.txt $(DESTDIR)$(runtimedir)/
diff --git a/runtime/gdp-launcher.py b/runtime/gdp-launcher.py
new file mode 100755
index 0000000..007a966
--- /dev/null
+++ b/runtime/gdp-launcher.py
@@ -0,0 +1,304 @@
+#!/usr/bin/python3
+# encoding=utf-8
+
+# game-data-packager Gtk launcher stub. See doc/launcher.mdwn for design
+
+# Copyright © 2015-2016 Simon McVittie <smcv at debian.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# You can find the GPL license text on a Debian system under
+# /usr/share/common-licenses/GPL-2.
+
+import fnmatch
+import logging
+import os
+import traceback
+
+import gi
+
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import (GLib, GObject)
+from gi.repository import Gtk
+
+from gdp_launcher_base import (
+ Launcher,
+ RUNTIME_BUILT,
+ )
+
+logger = logging.getLogger('game-data-packager.launcher')
+
+class IniEditor:
+ def __init__(self, edits):
+ self.lines = []
+ self.edits = edits
+ self.__section = None
+ self.__section_lines = []
+ self.__sections = set()
+
+ def load(self, reader):
+ # Simple INI parser. Not using ConfigParser because Unreal
+ # uses duplicate keys within sections, and we want to preserve
+ # comments, blank lines etc.
+ self.__section = None
+ self.__section_lines = []
+ self.__sections = set()
+
+ for line in reader:
+ line = line.rstrip('\r\n')
+
+ if line.startswith('[') and line.endswith(']'):
+ self.__end_section()
+ self.__section = line[1:-1]
+
+ self.__section_lines.append(line)
+
+ self.__end_section()
+
+ for edit in self.edits:
+ if edit['section'] not in self.__sections:
+ self.__section = edit['section']
+ self.__section_lines = ['[%s]' % edit['section']]
+ self.__end_section()
+
+ def __end_section(self):
+ self.__sections.add(self.__section)
+
+ for edit in self.edits:
+ if edit['section'] != self.__section:
+ continue
+
+ logger.debug('editing %s', self.__section)
+ extra_lines = []
+
+ for k, v in sorted(edit.get('replace_key', {}).items()):
+ logger.debug('replacing %s with %s', k, v)
+ self.__section_lines = [l for l in self.__section_lines
+ if not l.startswith(k + '=')]
+ extra_lines.append('%s=%s' % (k, v))
+
+ for pattern in sorted(edit.get('delete_matched', [])):
+ logger.debug('deleting lines matching %s', pattern)
+ self.__section_lines = [l for l in self.__section_lines
+ if not fnmatch.fnmatchcase(l, pattern)]
+
+ for pattern in edit.get('comment_out_matched', []):
+ logger.debug('commenting out lines matching %s', pattern)
+ for i in range(len(self.__section_lines)):
+ if fnmatch.fnmatchcase(self.__section_lines[i], pattern):
+ self.__section_lines[i] = ';' + self.__section_lines[i]
+ self.__section_lines.insert(i, '; ' +
+ edit['comment_out_reason'])
+
+ for append in edit.get('append_unique', []):
+ logger.debug('appending unique line %s', append)
+ for line in self.__section_lines:
+ if line == append:
+ break
+ else:
+ extra_lines.append(append)
+
+ i = len(self.__section_lines) - 1
+
+ while i >= 0:
+ if self.__section_lines[i]:
+ # _s_l[i] is the last non-empty line, insert after it
+ self.__section_lines[i + 1:i + 1] = extra_lines
+ break
+ i -= 1
+ else:
+ # no non-empty lines, insert after the section-opening heading
+ self.__section_lines[1:1] = extra_lines
+
+ self.lines.extend(self.__section_lines)
+ self.__section_lines = []
+ self.__section = None
+
+ def save(self, writer):
+ for line in self.lines:
+ print(line, file=writer)
+
+class FullLauncher(Launcher):
+ def set_id(self):
+ self.keyfile = GLib.KeyFile()
+ desktop = os.path.join(RUNTIME_BUILT, self.id + '.desktop')
+ if os.path.exists(desktop):
+ self.keyfile.load_from_file(desktop, GLib.KeyFileFlags.NONE)
+ else:
+ self.keyfile.load_from_data_dirs(
+ 'applications/%s.desktop' % self.id,
+ GLib.KeyFileFlags.NONE)
+
+ self.name = self.keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
+ GLib.KEY_FILE_DESKTOP_KEY_NAME)
+ logger.debug('Name: %s', self.name)
+ GLib.set_application_name(self.name)
+
+ self.icon_name = self.keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
+ GLib.KEY_FILE_DESKTOP_KEY_ICON)
+ logger.debug('Icon: %s', self.icon_name)
+
+ self.expansion_name = self.args.expansion
+
+ try:
+ override_id = self.keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
+ 'X-GameDataPackager-ExpansionFor')
+ except GLib.Error:
+ pass
+ else:
+ if self.expansion_name is None:
+ self.expansion_name = self.id
+
+ if self.expansion_name.startswith(override_id + '-'):
+ self.expansion_name = self.expansion_name[len(override_id) + 1:]
+
+ self.id = override_id
+
+ def exec_game(self, _unused=None):
+ # Edit before copying, so that we can detect whether this is
+ # the first run or not
+ for ini, details in self.data.get('edit_unreal_ini', {}).items():
+ assert self.dot_directory is not None
+ target = os.path.join(self.dot_directory, ini)
+ encoding = details.get('encoding', 'windows-1252')
+
+ if os.path.exists(target):
+ first_time = False
+ try:
+ reader = open(target, encoding='utf-16')
+ reader.readline()
+ except:
+ reader = open(target, encoding=encoding)
+ else:
+ reader.seek(0)
+ else:
+ first_time = True
+
+ if os.path.lexists(target):
+ logger.info('Removing dangling symlink %s', target)
+ os.remove(target)
+
+ for base in self.base_directories:
+ source = os.path.join(base, ini)
+
+ if os.path.exists(source):
+ try:
+ reader = open(source, encoding='utf-16')
+ reader.readline()
+ except:
+ reader = open(source, encoding=encoding)
+ else:
+ reader.seek(0)
+ break
+ else:
+ raise AssertionError('Required file %s not found', ini)
+
+ if first_time:
+ edits = details.get('once', []) + details.get('always', [])
+ else:
+ edits = details.get('always', [])
+
+ logger.debug('%s', edits)
+ editor = IniEditor(edits)
+
+ with reader:
+ editor.load(reader)
+
+ d = os.path.dirname(target)
+
+ if d:
+ logger.info('Creating directory: %s', d)
+ os.makedirs(d, exist_ok=True)
+
+ with open(target, 'w', encoding=encoding,
+ newline=details.get('newline', '\n')) as writer:
+ editor.save(writer)
+
+ super(FullLauncher, self).exec_game()
+
+ def write_confirm_binary_only_stamp(self):
+ open(self.warning_stamp, 'a').close()
+
+ def __init__(self):
+ super(FullLauncher, self).__init__()
+
+ self.window = Gtk.Window()
+ self.window.set_default_size(600, 300)
+ self.window.connect('delete-event', Gtk.main_quit)
+ self.window.set_title(self.name)
+ self.window.set_icon_name(self.icon_name)
+
+ 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)
+
+ image = Gtk.Image.new_from_icon_name(self.icon_name,
+ Gtk.IconSize.DIALOG)
+ image.set_valign(Gtk.Align.START)
+ self.grid.attach(image, 0, 0, 1, 1)
+
+ self.text_view = Gtk.TextView(editable=False, cursor_visible=False,
+ hexpand=True, vexpand=True, wrap_mode=Gtk.WrapMode.WORD,
+ top_margin=6, left_margin=6, right_margin=6, bottom_margin=6)
+ self.grid.attach(self.text_view, 1, 0, 1, 1)
+
+ subgrid = 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)
+ subgrid.attach(cancel_button, 0, 0, 1, 1)
+
+ self.check_box = Gtk.CheckButton.new_with_label("I'll be careful")
+ self.check_box.set_hexpand(True)
+ self.grid.attach(self.check_box, 0, 1, 2, 1)
+
+ self.ok_button = Gtk.Button.new_with_label('Run')
+ self.ok_button.set_sensitive(False)
+ subgrid.attach(self.ok_button, 1, 0, 1, 1)
+
+ self.grid.attach(subgrid, 0, 2, 2, 1)
+
+ self.window.show_all()
+
+ def run_error(self, message):
+ self.show_error(message)
+ Gtk.main()
+
+ def show_error(self, message):
+ self.text_view.get_buffer().set_text(message)
+ self.ok_button.set_sensitive(False)
+ self.window.show_all()
+ self.check_box.hide()
+
+ def run_confirm_binary_only(self):
+ self.text_view.get_buffer().set_text(
+ self.load_text('confirm-binary-only.txt',
+ 'Binary-only game, we cannot fix bugs or security '
+ 'vulnerabilities!'))
+ self.check_box.bind_property('active', self.ok_button, 'sensitive',
+ GObject.BindingFlags.SYNC_CREATE)
+ self.ok_button.connect('clicked',
+ lambda _: self.__confirm_binary_only_cb())
+ self.window.show_all()
+ Gtk.main()
+
+ def __confirm_binary_only_cb(self):
+ try:
+ self.write_confirm_binary_only_stamp()
+ self.exec_game()
+ except:
+ self.show_error(traceback.format_exc())
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ FullLauncher().main()
+
diff --git a/runtime/launcher.py b/runtime/gdp_launcher_base.py
similarity index 61%
rename from runtime/launcher.py
rename to runtime/gdp_launcher_base.py
index dcf9f48..8acd640 100755
--- a/runtime/launcher.py
+++ b/runtime/gdp_launcher_base.py
@@ -1,7 +1,7 @@
#!/usr/bin/python3
# encoding=utf-8
-# game-data-packager Gtk launcher stub. See doc/launcher.mdwn for design
+# game-data-packager command-line launcher stub
# Copyright © 2015-2016 Simon McVittie <smcv at debian.org>
#
@@ -18,9 +18,8 @@
# /usr/share/common-licenses/GPL-2.
import argparse
-import glob
-import fnmatch
import json
+import glob
import logging
import os
import shlex
@@ -29,13 +28,7 @@ import string
import sys
import traceback
-import gi
-from gi.repository import (GLib, GObject)
-
-# edited automatically, be careful
-GAME_PACKAGE_VERSION = '(uninstalled)'
-GAME_PACKAGE_RELEASE = ''
-#__insert_version_here__
+from gdp_launcher_version import GAME_PACKAGE_VERSION
if 'GDP_UNINSTALLED' in os.environ:
RUNTIME_BUILT = './out'
@@ -54,7 +47,7 @@ os.environ.setdefault('XDG_CONFIG_DIRS', '/etc/xdg')
os.environ.setdefault('XDG_DATA_HOME', os.path.expanduser('~/.local/share'))
os.environ.setdefault('XDG_DATA_DIRS', '/usr/local/share:/usr/share')
-logger = logging.getLogger('game-data-packager.launcher')
+logger = logging.getLogger('game-data-packager.launcher.base')
if os.environ.get('GDP_DEBUG'):
logger.setLevel(logging.DEBUG)
@@ -84,96 +77,6 @@ def expand(path, **kwargs):
return os.path.expanduser(string.Template(path).substitute(os.environ,
**kwargs))
-class IniEditor:
- def __init__(self, edits):
- self.lines = []
- self.edits = edits
- self.__section = None
- self.__section_lines = []
- self.__sections = set()
-
- def load(self, reader):
- # Simple INI parser. Not using ConfigParser because Unreal
- # uses duplicate keys within sections, and we want to preserve
- # comments, blank lines etc.
- self.__section = None
- self.__section_lines = []
- self.__sections = set()
-
- for line in reader:
- line = line.rstrip('\r\n')
-
- if line.startswith('[') and line.endswith(']'):
- self.__end_section()
- self.__section = line[1:-1]
-
- self.__section_lines.append(line)
-
- self.__end_section()
-
- for edit in self.edits:
- if edit['section'] not in self.__sections:
- self.__section = edit['section']
- self.__section_lines = ['[%s]' % edit['section']]
- self.__end_section()
-
- def __end_section(self):
- self.__sections.add(self.__section)
-
- for edit in self.edits:
- if edit['section'] != self.__section:
- continue
-
- logger.debug('editing %s', self.__section)
- extra_lines = []
-
- for k, v in sorted(edit.get('replace_key', {}).items()):
- logger.debug('replacing %s with %s', k, v)
- self.__section_lines = [l for l in self.__section_lines
- if not l.startswith(k + '=')]
- extra_lines.append('%s=%s' % (k, v))
-
- for pattern in sorted(edit.get('delete_matched', [])):
- logger.debug('deleting lines matching %s', pattern)
- self.__section_lines = [l for l in self.__section_lines
- if not fnmatch.fnmatchcase(l, pattern)]
-
- for pattern in edit.get('comment_out_matched', []):
- logger.debug('commenting out lines matching %s', pattern)
- for i in range(len(self.__section_lines)):
- if fnmatch.fnmatchcase(self.__section_lines[i], pattern):
- self.__section_lines[i] = ';' + self.__section_lines[i]
- self.__section_lines.insert(i, '; ' +
- edit['comment_out_reason'])
-
- for append in edit.get('append_unique', []):
- logger.debug('appending unique line %s', append)
- for line in self.__section_lines:
- if line == append:
- break
- else:
- extra_lines.append(append)
-
- i = len(self.__section_lines) - 1
-
- while i >= 0:
- if self.__section_lines[i]:
- # _s_l[i] is the last non-empty line, insert after it
- self.__section_lines[i + 1:i + 1] = extra_lines
- break
- i -= 1
- else:
- # no non-empty lines, insert after the section-opening heading
- self.__section_lines[1:1] = extra_lines
-
- self.lines.extend(self.__section_lines)
- self.__section_lines = []
- self.__section = None
-
- def save(self, writer):
- for line in self.lines:
- print(line, file=writer)
-
class Launcher:
def __init__(self, argv=None):
name = os.path.basename(sys.argv[0])
@@ -200,6 +103,9 @@ class Launcher:
help='run engine under a debugger')
parser.add_argument('--quiet', '-q', default=False, action='store_true',
help='silence console logging')
+ parser.add_argument('--allow-binary-only', default=False,
+ action='store_true',
+ help='Allow running binary-only games')
parser.add_argument('--version', action='version',
version='game-data-packager launcher ' + GAME_PACKAGE_VERSION)
parser.add_argument('arguments', nargs=argparse.REMAINDER,
@@ -212,39 +118,10 @@ class Launcher:
sys.exit(2)
self.id = self.args.id
- self.keyfile = GLib.KeyFile()
- desktop = os.path.join(RUNTIME_BUILT, self.id + '.desktop')
- if os.path.exists(desktop):
- self.keyfile.load_from_file(desktop, GLib.KeyFileFlags.NONE)
- else:
- self.keyfile.load_from_data_dirs(
- 'applications/%s.desktop' % self.id,
- GLib.KeyFileFlags.NONE)
-
- self.name = self.keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
- GLib.KEY_FILE_DESKTOP_KEY_NAME)
- logger.debug('Name: %s', self.name)
- GLib.set_application_name(self.name)
-
- self.icon_name = self.keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
- GLib.KEY_FILE_DESKTOP_KEY_ICON)
- logger.debug('Icon: %s', self.icon_name)
-
- self.expansion_name = self.args.expansion
-
- try:
- override_id = self.keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP,
- 'X-GameDataPackager-ExpansionFor')
- except GLib.Error:
- pass
- else:
- if self.expansion_name is None:
- self.expansion_name = self.id
-
- if self.expansion_name.startswith(override_id + '-'):
- self.expansion_name = self.expansion_name[len(override_id) + 1:]
+ self.name = self.id
+ self.expansion_name = None
- self.id = override_id
+ self.set_id()
self.data = json.load(open('%s/launch-%s.json' % (RUNTIME_BUILT,
self.id), encoding='utf-8'))
@@ -364,6 +241,9 @@ class Launcher:
logger.debug('Arguments: %r', self.argv)
+ def set_id(self):
+ pass
+
def check_required_files(self, base_directories, required_files,
warn=True):
for f in required_files:
@@ -381,6 +261,9 @@ class Launcher:
else:
return True
+ def run_error(self, message):
+ logger.error(message)
+
def main(self):
if self.engines:
for e in self.engines:
@@ -389,7 +272,7 @@ class Launcher:
self.engine = e
break
else:
- Gui.run_error(self,
+ self.run_error(
'\n'.join(
[self.load_text('missing-engine.txt',
'Game engine missing, tried:')] +
@@ -400,97 +283,37 @@ class Launcher:
os.makedirs(self.dot_directory, exist_ok=True)
if not self.have_all_data:
- Gui.run_error(self,
+ self.run_error(
self.load_text('missing-data.txt', 'Data files missing'))
sys.exit(72) # EX_OSFILE
- elif self.binary_only and not os.path.exists(self.warning_stamp):
+ if (self.binary_only and not os.path.exists(self.warning_stamp) and
+ not self.args.allow_binary_only):
self.exit_status = 77 # EX_NOPERM
- Gui.run_confirm_binary_only(self, self._confirm_binary_only_cb)
+ self.run_confirm_binary_only()
sys.exit(self.exit_status)
+ raise AssertionError('not reached')
+ try:
+ self.exec_game()
+ except:
+ self.run_error(traceback.format_exc())
+ sys.exit(self.exit_status)
else:
- try:
- self.exec_game()
- except:
- Gui.run_error(self, traceback.format_exc())
- sys.exit(self.exit_status)
- else:
- raise AssertionError('exec_game should never return')
+ raise AssertionError('exec_game should never return')
def flush(self):
for f in (sys.stdout, sys.stderr):
f.flush()
- def _confirm_binary_only_cb(self, gui):
- try:
- open(self.warning_stamp, 'a').close()
- self.exec_game()
- except:
- gui.show_error(traceback.format_exc())
+ def run_confirm_binary_only(self):
+ # don't do anything, we have no GUI
+ self.run_error('Not running binary-only game without '
+ '--allow-binary-only')
def exec_game(self, _unused=None):
self.exit_status = 69 # EX_UNAVAILABLE
- # Edit before copying, so that we can detect whether this is
- # the first run or not
- for ini, details in self.data.get('edit_unreal_ini', {}).items():
- assert self.dot_directory is not None
- target = os.path.join(self.dot_directory, ini)
- encoding = details.get('encoding', 'windows-1252')
-
- if os.path.exists(target):
- first_time = False
- try:
- reader = open(target, encoding='utf-16')
- reader.readline()
- except:
- reader = open(target, encoding=encoding)
- else:
- reader.seek(0)
- else:
- first_time = True
-
- if os.path.lexists(target):
- logger.info('Removing dangling symlink %s', target)
- os.remove(target)
-
- for base in self.base_directories:
- source = os.path.join(base, ini)
-
- if os.path.exists(source):
- try:
- reader = open(source, encoding='utf-16')
- reader.readline()
- except:
- reader = open(source, encoding=encoding)
- else:
- reader.seek(0)
- break
- else:
- raise AssertionError('Required file %s not found', ini)
-
- if first_time:
- edits = details.get('once', []) + details.get('always', [])
- else:
- edits = details.get('always', [])
-
- logger.debug('%s', edits)
- editor = IniEditor(edits)
-
- with reader:
- editor.load(reader)
-
- d = os.path.dirname(target)
-
- if d:
- logger.info('Creating directory: %s', d)
- os.makedirs(d, exist_ok=True)
-
- with open(target, 'w', encoding=encoding,
- newline=details.get('newline', '\n')) as writer:
- editor.save(writer)
-
# Copy before linking, so that the copies will suppress symlink
# creation
for pattern in self.data.get('copy_into_dot_directory', ()):
@@ -632,84 +455,6 @@ class Launcher:
else:
return placeholder
-class Gui:
- def __init__(self, launcher):
- gi.require_version('Gtk', '3.0')
- from gi.repository import Gtk
- self.Gtk = Gtk
-
- self.window = Gtk.Window()
- self.window.set_default_size(600, 300)
- self.window.connect('delete-event', Gtk.main_quit)
- self.window.set_title(launcher.name)
- self.window.set_icon_name(launcher.icon_name)
-
- 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)
-
- image = Gtk.Image.new_from_icon_name(launcher.icon_name,
- Gtk.IconSize.DIALOG)
- image.set_valign(Gtk.Align.START)
- self.grid.attach(image, 0, 0, 1, 1)
-
- self.text_view = Gtk.TextView(editable=False, cursor_visible=False,
- hexpand=True, vexpand=True, wrap_mode=Gtk.WrapMode.WORD,
- top_margin=6, left_margin=6, right_margin=6, bottom_margin=6)
- self.grid.attach(self.text_view, 1, 0, 1, 1)
-
- subgrid = 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)
- subgrid.attach(cancel_button, 0, 0, 1, 1)
-
- self.check_box = Gtk.CheckButton.new_with_label("I'll be careful")
- self.check_box.set_hexpand(True)
- self.grid.attach(self.check_box, 0, 1, 2, 1)
-
- self.ok_button = Gtk.Button.new_with_label('Run')
- self.ok_button.set_sensitive(False)
- subgrid.attach(self.ok_button, 1, 0, 1, 1)
-
- self.grid.attach(subgrid, 0, 2, 2, 1)
-
- self.window.show_all()
-
- @classmethod
- def run_error(cls, launcher, message):
- try:
- gui = cls(launcher)
- except:
- logger.error('Unable to show error in GUI:\n%s', message)
- else:
- gui.show_error(message)
- gui.Gtk.main()
-
- def show_error(self, message):
- self.text_view.get_buffer().set_text(message)
- self.ok_button.set_sensitive(False)
- self.window.show_all()
- self.check_box.hide()
-
- @classmethod
- def run_confirm_binary_only(cls, launcher, callback):
- try:
- gui = cls(launcher)
- except:
- logger.error('Unable to do binary-only confirmation in GUI')
- else:
- gui.text_view.get_buffer().set_text(
- launcher.load_text('confirm-binary-only.txt',
- 'Binary-only game, we cannot fix bugs or security '
- 'vulnerabilities!'))
- gui.check_box.bind_property('active', gui.ok_button, 'sensitive',
- GObject.BindingFlags.SYNC_CREATE)
- gui.ok_button.connect('clicked', lambda _: callback(gui))
- gui.window.show_all()
- gui.Gtk.main()
-
if __name__ == '__main__':
logging.basicConfig()
Launcher().main()
diff --git a/runtime/gdp_launcher_version.py b/runtime/gdp_launcher_version.py
new file mode 100644
index 0000000..6f81398
--- /dev/null
+++ b/runtime/gdp_launcher_version.py
@@ -0,0 +1,12 @@
+# encoding=utf-8
+
+# This version of this file is only used when run uninstalled. It is replaced
+# with a generated version during installation.
+from game_data_packager.version import (
+ GAME_PACKAGE_RELEASE,
+ GAME_PACKAGE_VERSION,
+ )
+
+# reassure pyflakes
+GAME_PACKAGE_RELEASE
+GAME_PACKAGE_VERSION
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/game-data-packager.git
More information about the Pkg-games-commits
mailing list