Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2# This file is part of Xpra. 

3# Copyright (C) 2020 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 signal 

9from gi.repository import Gtk, Gdk, GLib, Pango 

10 

11from xpra import __version__ 

12from xpra.util import envint 

13from xpra.os_util import SIGNAMES, OSX 

14from xpra.exit_codes import EXIT_TIMEOUT 

15from xpra.common import SPLASH_EXIT_DELAY 

16from xpra.gtk_common.gtk_util import add_close_accel, get_icon_pixbuf 

17from xpra.gtk_common.gobject_compat import install_signal_handlers 

18from xpra.client.gtk_base.css_overrides import inject_css_overrides 

19from xpra.platform.gui import force_focus 

20from xpra.log import Logger 

21 

22log = Logger("client", "util") 

23 

24inject_css_overrides() 

25 

26TIMEOUT = envint("XPRA_SPLASH_TIMEOUT", 60) 

27LINES = envint("XPRA_SPLASH_LINES", 4) 

28 

29#PULSE_CHARS = "▁▂▃▄▅▆▇█▇▆▅▄▃▁" 

30PULSE_CHARS = "◐◓◑◒" 

31if OSX: 

32 DONE_CHAR = "-" 

33else: 

34 DONE_CHAR = "⬤" 

35 

36W = 400 

37 

38 

39class SplashScreen(Gtk.Window): 

40 

41 def __init__(self): 

42 self.exit_code = None 

43 super().__init__(type=Gtk.WindowType.TOPLEVEL) 

44 self.connect("delete_event", self.exit) 

45 title = "Xpra %s" % __version__ 

46 self.set_title(title) 

47 self.set_size_request(W, 40+40*LINES) 

48 self.set_position(Gtk.WindowPosition.CENTER) 

49 self.set_decorated(False) 

50 self.set_opacity(0.9) 

51 self.set_accept_focus(False) 

52 self.set_focus_on_map(False) 

53 self.set_skip_pager_hint(True) 

54 self.set_skip_taskbar_hint(True) 

55 self.set_type_hint(Gdk.WindowTypeHint.SPLASHSCREEN) 

56 vbox = Gtk.VBox() 

57 hbox = Gtk.HBox(homogeneous=False) 

58 icon = get_icon_pixbuf("xpra.png") 

59 if icon: 

60 self.set_icon(icon) 

61 hbox.pack_start(Gtk.Image.new_from_pixbuf(icon), False, False, 20) 

62 label = Gtk.Label(label=title) 

63 label.modify_font(Pango.FontDescription("sans 18")) 

64 hbox.pack_start(label, True, True, 20) 

65 vbox.add(hbox) 

66 self.labels = [] 

67 for i in range(LINES): 

68 l = Gtk.Label(label=" ") 

69 l.set_opacity((i+1)/LINES) 

70 #l.set_line_wrap(True) 

71 self.labels.append(l) 

72 al = Gtk.Alignment(xalign=0, yalign=0.5, xscale=0, yscale=0) 

73 al.add(l) 

74 vbox.pack_start(al, True, True, 4) 

75 self.progress_bar = Gtk.ProgressBar() 

76 self.progress_bar.set_size_request(320, 30) 

77 self.progress_bar.set_show_text(False) 

78 self.progress_bar.set_fraction(0) 

79 self.progress_timer = None 

80 self.fade_out_timer = None 

81 self.exit_timer = None 

82 vbox.add(self.progress_bar) 

83 self.timeout_timer = 0 

84 self.pulse_timer = 0 

85 self.pulse_counter = 0 

86 self.current_label_text = None 

87 self.add(vbox) 

88 install_signal_handlers(None, self.handle_signal) 

89 SIGPIPE = getattr(signal, "SIGPIPE", None) 

90 if SIGPIPE: #ie: POSIX 

91 signal.signal(SIGPIPE, self.handle_signal) 

92 self.opacity = 100 

93 self.pct = 0 

94 

95 

96 def run(self): 

97 from xpra.make_thread import start_thread 

98 start_thread(self.read_stdin, "read-stdin", True) 

99 self.show_all() 

100 force_focus() 

101 self.present() 

102 self.timeout_timer = GLib.timeout_add(TIMEOUT*1000, self.timeout) 

103 self.pulse_timer = GLib.timeout_add(100, self.pulse) 

104 Gtk.main() 

105 return self.exit_code or 0 

106 

107 def timeout(self): 

108 log("timeout()") 

109 self.timeout_timer = None 

110 self.exit_code = EXIT_TIMEOUT 

111 self.show_progress_value(100) 

112 self.progress_bar.set_text("timeout") 

113 self.progress_bar.set_show_text(True) 

114 

115 def cancel_timeout_timer(self): 

116 tt = self.timeout_timer 

117 if tt: 

118 self.timeout_timer = 0 

119 GLib.source_remove(tt) 

120 

121 def pulse(self): 

122 if self.pct<100: 

123 pulse_char = PULSE_CHARS[self.pulse_counter % len(PULSE_CHARS)] 

124 else: 

125 pulse_char = DONE_CHAR 

126 label = " %s %s" % (pulse_char, self.current_label_text) 

127 self.labels[-1].set_label(label) 

128 self.pulse_counter += 1 

129 return True 

130 

131 def read_stdin(self): 

132 log("read_stdin()") 

133 while self.exit_code is None: 

134 line = sys.stdin.readline() 

135 self.handle_stdin_line(line) 

136 

137 def handle_stdin_line(self, line): 

138 parts = line.rstrip("\n\r").split(":", 1) 

139 log("handle_stdin_line(%r)", line) 

140 pct = self.pct 

141 if parts[0]: 

142 try: 

143 pct = int(parts[0]) 

144 except ValueError: 

145 pass 

146 else: 

147 self.show_progress_value(pct) 

148 if len(parts)>=2: 

149 self.current_label_text = parts[1] 

150 if self.pct!=pct: 

151 for i in range(len(self.labels)-1): 

152 next_line = self.labels[i+1].get_label() 

153 for c in PULSE_CHARS: 

154 next_line = next_line.replace(c, DONE_CHAR) 

155 self.labels[i].set_label(next_line) 

156 self.pulse_counter = 0 

157 self.pct = pct 

158 self.pulse() 

159 

160 def show_progress_value(self, pct): 

161 self.cancel_progress_timer() 

162 GLib.idle_add(self.progress_bar.set_fraction, pct/100.0) 

163 if pct>=100: 

164 self.cancel_pulse_timer() 

165 self.cancel_fade_out_timer() 

166 self.opacity = 100 

167 self.fade_out_timer = GLib.timeout_add(SPLASH_EXIT_DELAY*1000//100, self.fade_out) 

168 self.cancel_exit_timer() 

169 def exit_splash(): 

170 self.exit_timer = None 

171 self.exit() 

172 self.exit_timer = GLib.timeout_add(SPLASH_EXIT_DELAY*1000, exit_splash) 

173 else: 

174 self.progress_timer = GLib.timeout_add(40, self.increase_fraction, pct) 

175 

176 def cancel_exit_timer(self): 

177 et = self.exit_timer 

178 if et: 

179 self.exit_timer = None 

180 GLib.source_remove(et) 

181 

182 def cancel_fade_out_timer(self): 

183 fot = self.fade_out_timer 

184 if fot: 

185 self.fade_out_timer = None 

186 GLib.source_remove(fot) 

187 

188 def cancel_progress_timer(self): 

189 pt = self.progress_timer 

190 if pt: 

191 self.progress_timer = None 

192 GLib.source_remove(pt) 

193 

194 def cancel_pulse_timer(self): 

195 pt = self.pulse_timer 

196 if pt: 

197 self.pulse_timer = None 

198 GLib.source_remove(pt) 

199 

200 def increase_fraction(self, pct, inc=1, max_increase=10): 

201 log("increase_fraction%s", (pct, inc, max_increase)) 

202 self.cancel_progress_timer() 

203 GLib.idle_add(self.progress_bar.set_fraction, (pct+inc)/100.0) 

204 if inc<max_increase: 

205 self.progress_timer = GLib.timeout_add(40+20*(2+inc)**2, self.increase_fraction, pct, inc+1, max_increase) 

206 return False 

207 

208 

209 def fade_out(self): 

210 self.opacity = max(0, self.opacity-1) 

211 actual = int(self.get_opacity()*100) 

212 if actual>self.opacity: 

213 self.set_opacity(self.opacity/100.0) 

214 if actual<=0: 

215 self.fade_out_timer = None 

216 return actual>0 

217 

218 def exit(self, *args): 

219 log("exit%s calling %s", args, Gtk.main_quit) 

220 if self.exit_code is None: 

221 self.exit_code = 0 

222 self.cancel_progress_timer() 

223 self.cancel_timeout_timer() 

224 self.cancel_fade_out_timer() 

225 self.cancel_pulse_timer() 

226 self.cancel_exit_timer() 

227 Gtk.main_quit() 

228 

229 

230 def handle_signal(self, signum, frame=None): 

231 log("handle_signal(%s, %s)", SIGNAMES.get(signum, signum), frame) 

232 self.exit_code = 128-(signum or 0) 

233 GLib.idle_add(self.exit) 

234 

235 

236def main(_args): 

237 import os 

238 if os.environ.get("XPRA_HIDE_DOCK") is None: 

239 os.environ["XPRA_HIDE_DOCK"] = "1" 

240 from xpra.platform import program_context 

241 with program_context("splash", "Splash"): 

242 Gtk.Window.set_auto_startup_notification(False) 

243 w = SplashScreen() 

244 add_close_accel(w, Gtk.main_quit) 

245 return w.run() 

246 

247 

248if __name__ == "__main__": # pragma: no cover 

249 sys.exit(main(sys.argv))