Repository: lutris/lutris

Issue:
[Feature Request] minimize to tray
How about making lutris able to be minimized to tray (or still stay in tray bar) like steam or itch?
[Feature Request] minimize to tray
How about making lutris able to be minimized to tray (or still stay in tray bar) like steam or itch?


Discussion:

Resolving patch:
diff --git a/lutris/gui/application.py b/lutris/gui/application.py
--- a/lutris/gui/application.py
+++ b/lutris/gui/application.py
@@ -27,6 +27,7 @@
 gi.require_version('Gdk', '3.0')  # NOQA # isort:skip
 gi.require_version('Gtk', '3.0')  # NOQA # isort:skip
 
+
 from gi.repository import Gio, GLib, Gtk
 from lutris import pga
 from lutris.config import check_config
@@ -42,6 +43,7 @@
 from lutris.util.resources import parse_installer_url
 
 from .lutriswindow import LutrisWindow
+from lutris.gui.lutristray import LutrisTray, has_tray_support
 
 
 class Application(Gtk.Application):
@@ -60,6 +62,7 @@ def __init__(self):
 
         GLib.set_application_name(_('Lutris'))
         self.window = None
+        self.tray = None
         self.css_provider = Gtk.CssProvider.new()
 
         if os.geteuid() == 0:
@@ -176,6 +179,8 @@ def do_startup(self):
         )
         menubar = builder.get_object('menubar')
         self.set_menubar(menubar)
+        if has_tray_support():
+            self.tray = LutrisTray(application=self)
 
     def do_activate(self):
         if not self.window:
diff --git a/lutris/gui/lutristray.py b/lutris/gui/lutristray.py
new file mode 100644
--- /dev/null
+++ b/lutris/gui/lutristray.py
@@ -0,0 +1,93 @@
+import pprint
+from gi.repository import Gtk
+
+from lutris.gui import dialogs
+from lutris import runners
+from lutris import pga
+from lutris.gui.widgets.utils import get_runner_icon
+from lutris.util.system import get_desktop_environment, NO_TRAY_SUPPORT
+
+
+class LutrisTray(Gtk.StatusIcon):
+    def __init__(self, application, **kwargs):
+        super().__init__()
+        self.set_tooltip_text('Lutris')
+        self.set_visible(True)
+        self.application = application
+        self.set_from_icon_name('lutris')
+
+        self.installed_runners = None
+        self.active_platforms = None
+        self.menu = None
+
+        self.load_menu()
+
+        self.connect('activate', self.on_left_click)
+        self.connect('popup-menu', self.on_right_click)
+
+    def load_menu(self):
+        self.menu = Gtk.Menu()
+
+        about = Gtk.MenuItem()
+        about.set_label("About")
+        about.connect("activate", self.show_about_dialog)
+        self.menu.append(about)
+
+        self.menu.append(Gtk.SeparatorMenuItem())
+        self.add_runners()
+        self.menu.append(Gtk.SeparatorMenuItem())
+        self.add_platforms()
+        self.menu.append(Gtk.SeparatorMenuItem())
+
+        quit = Gtk.MenuItem()
+        quit.set_label("Quit")
+        quit.connect("activate", self.quit_application)
+        self.menu.append(quit)
+
+        self.menu.show_all()
+
+    def on_left_click(self, widget, event=None):
+        if self.application.window.is_active():
+            self.application.window.iconify()
+        else:
+            self.application.window.present()
+
+    def on_right_click(self, status, button, time):
+        self.menu.popup(None, None, None, None, button, time)
+
+    def show_about_dialog(self, widget):
+        dialogs.AboutDialog(parent=self.application.window)
+
+    def quit_application(self, widget):
+        self.application.do_shutdown()
+
+    def add_runners(self):
+        self.installed_runners = runners.get_installed()
+        for runner in self.installed_runners:
+            menu_item = Gtk.ImageMenuItem()
+            menu_item.set_label(runner.human_name)
+            menu_item.set_image(Gtk.Image.new_from_pixbuf(get_runner_icon(runner.name, format='pixbuf', size=(16, 16))))
+            menu_item.connect('activate', self.on_runner_selected, runner.name)
+            self.menu.append(menu_item)
+
+    def add_platforms(self):
+        self.active_platforms = pga.get_used_platforms()
+        for platform in self.active_platforms:
+            menu_item = Gtk.MenuItem()
+            menu_item.set_label(platform)
+            menu_item.connect('activate', self.on_platform_selected, platform)
+            self.menu.append(menu_item)
+
+    def on_runner_selected(self, widget, *data):
+        selected_runner = data[0]
+        self.application.window.set_selected_filter(selected_runner, None)
+        self.application.window.present()
+
+    def on_platform_selected(self, widget, *data):
+        selected_platform = data[0]
+        self.application.window.set_selected_filter(None, selected_platform)
+        self.application.window.present()
+
+
+def has_tray_support():
+    return False if get_desktop_environment() in NO_TRAY_SUPPORT else True
diff --git a/lutris/util/system.py b/lutris/util/system.py
--- a/lutris/util/system.py
+++ b/lutris/util/system.py
@@ -45,6 +45,10 @@
 
 IS_64BIT = sys.maxsize > 2**32
 
+NO_TRAY_SUPPORT = [
+    'gnome',
+]
+
 
 def execute(command, env=None, cwd=None, log_errors=False, quiet=False, shell=False):
     """
@@ -385,3 +389,60 @@ def open_uri(uri):
     """Opens a local or remote URI with the default application"""
     reset_library_preloads()
     Gtk.show_uri(None, uri, Gdk.CURRENT_TIME)
+
+
+def get_desktop_environment():
+    # From http://stackoverflow.com/questions/2035657/what-is-my-current-desktop-environment
+    # and http://ubuntuforums.org/showthread.php?t=652320
+    # and http://ubuntuforums.org/showthread.php?t=652320
+    # and http://ubuntuforums.org/showthread.php?t=1139057
+    if sys.platform in ["win32", "cygwin"]:
+        return "windows"
+    elif sys.platform == "darwin":
+        return "mac"
+    else:  # Most likely either a POSIX system or something not much common
+        desktop_session = os.environ.get("DESKTOP_SESSION")
+        if desktop_session is not None:  # easier to match if we doesn't have  to deal with caracter cases
+            desktop_session = desktop_session.lower()
+            if desktop_session in ["gnome", "unity", "cinnamon", "mate", "xfce4", "lxde", "fluxbox",
+                                   "blackbox", "openbox", "icewm", "jwm", "afterstep", "trinity", "kde"]:
+                return desktop_session
+            ## Special cases ##
+            # Canonical sets $DESKTOP_SESSION to Lubuntu rather than LXDE if using LXDE.
+            # There is no guarantee that they will not do the same with the other desktop environments.
+            elif "xfce" in desktop_session or desktop_session.startswith("xubuntu"):
+                return "xfce4"
+            elif desktop_session.startswith("ubuntu"):
+                return "unity"
+            elif desktop_session.startswith("lubuntu"):
+                return "lxde"
+            elif desktop_session.startswith("kubuntu"):
+                return "kde"
+            elif desktop_session.startswith("razor"):  # e.g. razorkwin
+                return "razor-qt"
+            elif desktop_session.startswith("wmaker"):  # e.g. wmaker-common
+                return "windowmaker"
+        if os.environ.get('KDE_FULL_SESSION') == 'true':
+            return "kde"
+        elif os.environ.get('GNOME_DESKTOP_SESSION_ID'):
+            if not "deprecated" in os.environ.get('GNOME_DESKTOP_SESSION_ID'):
+                return "gnome2"
+        # From http://ubuntuforums.org/showthread.php?t=652320
+        elif is_running("xfce-mcs-manage"):
+            return "xfce4"
+        elif is_running("ksmserver"):
+            return "kde"
+    return "unknown"
+
+
+def is_running(process):
+    # From http://www.bloggerpolis.com/2011/05/how-to-check-if-a-process-is-running-using-python/
+    # and http://richarddingwall.name/2009/06/18/windows-equivalents-of-ps-and-kill-commands/
+    try:  # Linux/Unix
+        s = subprocess.Popen(["ps", "axw"], stdout=subprocess.PIPE)
+    except:  # Windows
+        s = subprocess.Popen(["tasklist", "/v"], stdout=subprocess.PIPE)
+    for x in s.stdout:
+        if re.search(process, x):
+            return True
+    return False
\ No newline at end of file


Test patch: