From c6a7e5975688fef4dd9f3b9cc6dbd99df73ae549 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Sat, 13 Jan 2018 05:37:06 +0000 Subject: [PATCH] #1735: try to match the notification to the application tray that sent it git-svn-id: https://xpra.org/svn/Xpra/trunk@17997 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/client/client_tray.py | 5 +-- src/xpra/client/gtk3/gtk3_notifier.py | 2 +- src/xpra/client/tray_base.py | 5 ++- src/xpra/client/ui_client_base.py | 37 +++++++++++++++------ src/xpra/platform/win32/win32_NotifyIcon.py | 12 +++---- src/xpra/platform/win32/win32_balloon.py | 3 +- src/xpra/platform/win32/win32_notifier.py | 3 +- src/xpra/platform/win32/win32_tray.py | 6 ++-- 8 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/xpra/client/client_tray.py b/src/xpra/client/client_tray.py index c89c890633..e6a8e0bdd3 100644 --- a/src/xpra/client/client_tray.py +++ b/src/xpra/client/client_tray.py @@ -20,9 +20,10 @@ class ClientTray(ClientWidgetBase): DEFAULT_SIZE = [64, 64] DEFAULT_GEOMETRY = DEFAULT_LOCATION + DEFAULT_SIZE - def __init__(self, client, wid, w, h, tray_widget, mmap_enabled, mmap_area): + def __init__(self, client, wid, w, h, title, tray_widget, mmap_enabled, mmap_area): log("ClientTray%s", (client, wid, w, h, tray_widget, mmap_enabled, mmap_area)) ClientWidgetBase.__init__(self, client, 0, wid, True) + self.title = title self.tray_widget = tray_widget self._geometry = None self._window_alpha = True @@ -173,7 +174,7 @@ def destroy(self): tw.cleanup() def __repr__(self): - return "ClientTray(%s)" % self._id + return "ClientTray(%i:%s)" % (self._id, self.title) class TrayBacking(WindowBackingBase): diff --git a/src/xpra/client/gtk3/gtk3_notifier.py b/src/xpra/client/gtk3/gtk3_notifier.py index e1fcafedf6..cb82083956 100644 --- a/src/xpra/client/gtk3/gtk3_notifier.py +++ b/src/xpra/client/gtk3/gtk3_notifier.py @@ -14,7 +14,7 @@ class GTK3_Notifier(NotifierBase): - def show_notify(self, dbus_id, tray, nid, app_name, replaces_nid, app_icon, summary, body, expire_timeout, icon): + def show_notify(self, dbus_id, tray, nid, app_id, app_name, replaces_nid, app_icon, summary, body, expire_timeout, icon): if not self.dbus_check(dbus_id): return icon_string = self.get_icon_string(nid, app_icon, icon) diff --git a/src/xpra/client/tray_base.py b/src/xpra/client/tray_base.py index 3e3436dbda..b2416944ad 100644 --- a/src/xpra/client/tray_base.py +++ b/src/xpra/client/tray_base.py @@ -1,6 +1,6 @@ # This file is part of Xpra. # Copyright (C) 2010 Nathaniel Smith -# Copyright (C) 2011-2017 Antoine Martin +# Copyright (C) 2011-2018 Antoine Martin # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. @@ -32,6 +32,9 @@ def __init__(self, _client, app_id, menu, tooltip, icon_filename, size_changed_c self.tray_event_locations = deque(maxlen=512) self.default_icon_extension = "png" + def __repr__(self): + return "Tray(%i:%s)" % (self.app_id, self.tooltip) + def cleanup(self): if self.tray_widget: self.hide() diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index 91a3c9433c..2533e89ea7 100644 --- a/src/xpra/client/ui_client_base.py +++ b/src/xpra/client/ui_client_base.py @@ -460,10 +460,11 @@ def setup_xpra_tray(*args): if ICON_OVERLAY>0 and ICON_OVERLAY<=100: icon_filename = get_icon_filename("xpra") try: - from PIL import Image + from PIL import Image #@UnresolvedImport self.overlay_image = Image.open(icon_filename) except Exception as e: - log("init_ui failed to load overlay icon '%s': %s", icon_filename, e) + log.warn("Warning: failed to load overlay icon '%s':", icon_filename) + log.warn(" %s", e) notifylog("client_supports_notifications=%s", self.client_supports_notifications) if self.client_supports_notifications: @@ -922,7 +923,7 @@ def get_tray_title(self): traylog("get_tray_title()=%s (items=%s)", nonl(v), t) return v - def setup_system_tray(self, client, wid, w, h, title): + def setup_system_tray(self, client, app_id, wid, w, h, title): tray_widget = None #this is a tray forwarded for a remote application def tray_click(button, pressed, time=0): @@ -964,14 +965,11 @@ def tray_geometry(*args): self.idle_add(do_tray_geometry, *args) def tray_exit(*args): traylog("tray_exit(%s)", args) - #TODO: use the pid instead? - app_id = wid tray_widget = self.make_system_tray(app_id, None, title, None, tray_geometry, tray_click, tray_mouseover, tray_exit) - traylog("setup_system_tray%s tray_widget=%s", (client, wid, w, h, title), tray_widget) + traylog("setup_system_tray%s tray_widget=%s", (client, app_id, wid, w, h, title), tray_widget) assert tray_widget, "could not instantiate a system tray for tray id %s" % wid tray_widget.show() - return ClientTray(client, wid, w, h, tray_widget, self.mmap_enabled, self.mmap) - + return ClientTray(client, wid, w, h, title, tray_widget, self.mmap_enabled, self.mmap) def desktops_changed(self, *args): workspacelog("desktops_changed%s", args) @@ -3268,11 +3266,13 @@ def _process_new_tray(self, packet): if len(packet)>=5: metadata = self.cook_metadata(True, packet[4]) assert wid not in self._id_to_window, "we already have a window %s" % wid - tray = self.setup_system_tray(self, wid, w, h, metadata.get("title", "")) + app_id = wid + tray = self.setup_system_tray(self, app_id, wid, w, h, metadata.strget("title", "")) traylog("process_new_tray(%s) tray=%s", packet, tray) self._id_to_window[wid] = tray self._window_to_id[tray] = wid + def _process_window_move_resize(self, packet): wid, x, y, w, h = packet[1:6] ax = self.sx(x) @@ -3445,7 +3445,9 @@ def _process_notify_show(self, packet): body = body.decode("utf8") except: body = bytestostr(body) - tray = self.tray + app_name = bytestostr(app_name) + tray = self.get_tray_window(app_name) + traylog("get_tray_window(%s)=%s", app_name, tray) self.notifier.show_notify(dbus_id, tray, nid, app_name, replaces_nid, app_icon, summary, body, expire_timeout, icon) def _process_notify_close(self, packet): @@ -3456,6 +3458,21 @@ def _process_notify_close(self, packet): log("_process_notify_close(%s)", nid) self.notifier.close_notify(nid) + def get_tray_window(self, app_name): + #try to identify the application tray that generated this notification, + #so we can show it as coming from the correct systray icon + #on platforms that support it (ie: win32) + if app_name and app_name.lower()!="xpra": + #exact match: + for window in self._id_to_window.values(): + #traylog("window %s: is_tray=%s, title=%s", window, window.is_tray(), getattr(window, "title", None)) + if window.is_tray() and window.title==app_name: + return window.tray_widget + for window in self._id_to_window.values(): + if window.is_tray() and window.title.find(app_name)>=0: + return window.tray_widget + return self.tray + def _process_raise_window(self, packet): #only implemented in gtk2 for now diff --git a/src/xpra/platform/win32/win32_NotifyIcon.py b/src/xpra/platform/win32/win32_NotifyIcon.py index c11be84099..1f535880c0 100755 --- a/src/xpra/platform/win32/win32_NotifyIcon.py +++ b/src/xpra/platform/win32/win32_NotifyIcon.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # This file is part of Xpra. -# Copyright (C) 2011-2017 Antoine Martin +# Copyright (C) 2011-2018 Antoine Martin # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. @@ -317,6 +317,9 @@ def make_nid(self, flags): sprintf(byref(nid,NOTIFYICONDATA.szTip.offset), title) nid.dwState = 0 nid.dwStateMask = 0 + nid.guidItem = XPRA_GUID + nid.uID = self.app_id + flags |= NIF_GUID #balloon notification bits: #szInfo #uTimeout @@ -324,11 +327,6 @@ def make_nid(self, flags): #dwInfoFlags #hBalloonIcon #flags |= NIF_SHOWTIP - if self.app_id==XPRA_APP_ID: - nid.guidItem = XPRA_GUID - flags |= NIF_GUID - else: - nid.uID = self.app_id nid.uVersion = 4 nid.uFlags = flags log("make_nid(..)=%s tooltip='%s', app_id=%i, actual flags=%s", nid, nonl(title), self.app_id, csv([v for k,v in NIF_FLAGS.items() if k&flags])) @@ -541,7 +539,7 @@ def command_callback(hwnd, cid): icon = None else: pass - notify(hwnd, "hello", "world", timeout=1000, icon=icon) + notify(hwnd, 0, "hello", "world", timeout=1000, icon=icon) elif cid == 1025: print("Goodbye") DestroyWindow(hwnd) diff --git a/src/xpra/platform/win32/win32_balloon.py b/src/xpra/platform/win32/win32_balloon.py index 39aa1e4ee7..3e721e40e1 100755 --- a/src/xpra/platform/win32/win32_balloon.py +++ b/src/xpra/platform/win32/win32_balloon.py @@ -100,9 +100,10 @@ def __setattr__(self, name, value): self.__dict__[name] = value -def notify(hwnd, title, message, timeout=5000, icon=None): +def notify(hwnd, app_id, title, message, timeout=5000, icon=None): nid = PyNOTIFYICONDATA() nid.hWnd = hwnd + nid.uID = app_id nid.uFlags = NIF_INFO nid.guidItem = XPRA_GUID_BYTES nid.szInfo = chop_string(message, 255, False) #prevent overflow diff --git a/src/xpra/platform/win32/win32_notifier.py b/src/xpra/platform/win32/win32_notifier.py index 97379c242c..8d0ffbd114 100644 --- a/src/xpra/platform/win32/win32_notifier.py +++ b/src/xpra/platform/win32/win32_notifier.py @@ -17,7 +17,8 @@ def show_notify(self, dbus_id, tray, nid, app_name, replaces_nid, app_icon, summ log.warn(" the system tray class %s does not support hwnd", type(tray)) return hwnd = tray.getHWND() - notify(hwnd, summary, body, expire_timeout, icon) + app_id = tray.app_id + notify(hwnd, app_id, summary, body, expire_timeout, icon) def close_notify(self, nid): pass diff --git a/src/xpra/platform/win32/win32_tray.py b/src/xpra/platform/win32/win32_tray.py index dc78c2fdfe..3599b5c2f1 100755 --- a/src/xpra/platform/win32/win32_tray.py +++ b/src/xpra/platform/win32/win32_tray.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # This file is part of Xpra. -# Copyright (C) 2011-2017 Antoine Martin +# Copyright (C) 2011-2018 Antoine Martin # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. @@ -50,8 +50,8 @@ def get_size(self): def getHWND(self): if self.tray_widget is None: - return None - return self.tray_widget.hwnd + return None + return self.tray_widget.hwnd def cleanup(self): log("Win32Tray.cleanup() tray_widget=%s", self.tray_widget)