2 * X11 system tray management
4 * Copyright (C) 2004 Mike Hearn, for CodeWeavers
5 * Copyright (C) 2005 Robert Shearman
6 * Copyright (C) 2008 Alexandre Julliard
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
35 #define NONAMELESSUNION
44 #include "wine/list.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(systray
);
49 /* an individual systray icon */
53 HICON image
; /* the image to render */
54 HWND owner
; /* the HWND passed in to the Shell_NotifyIcon call */
55 HWND window
; /* the adaptor window */
56 HWND tooltip
; /* Icon tooltip */
57 UINT state
; /* state flags */
58 UINT id
; /* the unique id given by the app */
59 UINT callback_message
;
60 int display
; /* display index, or -1 if hidden */
61 WCHAR tiptext
[128]; /* tooltip text */
62 WCHAR info_text
[256]; /* info balloon text */
63 WCHAR info_title
[64]; /* info balloon title */
64 UINT info_flags
; /* flags for info balloon */
65 UINT info_timeout
; /* timeout for info balloon */
66 HICON info_icon
; /* info balloon icon */
69 static struct list icon_list
= LIST_INIT( icon_list
);
71 static const WCHAR icon_classname
[] = {'_','_','w','i','n','e','x','1','1','_','t','r','a','y','_','i','c','o','n',0};
72 static const WCHAR tray_classname
[] = {'_','_','w','i','n','e','x','1','1','_','s','t','a','n','d','a','l','o','n','e','_','t','r','a','y',0};
74 static BOOL
show_icon( struct tray_icon
*icon
);
75 static BOOL
hide_icon( struct tray_icon
*icon
);
76 static BOOL
delete_icon( struct tray_icon
*icon
);
78 #define SYSTEM_TRAY_REQUEST_DOCK 0
79 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
80 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
82 Atom systray_atom
= 0;
84 #define MIN_DISPLAYED 8
87 #define VALID_WIN_TIMER 1
88 #define BALLOON_CREATE_TIMER 2
89 #define BALLOON_SHOW_TIMER 3
91 #define VALID_WIN_TIMEOUT 2000
92 #define BALLOON_CREATE_TIMEOUT 2000
93 #define BALLOON_SHOW_MIN_TIMEOUT 10000
94 #define BALLOON_SHOW_MAX_TIMEOUT 30000
96 static struct tray_icon
*balloon_icon
;
97 static HWND balloon_window
;
98 static POINT balloon_pos
;
100 /* stand-alone tray window */
101 static HWND standalone_tray
;
102 static int icon_cx
, icon_cy
;
103 static unsigned int nb_displayed
;
105 /* retrieves icon record by owner window and ID */
106 static struct tray_icon
*get_icon(HWND owner
, UINT id
)
108 struct tray_icon
*this;
110 LIST_FOR_EACH_ENTRY( this, &icon_list
, struct tray_icon
, entry
)
111 if ((this->id
== id
) && (this->owner
== owner
)) return this;
115 static void init_common_controls(void)
117 static BOOL initialized
= FALSE
;
121 INITCOMMONCONTROLSEX init_tooltip
;
123 init_tooltip
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
124 init_tooltip
.dwICC
= ICC_TAB_CLASSES
;
126 InitCommonControlsEx(&init_tooltip
);
131 /* create tooltip window for icon */
132 static void create_tooltip(struct tray_icon
*icon
)
134 init_common_controls();
135 icon
->tooltip
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
136 WS_POPUP
| TTS_ALWAYSTIP
,
137 CW_USEDEFAULT
, CW_USEDEFAULT
,
138 CW_USEDEFAULT
, CW_USEDEFAULT
,
139 icon
->window
, NULL
, NULL
, NULL
);
143 ZeroMemory(&ti
, sizeof(ti
));
144 ti
.cbSize
= sizeof(TTTOOLINFOW
);
145 ti
.uFlags
= TTF_SUBCLASS
| TTF_IDISHWND
;
146 ti
.hwnd
= icon
->window
;
147 ti
.uId
= (UINT_PTR
)icon
->window
;
148 ti
.lpszText
= icon
->tiptext
;
149 SendMessageW(icon
->tooltip
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
153 void update_systray_balloon_position(void)
158 if (!balloon_icon
) return;
159 GetWindowRect( balloon_icon
->window
, &rect
);
160 pos
.x
= (rect
.left
+ rect
.right
) / 2;
161 pos
.y
= (rect
.top
+ rect
.bottom
) / 2;
162 if (pos
.x
== balloon_pos
.x
&& pos
.y
== balloon_pos
.y
) return; /* nothing changed */
164 SendMessageW( balloon_window
, TTM_TRACKPOSITION
, 0, MAKELONG( pos
.x
, pos
.y
));
167 static void balloon_create_timer( struct tray_icon
*icon
)
171 init_common_controls();
172 balloon_window
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
173 WS_POPUP
| TTS_ALWAYSTIP
| TTS_NOPREFIX
| TTS_BALLOON
| TTS_CLOSE
,
174 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
175 icon
->window
, NULL
, NULL
, NULL
);
177 memset( &ti
, 0, sizeof(ti
) );
178 ti
.cbSize
= sizeof(TTTOOLINFOW
);
179 ti
.hwnd
= icon
->window
;
180 ti
.uId
= (UINT_PTR
)icon
->window
;
181 ti
.uFlags
= TTF_TRACK
| TTF_IDISHWND
;
182 ti
.lpszText
= icon
->info_text
;
183 SendMessageW( balloon_window
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
184 if ((icon
->info_flags
& NIIF_ICONMASK
) == NIIF_USER
)
185 SendMessageW( balloon_window
, TTM_SETTITLEW
, (WPARAM
)icon
->info_icon
, (LPARAM
)icon
->info_title
);
187 SendMessageW( balloon_window
, TTM_SETTITLEW
, icon
->info_flags
, (LPARAM
)icon
->info_title
);
189 balloon_pos
.x
= balloon_pos
.y
= MAXLONG
;
190 update_systray_balloon_position();
191 SendMessageW( balloon_window
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
192 KillTimer( icon
->window
, BALLOON_CREATE_TIMER
);
193 SetTimer( icon
->window
, BALLOON_SHOW_TIMER
, icon
->info_timeout
, NULL
);
196 static BOOL
show_balloon( struct tray_icon
*icon
)
198 if (standalone_tray
&& !show_systray
) return FALSE
; /* no systray window */
199 if (!icon
->window
) return FALSE
; /* not displayed */
200 if (!icon
->info_text
[0]) return FALSE
; /* no balloon */
202 SetTimer( icon
->window
, BALLOON_CREATE_TIMER
, BALLOON_CREATE_TIMEOUT
, NULL
);
206 static void hide_balloon(void)
208 if (!balloon_icon
) return;
211 KillTimer( balloon_icon
->window
, BALLOON_SHOW_TIMER
);
212 DestroyWindow( balloon_window
);
215 else KillTimer( balloon_icon
->window
, BALLOON_CREATE_TIMER
);
219 static void show_next_balloon(void)
221 struct tray_icon
*icon
;
223 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct tray_icon
, entry
)
224 if (show_balloon( icon
)) break;
227 static void update_balloon( struct tray_icon
*icon
)
229 if (balloon_icon
== icon
)
232 show_balloon( icon
);
234 else if (!balloon_icon
)
236 if (!show_balloon( icon
)) return;
238 if (!balloon_icon
) show_next_balloon();
241 static void balloon_timer(void)
243 if (balloon_icon
) balloon_icon
->info_text
[0] = 0; /* clear text now that balloon has been shown */
248 /* synchronize tooltip text with tooltip window */
249 static void update_tooltip_text(struct tray_icon
*icon
)
253 ZeroMemory(&ti
, sizeof(ti
));
254 ti
.cbSize
= sizeof(TTTOOLINFOW
);
255 ti
.uFlags
= TTF_SUBCLASS
| TTF_IDISHWND
;
256 ti
.hwnd
= icon
->window
;
257 ti
.uId
= (UINT_PTR
)icon
->window
;
258 ti
.lpszText
= icon
->tiptext
;
260 SendMessageW(icon
->tooltip
, TTM_UPDATETIPTEXTW
, 0, (LPARAM
)&ti
);
263 /* get the size of the stand-alone tray window */
264 static SIZE
get_window_size(void)
271 rect
.right
= icon_cx
* max( nb_displayed
, MIN_DISPLAYED
);
272 rect
.bottom
= icon_cy
;
273 AdjustWindowRect( &rect
, WS_CAPTION
, FALSE
);
274 size
.cx
= rect
.right
- rect
.left
;
275 size
.cy
= rect
.bottom
- rect
.top
;
279 /* get the position of an icon in the stand-alone tray */
280 static POINT
get_icon_pos( struct tray_icon
*icon
)
284 pos
.x
= icon_cx
* icon
->display
;
289 /* window procedure for the standalone tray window */
290 static LRESULT WINAPI
standalone_tray_wndproc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
295 update_systray_balloon_position();
298 ShowWindow( hwnd
, SW_HIDE
);
300 show_systray
= FALSE
;
306 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
309 /* add an icon to the standalone tray window */
310 static void add_to_standalone_tray( struct tray_icon
*icon
)
315 if (!standalone_tray
)
317 static const WCHAR winname
[] = {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',0};
319 size
= get_window_size();
320 standalone_tray
= CreateWindowExW( 0, tray_classname
, winname
, WS_CAPTION
| WS_SYSMENU
,
321 CW_USEDEFAULT
, CW_USEDEFAULT
, size
.cx
, size
.cy
, 0, 0, 0, 0 );
322 if (!standalone_tray
) return;
325 icon
->display
= nb_displayed
;
326 pos
= get_icon_pos( icon
);
327 icon
->window
= CreateWindowW( icon_classname
, NULL
, WS_CHILD
| WS_VISIBLE
,
328 pos
.x
, pos
.y
, icon_cx
, icon_cy
, standalone_tray
, NULL
, NULL
, icon
);
334 create_tooltip( icon
);
337 size
= get_window_size();
338 SetWindowPos( standalone_tray
, 0, 0, 0, size
.cx
, size
.cy
, SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOZORDER
);
339 if (nb_displayed
== 1 && show_systray
) ShowWindow( standalone_tray
, SW_SHOWNA
);
340 TRACE( "added %u now %d icons\n", icon
->id
, nb_displayed
);
343 /* remove an icon from the stand-alone tray */
344 static void remove_from_standalone_tray( struct tray_icon
*icon
)
346 struct tray_icon
*ptr
;
349 if (icon
->display
== -1) return;
351 LIST_FOR_EACH_ENTRY( ptr
, &icon_list
, struct tray_icon
, entry
)
353 if (ptr
== icon
) continue;
354 if (ptr
->display
< icon
->display
) continue;
356 pos
= get_icon_pos( ptr
);
357 SetWindowPos( ptr
->window
, 0, pos
.x
, pos
.y
, 0, 0, SWP_NOSIZE
| SWP_NOACTIVATE
| SWP_NOZORDER
);
360 if (!--nb_displayed
) ShowWindow( standalone_tray
, SW_HIDE
);
361 TRACE( "removed %u now %d icons\n", icon
->id
, nb_displayed
);
364 /* window procedure for the individual tray icon window */
365 static LRESULT WINAPI
tray_icon_wndproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
367 struct tray_icon
*icon
= NULL
;
370 TRACE("hwnd=%p, msg=0x%x\n", hwnd
, msg
);
372 /* set the icon data for the window from the data passed into CreateWindow */
373 if (msg
== WM_NCCREATE
)
374 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LPARAM
)((const CREATESTRUCTW
*)lparam
)->lpCreateParams
);
376 icon
= (struct tray_icon
*) GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
381 SetTimer( hwnd
, VALID_WIN_TIMER
, VALID_WIN_TIMEOUT
, NULL
);
389 int cx
= GetSystemMetrics( SM_CXSMICON
);
390 int cy
= GetSystemMetrics( SM_CYSMICON
);
392 hdc
= BeginPaint(hwnd
, &ps
);
393 GetClientRect(hwnd
, &rc
);
394 TRACE("painting rect %s\n", wine_dbgstr_rect(&rc
));
395 DrawIconEx( hdc
, (rc
.left
+ rc
.right
- cx
) / 2, (rc
.top
+ rc
.bottom
- cy
) / 2,
396 icon
->image
, cx
, cy
, 0, 0, DI_DEFAULTSIZE
|DI_NORMAL
);
408 case WM_LBUTTONDBLCLK
:
409 case WM_RBUTTONDBLCLK
:
410 case WM_MBUTTONDBLCLK
:
411 /* notify the owner hwnd of the message */
412 TRACE("relaying 0x%x\n", msg
);
413 ret
= PostMessageW(icon
->owner
, icon
->callback_message
, icon
->id
, msg
);
414 if (!ret
&& (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
))
416 WARN( "application window was destroyed, removing icon %u\n", icon
->id
);
421 case WM_WINDOWPOSCHANGED
:
422 update_systray_balloon_position();
428 case VALID_WIN_TIMER
:
429 if (!IsWindow( icon
->owner
)) delete_icon( icon
);
431 case BALLOON_CREATE_TIMER
:
432 balloon_create_timer( icon
);
434 case BALLOON_SHOW_TIMER
:
441 if (icon
->display
== -1)
443 TRACE( "icon %u no longer embedded\n", icon
->id
);
445 add_to_standalone_tray( icon
);
449 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
452 /* find the X11 window owner the system tray selection */
453 static Window
get_systray_selection_owner( Display
*display
)
455 return XGetSelectionOwner( display
, systray_atom
);
458 static BOOL
init_systray(void)
460 static BOOL init_done
;
464 if (root_window
!= DefaultRootWindow( gdi_display
)) return FALSE
;
465 if (init_done
) return TRUE
;
467 icon_cx
= GetSystemMetrics( SM_CXSMICON
) + 2 * ICON_BORDER
;
468 icon_cy
= GetSystemMetrics( SM_CYSMICON
) + 2 * ICON_BORDER
;
470 memset( &class, 0, sizeof(class) );
471 class.cbSize
= sizeof(class);
472 class.lpfnWndProc
= tray_icon_wndproc
;
473 class.hIcon
= LoadIconW(0, (LPCWSTR
)IDI_WINLOGO
);
474 class.hCursor
= LoadCursorW( 0, (LPCWSTR
)IDC_ARROW
);
475 class.lpszClassName
= icon_classname
;
476 class.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
478 if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS
)
480 ERR( "Could not register icon tray window class\n" );
484 class.lpfnWndProc
= standalone_tray_wndproc
;
485 class.hbrBackground
= (HBRUSH
)COLOR_WINDOW
;
486 class.lpszClassName
= tray_classname
;
487 class.style
= CS_DBLCLKS
;
489 if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS
)
491 ERR( "Could not register standalone tray window class\n" );
495 display
= thread_init_display();
497 if (DefaultScreen( display
) == 0)
498 systray_atom
= x11drv_atom(_NET_SYSTEM_TRAY_S0
);
501 char systray_buffer
[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */
502 sprintf( systray_buffer
, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display
) );
503 systray_atom
= XInternAtom( display
, systray_buffer
, False
);
505 XSelectInput( display
, root_window
, StructureNotifyMask
);
512 /* dock the given icon with the NETWM system tray */
513 static void dock_systray_icon( Display
*display
, struct tray_icon
*icon
, Window systray_window
)
515 struct x11drv_win_data
*data
;
517 XSetWindowAttributes attr
;
519 icon
->window
= CreateWindowW( icon_classname
, NULL
, WS_CLIPSIBLINGS
| WS_POPUP
,
520 CW_USEDEFAULT
, CW_USEDEFAULT
, icon_cx
, icon_cy
,
521 NULL
, NULL
, NULL
, icon
);
522 if (!icon
->window
) return;
524 if (!(data
= X11DRV_get_win_data( icon
->window
)) &&
525 !(data
= X11DRV_create_win_data( icon
->window
))) return;
527 TRACE( "icon window %p/%lx managed %u\n", data
->hwnd
, data
->whole_window
, data
->managed
);
529 make_window_embedded( display
, data
);
530 create_tooltip( icon
);
531 ShowWindow( icon
->window
, SW_SHOWNA
);
533 /* send the docking request message */
534 ev
.xclient
.type
= ClientMessage
;
535 ev
.xclient
.window
= systray_window
;
536 ev
.xclient
.message_type
= x11drv_atom( _NET_SYSTEM_TRAY_OPCODE
);
537 ev
.xclient
.format
= 32;
538 ev
.xclient
.data
.l
[0] = CurrentTime
;
539 ev
.xclient
.data
.l
[1] = SYSTEM_TRAY_REQUEST_DOCK
;
540 ev
.xclient
.data
.l
[2] = data
->whole_window
;
541 ev
.xclient
.data
.l
[3] = 0;
542 ev
.xclient
.data
.l
[4] = 0;
543 XSendEvent( display
, systray_window
, False
, NoEventMask
, &ev
);
544 attr
.background_pixmap
= ParentRelative
;
545 attr
.bit_gravity
= ForgetGravity
;
546 XChangeWindowAttributes( display
, data
->whole_window
, CWBackPixmap
| CWBitGravity
, &attr
);
547 XChangeWindowAttributes( display
, data
->client_window
, CWBackPixmap
| CWBitGravity
, &attr
);
550 /* dock systray windows again with the new owner */
551 void change_systray_owner( Display
*display
, Window systray_window
)
553 struct tray_icon
*icon
;
555 TRACE( "new owner %lx\n", systray_window
);
556 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct tray_icon
, entry
)
558 if (icon
->display
== -1) continue;
560 dock_systray_icon( display
, icon
, systray_window
);
564 /* hide a tray icon */
565 static BOOL
hide_icon( struct tray_icon
*icon
)
567 struct x11drv_win_data
*data
;
569 TRACE( "id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
571 if (!icon
->window
) return TRUE
; /* already hidden */
573 /* make sure we don't try to unmap it, it confuses some systray docks */
574 if ((data
= X11DRV_get_win_data( icon
->window
)) && data
->embedded
) data
->mapped
= FALSE
;
576 DestroyWindow(icon
->window
);
577 DestroyWindow(icon
->tooltip
);
580 remove_from_standalone_tray( icon
);
581 update_balloon( icon
);
585 /* make the icon visible */
586 static BOOL
show_icon( struct tray_icon
*icon
)
588 Window systray_window
;
589 Display
*display
= thread_init_display();
591 TRACE( "id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
593 if (icon
->window
) return TRUE
; /* already shown */
595 if ((systray_window
= get_systray_selection_owner( display
)))
596 dock_systray_icon( display
, icon
, systray_window
);
598 add_to_standalone_tray( icon
);
600 update_balloon( icon
);
604 /* Modifies an existing icon record */
605 static BOOL
modify_icon( struct tray_icon
*icon
, NOTIFYICONDATAW
*nid
)
607 TRACE( "id=0x%x hwnd=%p flags=%x\n", nid
->uID
, nid
->hWnd
, nid
->uFlags
);
609 if (nid
->uFlags
& NIF_STATE
)
611 icon
->state
= (icon
->state
& ~nid
->dwStateMask
) | (nid
->dwState
& nid
->dwStateMask
);
614 if (nid
->uFlags
& NIF_ICON
)
616 if (icon
->image
) DestroyIcon(icon
->image
);
617 icon
->image
= CopyIcon(nid
->hIcon
);
620 if (icon
->display
!= -1) InvalidateRect( icon
->window
, NULL
, TRUE
);
623 struct x11drv_win_data
*data
= X11DRV_get_win_data( icon
->window
);
624 if (data
) XClearArea( gdi_display
, data
->client_window
, 0, 0, 0, 0, True
);
629 if (nid
->uFlags
& NIF_MESSAGE
)
631 icon
->callback_message
= nid
->uCallbackMessage
;
633 if (nid
->uFlags
& NIF_TIP
)
635 lstrcpynW(icon
->tiptext
, nid
->szTip
, sizeof(icon
->tiptext
)/sizeof(WCHAR
));
636 if (icon
->tooltip
) update_tooltip_text(icon
);
638 if (nid
->uFlags
& NIF_INFO
&& nid
->cbSize
>= NOTIFYICONDATAA_V2_SIZE
)
640 lstrcpynW( icon
->info_text
, nid
->szInfo
, sizeof(icon
->info_text
)/sizeof(WCHAR
) );
641 lstrcpynW( icon
->info_title
, nid
->szInfoTitle
, sizeof(icon
->info_title
)/sizeof(WCHAR
) );
642 icon
->info_flags
= nid
->dwInfoFlags
;
643 icon
->info_timeout
= max(min(nid
->u
.uTimeout
, BALLOON_SHOW_MAX_TIMEOUT
), BALLOON_SHOW_MIN_TIMEOUT
);
644 icon
->info_icon
= nid
->hBalloonIcon
;
645 update_balloon( icon
);
647 if (icon
->state
& NIS_HIDDEN
) hide_icon( icon
);
648 else show_icon( icon
);
652 /* Adds a new icon record to the list */
653 static BOOL
add_icon(NOTIFYICONDATAW
*nid
)
655 struct tray_icon
*icon
;
657 TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
659 if ((icon
= get_icon(nid
->hWnd
, nid
->uID
)))
661 WARN("duplicate tray icon add, buggy app?\n");
665 if (!(icon
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*icon
))))
667 ERR("out of memory\n");
671 ZeroMemory(icon
, sizeof(struct tray_icon
));
673 icon
->owner
= nid
->hWnd
;
676 list_add_tail(&icon_list
, &icon
->entry
);
678 return modify_icon( icon
, nid
);
681 /* delete tray icon window and icon structure */
682 static BOOL
delete_icon( struct tray_icon
*icon
)
685 list_remove( &icon
->entry
);
686 DestroyIcon( icon
->image
);
687 HeapFree( GetProcessHeap(), 0, icon
);
692 /***********************************************************************
693 * wine_notify_icon (X11DRV.@)
695 * Driver-side implementation of Shell_NotifyIcon.
697 int CDECL
wine_notify_icon( DWORD msg
, NOTIFYICONDATAW
*data
)
700 struct tray_icon
*icon
;
705 if (!init_systray()) return -1; /* fall back to default handling */
706 ret
= add_icon( data
);
709 if ((icon
= get_icon( data
->hWnd
, data
->uID
))) ret
= delete_icon( icon
);
712 if ((icon
= get_icon( data
->hWnd
, data
->uID
))) ret
= modify_icon( icon
, data
);
715 FIXME( "unhandled tray message: %u\n", msg
);