Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/gtk_base/statusicon_tray.py : 56%
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 Nathaniel Smith <njs@pobox.com>
3# Copyright (C) 2011-2019 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.
7# A tray implemented using Gtk.StatusIcon
9import os
10from time import time
11from gi.repository import Gtk, Gdk, GdkPixbuf
13from xpra.os_util import WIN32, OSX, POSIX, monotonic_time
14from xpra.util import envbool
15from xpra.client.tray_base import TrayBase, log
16from xpra.gtk_common.gtk_util import (
17 get_icon_from_file, get_pixbuf_from_data, get_default_root_window,
18 )
20ORIENTATION = {
21 Gtk.Orientation.HORIZONTAL : "HORIZONTAL",
22 Gtk.Orientation.VERTICAL : "VERTICAL",
23 }
25GUESS_GEOMETRY = WIN32 or OSX
26GUESS_GEOMETRY = envbool("XPRA_GUESS_ICON_GEOMETRY", GUESS_GEOMETRY)
27log("tray GUESS_GEOMETRY=%s", GUESS_GEOMETRY)
28SAVE = envbool("XPRA_SAVE_SYSTRAY", False)
31class GTKStatusIconTray(TrayBase):
33 def __init__(self, *args, **kwargs):
34 super().__init__(*args, **kwargs)
35 self.tray_widget = Gtk.StatusIcon()
36 self.tray_widget.set_tooltip_text(self.tooltip or "Xpra")
37 self.tray_widget.connect('activate', self.activate_menu)
38 self.tray_widget.connect('popup-menu', self.popup_menu)
39 if self.size_changed_cb:
40 self.tray_widget.connect('size-changed', self.size_changed_cb)
41 if self.default_icon_filename:
42 self.set_icon()
43 self.tray_widget.set_visible(True)
45 def may_guess(self):
46 log("may_guess() GUESS_GEOMETRY=%s, current guess=%s", GUESS_GEOMETRY, self.geometry_guess)
47 if GUESS_GEOMETRY:
48 x, y = get_default_root_window().get_pointer()[-3:-1]
49 w, h = self.get_size()
50 self.recalculate_geometry(x, y, w, h)
52 def activate_menu(self, widget):
53 modifiers_mask = get_default_root_window().get_pointer()[-1]
54 log("activate_menu(%s) modifiers_mask=%s", widget, modifiers_mask)
55 if (modifiers_mask & Gdk.ModifierType.SHIFT_MASK) ^ OSX:
56 self.handle_click(3)
57 else:
58 self.handle_click(1)
60 def popup_menu(self, widget, button, event_time, *args):
61 modifiers_mask = get_default_root_window().get_pointer()[-1]
62 log("popup_menu(%s, %s, %s, %s) modifiers_mask=%s", widget, button, event_time, args, modifiers_mask)
63 if (modifiers_mask & Gdk.ModifierType.SHIFT_MASK) ^ OSX:
64 self.handle_click(1)
65 else:
66 self.handle_click(3)
68 def handle_click(self, button, event_time=0):
69 log("handle_click(%i, %i)", button, event_time)
70 self.may_guess()
71 if self.click_cb:
72 self.click_cb(button, 1, event_time)
73 self.click_cb(button, 0, event_time)
76 def hide(self):
77 log("%s.set_visible(False)", self.tray_widget)
78 if self.tray_widget:
79 self.tray_widget.set_visible(False)
81 def show(self):
82 log("%s.set_visible(True)", self.tray_widget)
83 if self.tray_widget:
84 self.tray_widget.set_visible(True)
87 def get_screen(self):
88 if not self.tray_widget:
89 return -1
90 ag = self.tray_widget.get_geometry()
91 if ag is None:
92 return -1
93 screen = ag[-3]
94 if not screen:
95 return -1
96 return screen.get_number()
98 def get_orientation(self):
99 if not self.tray_widget:
100 return None
101 ag = self.tray_widget.get_geometry()
102 if ag is None:
103 return None
104 gtk_orientation = ag[-1]
105 return ORIENTATION.get(gtk_orientation)
107 def get_geometry(self):
108 assert self.tray_widget
109 #on X11, if we don't have an xid, don't bother querying its geometry,
110 #as this would trigger some ugly GTK warnings we can do nothing about
111 if POSIX and os.environ.get("DISPLAY") and self.tray_widget.get_x11_window_id()==0:
112 ag = None
113 else:
114 ag = self.tray_widget.get_geometry()
115 log("GTKStatusIconTray.get_geometry() %s.get_geometry()=%s", self.tray_widget, ag)
116 if ag is None:
117 if not self.geometry_guess:
118 self.may_guess()
119 #probably win32 or OSX, gnome-shell or KDE5..
120 log("GTKStatusIconTray.get_geometry() no geometry value available, returning guess: %s",
121 self.geometry_guess)
122 return self.geometry_guess or (0, 0, 0, 0)
123 #gtk3 adds an extra argument.. at the beginning
124 #so we index from the end of the array:
125 geom = ag[-2]
126 x, y, w, h = geom.x, geom.y, geom.width, geom.height
127 log("GTKStatusIconTray.get_geometry() geometry area rectangle=%s", (x, y, w, h))
128 if x==0 and y==0 and w==0 and h==0 and self.geometry_guess:
129 return self.geometry_guess
130 if x==0 and y==0 and w==200 and h==200:
131 #this isn't right, take a better guess, at least for the size:
132 w = 48
133 h = 48
134 return x, y, w, h
136 def get_size(self):
137 s = max(8, min(256, self.tray_widget.get_size()))
138 return [s, s]
141 def set_tooltip(self, tooltip=None):
142 if self.tray_widget:
143 self.tray_widget.set_tooltip_text(tooltip or "Xpra")
145 def set_blinking(self, on):
146 if self.tray_widget and hasattr(self.tray_widget, "set_blinking"):
147 self.tray_widget.set_blinking(on)
150 def set_icon_from_data(self, pixels, has_alpha, w, h, rowstride, _options=None):
151 tray_icon = get_pixbuf_from_data(pixels, has_alpha, w, h, rowstride)
152 self.set_icon_from_pixbuf(tray_icon)
154 def do_set_icon_from_file(self, filename):
155 tray_icon = get_icon_from_file(filename)
156 self.set_icon_from_pixbuf(tray_icon)
158 def set_icon_from_pixbuf(self, tray_icon):
159 if not tray_icon or not self.tray_widget:
160 return
161 tw, th = self.get_geometry()[2:4]
162 if (tw<=2 or th<=2) or (tw==200 and th==200):
163 log("bogus tray icon size: %ix%i", tw, th)
164 tw = th = 48
165 w = tray_icon.get_width()
166 h = tray_icon.get_height()
167 log("set_icon_from_pixbuf(%s) geometry=%s, icon size=%s", tray_icon, self.get_geometry(), (w, h))
168 if tw!=w or th!=h:
169 tray_icon = tray_icon.scale_simple(tw, th, GdkPixbuf.InterpType.HYPER)
170 log("tray icon scaled to %ix%i", tw, th)
171 if SAVE:
172 filename = "./statusicon-%s.png" % time()
173 tray_icon.save(filename, "png")
174 log.info("statusicon tray saved to %s", filename)
175 self.tray_widget.set_from_pixbuf(tray_icon)
176 self.icon_timestamp = monotonic_time()
179def main():
180 log.enable_debug()
181 from gi.repository import GLib
182 log.enable_debug()
183 s = GTKStatusIconTray(None, None, None, "test", "xpra.png", None, None, None, Gtk.main_quit)
184 GLib.timeout_add(1000*2, s.set_blinking, True)
185 GLib.timeout_add(1000*5, s.set_blinking, False)
186 GLib.timeout_add(1000*30, Gtk.main_quit)
187 Gtk.main()
190if __name__ == "__main__":
191 main()