Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/mixins/tray.py : 83%
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
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
13log = Logger("tray")
15TRAY_DELAY = envint("XPRA_TRAY_DELAY", 0)
18"""
19Mixin for supporting our system tray
20(not forwarding other application's trays - that's handled in WindowClient)
21"""
22class TrayClient(StubClientMixin):
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
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)
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)
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)
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()
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)
87 def show_menu(self, *_args):
88 if self.menu_helper:
89 self.menu_helper.activate()
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
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)
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