ws2_32: Define sipx_network / sipx_node for DragonFly BSD.
[wine] / dlls / winex11.drv / systray.c
1 /*
2  * X11 system tray management
3  *
4  * Copyright (C) 2004 Mike Hearn, for CodeWeavers
5  * Copyright (C) 2005 Robert Shearman
6  * Copyright (C) 2008 Alexandre Julliard
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #include <X11/Xlib.h>
34
35 #define NONAMELESSUNION
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "commctrl.h"
41 #include "shellapi.h"
42
43 #include "x11drv.h"
44 #include "wine/list.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(systray);
48
49 /* an individual systray icon */
50 struct tray_icon
51 {
52     struct list    entry;
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 */
67 };
68
69 static struct list icon_list = LIST_INIT( icon_list );
70
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};
73
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 );
77
78 #define SYSTEM_TRAY_REQUEST_DOCK  0
79 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
80 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
81
82 Atom systray_atom = 0;
83
84 #define MIN_DISPLAYED 8
85 #define ICON_BORDER 2
86
87 #define VALID_WIN_TIMER      1
88 #define BALLOON_CREATE_TIMER 2
89 #define BALLOON_SHOW_TIMER   3
90
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
95
96 static struct tray_icon *balloon_icon;
97 static HWND balloon_window;
98 static POINT balloon_pos;
99
100 /* stand-alone tray window */
101 static HWND standalone_tray;
102 static int icon_cx, icon_cy;
103 static unsigned int nb_displayed;
104
105 /* retrieves icon record by owner window and ID */
106 static struct tray_icon *get_icon(HWND owner, UINT id)
107 {
108     struct tray_icon *this;
109
110     LIST_FOR_EACH_ENTRY( this, &icon_list, struct tray_icon, entry )
111         if ((this->id == id) && (this->owner == owner)) return this;
112     return NULL;
113 }
114
115 static void init_common_controls(void)
116 {
117     static BOOL initialized = FALSE;
118
119     if (!initialized)
120     {
121         INITCOMMONCONTROLSEX init_tooltip;
122
123         init_tooltip.dwSize = sizeof(INITCOMMONCONTROLSEX);
124         init_tooltip.dwICC = ICC_TAB_CLASSES;
125
126         InitCommonControlsEx(&init_tooltip);
127         initialized = TRUE;
128     }
129 }
130
131 /* create tooltip window for icon */
132 static void create_tooltip(struct tray_icon *icon)
133 {
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);
140     if (icon->tooltip)
141     {
142         TTTOOLINFOW ti;
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);
150     }
151 }
152
153 void update_systray_balloon_position(void)
154 {
155     RECT rect;
156     POINT pos;
157
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 */
163     balloon_pos = pos;
164     SendMessageW( balloon_window, TTM_TRACKPOSITION, 0, MAKELONG( pos.x, pos.y ));
165 }
166
167 static void balloon_create_timer( struct tray_icon *icon )
168 {
169     TTTOOLINFOW ti;
170
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);
176
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 );
186     else
187         SendMessageW( balloon_window, TTM_SETTITLEW, icon->info_flags, (LPARAM)icon->info_title );
188     balloon_icon = icon;
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 );
194 }
195
196 static BOOL show_balloon( struct tray_icon *icon )
197 {
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 */
201     balloon_icon = icon;
202     SetTimer( icon->window, BALLOON_CREATE_TIMER, BALLOON_CREATE_TIMEOUT, NULL );
203     return TRUE;
204 }
205
206 static void hide_balloon(void)
207 {
208     if (!balloon_icon) return;
209     if (balloon_window)
210     {
211         KillTimer( balloon_icon->window, BALLOON_SHOW_TIMER );
212         DestroyWindow( balloon_window );
213         balloon_window = 0;
214     }
215     else KillTimer( balloon_icon->window, BALLOON_CREATE_TIMER );
216     balloon_icon = NULL;
217 }
218
219 static void show_next_balloon(void)
220 {
221     struct tray_icon *icon;
222
223     LIST_FOR_EACH_ENTRY( icon, &icon_list, struct tray_icon, entry )
224         if (show_balloon( icon )) break;
225 }
226
227 static void update_balloon( struct tray_icon *icon )
228 {
229     if (balloon_icon == icon)
230     {
231         hide_balloon();
232         show_balloon( icon );
233     }
234     else if (!balloon_icon)
235     {
236         if (!show_balloon( icon )) return;
237     }
238     if (!balloon_icon) show_next_balloon();
239 }
240
241 static void balloon_timer(void)
242 {
243     if (balloon_icon) balloon_icon->info_text[0] = 0;  /* clear text now that balloon has been shown */
244     hide_balloon();
245     show_next_balloon();
246 }
247
248 /* synchronize tooltip text with tooltip window */
249 static void update_tooltip_text(struct tray_icon *icon)
250 {
251     TTTOOLINFOW ti;
252
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;
259
260     SendMessageW(icon->tooltip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
261 }
262
263 /* get the size of the stand-alone tray window */
264 static SIZE get_window_size(void)
265 {
266     SIZE size;
267     RECT rect;
268
269     rect.left = 0;
270     rect.top = 0;
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;
276     return size;
277 }
278
279 /* get the position of an icon in the stand-alone tray */
280 static POINT get_icon_pos( struct tray_icon *icon )
281 {
282     POINT pos;
283
284     pos.x = icon_cx * icon->display;
285     pos.y = 0;
286     return pos;
287 }
288
289 /* window procedure for the standalone tray window */
290 static LRESULT WINAPI standalone_tray_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
291 {
292     switch (msg)
293     {
294     case WM_MOVE:
295         update_systray_balloon_position();
296         break;
297     case WM_CLOSE:
298         ShowWindow( hwnd, SW_HIDE );
299         hide_balloon();
300         show_systray = FALSE;
301         return 0;
302     case WM_DESTROY:
303         standalone_tray = 0;
304         break;
305     }
306     return DefWindowProcW( hwnd, msg, wparam, lparam );
307 }
308
309 /* add an icon to the standalone tray window */
310 static void add_to_standalone_tray( struct tray_icon *icon )
311 {
312     SIZE size;
313     POINT pos;
314
315     if (!standalone_tray)
316     {
317         static const WCHAR winname[] = {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',0};
318
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;
323     }
324
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 );
329     if (!icon->window)
330     {
331         icon->display = -1;
332         return;
333     }
334     create_tooltip( icon );
335
336     nb_displayed++;
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 );
341 }
342
343 /* remove an icon from the stand-alone tray */
344 static void remove_from_standalone_tray( struct tray_icon *icon )
345 {
346     struct tray_icon *ptr;
347     POINT pos;
348
349     if (icon->display == -1) return;
350
351     LIST_FOR_EACH_ENTRY( ptr, &icon_list, struct tray_icon, entry )
352     {
353         if (ptr == icon) continue;
354         if (ptr->display < icon->display) continue;
355         ptr->display--;
356         pos = get_icon_pos( ptr );
357         SetWindowPos( ptr->window, 0, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER );
358     }
359     icon->display = -1;
360     if (!--nb_displayed) ShowWindow( standalone_tray, SW_HIDE );
361     TRACE( "removed %u now %d icons\n", icon->id, nb_displayed );
362 }
363
364 /* window procedure for the individual tray icon window */
365 static LRESULT WINAPI tray_icon_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
366 {
367     struct tray_icon *icon = NULL;
368     BOOL ret;
369
370     WINE_TRACE("hwnd=%p, msg=0x%x\n", hwnd, msg);
371
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);
375
376     icon = (struct tray_icon *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
377
378     switch (msg)
379     {
380     case WM_CREATE:
381         SetTimer( hwnd, VALID_WIN_TIMER, VALID_WIN_TIMEOUT, NULL );
382         break;
383
384     case WM_PAINT:
385         {
386             PAINTSTRUCT ps;
387             RECT rc;
388             HDC hdc;
389             int cx = GetSystemMetrics( SM_CXSMICON );
390             int cy = GetSystemMetrics( SM_CYSMICON );
391
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 );
397             EndPaint(hwnd, &ps);
398             return 0;
399         }
400
401     case WM_MOUSEMOVE:
402     case WM_LBUTTONDOWN:
403     case WM_LBUTTONUP:
404     case WM_RBUTTONDOWN:
405     case WM_RBUTTONUP:
406     case WM_MBUTTONDOWN:
407     case WM_MBUTTONUP:
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))
415         {
416             WARN( "application window was destroyed, removing icon %u\n", icon->id );
417             delete_icon( icon );
418         }
419         return 0;
420
421     case WM_WINDOWPOSCHANGED:
422         update_systray_balloon_position();
423         break;
424
425     case WM_TIMER:
426         switch (wparam)
427         {
428         case VALID_WIN_TIMER:
429             if (!IsWindow( icon->owner )) delete_icon( icon );
430             break;
431         case BALLOON_CREATE_TIMER:
432             balloon_create_timer( icon );
433             break;
434         case BALLOON_SHOW_TIMER:
435             balloon_timer();
436             break;
437         }
438         return 0;
439
440     case WM_CLOSE:
441         if (icon->display == -1)
442         {
443             TRACE( "icon %u no longer embedded\n", icon->id );
444             hide_icon( icon );
445             add_to_standalone_tray( icon );
446         }
447         return 0;
448     }
449     return DefWindowProcW( hwnd, msg, wparam, lparam );
450 }
451
452 /* find the X11 window owner the system tray selection */
453 static Window get_systray_selection_owner( Display *display )
454 {
455     Window ret;
456
457     wine_tsx11_lock();
458     ret = XGetSelectionOwner( display, systray_atom );
459     wine_tsx11_unlock();
460     return ret;
461 }
462
463 static BOOL init_systray(void)
464 {
465     static BOOL init_done;
466     WNDCLASSEXW class;
467     Display *display;
468
469     if (root_window != DefaultRootWindow( gdi_display )) return FALSE;
470     if (init_done) return TRUE;
471
472     icon_cx = GetSystemMetrics( SM_CXSMICON ) + 2 * ICON_BORDER;
473     icon_cy = GetSystemMetrics( SM_CYSMICON ) + 2 * ICON_BORDER;
474
475     memset( &class, 0, sizeof(class) );
476     class.cbSize        = sizeof(class);
477     class.lpfnWndProc   = tray_icon_wndproc;
478     class.hIcon         = LoadIconW(0, (LPCWSTR)IDI_WINLOGO);
479     class.hCursor       = LoadCursorW( 0, (LPCWSTR)IDC_ARROW );
480     class.lpszClassName = icon_classname;
481     class.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
482
483     if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
484     {
485         ERR( "Could not register icon tray window class\n" );
486         return FALSE;
487     }
488
489     class.lpfnWndProc   = standalone_tray_wndproc;
490     class.hbrBackground = (HBRUSH)COLOR_WINDOW;
491     class.lpszClassName = tray_classname;
492     class.style         = CS_DBLCLKS;
493
494     if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
495     {
496         ERR( "Could not register standalone tray window class\n" );
497         return FALSE;
498     }
499
500     display = thread_init_display();
501     wine_tsx11_lock();
502     if (DefaultScreen( display ) == 0)
503         systray_atom = x11drv_atom(_NET_SYSTEM_TRAY_S0);
504     else
505     {
506         char systray_buffer[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */
507         sprintf( systray_buffer, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display ) );
508         systray_atom = XInternAtom( display, systray_buffer, False );
509     }
510     XSelectInput( display, root_window, StructureNotifyMask );
511     wine_tsx11_unlock();
512
513     init_done = TRUE;
514     return TRUE;
515 }
516
517 /* dock the given icon with the NETWM system tray */
518 static void dock_systray_icon( Display *display, struct tray_icon *icon, Window systray_window )
519 {
520     struct x11drv_win_data *data;
521     XEvent ev;
522     XSetWindowAttributes attr;
523
524     icon->window = CreateWindowW( icon_classname, NULL, WS_CLIPSIBLINGS | WS_POPUP,
525                                   CW_USEDEFAULT, CW_USEDEFAULT, icon_cx, icon_cy,
526                                   NULL, NULL, NULL, icon );
527     if (!icon->window) return;
528
529     if (!(data = X11DRV_get_win_data( icon->window )) &&
530         !(data = X11DRV_create_win_data( icon->window ))) return;
531
532     TRACE( "icon window %p/%lx managed %u\n", data->hwnd, data->whole_window, data->managed );
533
534     make_window_embedded( display, data );
535     create_tooltip( icon );
536     ShowWindow( icon->window, SW_SHOWNA );
537
538     /* send the docking request message */
539     ev.xclient.type = ClientMessage;
540     ev.xclient.window = systray_window;
541     ev.xclient.message_type = x11drv_atom( _NET_SYSTEM_TRAY_OPCODE );
542     ev.xclient.format = 32;
543     ev.xclient.data.l[0] = CurrentTime;
544     ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
545     ev.xclient.data.l[2] = data->whole_window;
546     ev.xclient.data.l[3] = 0;
547     ev.xclient.data.l[4] = 0;
548     wine_tsx11_lock();
549     XSendEvent( display, systray_window, False, NoEventMask, &ev );
550     attr.background_pixmap = ParentRelative;
551     attr.bit_gravity = ForgetGravity;
552     XChangeWindowAttributes( display, data->whole_window, CWBackPixmap | CWBitGravity, &attr );
553     XChangeWindowAttributes( display, data->client_window, CWBackPixmap | CWBitGravity, &attr );
554     wine_tsx11_unlock();
555 }
556
557 /* dock systray windows again with the new owner */
558 void change_systray_owner( Display *display, Window systray_window )
559 {
560     struct tray_icon *icon;
561
562     TRACE( "new owner %lx\n", systray_window );
563     LIST_FOR_EACH_ENTRY( icon, &icon_list, struct tray_icon, entry )
564     {
565         if (icon->display == -1) continue;
566         hide_icon( icon );
567         dock_systray_icon( display, icon, systray_window );
568     }
569 }
570
571 /* hide a tray icon */
572 static BOOL hide_icon( struct tray_icon *icon )
573 {
574     struct x11drv_win_data *data;
575
576     TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner );
577
578     if (!icon->window) return TRUE;  /* already hidden */
579
580     /* make sure we don't try to unmap it, it confuses some systray docks */
581     if ((data = X11DRV_get_win_data( icon->window )) && data->embedded) data->mapped = FALSE;
582
583     DestroyWindow(icon->window);
584     DestroyWindow(icon->tooltip);
585     icon->window = 0;
586     icon->tooltip = 0;
587     remove_from_standalone_tray( icon );
588     update_balloon( icon );
589     return TRUE;
590 }
591
592 /* make the icon visible */
593 static BOOL show_icon( struct tray_icon *icon )
594 {
595     Window systray_window;
596     Display *display = thread_init_display();
597
598     TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner );
599
600     if (icon->window) return TRUE;  /* already shown */
601
602     if ((systray_window = get_systray_selection_owner( display )))
603         dock_systray_icon( display, icon, systray_window );
604     else
605         add_to_standalone_tray( icon );
606
607     update_balloon( icon );
608     return TRUE;
609 }
610
611 /* Modifies an existing icon record */
612 static BOOL modify_icon( struct tray_icon *icon, NOTIFYICONDATAW *nid )
613 {
614     TRACE( "id=0x%x hwnd=%p flags=%x\n", nid->uID, nid->hWnd, nid->uFlags );
615
616     if (nid->uFlags & NIF_STATE)
617     {
618         icon->state = (icon->state & ~nid->dwStateMask) | (nid->dwState & nid->dwStateMask);
619     }
620
621     if (nid->uFlags & NIF_ICON)
622     {
623         if (icon->image) DestroyIcon(icon->image);
624         icon->image = CopyIcon(nid->hIcon);
625         if (icon->window)
626         {
627             if (icon->display != -1) InvalidateRect( icon->window, NULL, TRUE );
628             else
629             {
630                 struct x11drv_win_data *data = X11DRV_get_win_data( icon->window );
631                 if (data) XClearArea( gdi_display, data->client_window, 0, 0, 0, 0, True );
632             }
633         }
634     }
635
636     if (nid->uFlags & NIF_MESSAGE)
637     {
638         icon->callback_message = nid->uCallbackMessage;
639     }
640     if (nid->uFlags & NIF_TIP)
641     {
642         lstrcpynW(icon->tiptext, nid->szTip, sizeof(icon->tiptext)/sizeof(WCHAR));
643         if (icon->tooltip) update_tooltip_text(icon);
644     }
645     if (nid->uFlags & NIF_INFO && nid->cbSize >= NOTIFYICONDATAA_V2_SIZE)
646     {
647         lstrcpynW( icon->info_text, nid->szInfo, sizeof(icon->info_text)/sizeof(WCHAR) );
648         lstrcpynW( icon->info_title, nid->szInfoTitle, sizeof(icon->info_title)/sizeof(WCHAR) );
649         icon->info_flags = nid->dwInfoFlags;
650         icon->info_timeout = max(min(nid->u.uTimeout, BALLOON_SHOW_MAX_TIMEOUT), BALLOON_SHOW_MIN_TIMEOUT);
651         icon->info_icon = nid->hBalloonIcon;
652         update_balloon( icon );
653     }
654     if (icon->state & NIS_HIDDEN) hide_icon( icon );
655     else show_icon( icon );
656     return TRUE;
657 }
658
659 /* Adds a new icon record to the list */
660 static BOOL add_icon(NOTIFYICONDATAW *nid)
661 {
662     struct tray_icon  *icon;
663
664     WINE_TRACE("id=0x%x, hwnd=%p\n", nid->uID, nid->hWnd);
665
666     if ((icon = get_icon(nid->hWnd, nid->uID)))
667     {
668         WINE_WARN("duplicate tray icon add, buggy app?\n");
669         return FALSE;
670     }
671
672     if (!(icon = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*icon))))
673     {
674         WINE_ERR("out of memory\n");
675         return FALSE;
676     }
677
678     ZeroMemory(icon, sizeof(struct tray_icon));
679     icon->id     = nid->uID;
680     icon->owner  = nid->hWnd;
681     icon->display = -1;
682
683     list_add_tail(&icon_list, &icon->entry);
684
685     return modify_icon( icon, nid );
686 }
687
688 /* delete tray icon window and icon structure */
689 static BOOL delete_icon( struct tray_icon *icon )
690 {
691     hide_icon( icon );
692     list_remove( &icon->entry );
693     DestroyIcon( icon->image );
694     HeapFree( GetProcessHeap(), 0, icon );
695     return TRUE;
696 }
697
698
699 /***********************************************************************
700  *              wine_notify_icon   (X11DRV.@)
701  *
702  * Driver-side implementation of Shell_NotifyIcon.
703  */
704 int CDECL wine_notify_icon( DWORD msg, NOTIFYICONDATAW *data )
705 {
706     BOOL ret = FALSE;
707     struct tray_icon *icon;
708
709     switch (msg)
710     {
711     case NIM_ADD:
712         if (!init_systray()) return -1;  /* fall back to default handling */
713         ret = add_icon( data );
714         break;
715     case NIM_DELETE:
716         if ((icon = get_icon( data->hWnd, data->uID ))) ret = delete_icon( icon );
717         break;
718     case NIM_MODIFY:
719         if ((icon = get_icon( data->hWnd, data->uID ))) ret = modify_icon( icon, data );
720         break;
721     default:
722         FIXME( "unhandled tray message: %u\n", msg );
723         break;
724     }
725     return ret;
726 }