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
23 #define NONAMELESSUNION
24 #include "x11drv_dll.h"
28 #include "wine/list.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(systray
);
33 BOOL show_systray
= TRUE
;
35 /* an individual systray icon */
39 HICON image
; /* the image to render */
40 HWND owner
; /* the HWND passed in to the Shell_NotifyIcon call */
41 HWND window
; /* the adaptor window */
42 BOOL layered
; /* whether we are using a layered window */
43 HWND tooltip
; /* Icon tooltip */
44 UINT state
; /* state flags */
45 UINT id
; /* the unique id given by the app */
46 UINT callback_message
;
47 int display
; /* display index, or -1 if hidden */
48 WCHAR tiptext
[128]; /* tooltip text */
49 WCHAR info_text
[256]; /* info balloon text */
50 WCHAR info_title
[64]; /* info balloon title */
51 UINT info_flags
; /* flags for info balloon */
52 UINT info_timeout
; /* timeout for info balloon */
53 HICON info_icon
; /* info balloon icon */
54 UINT version
; /* notify icon api version */
57 static struct list icon_list
= LIST_INIT( icon_list
);
59 static const WCHAR icon_classname
[] = {'_','_','w','i','n','e','x','1','1','_','t','r','a','y','_','i','c','o','n',0};
60 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};
62 static BOOL
show_icon( struct tray_icon
*icon
);
63 static BOOL
hide_icon( struct tray_icon
*icon
);
64 static BOOL
delete_icon( struct tray_icon
*icon
);
66 #define MIN_DISPLAYED 8
69 #define BALLOON_CREATE_TIMER 1
70 #define BALLOON_SHOW_TIMER 2
72 #define BALLOON_CREATE_TIMEOUT 2000
73 #define BALLOON_SHOW_MIN_TIMEOUT 10000
74 #define BALLOON_SHOW_MAX_TIMEOUT 30000
76 static struct tray_icon
*balloon_icon
;
77 static HWND balloon_window
;
78 static POINT balloon_pos
;
80 /* stand-alone tray window */
81 static HWND standalone_tray
;
82 static int icon_cx
, icon_cy
;
83 static unsigned int nb_displayed
;
85 /* retrieves icon record by owner window and ID */
86 static struct tray_icon
*get_icon(HWND owner
, UINT id
)
88 struct tray_icon
*this;
90 LIST_FOR_EACH_ENTRY( this, &icon_list
, struct tray_icon
, entry
)
91 if ((this->id
== id
) && (this->owner
== owner
)) return this;
95 static void init_common_controls(void)
97 static BOOL initialized
= FALSE
;
101 INITCOMMONCONTROLSEX init_tooltip
;
103 init_tooltip
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
104 init_tooltip
.dwICC
= ICC_TAB_CLASSES
;
106 InitCommonControlsEx(&init_tooltip
);
111 /* create tooltip window for icon */
112 static void create_tooltip(struct tray_icon
*icon
)
114 init_common_controls();
115 icon
->tooltip
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
116 WS_POPUP
| TTS_ALWAYSTIP
,
117 CW_USEDEFAULT
, CW_USEDEFAULT
,
118 CW_USEDEFAULT
, CW_USEDEFAULT
,
119 icon
->window
, NULL
, NULL
, NULL
);
123 ZeroMemory(&ti
, sizeof(ti
));
124 ti
.cbSize
= sizeof(TTTOOLINFOW
);
125 ti
.uFlags
= TTF_SUBCLASS
| TTF_IDISHWND
;
126 ti
.hwnd
= icon
->window
;
127 ti
.uId
= (UINT_PTR
)icon
->window
;
128 ti
.lpszText
= icon
->tiptext
;
129 SendMessageW(icon
->tooltip
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
133 static void update_systray_balloon_position(void)
138 if (!balloon_icon
) return;
139 GetWindowRect( balloon_icon
->window
, &rect
);
140 pos
.x
= (rect
.left
+ rect
.right
) / 2;
141 pos
.y
= (rect
.top
+ rect
.bottom
) / 2;
142 if (pos
.x
== balloon_pos
.x
&& pos
.y
== balloon_pos
.y
) return; /* nothing changed */
144 SendMessageW( balloon_window
, TTM_TRACKPOSITION
, 0, MAKELONG( pos
.x
, pos
.y
));
147 static void balloon_create_timer( struct tray_icon
*icon
)
151 init_common_controls();
152 balloon_window
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
153 WS_POPUP
| TTS_ALWAYSTIP
| TTS_NOPREFIX
| TTS_BALLOON
| TTS_CLOSE
,
154 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
155 icon
->window
, NULL
, NULL
, NULL
);
157 memset( &ti
, 0, sizeof(ti
) );
158 ti
.cbSize
= sizeof(TTTOOLINFOW
);
159 ti
.hwnd
= icon
->window
;
160 ti
.uId
= (UINT_PTR
)icon
->window
;
161 ti
.uFlags
= TTF_TRACK
| TTF_IDISHWND
;
162 ti
.lpszText
= icon
->info_text
;
163 SendMessageW( balloon_window
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
164 if ((icon
->info_flags
& NIIF_ICONMASK
) == NIIF_USER
)
165 SendMessageW( balloon_window
, TTM_SETTITLEW
, (WPARAM
)icon
->info_icon
, (LPARAM
)icon
->info_title
);
167 SendMessageW( balloon_window
, TTM_SETTITLEW
, icon
->info_flags
, (LPARAM
)icon
->info_title
);
169 balloon_pos
.x
= balloon_pos
.y
= MAXLONG
;
170 update_systray_balloon_position();
171 SendMessageW( balloon_window
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
172 KillTimer( icon
->window
, BALLOON_CREATE_TIMER
);
173 SetTimer( icon
->window
, BALLOON_SHOW_TIMER
, icon
->info_timeout
, NULL
);
176 static BOOL
show_balloon( struct tray_icon
*icon
)
178 if (standalone_tray
&& !show_systray
) return FALSE
; /* no systray window */
179 if (!icon
->window
) return FALSE
; /* not displayed */
180 if (!icon
->info_text
[0]) return FALSE
; /* no balloon */
182 SetTimer( icon
->window
, BALLOON_CREATE_TIMER
, BALLOON_CREATE_TIMEOUT
, NULL
);
186 static void hide_balloon(void)
188 if (!balloon_icon
) return;
191 KillTimer( balloon_icon
->window
, BALLOON_SHOW_TIMER
);
192 DestroyWindow( balloon_window
);
195 else KillTimer( balloon_icon
->window
, BALLOON_CREATE_TIMER
);
199 static void show_next_balloon(void)
201 struct tray_icon
*icon
;
203 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct tray_icon
, entry
)
204 if (show_balloon( icon
)) break;
207 static void update_balloon( struct tray_icon
*icon
)
209 if (balloon_icon
== icon
)
212 show_balloon( icon
);
214 else if (!balloon_icon
)
216 if (!show_balloon( icon
)) return;
218 if (!balloon_icon
) show_next_balloon();
221 static void balloon_timer(void)
223 if (balloon_icon
) balloon_icon
->info_text
[0] = 0; /* clear text now that balloon has been shown */
228 /* synchronize tooltip text with tooltip window */
229 static void update_tooltip_text(struct tray_icon
*icon
)
233 ZeroMemory(&ti
, sizeof(ti
));
234 ti
.cbSize
= sizeof(TTTOOLINFOW
);
235 ti
.uFlags
= TTF_SUBCLASS
| TTF_IDISHWND
;
236 ti
.hwnd
= icon
->window
;
237 ti
.uId
= (UINT_PTR
)icon
->window
;
238 ti
.lpszText
= icon
->tiptext
;
240 SendMessageW(icon
->tooltip
, TTM_UPDATETIPTEXTW
, 0, (LPARAM
)&ti
);
243 /* get the size of the stand-alone tray window */
244 static SIZE
get_window_size(void)
251 rect
.right
= icon_cx
* max( nb_displayed
, MIN_DISPLAYED
);
252 rect
.bottom
= icon_cy
;
253 AdjustWindowRect( &rect
, WS_CAPTION
, FALSE
);
254 size
.cx
= rect
.right
- rect
.left
;
255 size
.cy
= rect
.bottom
- rect
.top
;
259 /* get the position of an icon in the stand-alone tray */
260 static POINT
get_icon_pos( struct tray_icon
*icon
)
264 pos
.x
= icon_cx
* icon
->display
;
269 /* window procedure for the standalone tray window */
270 static LRESULT WINAPI
standalone_tray_wndproc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
275 update_systray_balloon_position();
278 ShowWindow( hwnd
, SW_HIDE
);
280 show_systray
= FALSE
;
286 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
289 /* add an icon to the standalone tray window */
290 static void add_to_standalone_tray( struct tray_icon
*icon
)
295 if (!standalone_tray
)
297 static const WCHAR winname
[] = {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',0};
299 size
= get_window_size();
300 standalone_tray
= CreateWindowExW( 0, tray_classname
, winname
, WS_CAPTION
| WS_SYSMENU
,
301 CW_USEDEFAULT
, CW_USEDEFAULT
, size
.cx
, size
.cy
, 0, 0, 0, 0 );
302 if (!standalone_tray
) return;
305 icon
->display
= nb_displayed
;
306 pos
= get_icon_pos( icon
);
307 CreateWindowW( icon_classname
, NULL
, WS_CHILD
| WS_VISIBLE
,
308 pos
.x
, pos
.y
, icon_cx
, icon_cy
, standalone_tray
, NULL
, NULL
, icon
);
316 size
= get_window_size();
317 SetWindowPos( standalone_tray
, 0, 0, 0, size
.cx
, size
.cy
, SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOZORDER
);
318 if (nb_displayed
== 1 && show_systray
) ShowWindow( standalone_tray
, SW_SHOWNA
);
319 TRACE( "added %u now %d icons\n", icon
->id
, nb_displayed
);
322 /* remove an icon from the stand-alone tray */
323 static void remove_from_standalone_tray( struct tray_icon
*icon
)
325 struct tray_icon
*ptr
;
328 if (icon
->display
== -1) return;
330 LIST_FOR_EACH_ENTRY( ptr
, &icon_list
, struct tray_icon
, entry
)
332 if (ptr
== icon
) continue;
333 if (ptr
->display
< icon
->display
) continue;
335 pos
= get_icon_pos( ptr
);
336 SetWindowPos( ptr
->window
, 0, pos
.x
, pos
.y
, 0, 0, SWP_NOSIZE
| SWP_NOACTIVATE
| SWP_NOZORDER
);
339 if (!--nb_displayed
) ShowWindow( standalone_tray
, SW_HIDE
);
340 TRACE( "removed %u now %d icons\n", icon
->id
, nb_displayed
);
343 static void repaint_tray_icon( struct tray_icon
*icon
)
345 BLENDFUNCTION blend
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
346 int width
= GetSystemMetrics( SM_CXSMICON
);
347 int height
= GetSystemMetrics( SM_CYSMICON
);
355 void *color_bits
, *mask_bits
;
357 BOOL has_alpha
= FALSE
;
359 GetWindowRect( icon
->window
, &rc
);
360 size
.cx
= rc
.right
- rc
.left
;
361 size
.cy
= rc
.bottom
- rc
.top
;
362 pos
.x
= (size
.cx
- width
) / 2;
363 pos
.y
= (size
.cy
- height
) / 2;
365 info
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, FIELD_OFFSET( BITMAPINFO
, bmiColors
[2] ));
367 info
->bmiHeader
.biSize
= sizeof(info
->bmiHeader
);
368 info
->bmiHeader
.biWidth
= size
.cx
;
369 info
->bmiHeader
.biHeight
= size
.cy
;
370 info
->bmiHeader
.biBitCount
= 32;
371 info
->bmiHeader
.biPlanes
= 1;
372 info
->bmiHeader
.biCompression
= BI_RGB
;
374 hdc
= CreateCompatibleDC( 0 );
375 if (!(dib
= CreateDIBSection( 0, info
, DIB_RGB_COLORS
, &color_bits
, NULL
, 0 ))) goto done
;
376 SelectObject( hdc
, dib
);
377 DrawIconEx( hdc
, pos
.x
, pos
.y
, icon
->image
, width
, height
, 0, 0, DI_DEFAULTSIZE
| DI_NORMAL
);
379 /* check if the icon was drawn with an alpha channel */
380 for (i
= 0, ptr
= color_bits
; i
< size
.cx
* size
.cy
; i
++)
381 if ((has_alpha
= (ptr
[i
] & 0xff000000) != 0)) break;
385 unsigned int width_bytes
= (size
.cx
+ 31) / 32 * 4;
387 info
->bmiHeader
.biBitCount
= 1;
388 info
->bmiColors
[0].rgbRed
= 0;
389 info
->bmiColors
[0].rgbGreen
= 0;
390 info
->bmiColors
[0].rgbBlue
= 0;
391 info
->bmiColors
[0].rgbReserved
= 0;
392 info
->bmiColors
[1].rgbRed
= 0xff;
393 info
->bmiColors
[1].rgbGreen
= 0xff;
394 info
->bmiColors
[1].rgbBlue
= 0xff;
395 info
->bmiColors
[1].rgbReserved
= 0;
397 if (!(mask
= CreateDIBSection( 0, info
, DIB_RGB_COLORS
, &mask_bits
, NULL
, 0 ))) goto done
;
398 memset( mask_bits
, 0xff, width_bytes
* size
.cy
);
399 SelectObject( hdc
, mask
);
400 DrawIconEx( hdc
, pos
.x
, pos
.y
, icon
->image
, width
, height
, 0, 0, DI_DEFAULTSIZE
| DI_MASK
);
402 for (y
= 0, ptr
= color_bits
; y
< size
.cy
; y
++)
403 for (x
= 0; x
< size
.cx
; x
++, ptr
++)
404 if (!((((BYTE
*)mask_bits
)[y
* width_bytes
+ x
/ 8] << (x
% 8)) & 0x80))
407 SelectObject( hdc
, dib
);
408 DeleteObject( mask
);
411 UpdateLayeredWindow( icon
->window
, 0, NULL
, NULL
, hdc
, NULL
, 0, &blend
, ULW_ALPHA
);
413 HeapFree (GetProcessHeap(), 0, info
);
414 if (hdc
) DeleteDC( hdc
);
415 if (dib
) DeleteObject( dib
);
418 static BOOL
notify_owner( struct tray_icon
*icon
, UINT msg
, LPARAM lparam
)
420 WPARAM wp
= icon
->id
;
423 if (icon
->version
>= NOTIFYICON_VERSION_4
)
425 POINT pt
= { (short)LOWORD(lparam
), (short)HIWORD(lparam
) };
427 ClientToScreen( icon
->window
, &pt
);
428 wp
= MAKEWPARAM( pt
.x
, pt
.y
);
429 lp
= MAKELPARAM( msg
, icon
->id
);
432 TRACE( "relaying 0x%x\n", msg
);
433 if (!SendNotifyMessageW( icon
->owner
, icon
->callback_message
, wp
, lp
) &&
434 (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
))
436 WARN( "application window was destroyed, removing icon %u\n", icon
->id
);
443 /* window procedure for the individual tray icon window */
444 static LRESULT WINAPI
tray_icon_wndproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
446 struct tray_icon
*icon
= NULL
;
448 TRACE("hwnd=%p, msg=0x%x\n", hwnd
, msg
);
450 /* set the icon data for the window from the data passed into CreateWindow */
451 if (msg
== WM_NCCREATE
)
452 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LPARAM
)((const CREATESTRUCTW
*)lparam
)->lpCreateParams
);
454 icon
= (struct tray_icon
*) GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
460 create_tooltip( icon
);
464 if (icon
->window
&& icon
->layered
) repaint_tray_icon( icon
);
473 int cx
= GetSystemMetrics( SM_CXSMICON
);
474 int cy
= GetSystemMetrics( SM_CYSMICON
);
476 hdc
= BeginPaint(hwnd
, &ps
);
477 GetClientRect(hwnd
, &rc
);
478 TRACE("painting rect %s\n", wine_dbgstr_rect(&rc
));
479 DrawIconEx( hdc
, (rc
.left
+ rc
.right
- cx
) / 2, (rc
.top
+ rc
.bottom
- cy
) / 2,
480 icon
->image
, cx
, cy
, 0, 0, DI_DEFAULTSIZE
|DI_NORMAL
);
491 case WM_LBUTTONDBLCLK
:
492 case WM_RBUTTONDBLCLK
:
493 case WM_MBUTTONDBLCLK
:
494 notify_owner( icon
, msg
, lparam
);
498 if (!notify_owner( icon
, msg
, lparam
)) break;
499 if (icon
->version
> 0) notify_owner( icon
, NIN_SELECT
, lparam
);
503 if (!notify_owner( icon
, msg
, lparam
)) break;
504 if (icon
->version
> 0) notify_owner( icon
, WM_CONTEXTMENU
, lparam
);
507 case WM_WINDOWPOSCHANGED
:
508 update_systray_balloon_position();
514 case BALLOON_CREATE_TIMER
:
515 balloon_create_timer( icon
);
517 case BALLOON_SHOW_TIMER
:
524 if (icon
->display
== -1)
526 TRACE( "icon %u no longer embedded\n", icon
->id
);
528 add_to_standalone_tray( icon
);
532 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
535 static BOOL
init_systray(void)
537 static BOOL init_done
;
540 if (init_done
) return TRUE
;
541 if (!X11DRV_CALL( systray_init
, NULL
))
547 icon_cx
= GetSystemMetrics( SM_CXSMICON
) + 2 * ICON_BORDER
;
548 icon_cy
= GetSystemMetrics( SM_CYSMICON
) + 2 * ICON_BORDER
;
550 memset( &class, 0, sizeof(class) );
551 class.cbSize
= sizeof(class);
552 class.lpfnWndProc
= tray_icon_wndproc
;
553 class.hIcon
= LoadIconW(0, (LPCWSTR
)IDI_WINLOGO
);
554 class.hCursor
= LoadCursorW( 0, (LPCWSTR
)IDC_ARROW
);
555 class.lpszClassName
= icon_classname
;
556 class.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
558 if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS
)
560 ERR( "Could not register icon tray window class\n" );
564 class.lpfnWndProc
= standalone_tray_wndproc
;
565 class.hbrBackground
= (HBRUSH
)COLOR_WINDOW
;
566 class.lpszClassName
= tray_classname
;
567 class.style
= CS_DBLCLKS
;
569 if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS
)
571 ERR( "Could not register standalone tray window class\n" );
579 /* dock systray windows again with the new owner */
580 NTSTATUS WINAPI
x11drv_systray_change_owner( void *arg
, ULONG size
)
582 struct systray_change_owner_params
*params
= arg
;
583 struct systray_dock_params dock_params
;
584 struct tray_icon
*icon
;
586 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct tray_icon
, entry
)
588 if (icon
->display
== -1) continue;
591 dock_params
.event_handle
= params
->event_handle
;
592 dock_params
.icon
= icon
;
593 dock_params
.cx
= icon_cx
;
594 dock_params
.cy
= icon_cy
;
595 dock_params
.layered
= &icon
->layered
;
596 X11DRV_CALL( systray_dock
, &dock_params
);
602 /* hide a tray icon */
603 static BOOL
hide_icon( struct tray_icon
*icon
)
605 TRACE( "id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
607 if (!icon
->window
) return TRUE
; /* already hidden */
609 X11DRV_CALL( systray_hide
, &icon
->window
);
610 DestroyWindow(icon
->window
);
611 DestroyWindow(icon
->tooltip
);
613 icon
->layered
= FALSE
;
615 remove_from_standalone_tray( icon
);
616 update_balloon( icon
);
620 /* make the icon visible */
621 static BOOL
show_icon( struct tray_icon
*icon
)
623 struct systray_dock_params params
;
625 if (icon
->window
) return TRUE
; /* already shown */
627 TRACE( "id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
629 params
.event_handle
= 0;
633 params
.layered
= &icon
->layered
;
635 if (X11DRV_CALL( systray_dock
, ¶ms
))
636 add_to_standalone_tray( icon
);
638 update_balloon( icon
);
642 /* Modifies an existing icon record */
643 static BOOL
modify_icon( struct tray_icon
*icon
, NOTIFYICONDATAW
*nid
)
645 TRACE( "id=0x%x hwnd=%p flags=%x\n", nid
->uID
, nid
->hWnd
, nid
->uFlags
);
647 if (nid
->uFlags
& NIF_STATE
)
649 icon
->state
= (icon
->state
& ~nid
->dwStateMask
) | (nid
->dwState
& nid
->dwStateMask
);
652 if (nid
->uFlags
& NIF_ICON
)
654 if (icon
->image
) DestroyIcon(icon
->image
);
655 icon
->image
= CopyIcon(nid
->hIcon
);
658 if (icon
->display
!= -1) InvalidateRect( icon
->window
, NULL
, TRUE
);
659 else if (icon
->layered
) repaint_tray_icon( icon
);
660 else X11DRV_CALL( systray_clear
, &icon
->window
);
664 if (nid
->uFlags
& NIF_MESSAGE
)
666 icon
->callback_message
= nid
->uCallbackMessage
;
668 if (nid
->uFlags
& NIF_TIP
)
670 lstrcpynW(icon
->tiptext
, nid
->szTip
, ARRAY_SIZE( icon
->tiptext
));
671 if (icon
->tooltip
) update_tooltip_text(icon
);
673 if (nid
->uFlags
& NIF_INFO
&& nid
->cbSize
>= NOTIFYICONDATAA_V2_SIZE
)
675 lstrcpynW( icon
->info_text
, nid
->szInfo
, ARRAY_SIZE( icon
->info_text
));
676 lstrcpynW( icon
->info_title
, nid
->szInfoTitle
, ARRAY_SIZE( icon
->info_title
));
677 icon
->info_flags
= nid
->dwInfoFlags
;
678 icon
->info_timeout
= max(min(nid
->u
.uTimeout
, BALLOON_SHOW_MAX_TIMEOUT
), BALLOON_SHOW_MIN_TIMEOUT
);
679 icon
->info_icon
= nid
->hBalloonIcon
;
680 update_balloon( icon
);
682 if (icon
->state
& NIS_HIDDEN
) hide_icon( icon
);
683 else show_icon( icon
);
687 /* Adds a new icon record to the list */
688 static BOOL
add_icon(NOTIFYICONDATAW
*nid
)
690 struct tray_icon
*icon
;
692 TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
694 if ((icon
= get_icon(nid
->hWnd
, nid
->uID
)))
696 WARN("duplicate tray icon add, buggy app?\n");
700 if (!(icon
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*icon
))))
702 ERR("out of memory\n");
706 ZeroMemory(icon
, sizeof(struct tray_icon
));
708 icon
->owner
= nid
->hWnd
;
711 list_add_tail(&icon_list
, &icon
->entry
);
713 return modify_icon( icon
, nid
);
716 /* delete tray icon window and icon structure */
717 static BOOL
delete_icon( struct tray_icon
*icon
)
720 list_remove( &icon
->entry
);
721 DestroyIcon( icon
->image
);
722 HeapFree( GetProcessHeap(), 0, icon
);
726 /* cleanup all icons for a given window */
727 static void cleanup_icons( HWND owner
)
729 struct tray_icon
*this, *next
;
731 LIST_FOR_EACH_ENTRY_SAFE( this, next
, &icon_list
, struct tray_icon
, entry
)
732 if (this->owner
== owner
) delete_icon( this );
736 /***********************************************************************
737 * wine_notify_icon (X11DRV.@)
739 * Driver-side implementation of Shell_NotifyIcon.
741 int CDECL
wine_notify_icon( DWORD msg
, NOTIFYICONDATAW
*data
)
744 struct tray_icon
*icon
;
749 if (!init_systray()) return -1; /* fall back to default handling */
750 ret
= add_icon( data
);
753 if ((icon
= get_icon( data
->hWnd
, data
->uID
))) ret
= delete_icon( icon
);
756 if ((icon
= get_icon( data
->hWnd
, data
->uID
))) ret
= modify_icon( icon
, data
);
759 if ((icon
= get_icon( data
->hWnd
, data
->uID
)))
761 icon
->version
= data
->u
.uVersion
;
765 case 0xdead: /* Wine extension: owner window has died */
766 cleanup_icons( data
->hWnd
);
769 FIXME( "unhandled tray message: %lu\n", msg
);
776 /* window procedure for foreign windows */
777 LRESULT WINAPI
foreign_window_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
781 case WM_WINDOWPOSCHANGED
:
782 update_systray_balloon_position();
784 case WM_PARENTNOTIFY
:
785 if (LOWORD(wparam
) == WM_DESTROY
)
787 TRACE( "%p: got parent notify destroy for win %Ix\n", hwnd
, lparam
);
788 PostMessageW( hwnd
, WM_CLOSE
, 0, 0 ); /* so that we come back here once the child is gone */
792 if (GetWindow( hwnd
, GW_CHILD
)) return 0; /* refuse to die if we still have children */
795 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);