mirror of
https://github.com/lutris/lutris
synced 2024-10-14 19:53:53 +00:00
Merge pull request #453 from TingPing/kill-service
Properly use Gtk.Application, kill custom DBus service
This commit is contained in:
commit
5272be5240
150
bin/lutris
150
bin/lutris
|
@ -13,26 +13,8 @@
|
|||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dbus
|
||||
import dbus.service
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import optparse
|
||||
import signal
|
||||
import time
|
||||
import json
|
||||
|
||||
# pylint: disable=E0611
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
|
||||
from gi.repository import Gdk, Gtk, GObject, GLib
|
||||
|
||||
from os.path import realpath, dirname, normpath
|
||||
|
||||
LAUNCH_PATH = dirname(realpath(__file__))
|
||||
|
@ -40,132 +22,11 @@ if LAUNCH_PATH != "/usr/bin":
|
|||
SOURCE_PATH = normpath(os.path.join(LAUNCH_PATH, '..'))
|
||||
sys.path.insert(0, SOURCE_PATH)
|
||||
|
||||
from lutris.migrations import migrate
|
||||
from lutris.gui.application import Application
|
||||
|
||||
from lutris import pga
|
||||
from lutris.runtime import RuntimeUpdater
|
||||
from lutris.config import check_config # , register_handler
|
||||
from lutris.util.log import logger
|
||||
from lutris.game import Game
|
||||
from lutris.gui.installgamedialog import InstallerDialog
|
||||
from lutris.settings import VERSION
|
||||
from lutris.util import service
|
||||
from lutris.util.steam import get_steamapps_paths, AppManifest, get_appmanifests
|
||||
app = Application()
|
||||
sys.exit(app.run(sys.argv))
|
||||
|
||||
# Support for command line options.
|
||||
parser = optparse.OptionParser(version="%prog " + VERSION)
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Show debug messages")
|
||||
parser.add_option("-i", "--install", dest="installer_file",
|
||||
help="Install a game from a yml file")
|
||||
parser.add_option("-l", "--list-games", action="store_true", dest="list_games",
|
||||
help="List games in database")
|
||||
parser.add_option("-o", "--installed", action="store_true", dest="list_installed",
|
||||
help="Only list installed games")
|
||||
parser.add_option("--list-steam-games", action="store_true",
|
||||
help="List available Steam games")
|
||||
parser.add_option("--list-steam-folders", action="store_true",
|
||||
help="List all known Steam library folders")
|
||||
parser.add_option("-j", "--json", action="store_true", dest="json",
|
||||
help="Display the list of games in JSON format")
|
||||
parser.add_option("--reinstall", action="store_true", help="Reinstall game")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
if options.list_games:
|
||||
game_list = pga.get_games()
|
||||
if options.list_installed:
|
||||
game_list = [game for game in game_list if game['installed']]
|
||||
if options.json:
|
||||
games = []
|
||||
for game in game_list:
|
||||
games.append({
|
||||
'id': game['id'],
|
||||
'slug': game['slug'],
|
||||
'name': game['name'],
|
||||
'runner': game['runner'],
|
||||
'directory': game['directory']
|
||||
})
|
||||
print(json.dumps(games, indent=2))
|
||||
else:
|
||||
for game in game_list:
|
||||
print("{:4} | {:<40} | {:<40} | {:<15} | {:<64}".format(
|
||||
game['id'],
|
||||
game['name'][:40],
|
||||
game['slug'][:40],
|
||||
game['runner'] or '-',
|
||||
game['directory'] or '-'
|
||||
))
|
||||
exit()
|
||||
if options.list_steam_games:
|
||||
steamapps_paths = get_steamapps_paths()
|
||||
for platform in ('linux', 'windows'):
|
||||
for path in steamapps_paths[platform]:
|
||||
appmanifest_files = get_appmanifests(path)
|
||||
for appmanifest_file in appmanifest_files:
|
||||
appmanifest = AppManifest(os.path.join(path, appmanifest_file))
|
||||
print(" {:8} | {:<60} | {:10} | {}".format(
|
||||
appmanifest.steamid,
|
||||
appmanifest.name or '-',
|
||||
platform,
|
||||
", ".join(appmanifest.states)
|
||||
|
||||
))
|
||||
exit()
|
||||
if options.list_steam_folders:
|
||||
steamapps_paths = get_steamapps_paths()
|
||||
for platform in ('linux', 'windows'):
|
||||
for path in steamapps_paths[platform]:
|
||||
print(path)
|
||||
exit()
|
||||
|
||||
|
||||
check_config(force_wipe=False)
|
||||
|
||||
installer = False
|
||||
game = None
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# D-Bus init
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
lutris = service.get_service(bus)
|
||||
|
||||
# Make sure the existing process is not frozen
|
||||
if type(lutris) is dbus.Interface:
|
||||
try:
|
||||
lutris.is_running()
|
||||
except dbus.exceptions.DBusException as e:
|
||||
logger.error(e)
|
||||
try:
|
||||
# Get existing process' PID
|
||||
dbus_proxy = bus.get_object('org.freedesktop.DBus',
|
||||
'/org/freedesktop/DBus/Bus')
|
||||
dbus_interface = dbus.Interface(dbus_proxy, 'org.freedesktop.DBus')
|
||||
pid = dbus_interface.GetConnectionUnixProcessID(lutris.bus_name)
|
||||
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
except (OSError, dbus.exceptions.DBusException) as ex:
|
||||
logger.error("Lutris was non responsive, we tried, we failed and failed "
|
||||
"some more. Please now try to restart Lutris")
|
||||
logger.error(ex)
|
||||
exit()
|
||||
else:
|
||||
time.sleep(1) # Wait for bus name to be available again
|
||||
lutris = service.LutrisService(bus, '/', service.DBUS_INTERFACE)
|
||||
|
||||
migrate()
|
||||
|
||||
game_slug = ""
|
||||
for arg in args:
|
||||
if arg.startswith('lutris:'):
|
||||
game_slug = arg[7:]
|
||||
break
|
||||
|
||||
installer = options.installer_file
|
||||
|
||||
if game_slug or installer:
|
||||
if not game_slug and not os.path.isfile(installer):
|
||||
|
@ -200,10 +61,5 @@ if game_slug or installer:
|
|||
else:
|
||||
runtime_updater.update()
|
||||
InstallerDialog(installer or game_slug)
|
||||
GObject.threads_init()
|
||||
Gtk.main()
|
||||
exit()
|
||||
|
||||
lutris.run(int(time.time()))
|
||||
if lutris.is_running():
|
||||
Gdk.notify_startup_complete()
|
||||
|
|
1
debian/control
vendored
1
debian/control
vendored
|
@ -20,7 +20,6 @@ Architecture: any
|
|||
Depends: ${misc:Depends},
|
||||
${python3:Depends},
|
||||
python3-yaml,
|
||||
python3-dbus,
|
||||
python3-gi,
|
||||
python3-pyinotify,
|
||||
gir1.2-gtk-3.0,
|
||||
|
|
1
debian/py3dist-overrides
vendored
1
debian/py3dist-overrides
vendored
|
@ -1 +0,0 @@
|
|||
dbus_python python3-dbus
|
|
@ -21,11 +21,11 @@ BuildRequires: python3-devel
|
|||
|
||||
%if 0%{?fedora_version}
|
||||
BuildRequires: python3-gobject, python3-wheel, python3-setuptools, python3-gobject
|
||||
Requires: python3-gobject, python3-PyYAML, python3-dbus
|
||||
Requires: python3-gobject, python3-PyYAML
|
||||
%endif
|
||||
%if 0%{?rhel_version} || 0%{?centos_version}
|
||||
BuildRequires: python3-gobject
|
||||
Requires: python3-gobject, python3-PyYAML, python3-dbus
|
||||
Requires: python3-gobject, python3-PyYAML
|
||||
%endif
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: python3-gobject
|
||||
|
@ -34,7 +34,7 @@ BuildRequires: update-desktop-files
|
|||
BuildRequires: hicolor-icon-theme
|
||||
BuildRequires: polkit
|
||||
BuildRequires: python3-setuptools
|
||||
Requires: python3-gobject, python3-PyYAML, dbus-1-python3
|
||||
Requires: python3-gobject, python3-PyYAML
|
||||
%endif
|
||||
%if 0%{?fedora_version} || 0%{?suse_version}
|
||||
BuildRequires: fdupes
|
||||
|
|
203
lutris/gui/application.py
Normal file
203
lutris/gui/application.py
Normal file
|
@ -0,0 +1,203 @@
|
|||
# application.py
|
||||
#
|
||||
# Copyright (C) 2016 Patrick Griffis <tingping@tingping.se>
|
||||
#
|
||||
# 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 3 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. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import signal
|
||||
import json
|
||||
from gettext import gettext as _
|
||||
|
||||
# pylint: disable=E0611
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import GLib, Gio, Gtk
|
||||
|
||||
from lutris.migrations import migrate
|
||||
from lutris import pga
|
||||
from lutris.runtime import RuntimeUpdater
|
||||
from lutris.config import check_config # , register_handler
|
||||
from lutris.util.log import logger
|
||||
from lutris.game import Game
|
||||
from lutris.gui.installgamedialog import InstallerDialog
|
||||
from lutris.settings import VERSION
|
||||
from lutris.util.steam import get_steamapps_paths, AppManifest, get_appmanifests
|
||||
from .lutriswindow import LutrisWindow
|
||||
|
||||
class Application(Gtk.Application):
|
||||
|
||||
def __init__(self):
|
||||
Gtk.Application.__init__(self, application_id='net.lutris.Lutris',
|
||||
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
|
||||
GLib.set_application_name(_('Lutris'))
|
||||
self.window = None
|
||||
|
||||
self.add_main_option('debug', ord('d'), GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('Show debug messages'), None)
|
||||
self.add_main_option('install', ord('i'), GLib.OptionFlags.NONE, GLib.OptionArg.STRING,
|
||||
_('Install a game from a yml file'), None)
|
||||
self.add_main_option('list-games', ord('l'), GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('List all games in database'), None)
|
||||
self.add_main_option('installed', ord('o'), GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('Only list installed games'), None)
|
||||
self.add_main_option('list-steam-games', ord('s'), GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('List available Steam games'), None)
|
||||
self.add_main_option('list-steam-folders', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('List all known Steam library folders'), None)
|
||||
self.add_main_option('json', ord('j'), GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('Display the list of games in JSON format'), None)
|
||||
self.add_main_option('reinstall', 0, GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
|
||||
_('Reinstall game'), None)
|
||||
self.add_main_option(GLib.OPTION_REMAINING, 0, GLib.OptionFlags.NONE, GLib.OptionArg.STRING_ARRAY,
|
||||
_('uri to open'), 'URI')
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
def do_activate(self):
|
||||
if not self.window:
|
||||
self.window = LutrisWindow()
|
||||
self.add_window(self.window.window)
|
||||
self.window.window.present()
|
||||
|
||||
@staticmethod
|
||||
def _print(command_line, string):
|
||||
# Workaround broken pygobject bindings
|
||||
command_line.do_print_literal(command_line, string + '\n')
|
||||
|
||||
def do_command_line(self, command_line):
|
||||
options = command_line.get_options_dict()
|
||||
|
||||
if options.contains('debug'):
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
if options.contains('list-games'):
|
||||
game_list = pga.get_games()
|
||||
if options.contains('installed'):
|
||||
game_list = [game for game in game_list if game['installed']]
|
||||
if options.contains('json'):
|
||||
games = []
|
||||
for game in game_list:
|
||||
games.append({
|
||||
'id': game['id'],
|
||||
'slug': game['slug'],
|
||||
'name': game['name'],
|
||||
'runner': game['runner'],
|
||||
'directory': game['directory']
|
||||
})
|
||||
self._print(command_line, json.dumps(games, indent=2))
|
||||
else:
|
||||
for game in game_list:
|
||||
self._print(command_line, "{:4} | {:<40} | {:<40} | {:<15} | {:<64}".format(
|
||||
game['id'],
|
||||
game['name'][:40],
|
||||
game['slug'][:40],
|
||||
game['runner'] or '-',
|
||||
game['directory'] or '-'
|
||||
))
|
||||
return 0
|
||||
|
||||
if options.contains('list-steam-games'):
|
||||
steamapps_paths = get_steamapps_paths()
|
||||
for platform in ('linux', 'windows'):
|
||||
for path in steamapps_paths[platform]:
|
||||
appmanifest_files = get_appmanifests(path)
|
||||
for appmanifest_file in appmanifest_files:
|
||||
appmanifest = AppManifest(os.path.join(path, appmanifest_file))
|
||||
self._print(command_line, " {:8} | {:<60} | {:10} | {}".format(
|
||||
appmanifest.steamid,
|
||||
appmanifest.name or '-',
|
||||
platform,
|
||||
", ".join(appmanifest.states)
|
||||
|
||||
))
|
||||
return 0
|
||||
|
||||
if options.contains('list-steam-folders'):
|
||||
steamapps_paths = get_steamapps_paths()
|
||||
for platform in ('linux', 'windows'):
|
||||
for path in steamapps_paths[platform]:
|
||||
self._print(command_line, path)
|
||||
return 0
|
||||
|
||||
check_config(force_wipe=False)
|
||||
migrate()
|
||||
game = None
|
||||
|
||||
game_slug = ''
|
||||
uri = options.lookup_value(GLib.OPTION_REMAINING)
|
||||
if uri and len(uri):
|
||||
uri = uri[0] # TODO: Support multiple
|
||||
if not uri.startswith('lutris:'):
|
||||
self._print(command_line, '%s is not a valid URI' %uri)
|
||||
return 1
|
||||
game_slug = uri[7:]
|
||||
|
||||
if game_slug or options.contains('install'):
|
||||
installer = options.lookup_value('install')
|
||||
if not game_slug and not os.path.isfile(installer):
|
||||
self._print(command_line, "No such file: %s" % installer)
|
||||
return 1
|
||||
|
||||
db_game = None
|
||||
if game_slug:
|
||||
db_game = (pga.get_game_by_field(game_slug, 'id')
|
||||
or pga.get_game_by_field(game_slug, 'slug')
|
||||
or pga.get_game_by_field(game_slug, 'installer_slug'))
|
||||
|
||||
if db_game and db_game['installed'] and not options.contains('reinstall'):
|
||||
self._print(command_line, "Launching %s", db_game['name'])
|
||||
if self.window:
|
||||
self.run_game(db_game['id'])
|
||||
else:
|
||||
lutris_game = Game(db_game['id'])
|
||||
# FIXME: This is awful
|
||||
lutris_game.exit_main_loop = True
|
||||
lutris_game.play()
|
||||
try:
|
||||
GLib.MainLoop().run()
|
||||
except KeyboardInterrupt:
|
||||
lutris_game.stop()
|
||||
return 0
|
||||
else:
|
||||
self._print(command_line, "Installing %s", game_slug)
|
||||
if self.window:
|
||||
self.install_game(installer or game_slug)
|
||||
else:
|
||||
runtime_updater = RuntimeUpdater()
|
||||
runtime_updater.update()
|
||||
# FIXME: This should be a Gtk.Dialog child of LutrisWindow
|
||||
dialog = InstallerDialog(installer or game_slug)
|
||||
self.add_window(dialog)
|
||||
return 0
|
||||
|
||||
self.activate()
|
||||
return 0
|
||||
|
||||
def do_shutdown(self):
|
||||
Gtk.Application.do_shutdown(self)
|
||||
if self.window:
|
||||
self.window.window.destroy()
|
||||
|
||||
def install_game(self, game_ref):
|
||||
self.window.on_install_clicked(game_ref=game_ref)
|
||||
|
||||
def run_game(self, game_id):
|
||||
self.window.on_game_run(game_id=game_id)
|
||||
|
|
@ -33,21 +33,16 @@ from lutris.gui.gameviews import (
|
|||
)
|
||||
|
||||
|
||||
class LutrisWindow(Gtk.Application):
|
||||
class LutrisWindow:
|
||||
"""Handler class for main window signals."""
|
||||
def __init__(self, service=None):
|
||||
def __init__(self):
|
||||
|
||||
Gtk.Application.__init__(
|
||||
self, application_id="net.lutris.main",
|
||||
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE
|
||||
)
|
||||
ui_filename = os.path.join(
|
||||
datapath.get(), 'ui', 'lutris-window.ui'
|
||||
)
|
||||
if not os.path.exists(ui_filename):
|
||||
raise IOError('File %s not found' % ui_filename)
|
||||
|
||||
self.service = service
|
||||
self.runtime_updater = RuntimeUpdater()
|
||||
self.running_game = None
|
||||
self.threads_stoppers = []
|
||||
|
@ -465,16 +460,11 @@ class LutrisWindow(Gtk.Application):
|
|||
logger.info("%s is still running, stopping it", self.running_game.name)
|
||||
self.running_game.stop()
|
||||
|
||||
if self.service:
|
||||
self.service.stop()
|
||||
|
||||
# Save settings
|
||||
width, height = self.window_size
|
||||
settings.write_setting('width', width)
|
||||
settings.write_setting('height', height)
|
||||
|
||||
Gtk.main_quit(*args)
|
||||
|
||||
def on_runners_activate(self, _widget, _data=None):
|
||||
"""Callback when manage runners is activated."""
|
||||
RunnersDialog()
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
import dbus
|
||||
from gi.repository import Gtk, GObject
|
||||
from lutris.gui.lutriswindow import LutrisWindow
|
||||
from lutris.util.log import logger
|
||||
|
||||
DBUS_INTERFACE = 'net.lutris.main'
|
||||
|
||||
|
||||
class LutrisService(dbus.service.Object):
|
||||
"""Main D-Bus Lutris service."""
|
||||
def __init__(self, bus_name, object_path, name):
|
||||
dbus.service.Object.__init__(self, bus_name, object_path, name)
|
||||
self.running = False
|
||||
self.lutris_window = None
|
||||
|
||||
def stop(self):
|
||||
""" stop the dbus controller and remove from the bus """
|
||||
self.remove_from_connection()
|
||||
|
||||
@dbus.service.method(DBUS_INTERFACE, out_signature='b')
|
||||
def is_running(self):
|
||||
return self.running
|
||||
|
||||
@dbus.service.method(DBUS_INTERFACE, in_signature='i')
|
||||
def run(self, timestamp):
|
||||
if self.is_running():
|
||||
self.lutris_window.window.present_with_time(timestamp)
|
||||
else:
|
||||
logger.info("Welcome to Lutris")
|
||||
self.running = True
|
||||
self.lutris_window = LutrisWindow(service=self)
|
||||
GObject.threads_init()
|
||||
Gtk.main()
|
||||
self.running = False
|
||||
|
||||
@dbus.service.method(DBUS_INTERFACE, in_signature='s')
|
||||
def install_game(self, game_ref):
|
||||
self.lutris_window.on_install_clicked(game_ref=game_ref)
|
||||
|
||||
@dbus.service.method(DBUS_INTERFACE, in_signature='i')
|
||||
def run_game(self, game_id):
|
||||
self.lutris_window.on_game_run(game_id=game_id)
|
||||
|
||||
|
||||
def get_bus():
|
||||
return dbus.SessionBus()
|
||||
|
||||
|
||||
def get_service(bus):
|
||||
request = bus.request_name(DBUS_INTERFACE, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
|
||||
if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
|
||||
service = LutrisService(bus, '/', DBUS_INTERFACE)
|
||||
else:
|
||||
proxy = bus.get_object(DBUS_INTERFACE, "/")
|
||||
service = dbus.Interface(proxy, DBUS_INTERFACE)
|
||||
return service
|
1
setup.py
1
setup.py
|
@ -32,7 +32,6 @@ setup(
|
|||
install_requires=[
|
||||
'PyYAML',
|
||||
'PyGObject',
|
||||
'dbus-python',
|
||||
'pyinotify',
|
||||
'evdev'
|
||||
],
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<property name="pixel_size">16</property>
|
||||
<property name="icon_name">view-list-symbolic</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<object class="GtkApplicationWindow" id="window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Lutris</property>
|
||||
<property name="window_position">center</property>
|
||||
|
|
Loading…
Reference in a new issue