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
33 #define NONAMELESSUNION
42 #include "wine/list.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(systray
);
47 /* an individual systray icon */
51 HICON image
; /* the image to render */
52 HWND owner
; /* the HWND passed in to the Shell_NotifyIcon call */
53 HWND window
; /* the adaptor window */
54 BOOL layered
; /* whether we are using a layered window */
55 HWND tooltip
; /* Icon tooltip */
56 UINT state
; /* state flags */
57 UINT id
; /* the unique id given by the app */
58 UINT callback_message
;
59 int display
; /* display index, or -1 if hidden */
60 WCHAR tiptext
[128]; /* tooltip text */
61 WCHAR info_text
[256]; /* info balloon text */
62 WCHAR info_title
[64]; /* info balloon title */
63 UINT info_flags
; /* flags for info balloon */
64 UINT info_timeout
; /* timeout for info balloon */
65 HICON info_icon
; /* info balloon icon */
66 UINT version
; /* notify icon api version */
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 BALLOON_CREATE_TIMER 1
88 #define BALLOON_SHOW_TIMER 2
90 #define BALLOON_CREATE_TIMEOUT 2000
91 #define BALLOON_SHOW_MIN_TIMEOUT 10000
92 #define BALLOON_SHOW_MAX_TIMEOUT 30000
94 static struct tray_icon
*balloon_icon
;
95 static HWND balloon_window
;
96 static POINT balloon_pos
;
98 /* stand-alone tray window */
99 static HWND standalone_tray
;
100 static int icon_cx
, icon_cy
;
101 static unsigned int nb_displayed
;
103 /* retrieves icon record by owner window and ID */
104 static struct tray_icon
*get_icon(HWND owner
, UINT id
)
106 struct tray_icon
*this;
108 LIST_FOR_EACH_ENTRY( this, &icon_list
, struct tray_icon
, entry
)
109 if ((this->id
== id
) && (this->owner
== owner
)) return this;
113 static void init_common_controls(void)
115 static BOOL initialized
= FALSE
;
119 INITCOMMONCONTROLSEX init_tooltip
;
121 init_tooltip
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
122 init_tooltip
.dwICC
= ICC_TAB_CLASSES
;
124 InitCommonControlsEx(&init_tooltip
);
129 /* create tooltip window for icon */
130 static void create_tooltip(struct tray_icon
*icon
)
132 init_common_controls();
133 icon
->tooltip
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
134 WS_POPUP
| TTS_ALWAYSTIP
,
135 CW_USEDEFAULT
, CW_USEDEFAULT
,
136 CW_USEDEFAULT
, CW_USEDEFAULT
,
137 icon
->window
, NULL
, NULL
, NULL
);
141 ZeroMemory(&ti
, sizeof(ti
));
142 ti
.cbSize
= sizeof(TTTOOLINFOW
);
143 ti
.uFlags
= TTF_SUBCLASS
| TTF_IDISHWND
;
144 ti
.hwnd
= icon
->window
;
145 ti
.uId
= (UINT_PTR
)icon
->window
;
146 ti
.lpszText
= icon
->tiptext
;
147 SendMessageW(icon
->tooltip
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
151 void update_systray_balloon_position(void)
156 if (!balloon_icon
) return;
157 GetWindowRect( balloon_icon
->window
, &rect
);
158 pos
.x
= (rect
.left
+ rect
.right
) / 2;
159 pos
.y
= (rect
.top
+ rect
.bottom
) / 2;
160 if (pos
.x
== balloon_pos
.x
&& pos
.y
== balloon_pos
.y
) return; /* nothing changed */
162 SendMessageW( balloon_window
, TTM_TRACKPOSITION
, 0, MAKELONG( pos
.x
, pos
.y
));
165 static void balloon_create_timer( struct tray_icon
*icon
)
169 init_common_controls();
170 balloon_window
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
171 WS_POPUP
| TTS_ALWAYSTIP
| TTS_NOPREFIX
| TTS_BALLOON
| TTS_CLOSE
,
172 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
173 icon
->window
, NULL
, NULL
, NULL
);
175 memset( &ti
, 0, sizeof(ti
) );
176 ti
.cbSize
= sizeof(TTTOOLINFOW
);
177 ti
.hwnd
= icon
->window
;
178 ti
.uId
= (UINT_PTR
)icon
->window
;
179 ti
.uFlags
= TTF_TRACK
| TTF_IDISHWND
;
180 ti
.lpszText
= icon
->info_text
;
181 SendMessageW( balloon_window
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
182 if ((icon
->info_flags
& NIIF_ICONMASK
) == NIIF_USER
)
183 SendMessageW( balloon_window
, TTM_SETTITLEW
, (WPARAM
)icon
->info_icon
, (LPARAM
)icon
->info_title
);
185 SendMessageW( balloon_window
, TTM_SETTITLEW
, icon
->info_flags
, (LPARAM
)icon
->info_title
);
187 balloon_pos
.x
= balloon_pos
.y
= MAXLONG
;
188 update_systray_balloon_position();
189 SendMessageW( balloon_window
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
190 KillTimer( icon
->window
, BALLOON_CREATE_TIMER
);
191 SetTimer( icon
->window
, BALLOON_SHOW_TIMER
, icon
->info_timeout
, NULL
);
194 static BOOL
show_balloon( struct tray_icon
*icon
)
196 if (standalone_tray
&& !show_systray
) return FALSE
; /* no systray window */
197 if (!icon
->window
) return FALSE
; /* not displayed */
198 if (!icon
->info_text
[0]) return FALSE
; /* no balloon */
200 SetTimer( icon
->window
, BALLOON_CREATE_TIMER
, BALLOON_CREATE_TIMEOUT
, NULL
);
204 static void hide_balloon(void)
206 if (!balloon_icon
) return;
209 KillTimer( balloon_icon
->window
, BALLOON_SHOW_TIMER
);
210 DestroyWindow( balloon_window
);
213 else KillTimer( balloon_icon
->window
, BALLOON_CREATE_TIMER
);
217 static void show_next_balloon(void)
219 struct tray_icon
*icon
;
221 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct tray_icon
, entry
)
222 if (show_balloon( icon
)) break;
225 static void update_balloon( struct tray_icon
*icon
)
227 if (balloon_icon
== icon
)
230 show_balloon( icon
);
232 else if (!balloon_icon
)
234 if (!show_balloon( icon
)) return;
236 if (!balloon_icon
) show_next_balloon();
239 static void balloon_timer(void)
241 if (balloon_icon
) balloon_icon
->info_text
[0] = 0; /* clear text now that balloon has been shown */
246 /* synchronize tooltip text with tooltip window */
247 static void update_tooltip_text(struct tray_icon
*icon
)
251 ZeroMemory(&ti
, sizeof(ti
));
252 ti
.cbSize
= sizeof(TTTOOLINFOW
);
253 ti
.uFlags
= TTF_SUBCLASS
| TTF_IDISHWND
;
254 ti
.hwnd
= icon
->window
;
255 ti
.uId
= (UINT_PTR
)icon
->window
;
256 ti
.lpszText
= icon
->tiptext
;
258 SendMessageW(icon
->tooltip
, TTM_UPDATETIPTEXTW
, 0, (LPARAM
)&ti
);
261 /* get the size of the stand-alone tray window */
262 static SIZE
get_window_size(void)
269 rect
.right
= icon_cx
* max( nb_displayed
, MIN_DISPLAYED
);
270 rect
.bottom
= icon_cy
;
271 AdjustWindowRect( &rect
, WS_CAPTION
, FALSE
);
272 size
.cx
= rect
.right
- rect
.left
;
273 size
.cy
= rect
.bottom
- rect
.top
;
277 /* get the position of an icon in the stand-alone tray */
278 static POINT
get_icon_pos( struct tray_icon
*icon
)
282 pos
.x
= icon_cx
* icon
->display
;
287 /* window procedure for the standalone tray window */
288 static LRESULT WINAPI
standalone_tray_wndproc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
293 update_systray_balloon_position();
296 ShowWindow( hwnd
, SW_HIDE
);
298 show_systray
= FALSE
;
304 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
307 /* add an icon to the standalone tray window */
308 static void add_to_standalone_tray( struct tray_icon
*icon
)
313 if (!standalone_tray
)
315 static const WCHAR winname
[] = {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',0};
317 size
= get_window_size();
318 standalone_tray
= CreateWindowExW( 0, tray_classname
, winname
, WS_CAPTION
| WS_SYSMENU
,
319 CW_USEDEFAULT
, CW_USEDEFAULT
, size
.cx
, size
.cy
, 0, 0, 0, 0 );
320 if (!standalone_tray
) return;
323 icon
->display
= nb_displayed
;
324 pos
= get_icon_pos( icon
);
325 CreateWindowW( icon_classname
, NULL
, WS_CHILD
| WS_VISIBLE
,
326 pos
.x
, pos
.y
, icon_cx
, icon_cy
, standalone_tray
, NULL
, NULL
, icon
);
334 size
= get_window_size();
335 SetWindowPos( standalone_tray
, 0, 0, 0, size
.cx
, size
.cy
, SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOZORDER
);
336 if (nb_displayed
== 1 && show_systray
) ShowWindow( standalone_tray
, SW_SHOWNA
);
337 TRACE( "added %u now %d icons\n", icon
->id
, nb_displayed
);
340 /* remove an icon from the stand-alone tray */
341 static void remove_from_standalone_tray( struct tray_icon
*icon
)
343 struct tray_icon
*ptr
;
346 if (icon
->display
== -1) return;
348 LIST_FOR_EACH_ENTRY( ptr
, &icon_list
, struct tray_icon
, entry
)
350 if (ptr
== icon
) continue;
351 if (ptr
->display
< icon
->display
) continue;
353 pos
= get_icon_pos( ptr
);
354 SetWindowPos( ptr
->window
, 0, pos
.x
, pos
.y
, 0, 0, SWP_NOSIZE
| SWP_NOACTIVATE
| SWP_NOZORDER
);
357 if (!--nb_displayed
) ShowWindow( standalone_tray
, SW_HIDE
);
358 TRACE( "removed %u now %d icons\n", icon
->id
, nb_displayed
);
361 static void repaint_tray_icon( struct tray_icon
*icon
)
363 BLENDFUNCTION blend
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
364 int width
= GetSystemMetrics( SM_CXSMICON
);
365 int height
= GetSystemMetrics( SM_CYSMICON
);
373 void *color_bits
, *mask_bits
;
375 BOOL has_alpha
= FALSE
;
377 GetWindowRect( icon
->window
, &rc
);
378 size
.cx
= rc
.right
- rc
.left
;
379 size
.cy
= rc
.bottom
- rc
.top
;
380 pos
.x
= (size
.cx
- width
) / 2;
381 pos
.y
= (size
.cy
- height
) / 2;
383 info
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, FIELD_OFFSET( BITMAPINFO
, bmiColors
[2] ));
385 info
->bmiHeader
.biSize
= sizeof(info
->bmiHeader
);
386 info
->bmiHeader
.biWidth
= size
.cx
;
387 info
->bmiHeader
.biHeight
= size
.cy
;
388 info
->bmiHeader
.biBitCount
= 32;
389 info
->bmiHeader
.biPlanes
= 1;
390 info
->bmiHeader
.biCompression
= BI_RGB
;
392 hdc
= CreateCompatibleDC( 0 );
393 if (!(dib
= CreateDIBSection( 0, info
, DIB_RGB_COLORS
, &color_bits
, NULL
, 0 ))) goto done
;
394 SelectObject( hdc
, dib
);
395 DrawIconEx( hdc
, pos
.x
, pos
.y
, icon
->image
, width
, height
, 0, 0, DI_DEFAULTSIZE
| DI_NORMAL
);
397 /* check if the icon was drawn with an alpha channel */
398 for (i
= 0, ptr
= color_bits
; i
< size
.cx
* size
.cy
; i
++)
399 if ((has_alpha
= (ptr
[i
] & 0xff000000) != 0)) break;
403 unsigned int width_bytes
= (size
.cx
+ 31) / 32 * 4;
405 info
->bmiHeader
.biBitCount
= 1;
406 info
->bmiColors
[0].rgbRed
= 0;
407 info
->bmiColors
[0].rgbGreen
= 0;
408 info
->bmiColors
[0].rgbBlue
= 0;
409 info
->bmiColors
[0].rgbReserved
= 0;
410 info
->bmiColors
[1].rgbRed
= 0xff;
411 info
->bmiColors
[1].rgbGreen
= 0xff;
412 info
->bmiColors
[1].rgbBlue
= 0xff;
413 info
->bmiColors
[1].rgbReserved
= 0;
415 if (!(mask
= CreateDIBSection( 0, info
, DIB_RGB_COLORS
, &mask_bits
, NULL
, 0 ))) goto done
;
416 memset( mask_bits
, 0xff, width_bytes
* size
.cy
);
417 SelectObject( hdc
, mask
);
418 DrawIconEx( hdc
, pos
.x
, pos
.y
, icon
->image
, width
, height
, 0, 0, DI_DEFAULTSIZE
| DI_MASK
);
420 for (y
= 0, ptr
= color_bits
; y
< size
.cy
; y
++)
421 for (x
= 0; x
< size
.cx
; x
++, ptr
++)
422 if (!((((BYTE
*)mask_bits
)[y
* width_bytes
+ x
/ 8] << (x
% 8)) & 0x80))
425 SelectObject( hdc
, dib
);
426 DeleteObject( mask
);
429 UpdateLayeredWindow( icon
->window
, 0, NULL
, NULL
, hdc
, NULL
, 0, &blend
, ULW_ALPHA
);
431 HeapFree (GetProcessHeap(), 0, info
);
432 if (hdc
) DeleteDC( hdc
);
433 if (dib
) DeleteObject( dib
);
436 static BOOL
notify_owner( struct tray_icon
*icon
, UINT msg
, LPARAM lparam
)
438 WPARAM wp
= icon
->id
;
441 if (icon
->version
>= NOTIFYICON_VERSION_4
)
443 POINT pt
= { (short)LOWORD(lparam
), (short)HIWORD(lparam
) };
445 ClientToScreen( icon
->window
, &pt
);
446 wp
= MAKEWPARAM( pt
.x
, pt
.y
);
447 lp
= MAKELPARAM( msg
, icon
->id
);
450 TRACE( "relaying 0x%x\n", msg
);
451 if (!SendNotifyMessageW( icon
->owner
, icon
->callback_message
, wp
, lp
) &&
452 (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
))
454 WARN( "application window was destroyed, removing icon %u\n", icon
->id
);
461 /* window procedure for the individual tray icon window */
462 static LRESULT WINAPI
tray_icon_wndproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
464 struct tray_icon
*icon
= NULL
;
466 TRACE("hwnd=%p, msg=0x%x\n", hwnd
, msg
);
468 /* set the icon data for the window from the data passed into CreateWindow */
469 if (msg
== WM_NCCREATE
)
470 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LPARAM
)((const CREATESTRUCTW
*)lparam
)->lpCreateParams
);
472 icon
= (struct tray_icon
*) GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
478 create_tooltip( icon
);
482 if (icon
->window
&& icon
->layered
) repaint_tray_icon( icon
);
491 int cx
= GetSystemMetrics( SM_CXSMICON
);
492 int cy
= GetSystemMetrics( SM_CYSMICON
);
494 hdc
= BeginPaint(hwnd
, &ps
);
495 GetClientRect(hwnd
, &rc
);
496 TRACE("painting rect %s\n", wine_dbgstr_rect(&rc
));
497 DrawIconEx( hdc
, (rc
.left
+ rc
.right
- cx
) / 2, (rc
.top
+ rc
.bottom
- cy
) / 2,
498 icon
->image
, cx
, cy
, 0, 0, DI_DEFAULTSIZE
|DI_NORMAL
);
509 case WM_LBUTTONDBLCLK
:
510 case WM_RBUTTONDBLCLK
:
511 case WM_MBUTTONDBLCLK
:
512 notify_owner( icon
, msg
, lparam
);
516 if (!notify_owner( icon
, msg
, lparam
)) break;
517 if (icon
->version
> 0) notify_owner( icon
, NIN_SELECT
, lparam
);
521 if (!notify_owner( icon
, msg
, lparam
)) break;
522 if (icon
->version
> 0) notify_owner( icon
, WM_CONTEXTMENU
, lparam
);
525 case WM_WINDOWPOSCHANGED
:
526 update_systray_balloon_position();
532 case BALLOON_CREATE_TIMER
:
533 balloon_create_timer( icon
);
535 case BALLOON_SHOW_TIMER
:
542 if (icon
->display
== -1)
544 TRACE( "icon %u no longer embedded\n", icon
->id
);
546 add_to_standalone_tray( icon
);
550 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
553 /* find the X11 window owner the system tray selection */
554 static Window
get_systray_selection_owner( Display
*display
)
556 return XGetSelectionOwner( display
, systray_atom
);
559 static void get_systray_visual_info( Display
*display
, Window systray_window
, XVisualInfo
*info
)
561 XVisualInfo
*list
, template;
565 unsigned long count
, remaining
;
567 *info
= default_visual
;
568 if (XGetWindowProperty( display
, systray_window
, x11drv_atom(_NET_SYSTEM_TRAY_VISUAL
), 0,
569 65536/sizeof(CARD32
), False
, XA_VISUALID
, &type
, &format
, &count
,
570 &remaining
, (unsigned char **)&visual_id
))
573 if (type
== XA_VISUALID
&& format
== 32)
575 template.visualid
= visual_id
[0];
576 if ((list
= XGetVisualInfo( display
, VisualIDMask
, &template, &num
)))
579 TRACE( "systray window %lx got visual %lx\n", systray_window
, info
->visualid
);
586 static BOOL
init_systray(void)
588 static BOOL init_done
;
591 if (init_done
) return TRUE
;
592 if (!X11DRV_CALL( systray_init
, NULL
))
598 icon_cx
= GetSystemMetrics( SM_CXSMICON
) + 2 * ICON_BORDER
;
599 icon_cy
= GetSystemMetrics( SM_CYSMICON
) + 2 * ICON_BORDER
;
601 memset( &class, 0, sizeof(class) );
602 class.cbSize
= sizeof(class);
603 class.lpfnWndProc
= tray_icon_wndproc
;
604 class.hIcon
= LoadIconW(0, (LPCWSTR
)IDI_WINLOGO
);
605 class.hCursor
= LoadCursorW( 0, (LPCWSTR
)IDC_ARROW
);
606 class.lpszClassName
= icon_classname
;
607 class.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
;
609 if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS
)
611 ERR( "Could not register icon tray window class\n" );
615 class.lpfnWndProc
= standalone_tray_wndproc
;
616 class.hbrBackground
= (HBRUSH
)COLOR_WINDOW
;
617 class.lpszClassName
= tray_classname
;
618 class.style
= CS_DBLCLKS
;
620 if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS
)
622 ERR( "Could not register standalone tray window class\n" );
630 /* dock the given icon with the NETWM system tray */
631 static void dock_systray_icon( Display
*display
, struct tray_icon
*icon
, Window systray_window
)
635 XSetWindowAttributes attr
;
637 struct x11drv_win_data
*data
;
639 get_systray_visual_info( display
, systray_window
, &visual
);
641 icon
->layered
= (visual
.depth
== 32);
642 CreateWindowExW( icon
->layered
? WS_EX_LAYERED
: 0,
643 icon_classname
, NULL
, WS_CLIPSIBLINGS
| WS_POPUP
,
644 CW_USEDEFAULT
, CW_USEDEFAULT
, icon_cx
, icon_cy
,
645 NULL
, NULL
, NULL
, icon
);
647 if (!(data
= get_win_data( icon
->window
))) return;
648 if (icon
->layered
) set_window_visual( data
, &visual
, TRUE
);
649 make_window_embedded( data
);
650 window
= data
->whole_window
;
651 release_win_data( data
);
653 ShowWindow( icon
->window
, SW_SHOWNA
);
655 TRACE( "icon window %p/%lx\n", icon
->window
, window
);
657 /* send the docking request message */
658 ev
.xclient
.type
= ClientMessage
;
659 ev
.xclient
.window
= systray_window
;
660 ev
.xclient
.message_type
= x11drv_atom( _NET_SYSTEM_TRAY_OPCODE
);
661 ev
.xclient
.format
= 32;
662 ev
.xclient
.data
.l
[0] = CurrentTime
;
663 ev
.xclient
.data
.l
[1] = SYSTEM_TRAY_REQUEST_DOCK
;
664 ev
.xclient
.data
.l
[2] = window
;
665 ev
.xclient
.data
.l
[3] = 0;
666 ev
.xclient
.data
.l
[4] = 0;
667 XSendEvent( display
, systray_window
, False
, NoEventMask
, &ev
);
671 attr
.background_pixmap
= ParentRelative
;
672 attr
.bit_gravity
= ForgetGravity
;
673 XChangeWindowAttributes( display
, window
, CWBackPixmap
| CWBitGravity
, &attr
);
675 else repaint_tray_icon( icon
);
678 /* dock systray windows again with the new owner */
679 void change_systray_owner( Display
*display
, Window systray_window
)
681 struct tray_icon
*icon
;
683 TRACE( "new owner %lx\n", systray_window
);
684 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct tray_icon
, entry
)
686 if (icon
->display
== -1) continue;
688 dock_systray_icon( display
, icon
, systray_window
);
692 /* hide a tray icon */
693 static BOOL
hide_icon( struct tray_icon
*icon
)
695 TRACE( "id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
697 if (!icon
->window
) return TRUE
; /* already hidden */
699 X11DRV_CALL( systray_hide
, &icon
->window
);
700 DestroyWindow(icon
->window
);
701 DestroyWindow(icon
->tooltip
);
703 icon
->layered
= FALSE
;
705 remove_from_standalone_tray( icon
);
706 update_balloon( icon
);
710 /* make the icon visible */
711 static BOOL
show_icon( struct tray_icon
*icon
)
713 Window systray_window
;
714 Display
*display
= thread_init_display();
716 TRACE( "id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
718 if (icon
->window
) return TRUE
; /* already shown */
720 if ((systray_window
= get_systray_selection_owner( display
)))
721 dock_systray_icon( display
, icon
, systray_window
);
723 add_to_standalone_tray( icon
);
725 update_balloon( icon
);
729 /* Modifies an existing icon record */
730 static BOOL
modify_icon( struct tray_icon
*icon
, NOTIFYICONDATAW
*nid
)
732 TRACE( "id=0x%x hwnd=%p flags=%x\n", nid
->uID
, nid
->hWnd
, nid
->uFlags
);
734 if (nid
->uFlags
& NIF_STATE
)
736 icon
->state
= (icon
->state
& ~nid
->dwStateMask
) | (nid
->dwState
& nid
->dwStateMask
);
739 if (nid
->uFlags
& NIF_ICON
)
741 if (icon
->image
) DestroyIcon(icon
->image
);
742 icon
->image
= CopyIcon(nid
->hIcon
);
745 if (icon
->display
!= -1) InvalidateRect( icon
->window
, NULL
, TRUE
);
746 else if (icon
->layered
) repaint_tray_icon( icon
);
747 else X11DRV_CALL( systray_clear
, &icon
->window
);
751 if (nid
->uFlags
& NIF_MESSAGE
)
753 icon
->callback_message
= nid
->uCallbackMessage
;
755 if (nid
->uFlags
& NIF_TIP
)
757 lstrcpynW(icon
->tiptext
, nid
->szTip
, ARRAY_SIZE( icon
->tiptext
));
758 if (icon
->tooltip
) update_tooltip_text(icon
);
760 if (nid
->uFlags
& NIF_INFO
&& nid
->cbSize
>= NOTIFYICONDATAA_V2_SIZE
)
762 lstrcpynW( icon
->info_text
, nid
->szInfo
, ARRAY_SIZE( icon
->info_text
));
763 lstrcpynW( icon
->info_title
, nid
->szInfoTitle
, ARRAY_SIZE( icon
->info_title
));
764 icon
->info_flags
= nid
->dwInfoFlags
;
765 icon
->info_timeout
= max(min(nid
->u
.uTimeout
, BALLOON_SHOW_MAX_TIMEOUT
), BALLOON_SHOW_MIN_TIMEOUT
);
766 icon
->info_icon
= nid
->hBalloonIcon
;
767 update_balloon( icon
);
769 if (icon
->state
& NIS_HIDDEN
) hide_icon( icon
);
770 else show_icon( icon
);
774 /* Adds a new icon record to the list */
775 static BOOL
add_icon(NOTIFYICONDATAW
*nid
)
777 struct tray_icon
*icon
;
779 TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
781 if ((icon
= get_icon(nid
->hWnd
, nid
->uID
)))
783 WARN("duplicate tray icon add, buggy app?\n");
787 if (!(icon
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*icon
))))
789 ERR("out of memory\n");
793 ZeroMemory(icon
, sizeof(struct tray_icon
));
795 icon
->owner
= nid
->hWnd
;
798 list_add_tail(&icon_list
, &icon
->entry
);
800 return modify_icon( icon
, nid
);
803 /* delete tray icon window and icon structure */
804 static BOOL
delete_icon( struct tray_icon
*icon
)
807 list_remove( &icon
->entry
);
808 DestroyIcon( icon
->image
);
809 HeapFree( GetProcessHeap(), 0, icon
);
813 /* cleanup all icons for a given window */
814 static void cleanup_icons( HWND owner
)
816 struct tray_icon
*this, *next
;
818 LIST_FOR_EACH_ENTRY_SAFE( this, next
, &icon_list
, struct tray_icon
, entry
)
819 if (this->owner
== owner
) delete_icon( this );
823 /***********************************************************************
824 * wine_notify_icon (X11DRV.@)
826 * Driver-side implementation of Shell_NotifyIcon.
828 int CDECL
wine_notify_icon( DWORD msg
, NOTIFYICONDATAW
*data
)
831 struct tray_icon
*icon
;
836 if (!init_systray()) return -1; /* fall back to default handling */
837 ret
= add_icon( data
);
840 if ((icon
= get_icon( data
->hWnd
, data
->uID
))) ret
= delete_icon( icon
);
843 if ((icon
= get_icon( data
->hWnd
, data
->uID
))) ret
= modify_icon( icon
, data
);
846 if ((icon
= get_icon( data
->hWnd
, data
->uID
)))
848 icon
->version
= data
->u
.uVersion
;
852 case 0xdead: /* Wine extension: owner window has died */
853 cleanup_icons( data
->hWnd
);
856 FIXME( "unhandled tray message: %u\n", msg
);