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# This file is part of Xpra. 

2# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org> 

3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

4# later version. See the file COPYING for details. 

5#pylint: disable-msg=E1101 

6 

7from xpra.platform.gui import get_native_tray_classes, get_native_tray_menu_helper_class 

8from xpra.os_util import bytestostr 

9from xpra.util import envint, make_instance, CLIENT_EXIT, XPRA_APP_ID 

10from xpra.client.mixins.stub_client_mixin import StubClientMixin 

11from xpra.log import Logger 

12 

13log = Logger("tray") 

14 

15TRAY_DELAY = envint("XPRA_TRAY_DELAY", 0) 

16 

17 

18""" 

19Mixin for supporting our system tray 

20(not forwarding other application's trays - that's handled in WindowClient) 

21""" 

22class TrayClient(StubClientMixin): 

23 

24 def __init__(self): 

25 StubClientMixin.__init__(self) 

26 #settings: 

27 self.tray_enabled = False 

28 self.delay_tray = False 

29 self.tray_icon = None 

30 #state: 

31 self.tray = None 

32 self.menu_helper = None 

33 

34 def init(self, opts): 

35 self.tray_enabled = opts.tray 

36 self.delay_tray = opts.delay_tray 

37 self.tray_icon = opts.tray_icon 

38 if not self.tray_enabled: 

39 return 

40 self.menu_helper = self.make_tray_menu_helper() 

41 if self.delay_tray: 

42 self.connect("first-ui-received", self.setup_xpra_tray) 

43 else: 

44 #show shortly after the main loop starts running: 

45 self.timeout_add(TRAY_DELAY, self.setup_xpra_tray) 

46 

47 def setup_xpra_tray(self, *args): 

48 log("setup_xpra_tray%s", args) 

49 tray = self.create_xpra_tray(self.tray_icon or "xpra") 

50 self.tray = tray 

51 if tray: 

52 tray.show() 

53 icon_timestamp = tray.icon_timestamp 

54 def reset_icon(): 

55 if not self.tray: 

56 return 

57 #re-set the icon after a short delay, 

58 #seems to help with buggy tray geometries, 

59 #but don't do it if we have already changed the icon 

60 #(ie: the dynamic window icon code may have set a new one) 

61 if icon_timestamp==tray.icon_timestamp: 

62 tray.set_icon() 

63 self.timeout_add(1000, reset_icon) 

64 

65 def cleanup(self): 

66 t = self.tray 

67 if t: 

68 self.tray = None 

69 try: 

70 t.cleanup() 

71 except Exception: 

72 log.error("error on tray cleanup", exc_info=True) 

73 

74 

75 def get_tray_classes(self): 

76 #subclasses may add their toolkit specific variants, if any 

77 #by overriding this method 

78 #use the native ones first: 

79 return get_native_tray_classes() 

80 

81 def make_tray_menu_helper(self): 

82 """ menu helper class used by our tray (make_tray / setup_xpra_tray) """ 

83 mhc = (get_native_tray_menu_helper_class(), self.get_tray_menu_helper_class()) 

84 log("make_tray_menu_helper() tray menu helper classes: %s", mhc) 

85 return make_instance(mhc, self) 

86 

87 def show_menu(self, *_args): 

88 if self.menu_helper: 

89 self.menu_helper.activate() 

90 

91 def create_xpra_tray(self, tray_icon_filename): 

92 tray = None 

93 #this is our own tray 

94 def xpra_tray_click(button, pressed, time=0): 

95 log("xpra_tray_click(%s, %s, %s)", button, pressed, time) 

96 if button==1 and pressed: 

97 self.idle_add(self.menu_helper.activate, button, time) 

98 elif button in (2, 3) and not pressed: 

99 self.idle_add(self.menu_helper.popup, button, time) 

100 def xpra_tray_mouseover(*args): 

101 log("xpra_tray_mouseover%s", args) 

102 def xpra_tray_exit(*args): 

103 log("xpra_tray_exit%s", args) 

104 self.disconnect_and_quit(0, CLIENT_EXIT) 

105 def xpra_tray_geometry(*args): 

106 if tray: 

107 log("xpra_tray_geometry%s geometry=%s", args, tray.get_geometry()) 

108 menu = None 

109 if self.menu_helper: 

110 menu = self.menu_helper.build() 

111 tray = self.make_tray(XPRA_APP_ID, menu, self.get_tray_title(), tray_icon_filename, 

112 xpra_tray_geometry, xpra_tray_click, xpra_tray_mouseover, xpra_tray_exit) 

113 log("setup_xpra_tray(%s)=%s (%s)", tray_icon_filename, tray, type(tray)) 

114 if tray: 

115 def reset_tray_title(): 

116 tray.set_tooltip(self.get_tray_title()) 

117 self.after_handshake(reset_tray_title) 

118 return tray 

119 

120 def make_tray(self, *args): 

121 """ tray used by our own application """ 

122 tc = self.get_tray_classes() 

123 log("make_tray%s tray classes=%s", args, tc) 

124 return make_instance(tc, self, *args) 

125 

126 def get_tray_title(self) -> list: 

127 t = [] 

128 if self.session_name or self.server_session_name: 

129 t.append(self.session_name or self.server_session_name) 

130 p = self._protocol 

131 if p: 

132 conn = getattr(p, "_conn", None) 

133 if conn: 

134 from xpra.net.bytestreams import pretty_socket 

135 cinfo = conn.get_info() 

136 endpoint = pretty_socket(cinfo.get("endpoint", conn.target)).split("?")[0] 

137 t.append(endpoint) 

138 if not t: 

139 t.insert(0, "Xpra") 

140 v = "\n".join(str(x) for x in t) 

141 log("get_tray_title()=%r (items=%s)", v, tuple(bytestostr(x) for x in t)) 

142 return v