1# -*- coding: utf-8 -*-
2# This file is part of Xpra.
3# Copyright (C) 2021 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
6
7import sys
8import os.path
9import subprocess
10
11from gi.repository import Gtk, Pango, GLib
12
13from xpra.os_util import POSIX, OSX, which
14from xpra.gtk_common.gobject_compat import register_os_signals
15from xpra.gtk_common.gtk_util import (
16    add_close_accel,
17    get_icon_pixbuf,
18    imagebutton,
19    )
20from xpra.make_thread import start_thread
21from xpra.log import Logger
22
23log = Logger("client", "util")
24
25
26def exec_command(cmd):
27    env = os.environ.copy()
28    proc = subprocess.Popen(cmd, env=env)
29    return proc
30
31
32def xal(widget, xalign=1):
33    al = Gtk.Alignment(xalign=xalign, yalign=0.5, xscale=0, yscale=0)
34    al.add(widget)
35    return al
36
37def sf(w, font="sans 14"):
38    w.modify_font(Pango.FontDescription(font))
39    return w
40
41def l(label):
42    widget = Gtk.Label(label)
43    return sf(widget)
44
45
46class DesktopGreeter(Gtk.Window):
47
48    def __init__(self):
49        self.exit_code = None
50        super().__init__()
51        self.set_border_width(20)
52        self.set_title("Start Desktop Environment")
53        self.set_position(Gtk.WindowPosition.CENTER)
54        self.set_size_request(640, 300)
55        icon = get_icon_pixbuf("xpra.png")
56        if icon:
57            self.set_icon(icon)
58        self.connect("delete-event", self.quit)
59        add_close_accel(self, self.quit)
60
61        vbox = Gtk.VBox(False, 0)
62        vbox.set_spacing(10)
63
64        #self.entry_label = l("Command:")
65        #vbox.add(self.entry_label)
66        self.desktop_combo = sf(Gtk.ComboBoxText())
67        vbox.add(self.desktop_combo)
68
69        # Action buttons:
70        hbox = Gtk.HBox(False, 20)
71        vbox.pack_start(hbox, False, True, 20)
72        def btn(label, tooltip, callback, default=False):
73            ib = imagebutton(label, tooltip=tooltip, clicked_callback=callback, icon_size=32,
74                            default=default, label_font=Pango.FontDescription("sans 16"))
75            hbox.pack_start(ib)
76            return ib
77        self.cancel_btn = btn("Exit", "", self.quit)
78        self.run_btn = btn("Start", "Start the desktop environment", self.run_command)
79
80        vbox.show_all()
81        self.add(vbox)
82        start_thread(self.load_desktop_session, "load-desktop-sessions", daemon=True)
83
84    def app_signal(self, signum):
85        if self.exit_code is None:
86            self.exit_code = 128 + signum
87        log("app_signal(%s) exit_code=%i", signum, self.exit_code)
88        self.quit()
89
90    def quit(self, *args):
91        log("quit%s", args)
92        if self.exit_code is None:
93            self.exit_code = 0
94        self.do_quit()
95
96    def do_quit(self):
97        log("do_quit()")
98        Gtk.main_quit()
99
100    def run_command(self, *_args):
101        name = self.desktop_combo.get_active_text()
102        cmd = name
103        if cmd in self.desktop_sessions:
104            session = self.desktop_sessions.get(cmd)
105            #ie:
106            # session={
107            #    'Type': '',
108            #    'VersionString': '',
109            #    'Name': 'Deepin',
110            #    'GenericName': '',
111            #    'NoDisplay': False,
112            #    'Comment': 'Deepin Desktop Environment',
113            #    'Icon': '',
114            #    'Hidden': False,
115            #    'OnlyShowIn': [],
116            #    'NotShowIn': [],
117            #    'Exec': '/usr/bin/startdde',
118            #    'TryExec': '/usr/bin/startdde',
119            #    'Path': '',
120            #    'Terminal': False,
121            #    'MimeTypes': [],
122            #    'Categories': [],
123            #    'StartupNotify': False,
124            #    'StartupWMClass': '',
125            #    'URL': '',
126            #    'command': '/usr/bin/startdde',
127            #    'IconData': b'<svg>...</svg>\n',
128            #    'IconType': 'svg'
129            #    }
130            for k in ("command", "Exec", "TryExec"):
131                cmd = session.get(k)
132                if cmd:
133                    break
134        if cmd and not os.path.isabs(cmd):
135            cmd = which(cmd)
136        if not cmd:
137            log.warn("no command found for '%s'", name)
138            return
139        argv = [cmd]
140        self.destroy()
141        os.execv(cmd, argv)
142
143    def wait_for_subprocess(self, proc):
144        proc.wait()
145        log("return code: %s", proc.returncode)
146        GLib.idle_add(self.show)
147
148    def load_desktop_session(self):
149        from xpra.server.menu_provider import get_menu_provider
150        self.desktop_sessions = get_menu_provider().get_desktop_sessions()
151        GLib.idle_add(self.populate_xsessions)
152
153    def populate_xsessions(self):
154        log("populate_xsessions()")
155        self.desktop_combo.get_model().clear()
156        if self.desktop_sessions:
157            for name in sorted(self.desktop_sessions.keys()):
158                self.desktop_combo.append_text(name)
159        else:
160            self.desktop_combo.append_text("xterm")
161        self.desktop_combo.set_active(0)
162
163
164def main(options=None): # pragma: no cover
165    assert POSIX and not OSX
166    from xpra.platform import program_context
167    from xpra.log import enable_color
168    from xpra.platform.gui import init, ready
169    with program_context("xpra-start-gui", "Xpra Start GUI"):
170        enable_color()
171        init()
172        gui = DesktopGreeter()
173        register_os_signals(gui.app_signal)
174        ready()
175        gui.show()
176        gui.present()
177        Gtk.main()
178        log("do_main() gui.exit_code=%s", gui.exit_code)
179        return gui.exit_code
180
181
182if __name__ == "__main__":  # pragma: no cover
183    r = main()
184    sys.exit(r)
185