opengl32: Correctly interpret glMapBuffer() access in wow64 mapping.
[wine.git] / dlls / winex11.drv / systray.c
blobdde2feb6b0e599de4e9f2a2a1c4defb77595907d
1 /*
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"
25 #include "commctrl.h"
26 #include "shellapi.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 */
36 struct tray_icon
38 struct list entry;
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
67 #define ICON_BORDER 2
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;
92 return NULL;
95 static void init_common_controls(void)
97 static BOOL initialized = FALSE;
99 if (!initialized)
101 INITCOMMONCONTROLSEX init_tooltip;
103 init_tooltip.dwSize = sizeof(INITCOMMONCONTROLSEX);
104 init_tooltip.dwICC = ICC_TAB_CLASSES;
106 InitCommonControlsEx(&init_tooltip);
107 initialized = TRUE;
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);
120 if (icon->tooltip)
122 TTTOOLINFOW ti;
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)
135 RECT rect;
136 POINT pos;
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 */
143 balloon_pos = pos;
144 SendMessageW( balloon_window, TTM_TRACKPOSITION, 0, MAKELONG( pos.x, pos.y ));
147 static void balloon_create_timer( struct tray_icon *icon )
149 TTTOOLINFOW ti;
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 );
166 else
167 SendMessageW( balloon_window, TTM_SETTITLEW, icon->info_flags, (LPARAM)icon->info_title );
168 balloon_icon = icon;
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 */
181 balloon_icon = icon;
182 SetTimer( icon->window, BALLOON_CREATE_TIMER, BALLOON_CREATE_TIMEOUT, NULL );
183 return TRUE;
186 static void hide_balloon(void)
188 if (!balloon_icon) return;
189 if (balloon_window)
191 KillTimer( balloon_icon->window, BALLOON_SHOW_TIMER );
192 DestroyWindow( balloon_window );
193 balloon_window = 0;
195 else KillTimer( balloon_icon->window, BALLOON_CREATE_TIMER );
196 balloon_icon = NULL;
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)
211 hide_balloon();
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 */
224 hide_balloon();
225 show_next_balloon();
228 /* synchronize tooltip text with tooltip window */
229 static void update_tooltip_text(struct tray_icon *icon)
231 TTTOOLINFOW ti;
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)
246 SIZE size;
247 RECT rect;
249 rect.left = 0;
250 rect.top = 0;
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;
256 return size;
259 /* get the position of an icon in the stand-alone tray */
260 static POINT get_icon_pos( struct tray_icon *icon )
262 POINT pos;
264 pos.x = icon_cx * icon->display;
265 pos.y = 0;
266 return pos;
269 /* window procedure for the standalone tray window */
270 static LRESULT WINAPI standalone_tray_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
272 switch (msg)
274 case WM_MOVE:
275 update_systray_balloon_position();
276 break;
277 case WM_CLOSE:
278 ShowWindow( hwnd, SW_HIDE );
279 hide_balloon();
280 show_systray = FALSE;
281 return 0;
282 case WM_DESTROY:
283 standalone_tray = 0;
284 break;
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 )
292 SIZE size;
293 POINT pos;
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 );
309 if (!icon->window)
311 icon->display = -1;
312 return;
315 nb_displayed++;
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;
326 POINT pos;
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;
334 ptr->display--;
335 pos = get_icon_pos( ptr );
336 SetWindowPos( ptr->window, 0, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER );
338 icon->display = -1;
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 );
348 BITMAPINFO *info;
349 HBITMAP dib, mask;
350 HDC hdc;
351 RECT rc;
352 SIZE size;
353 POINT pos;
354 int i, x, y;
355 void *color_bits, *mask_bits;
356 DWORD *ptr;
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] ));
366 if (!info) return;
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;
383 if (!has_alpha)
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))
405 *ptr |= 0xff000000;
407 SelectObject( hdc, dib );
408 DeleteObject( mask );
411 UpdateLayeredWindow( icon->window, 0, NULL, NULL, hdc, NULL, 0, &blend, ULW_ALPHA );
412 done:
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;
421 LPARAM lp = msg;
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 );
437 delete_icon( icon );
438 return FALSE;
440 return TRUE;
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);
456 switch (msg)
458 case WM_CREATE:
459 icon->window = hwnd;
460 create_tooltip( icon );
461 break;
463 case WM_SIZE:
464 if (icon->window && icon->layered) repaint_tray_icon( icon );
465 break;
467 case WM_PAINT:
468 if (!icon->layered)
470 PAINTSTRUCT ps;
471 RECT rc;
472 HDC hdc;
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 );
481 EndPaint(hwnd, &ps);
482 return 0;
484 break;
486 case WM_MOUSEMOVE:
487 case WM_LBUTTONDOWN:
488 case WM_RBUTTONDOWN:
489 case WM_MBUTTONDOWN:
490 case WM_MBUTTONUP:
491 case WM_LBUTTONDBLCLK:
492 case WM_RBUTTONDBLCLK:
493 case WM_MBUTTONDBLCLK:
494 notify_owner( icon, msg, lparam );
495 break;
497 case WM_LBUTTONUP:
498 if (!notify_owner( icon, msg, lparam )) break;
499 if (icon->version > 0) notify_owner( icon, NIN_SELECT, lparam );
500 break;
502 case WM_RBUTTONUP:
503 if (!notify_owner( icon, msg, lparam )) break;
504 if (icon->version > 0) notify_owner( icon, WM_CONTEXTMENU, lparam );
505 break;
507 case WM_WINDOWPOSCHANGED:
508 update_systray_balloon_position();
509 break;
511 case WM_TIMER:
512 switch (wparam)
514 case BALLOON_CREATE_TIMER:
515 balloon_create_timer( icon );
516 break;
517 case BALLOON_SHOW_TIMER:
518 balloon_timer();
519 break;
521 return 0;
523 case WM_CLOSE:
524 if (icon->display == -1)
526 TRACE( "icon %u no longer embedded\n", icon->id );
527 hide_icon( icon );
528 add_to_standalone_tray( icon );
530 return 0;
532 return DefWindowProcW( hwnd, msg, wparam, lparam );
535 static BOOL init_systray(void)
537 static BOOL init_done;
538 WNDCLASSEXW class;
540 if (init_done) return TRUE;
541 if (!X11DRV_CALL( systray_init, NULL ))
543 init_done = TRUE;
544 return FALSE;
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" );
561 return FALSE;
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" );
572 return FALSE;
575 init_done = TRUE;
576 return TRUE;
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;
589 hide_icon( icon );
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 );
599 return 0;
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);
612 icon->window = 0;
613 icon->layered = FALSE;
614 icon->tooltip = 0;
615 remove_from_standalone_tray( icon );
616 update_balloon( icon );
617 return TRUE;
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;
630 params.icon = icon;
631 params.cx = icon_cx;
632 params.cy = icon_cy;
633 params.layered = &icon->layered;
635 if (X11DRV_CALL( systray_dock, &params ))
636 add_to_standalone_tray( icon );
638 update_balloon( icon );
639 return TRUE;
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);
656 if (icon->window)
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 );
684 return TRUE;
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");
697 return FALSE;
700 if (!(icon = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*icon))))
702 ERR("out of memory\n");
703 return FALSE;
706 ZeroMemory(icon, sizeof(struct tray_icon));
707 icon->id = nid->uID;
708 icon->owner = nid->hWnd;
709 icon->display = -1;
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 )
719 hide_icon( icon );
720 list_remove( &icon->entry );
721 DestroyIcon( icon->image );
722 HeapFree( GetProcessHeap(), 0, icon );
723 return TRUE;
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 )
743 BOOL ret = FALSE;
744 struct tray_icon *icon;
746 switch (msg)
748 case NIM_ADD:
749 if (!init_systray()) return -1; /* fall back to default handling */
750 ret = add_icon( data );
751 break;
752 case NIM_DELETE:
753 if ((icon = get_icon( data->hWnd, data->uID ))) ret = delete_icon( icon );
754 break;
755 case NIM_MODIFY:
756 if ((icon = get_icon( data->hWnd, data->uID ))) ret = modify_icon( icon, data );
757 break;
758 case NIM_SETVERSION:
759 if ((icon = get_icon( data->hWnd, data->uID )))
761 icon->version = data->u.uVersion;
762 ret = TRUE;
764 break;
765 case 0xdead: /* Wine extension: owner window has died */
766 cleanup_icons( data->hWnd );
767 break;
768 default:
769 FIXME( "unhandled tray message: %lu\n", msg );
770 break;
772 return ret;
776 /* window procedure for foreign windows */
777 LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
779 switch(msg)
781 case WM_WINDOWPOSCHANGED:
782 update_systray_balloon_position();
783 break;
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 */
790 return 0;
791 case WM_CLOSE:
792 if (GetWindow( hwnd, GW_CHILD )) return 0; /* refuse to die if we still have children */
793 break;
795 return DefWindowProcW( hwnd, msg, wparam, lparam );