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