Skip to content

Commit

Permalink
#1492 custom notification icons support for win32
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@17940 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jan 8, 2018
1 parent 65dff80 commit 9b9f36b
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 17 deletions.
17 changes: 16 additions & 1 deletion src/xpra/platform/win32/win32_NotifyIcon.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ def NotifyIconWndProc(hwnd, msg, wParam, lParam):
raise ctypes.WinError(ctypes.get_last_error())
log("RegisterClassExA(%s)=%i", NIwc.lpszClassName, NIclassAtom)


def main():
import os
from xpra.platform.win32.common import user32
Expand All @@ -526,7 +527,21 @@ def click_callback(button, pressed):
def command_callback(hwnd, cid):
if cid == 1024:
from xpra.platform.win32.win32_balloon import notify
notify(hwnd, "hello", "world")
from xpra.os_util import BytesIOClass
try:
from PIL import Image
img = Image.open("icons\\printer.png")
buf = BytesIOClass()
img.save(buf, "PNG")
data = buf.getvalue()
buf.close()
icon = (b"png", img.size[0], img.size[1], data)
except Exception as e:
print("could not find icon: %s" % (e,))
icon = None
else:
pass
notify(hwnd, "hello", "world", timeout=1000, icon=icon)
elif cid == 1025:
print("Goodbye")
DestroyWindow(hwnd)
Expand Down
56 changes: 43 additions & 13 deletions src/xpra/platform/win32/win32_balloon.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
# Support for "balloon" notifications on MS Windows
# Based on code from winswitch, itself based on "win32gui_taskbar demo"

from xpra.os_util import BytesIOClass
from xpra.platform.win32.constants import SM_CXSMICON, SM_CYSMICON
from xpra.platform.win32.common import GetSystemMetrics
from xpra.log import Logger
log = Logger("notify", "win32")

import struct
from ctypes import windll

NIIF_USER = 4
NIF_INFO = 16
NIIF_INFO = 1
NIM_MODIFY = 1
Expand Down Expand Up @@ -45,6 +52,8 @@ class PyNOTIFYICONDATA:
"64s" # TCHAR szInfoTitle[64];
"I" # DWORD dwInfoFlags;
# GUID guidItem;
"16s"# GUID guidItem;
"I" # HICON hBalloonIcon
)
_struct = struct.Struct(_struct_format)

Expand All @@ -60,6 +69,8 @@ class PyNOTIFYICONDATA:
uTimeoutOrVersion = 0
szInfoTitle = ''
dwInfoFlags = 0
guidItem = ''
hBalloonIcon = 0

def pack(self):
return self._struct.pack(
Expand All @@ -75,7 +86,10 @@ def pack(self):
self.szInfo,
self.uTimeoutOrVersion,
self.szInfoTitle,
self.dwInfoFlags)
self.dwInfoFlags,
self.guidItem,
self.hBalloonIcon,
)

def __setattr__(self, name, value):
# avoid wrong field names
Expand All @@ -84,30 +98,46 @@ def __setattr__(self, name, value):
self.__dict__[name] = value


def notify(hwnd, title, message, timeout=5000):
# For this message I can't use the win32gui structure because
# it doesn't declare the new, required fields
def notify(hwnd, title, message, timeout=5000, icon=None):
nid = PyNOTIFYICONDATA()
nid.hWnd = hwnd
nid.uFlags = NIF_INFO
# type of balloon and text are random
nid.dwInfoFlags = NIIF_INFO #choice([NIIF_INFO, NIIF_WARNING, NIIF_ERROR])
nid.szInfo = "%s" % visible_command(message, 255, False) #prevent overflow
nid.szInfoTitle = "%s" % visible_command(title, 63)
if timeout<=0:
timeout = 5000
nid.uTimeoutOrVersion = timeout
#WM_TRAYICON = win32con.WM_USER + 20
#nid.uCallbackMessage = WM_TRAYICON
# Call the Windows function, not the wrapped one
Shell_NotifyIcon = windll.shell32.Shell_NotifyIconA
#if no icon is supplied, we can use:
# NIIF_INFO, NIIF_WARNING or NIIF_ERROR
nid.dwInfoFlags = NIIF_INFO
if icon:
try:
w, h, data = icon[1:4]
buf = BytesIOClass(data)
from PIL import Image #@UnresolvedImport
from xpra.platform.win32.win32_NotifyIcon import image_to_ICONINFO
img = Image.open(buf)
iw = GetSystemMetrics(SM_CXSMICON)
ih = GetSystemMetrics(SM_CYSMICON)
if w!=iw or h!=ih:
img = img.resize((iw, ih), Image.ANTIALIAS)
log("notification icon resized to %s", img.size)
hicon = image_to_ICONINFO(img)
log("notify: image_to_ICONINFO(%s)=%#x", img, hicon)
nid.hIcon = hicon
nid.hBalloonIcon = hicon
except Exception as e:
log.error("Error: failed to set notification icon:")
log.error(" %s", e)
else:
nid.dwInfoFlags = NIIF_USER
Shell_NotifyIcon = windll.shell32.Shell_NotifyIcon
Shell_NotifyIcon(NIM_MODIFY, nid.pack())


def main():
notify(0, "title", "message")
import time
time.sleep(10)
from xpra.platform.win32.win32_NotifyIcon import main
main()

if __name__=='__main__':
main()
7 changes: 4 additions & 3 deletions src/xpra/platform/win32/win32_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ class Win32_Notifier(NotifierBase):

def show_notify(self, dbus_id, tray, nid, app_name, replaces_nid, app_icon, summary, body, expire_timeout, icon):
if tray is None:
log.error("no tray - cannot show notification!")
log.warn("Warning: no system tray - cannot show notification!")
return
if not hasattr(tray, "getHWND"):
log.error("tray class %s does not support getHWND!", type(tray))
log.warn("Warning: cannot show notification,")
log.warn(" the system tray class %s does not support hwnd", type(tray))
return
hwnd = tray.getHWND()
notify(hwnd, summary, body, expire_timeout)
notify(hwnd, summary, body, expire_timeout, icon)

def close_notify(self, nid):
pass

0 comments on commit 9b9f36b

Please sign in to comment.