systray rewrite part 2

Mike Hearn mh at codeweavers.com
Tue Nov 30 11:23:33 CST 2004


Clearly, I need to figure out a system for generating patches. This time
with (hopefully) all the files in the patch.

- Add a new wineshell process, and put system tray handling in there
- Rewrite the shell32 systray handling to be out of process
- Support the freedesktop.org XEMBED protocol
-------------- next part --------------
? dlls/x11drv/wineclipsrv
Index: dlls/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/event.c,v
retrieving revision 1.37
diff -u -p -r1.37 event.c
--- dlls/x11drv/event.c	29 Sep 2004 21:11:28 -0000	1.37
+++ dlls/x11drv/event.c	30 Nov 2004 00:12:52 -0000
@@ -3,6 +3,7 @@
  *
  * Copyright 1993 Alexandre Julliard
  *	     1999 Noel Borthwick
+ *           2003 Mike Hearn
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -50,10 +51,14 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(event);
 WINE_DECLARE_DEBUG_CHANNEL(clipboard);
+WINE_DECLARE_DEBUG_CHANNEL(systray);
 
 /* X context to associate a hwnd to an X window */
 extern XContext winContext;
 
+extern Atom systray_selection;
+extern Window systray_window;
+
 extern BOOL ximInComposeMode;
 
 #define DndNotDnd       -1    /* OffiX drag&drop */
@@ -1248,7 +1253,57 @@ static void EVENT_DropURLs( HWND hWnd, X
 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
 {
   if (event->message_type != None && event->format == 32) {
-    if (event->message_type == x11drv_atom(WM_PROTOCOLS))
+    if (event->message_type == x11drv_atom(MANAGER)) {
+        if (event->data.l[1] == systray_selection) {
+            TRACE_(systray)("New NETWM systray manager detected, id=%ld\n", event->data.l[2]);
+            
+            /* NOTE: It turns out that the ability to detect when a
+             * new tray applet joins the desktop is not as helpful as
+             * you might think. In order to do something useful with
+             * it, we would need to be able to "store" icons unmapped
+             * as children of the root window while no tray applet is
+             * available.
+             *
+             * The basic problem is that tray icons are always
+             * destroyed when the applet is removed.  This is
+             * apparently an issue with X itself, which the upcoming
+             * XFIXES extension should hopefully address. The
+             * EggTrayIcon code which will be soon moving into GTK+
+             * doesn't attempt to handle this situation, so for now
+             * neither do we.
+             *
+             * This is theoretically fixable in Wine with enough work,
+             * we just have to modify the code in shell32/systray.c to
+             * save the image and recreate the window on
+             * demand. Exactly how the communication between the
+             * x11drv and shell32 takes place is left as an excercise
+             * for the reader.
+             *
+             *                         -mike (3rd August 2003)
+             */
+        }
+    } else if (event->message_type == x11drv_atom(_XEMBED)) {
+        char *opcode;
+        
+        switch (event->data.l[1]) {
+            case 0: opcode = "XEMBED_EMBEDDED_NOTIFY"; break;
+            case 1: opcode = "XEMBED_WINDOW_ACTIVATE"; break;
+            case 2: opcode = "XEMBED_WINDOW_DEACTIVATE"; break;
+            case 3: opcode = "XEMBED_REQUEST_FOCUS"; break;
+            case 4: opcode = "XEMBED_FOCUS_IN"; break;
+            case 5: opcode = "XEMBED_FOCUS_OUT"; break;
+            case 6: opcode = "XEMBED_FOCUS_NEXT"; break;
+            case 7: opcode = "XEMEBD_FOCUS_PREV"; break;
+            case 10: opcode = "XEMBED_MODALITY_ON"; break;
+            case 11: opcode = "XEMBED_MODALITY_OFF"; break;
+            case 12: opcode = "XEMBED_REGISTER_ACCELERATOR"; break;
+            case 13: opcode = "XEMBED_UNREGISTER_ACCELERATOR"; break;
+            case 14: opcode = "XEMBED_ACTIVATE_ACCELERATOR"; break;
+            default: opcode = "[Unknown opcode]"; break;
+        }
+        TRACE_(systray)("XEmbed message, opcode is %s : %ld\n", opcode, event->data.l[1]);
+        /* we currently don't handle these messages */
+    } else if (event->message_type == x11drv_atom(WM_PROTOCOLS))
         handle_wm_protocols_message( hWnd, event );
     else if (event->message_type == x11drv_atom(DndProtocol))
     {
Index: dlls/x11drv/window.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/window.c,v
retrieving revision 1.81
diff -u -p -r1.81 window.c
--- dlls/x11drv/window.c	22 Sep 2004 19:14:19 -0000	1.81
+++ dlls/x11drv/window.c	30 Nov 2004 00:12:54 -0000
@@ -4,6 +4,7 @@
  * Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard
  * Copyright 1993 David Metcalfe
  * Copyright 1995, 1996 Alex Korobka
+ * Copyright 2003 Mike Hearn
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -24,6 +25,7 @@
 
 #include <stdarg.h>
 #include <stdlib.h>
+#include <stdio.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
@@ -47,6 +49,7 @@
 #include "mwm.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
+WINE_DECLARE_DEBUG_CHANNEL(systray);
 
 extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP );
 
@@ -70,6 +73,7 @@ static const char * const atom_names[NB_
     "WM_PROTOCOLS",
     "WM_DELETE_WINDOW",
     "WM_TAKE_FOCUS",
+    "MANAGER",
     "KWM_DOCKWINDOW",
     "DndProtocol",
     "DndSelection",
@@ -79,6 +83,9 @@ static const char * const atom_names[NB_
     "_NET_WM_PID",
     "_NET_WM_PING",
     "_NET_WM_NAME",
+    "_XEMBED_INFO",
+    "_XEMBED",
+    "_NET_SYSTEM_TRAY_OPCODE",    
     "_NET_WM_WINDOW_TYPE",
     "_NET_WM_WINDOW_TYPE_UTILITY",
     "XdndAware",
@@ -104,6 +111,14 @@ static const char * const atom_names[NB_
     "text/richtext"
 };
 
+/* for XDG systray icons */
+Atom systray_selection;
+Window systray_window;
+#define SYSTEM_TRAY_REQUEST_DOCK    0
+#define SYSTEM_TRAY_BEGIN_MESSAGE   1
+#define SYSTEM_TRAY_CANCEL_MESSAGE  2
+
+
 static LPCSTR whole_window_atom;
 static LPCSTR client_window_atom;
 static LPCSTR icon_window_atom;
@@ -382,11 +397,72 @@ static void set_size_hints( Display *dis
             size_hints->min_height = size_hints->max_height;
             size_hints->flags |= PMinSize | PMaxSize;
         }
+        if (win->dwExStyle & WS_EX_TRAYWINDOW)
+        {
+            /* force the window to be the correct width */
+            size_hints->min_width = GetSystemMetrics(SM_CXSMICON) + 5; /* give some padding to make icons not bunched up */
+        }
+
         XSetWMNormalHints( display, data->whole_window, size_hints );
         XFree( size_hints );
     }
 }
 
+/***********************************************************************
+ *              systray_dock_window
+ *
+ * Docks the given X window with the NETWM system tray.
+ */
+static BOOL systray_dock_window( HWND hwnd, Display *display )
+{
+    WND *win = WIN_GetPtr(hwnd);
+    struct x11drv_win_data *data = win->pDriverData;
+    XEvent ev;
+    unsigned long info[2];
+    LONG exstyle;
+
+    /* is the window a tray window? */
+    if (IsWindowUnicode(hwnd))
+    {
+        exstyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
+    }
+    else
+    {
+        exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+    }
+    
+    if ( !(exstyle & WS_EX_TRAYWINDOW) )
+    {
+        WIN_ReleasePtr(win);
+        return TRUE;
+    }
+  
+    TRACE_(systray)("Docking tray icon %p\n", hwnd);
+
+    /* set XEMBED protocol data on the window */
+    info[0] = 0; /* protocol version */
+    info[1] = 0; /* mapped = true */
+    XChangeProperty(display, data->whole_window, x11drv_atom(_XEMBED_INFO),
+                    x11drv_atom(_XEMBED_INFO), 32, PropModeReplace,
+                    (unsigned char*)info, 2);
+
+    /* send the docking request message */
+    ZeroMemory(&ev, sizeof(ev));
+    ev.xclient.type = ClientMessage;
+    ev.xclient.window = systray_window;
+    ev.xclient.message_type = x11drv_atom(_NET_SYSTEM_TRAY_OPCODE);
+    ev.xclient.format = 32;
+    ev.xclient.data.l[0] = CurrentTime;
+    ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
+    ev.xclient.data.l[2] = data->whole_window;
+    XSendEvent(display, systray_window, False, NoEventMask, &ev);
+    XSync(display, False);
+
+    WIN_ReleasePtr(win);
+    return TRUE;
+}
+
+
 
 /***********************************************************************
  *              X11DRV_set_wm_hints
@@ -436,7 +512,7 @@ void X11DRV_set_wm_hints( Display *displ
     set_size_hints( display, win );
 
     /* systray properties (KDE only for now) */
-    if (win->dwExStyle & WS_EX_TRAYWINDOW)
+    if ((win->dwExStyle & WS_EX_TRAYWINDOW) && (systray_window == None))
     {
         int val = 1;
         XChangeProperty( display, data->whole_window, x11drv_atom(KWM_DOCKWINDOW),
@@ -789,10 +865,19 @@ void X11DRV_set_window_rectangles( HWND 
 static void create_desktop( Display *display, WND *wndPtr )
 {
     X11DRV_WND_DATA *data = wndPtr->pDriverData;
-
+    char *systray_buffer;
+    
     wine_tsx11_lock();
     winContext = XUniqueContext();
     XInternAtoms( display, (char **)atom_names, NB_XATOMS - FIRST_XATOM, False, X11DRV_Atoms );
+
+    /* we can't intern this with the rest as it depends on the screen we are connecting to */
+    systray_buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char)*20);
+    sprintf(systray_buffer, "_NET_SYSTEM_TRAY_S%d", DefaultScreen(display));
+    systray_selection = XInternAtom(display, systray_buffer, False);
+    HeapFree(GetProcessHeap(), 0, systray_buffer);
+
+    
     wine_tsx11_unlock();
 
     whole_window_atom  = MAKEINTATOMA( GlobalAddAtomA( "__wine_x11_whole_window" ));
@@ -1179,6 +1264,14 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE
                       newPos.right, newPos.bottom, swFlag );
     }
 
+    /* if it's a tray window, dock it */
+    if (wndPtr->dwExStyle & WS_EX_TRAYWINDOW)
+    {
+        /* get the tray window if present */
+        systray_window = XGetSelectionOwner(display, systray_selection);
+        if (systray_window != None) systray_dock_window(hwnd, display);
+    }
+    
     WIN_ReleaseWndPtr( wndPtr );
     return TRUE;
 
Index: dlls/x11drv/x11drv.h
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/x11drv.h,v
retrieving revision 1.36
diff -u -p -r1.36 x11drv.h
--- dlls/x11drv/x11drv.h	16 Sep 2004 19:10:14 -0000	1.36
+++ dlls/x11drv/x11drv.h	30 Nov 2004 00:12:55 -0000
@@ -416,6 +416,7 @@ enum x11drv_atoms
     XATOM_WM_PROTOCOLS,
     XATOM_WM_DELETE_WINDOW,
     XATOM_WM_TAKE_FOCUS,
+    XATOM_MANAGER,
     XATOM_KWM_DOCKWINDOW,
     XATOM_DndProtocol,
     XATOM_DndSelection,
@@ -425,6 +426,9 @@ enum x11drv_atoms
     XATOM__NET_WM_PID,
     XATOM__NET_WM_PING,
     XATOM__NET_WM_NAME,
+    XATOM__XEMBED_INFO,
+    XATOM__XEMBED,
+    XATOM__NET_SYSTEM_TRAY_OPCODE,     
     XATOM__NET_WM_WINDOW_TYPE,
     XATOM__NET_WM_WINDOW_TYPE_UTILITY,
     XATOM_XdndAware,
Index: dlls/shell32/systray.c
===================================================================
RCS file: /home/wine/wine/dlls/shell32/systray.c,v
retrieving revision 1.27
diff -u -p -r1.27 systray.c
--- dlls/shell32/systray.c	9 Aug 2004 19:46:47 -0000	1.27
+++ dlls/shell32/systray.c	30 Nov 2004 00:13:08 -0000
@@ -1,11 +1,8 @@
 /*
- *	Systray
+ * Systray handling
  *
- *	Copyright 1999 Kai Morich	<kai.morich at bigfoot.de>
- *
- *  Manage the systray window. That it actually appears in the docking
- *  area of KDE is handled in dlls/x11drv/window.c,
- *  X11DRV_set_wm_hints using KWM_DOCKWINDOW.
+ * Copyright 1999 Kai Morich	<kai.morich at bigfoot.de>
+ * Copyright 2004 Mike Hearn, for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -23,336 +20,112 @@
  */
 
 #include "config.h"
+#include <wine/debug.h>
 
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <stdarg.h>
-#include <string.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "winnls.h"
-#include "wingdi.h"
-#include "winuser.h"
-#include "shlobj.h"
-#include "shellapi.h"
-#include "shell32_main.h"
-#include "commctrl.h"
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(shell);
-
-typedef struct SystrayItem {
-  HWND                  hWnd;
-  HWND                  hWndToolTip;
-  NOTIFYICONDATAA       notifyIcon;
-  struct SystrayItem    *nextTrayItem;
-} SystrayItem;
-
-static SystrayItem *systray=NULL;
-static int firstSystray=TRUE; /* defer creation of window class until first systray item is created */
-
-static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid);
-
-
-#define ICON_SIZE GetSystemMetrics(SM_CXSMICON)
-/* space around icon (forces icon to center of KDE systray area) */
-#define ICON_BORDER  4
+#include <windef.h>
+#include <winbase.h>
+#include <winuser.h>
+#include <wine/winuser16.h>
+#include <user.h>
+#include <shellapi.h>
 
+WINE_DEFAULT_DEBUG_CHANNEL(systray);
 
+const static WCHAR classname[] = /* Shell_TrayWnd */ {'S','h','e','l','l','_','T','r','a','y','W','n','d','\0'};
 
-static BOOL SYSTRAY_ItemIsEqual(PNOTIFYICONDATAA pnid1, PNOTIFYICONDATAA pnid2)
+struct protocol
 {
-  if (pnid1->hWnd != pnid2->hWnd) return FALSE;
-  if (pnid1->uID  != pnid2->uID)  return FALSE;
-  return TRUE;
-}
+    DWORD cookie;
+    DWORD code;
+    BYTE  data[1];
+};
 
-static LRESULT CALLBACK SYSTRAY_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+static void send_to_tray(DWORD message, void *data, BOOL unicode)
 {
-  HDC hdc;
-  PAINTSTRUCT ps;
-
-  switch (message) {
-  case WM_PAINT:
-  {
-    RECT rc;
-    SystrayItem  *ptrayItem = systray;
-
-    while (ptrayItem) {
-      if (ptrayItem->hWnd==hWnd) {
-	if (ptrayItem->notifyIcon.hIcon) {
-	  hdc = BeginPaint(hWnd, &ps);
-	  GetClientRect(hWnd, &rc);
-	  if (!DrawIconEx(hdc, rc.left+ICON_BORDER, rc.top+ICON_BORDER, ptrayItem->notifyIcon.hIcon,
-			  ICON_SIZE, ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL)) {
-	    ERR("Paint(SystrayWindow %p) failed -> removing SystrayItem %p\n", hWnd, ptrayItem);
-	    SYSTRAY_Delete(&ptrayItem->notifyIcon);
-	  }
-	}
-	break;
-      }
-      ptrayItem = ptrayItem->nextTrayItem;
-    }
-    EndPaint(hWnd, &ps);
-  }
-  break;
-
-  case WM_MOUSEMOVE:
-  case WM_LBUTTONDOWN:
-  case WM_LBUTTONUP:
-  case WM_RBUTTONDOWN:
-  case WM_RBUTTONUP:
-  case WM_MBUTTONDOWN:
-  case WM_MBUTTONUP:
-  {
-    MSG msg;
-    SystrayItem *ptrayItem = systray;
-
-    while ( ptrayItem ) {
-      if (ptrayItem->hWnd == hWnd) {
-        msg.hwnd=hWnd;
-        msg.message=message;
-        msg.wParam=wParam;
-        msg.lParam=lParam;
-        msg.time = GetMessageTime ();
-        msg.pt.x = LOWORD(GetMessagePos ());
-        msg.pt.y = HIWORD(GetMessagePos ());
-
-        SendMessageA(ptrayItem->hWndToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
-      }
-      ptrayItem = ptrayItem->nextTrayItem;
-    }
-  }
-  /* fall through */
+    HWND             tray;
+    COPYDATASTRUCT   transfer;
+    DWORD            size;
+    struct protocol *protocol;
+    CURSORICONINFO  *icon;
+    DWORD            iconsize = 0;
+    NOTIFYICONDATAW *nid;
 
-  case WM_LBUTTONDBLCLK:
-  case WM_RBUTTONDBLCLK:
-  case WM_MBUTTONDBLCLK:
-  {
-    SystrayItem *ptrayItem = systray;
-
-    while (ptrayItem) {
-      if (ptrayItem->hWnd == hWnd) {
-	if (ptrayItem->notifyIcon.hWnd && ptrayItem->notifyIcon.uCallbackMessage) {
-          if (!PostMessageA(ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.uCallbackMessage,
-                            (WPARAM)ptrayItem->notifyIcon.uID, (LPARAM)message)) {
-	      ERR("PostMessage(SystrayWindow %p) failed -> removing SystrayItem %p\n", hWnd, ptrayItem);
-	      SYSTRAY_Delete(&ptrayItem->notifyIcon);
-	    }
-        }
-	break;
-      }
-      ptrayItem = ptrayItem->nextTrayItem;
-    }
-  }
-  break;
+    TRACE("message=%ld\n", message);
 
-  default:
-    return (DefWindowProcA(hWnd, message, wParam, lParam));
-  }
-  return (0);
-
-}
+    size = unicode ? sizeof(NOTIFYICONDATAW) : sizeof(NOTIFYICONDATAA);
 
-
-BOOL SYSTRAY_RegisterClass(void)
-{
-  WNDCLASSA  wc;
-
-  wc.style         = CS_SAVEBITS|CS_DBLCLKS;
-  wc.lpfnWndProc   = (WNDPROC)SYSTRAY_WndProc;
-  wc.cbClsExtra    = 0;
-  wc.cbWndExtra    = 0;
-  wc.hInstance     = 0;
-  wc.hIcon         = 0;
-  wc.hCursor       = LoadCursorA(0, (LPSTR)IDC_ARROW);
-  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
-  wc.lpszMenuName  = NULL;
-  wc.lpszClassName = "WineSystray";
-
-  if (!RegisterClassA(&wc)) {
-    ERR("RegisterClass(WineSystray) failed\n");
-    return FALSE;
-  }
-  return TRUE;
-}
-
-
-BOOL SYSTRAY_ItemInit(SystrayItem *ptrayItem)
-{
-  RECT rect;
-
-  /* Register the class if this is our first tray item. */
-  if ( firstSystray ) {
-    firstSystray = FALSE;
-    if ( !SYSTRAY_RegisterClass() ) {
-      ERR( "RegisterClass(WineSystray) failed\n" );
-      return FALSE;
+    nid = (NOTIFYICONDATAW *) data;
+    if (nid->uFlags & NIF_ICON)
+    {
+        iconsize = GlobalSize16(HICON_16(nid->hIcon));
+        TRACE("iconsize = %ld, icon = %p\n", iconsize, nid->hIcon);
     }
-  }
-
-  /* Initialize the window size. */
-  rect.left   = 0;
-  rect.top    = 0;
-  rect.right  = ICON_SIZE+2*ICON_BORDER;
-  rect.bottom = ICON_SIZE+2*ICON_BORDER;
-
-  ZeroMemory( ptrayItem, sizeof(SystrayItem) );
-  /* Create tray window for icon. */
-  ptrayItem->hWnd = CreateWindowExA( WS_EX_TRAYWINDOW,
-                                "WineSystray", "Wine-Systray",
-                                WS_VISIBLE,
-                                CW_USEDEFAULT, CW_USEDEFAULT,
-                                rect.right-rect.left, rect.bottom-rect.top,
-                                0, 0, 0, 0 );
-  if ( !ptrayItem->hWnd ) {
-    ERR( "CreateWindow(WineSystray) failed\n" );
-    return FALSE;
-  }
-
-  /* Create tooltip for icon. */
-  ptrayItem->hWndToolTip = CreateWindowA( TOOLTIPS_CLASSA,NULL,TTS_ALWAYSTIP,
-                                     CW_USEDEFAULT, CW_USEDEFAULT,
-                                     CW_USEDEFAULT, CW_USEDEFAULT,
-                                     ptrayItem->hWnd, 0, 0, 0 );
-  if ( !ptrayItem->hWndToolTip ) {
-    ERR( "CreateWindow(TOOLTIP) failed\n" );
-    return FALSE;
-  }
-  return TRUE;
-}
 
+    protocol = HeapAlloc(GetProcessHeap(), 0, sizeof(struct protocol) - sizeof(DWORD) + size + iconsize);
+    protocol->cookie = 1;
+    protocol->code   = message;
+    memcpy(&protocol->data, data, size);
+    ((NOTIFYICONDATAW *)&protocol->data)->hIcon = (HICON) -1;
 
-static void SYSTRAY_ItemTerm(SystrayItem *ptrayItem)
-{
-  if(ptrayItem->notifyIcon.hIcon)
-     DestroyIcon(ptrayItem->notifyIcon.hIcon);
-  if(ptrayItem->hWndToolTip)
-      DestroyWindow(ptrayItem->hWndToolTip);
-  if(ptrayItem->hWnd)
-    DestroyWindow(ptrayItem->hWnd);
-  return;
-}
+    if (nid->uFlags & NIF_ICON)
+    {
+        /* we currently don't just pass the HICON directly as Wine doesn't
+           implement shared images, so marshal the icon data onto the end
+           of the request and set the HICON to be -1  */
 
+        icon = GlobalLock16(HICON_16(nid->hIcon));
+        memcpy(&protocol->data[0] + size, icon, iconsize);
+        GlobalUnlock16(HICON_16(nid->hIcon));
 
-void SYSTRAY_ItemSetMessage(SystrayItem *ptrayItem, UINT uCallbackMessage)
-{
-  ptrayItem->notifyIcon.uCallbackMessage = uCallbackMessage;
-}
-
-
-void SYSTRAY_ItemSetIcon(SystrayItem *ptrayItem, HICON hIcon)
-{
-  if(ptrayItem->notifyIcon.hIcon)
-    DestroyIcon(ptrayItem->notifyIcon.hIcon);
-  ptrayItem->notifyIcon.hIcon = CopyIcon(hIcon);
-  InvalidateRect(ptrayItem->hWnd, NULL, TRUE);
-}
-
-
-void SYSTRAY_ItemSetTip(SystrayItem *ptrayItem, CHAR* szTip, int modify)
-{
-  TTTOOLINFOA ti;
-
-  strncpy(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip));
-  ptrayItem->notifyIcon.szTip[sizeof(ptrayItem->notifyIcon.szTip)-1]=0;
-
-  ti.cbSize = sizeof(TTTOOLINFOA);
-  ti.uFlags = 0;
-  ti.hwnd = ptrayItem->hWnd;
-  ti.hinst = 0;
-  ti.uId = 0;
-  ti.lpszText = ptrayItem->notifyIcon.szTip;
-  ti.rect.left   = 0;
-  ti.rect.top    = 0;
-  ti.rect.right  = ICON_SIZE+2*ICON_BORDER;
-  ti.rect.bottom = ICON_SIZE+2*ICON_BORDER;
-
-  if(modify)
-    SendMessageA(ptrayItem->hWndToolTip, TTM_UPDATETIPTEXTA, 0, (LPARAM)&ti);
-  else
-    SendMessageA(ptrayItem->hWndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
-}
-
-
-static BOOL SYSTRAY_Add(PNOTIFYICONDATAA pnid)
-{
-  SystrayItem **ptrayItem = &systray;
-
-  /* Find last element. */
-  while( *ptrayItem ) {
-    if ( SYSTRAY_ItemIsEqual(pnid, &(*ptrayItem)->notifyIcon) )
-      return FALSE;
-    ptrayItem = &((*ptrayItem)->nextTrayItem);
-  }
-  /* Allocate SystrayItem for element and add to end of list. */
-  (*ptrayItem) = HeapAlloc(GetProcessHeap(),0,sizeof(SystrayItem));
-
-  /* Initialize and set data for the tray element. */
-  SYSTRAY_ItemInit( (*ptrayItem) );
-  (*ptrayItem)->notifyIcon.uID = pnid->uID; /* only needed for callback message */
-  (*ptrayItem)->notifyIcon.hWnd = pnid->hWnd; /* only needed for callback message */
-  SYSTRAY_ItemSetIcon   (*ptrayItem, (pnid->uFlags&NIF_ICON)   ?pnid->hIcon           :0);
-  SYSTRAY_ItemSetMessage(*ptrayItem, (pnid->uFlags&NIF_MESSAGE)?pnid->uCallbackMessage:0);
-  SYSTRAY_ItemSetTip    (*ptrayItem, (pnid->uFlags&NIF_TIP)    ?pnid->szTip           :"", FALSE);
-
-  TRACE("%p: %p %s\n",  (*ptrayItem), (*ptrayItem)->notifyIcon.hWnd,
-                                          (*ptrayItem)->notifyIcon.szTip);
-  return TRUE;
-}
-
+    }
 
-static BOOL SYSTRAY_Modify(PNOTIFYICONDATAA pnid)
-{
-  SystrayItem *ptrayItem = systray;
+    transfer.dwData = 1;
+    transfer.cbData = (sizeof(DWORD)*2) + size + iconsize;
+    transfer.lpData = protocol;
 
-  while ( ptrayItem ) {
-    if ( SYSTRAY_ItemIsEqual(pnid, &ptrayItem->notifyIcon) ) {
-      if (pnid->uFlags & NIF_ICON)
-        SYSTRAY_ItemSetIcon(ptrayItem, pnid->hIcon);
-      if (pnid->uFlags & NIF_MESSAGE)
-        SYSTRAY_ItemSetMessage(ptrayItem, pnid->uCallbackMessage);
-      if (pnid->uFlags & NIF_TIP)
-        SYSTRAY_ItemSetTip(ptrayItem, pnid->szTip, TRUE);
+    tray = FindWindowExW(0, NULL, classname, NULL);
 
-      TRACE("%p: %p %s\n", ptrayItem, ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.szTip);
-      return TRUE;
-    }
-    ptrayItem = ptrayItem->nextTrayItem;
-  }
-  return FALSE; /* not found */
-}
+    if (!tray)
+    {
+        STARTUPINFOA        sinfo;
+        int                 timeout = 5;
 
+        TRACE("No tray window found, starting wineshell\n");
 
-static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid)
-{
-  SystrayItem **ptrayItem = &systray;
+        ZeroMemory(&sinfo, sizeof(STARTUPINFOA));
+        sinfo.cb = sizeof(STARTUPINFOA);
 
-  while (*ptrayItem) {
-    if (SYSTRAY_ItemIsEqual(pnid, &(*ptrayItem)->notifyIcon)) {
-      SystrayItem *next = (*ptrayItem)->nextTrayItem;
-      TRACE("%p: %p %s\n", *ptrayItem, (*ptrayItem)->notifyIcon.hWnd, (*ptrayItem)->notifyIcon.szTip);
-      SYSTRAY_ItemTerm(*ptrayItem);
+        if (CreateProcessA(NULL, "wineshell", NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, NULL) == 0)
+        {
+            ERR("Could not start wineshell, error 0x%lx\n", GetLastError());
+            return;
+        }
 
-      HeapFree(GetProcessHeap(),0,*ptrayItem);
-      *ptrayItem = next;
+        /* wait for at least 5 seconds for the wineshell to start, checking once a second */
+        do
+        {
+            tray = FindWindowExW(0, NULL, classname, NULL);
+            if (tray) break;
+
+            timeout--;
+            Sleep(1000);
+        } while (timeout);
+
+        if (!tray)
+        {
+            ERR("Timeout waiting for wineshell to start, ignoring tray request\n");
+            return;
+        }
 
-      return TRUE;
+        TRACE("synced with wineshell OK\n");
     }
-    ptrayItem = &((*ptrayItem)->nextTrayItem);
-  }
 
-  return FALSE; /* not found */
-}
+    TRACE("sending %s opcode %ld to tray window %p\n", unicode ? "unicode" : "ansi", message, tray);
 
-/*************************************************************************
- *
- */
-BOOL SYSTRAY_Init(void)
-{
-  return TRUE;
+    if (unicode)
+        SendMessageW(tray, WM_COPYDATA, (WPARAM)((NOTIFYICONDATAW *)data)->hWnd, (LPARAM)&transfer);
+    else
+        SendMessageA(tray, WM_COPYDATA, (WPARAM)((NOTIFYICONDATAW *)data)->hWnd, (LPARAM)&transfer);
 }
 
 /*************************************************************************
@@ -361,37 +134,15 @@ BOOL SYSTRAY_Init(void)
  */
 BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid )
 {
-  BOOL flag=FALSE;
-  TRACE("enter %p %d %ld\n", pnid->hWnd, pnid->uID, dwMessage);
-  switch(dwMessage) {
-  case NIM_ADD:
-    flag = SYSTRAY_Add(pnid);
-    break;
-  case NIM_MODIFY:
-    flag = SYSTRAY_Modify(pnid);
-    break;
-  case NIM_DELETE:
-    flag = SYSTRAY_Delete(pnid);
-    break;
-  }
-  TRACE("leave %p %d %ld=%d\n", pnid->hWnd, pnid->uID, dwMessage, flag);
-  return flag;
+    send_to_tray(dwMessage, pnid, FALSE);
+    return TRUE;
 }
 
 /*************************************************************************
  * Shell_NotifyIconW			[SHELL32.298]
  */
-BOOL WINAPI Shell_NotifyIconW (DWORD dwMessage, PNOTIFYICONDATAW pnid )
+BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW pnid )
 {
-	BOOL ret;
-
-	PNOTIFYICONDATAA p = HeapAlloc(GetProcessHeap(),0,sizeof(NOTIFYICONDATAA));
-	memcpy(p, pnid, sizeof(NOTIFYICONDATAA));
-        WideCharToMultiByte( CP_ACP, 0, pnid->szTip, -1, p->szTip, sizeof(p->szTip), NULL, NULL );
-        p->szTip[sizeof(p->szTip)-1] = 0;
-
-	ret = Shell_NotifyIconA(dwMessage, p );
-
-	HeapFree(GetProcessHeap(),0,p);
-	return ret;
+    send_to_tray(dwMessage, pnid, TRUE);
+    return TRUE;
 }
--- /dev/null	2004-11-29 17:35:34.335853480 +0000
+++ programs/wineshell/Makefile.in	2004-11-27 22:12:01.000000000 +0000
@@ -0,0 +1,16 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = wineshell.exe
+APPMODE   = -mconsole
+IMPORTS   = advapi32 kernel32 user32 gdi32
+
+C_SRCS = \
+	wineshell.c \
+	systray.c
+
+
+ at MAKE_PROG_RULES@
+
+### Dependencies:
--- /dev/null	2004-11-29 17:35:34.335853480 +0000
+++ programs/wineshell/wineshell.c	2004-11-29 23:59:37.613375672 +0000
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2004 Mike Hearn, for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This program is a general purpose bridge between the Windows
+ * environment and native. Currently it deals with system tray
+ * windows, in future it may take on other functions that the standard
+ * windows shell (explorer) deals with, for instance responding to DDE
+ * requests and syncing the wallpaper with native.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <windows.h>
+#include <wine/debug.h>
+
+#include <wineshell.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(wineshell);
+
+unsigned int shell_refs = 0;
+
+void *xmalloc(unsigned int size)
+{
+    void *p = HeapAlloc(GetProcessHeap(), 0, size);
+    
+    if (!p)
+    {
+        fprintf(stderr, "wineshell: virtual memory exhausted\n");
+        exit(1);
+    }
+
+    ZeroMemory(p, size);
+    
+    return p;
+}
+
+int main(int argc, char *argv[])
+{
+    initialize_systray();
+
+    while (TRUE)
+    {
+        const int timeout = 5;
+        MSG message;
+        DWORD res;
+
+        res = MsgWaitForMultipleObjectsEx(0, NULL, shell_refs ? INFINITE : timeout * 1000,
+                                          QS_ALLINPUT, MWMO_WAITALL);
+        if (res == WAIT_TIMEOUT) break;
+
+        res = PeekMessage(&message, 0, 0, 0, PM_REMOVE);
+        if (!res) continue;
+
+        if (message.message == WM_QUIT)
+        {
+            WINE_FIXME("Somebody sent the shell a WM_QUIT message, should we reboot?");
+
+            /* Sending the tray window a WM_QUIT message is actually a
+             * tip given by some programming websites as a way of
+             * forcing a reboot! let's delay implementing this hack
+             * until we find a program that really needs it. for now
+             * just bail out.
+             */
+            
+            break;
+        }
+
+        TranslateMessage(&message);
+        DispatchMessage(&message);
+    }
+
+    shutdown_systray();
+    
+    return 0;
+}
--- /dev/null	2004-11-29 17:35:34.335853480 +0000
+++ programs/wineshell/wineshell.h	2004-11-29 23:21:15.000000000 +0000
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2004 Mike Hearn, for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+void initialize_systray();
+void shutdown_systray();
+void *xmalloc(unsigned int size);
+
+/* when this drops to zero, a few seconds later the shell will shut down */
+extern unsigned int shell_refs;       
--- /dev/null	2004-11-29 17:35:34.335853480 +0000
+++ programs/wineshell/systray.c	2004-11-30 00:15:16.543636576 +0000
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2004 Mike Hearn, for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* There are two types of window involved here. The first is the
+ * listener window. This is like the taskbar in Windows. It doesn't
+ * ever appear on-screen in our implementation, instead we create
+ * individual mini "adaptor" windows which are docked by the native
+ * systray host.
+ *
+ * In future for those who don't have a systray we could make the
+ * listener window more clever so it can draw itself like the Windows
+ * tray area does (with a clock and stuff).
+ */
+
+#include <assert.h>
+
+#define UNICODE
+#define _WIN32_IE 0x500
+#include <windows.h>
+#include <user.h>
+
+#include <wine/debug.h>
+#include <wine/winuser16.h>
+#include <wine/list.h>
+
+#include "wineshell.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(systray);
+
+const static WCHAR adaptor_classname[] = /* Adaptor */ {'A','d','a','p','t','o','r',0};
+
+struct request {
+    NOTIFYICONDATAA ansi;
+    NOTIFYICONDATAW wide;
+    BOOL            unicode;
+    BYTE           *copydata;
+
+    /* some convenience fields */
+    HWND            owner;
+    UINT            id;
+    DWORD           flags;
+};
+
+/* tray state */
+struct tray
+{
+    HWND           window;
+    struct list    icons;
+};
+
+/* an individual systray icon, unpacked from the NOTIFYICONDATA and always in unicode */
+struct icon
+{
+    struct list    entry;
+    HICON          image;    /* the image to render */
+    HWND           owner;    /* the HWND passed in to the Shell_NotifyIcon call */
+    HWND           window;   /* the adaptor window */
+    UINT           id;       /* the unique id given by the app */
+    UINT           callback_message;
+};
+
+static struct tray tray;
+
+
+
+/* adaptor code */
+
+#define ICON_SIZE GetSystemMetrics(SM_CXSMICON)
+/* space around icon (forces icon to center of KDE systray area) */
+#define ICON_BORDER  4
+
+static LRESULT WINAPI adaptor_wndproc(HWND window, UINT msg,
+                                      WPARAM wparam, LPARAM lparam)
+{
+    struct icon *icon = NULL;
+    
+    WINE_TRACE("hwnd=%p, msg=0x%x\n", window, msg);
+
+    icon = (struct icon *) GetWindowLong(window, GWL_USERDATA);
+    
+    if (!icon)  /* not initialized yet */
+        return DefWindowProc(window, msg, wparam, lparam);
+    
+    switch (msg)
+    {
+        case WM_MOVE:
+        case WM_SIZE:
+        case WM_WINDOWPOSCHANGING:
+            /* ignore */
+            return 0;
+            
+        case WM_PAINT:
+        {
+            RECT rc;
+            int top;
+            PAINTSTRUCT  ps;
+            HDC          hdc;
+
+            WINE_TRACE("painting\n");
+            
+            hdc = BeginPaint(window, &ps);
+            GetClientRect(window, &rc);
+            
+            /* calculate top so we can deal with arbitrary sized trays */
+            top = ((rc.bottom-rc.top)/2) - ((ICON_SIZE)/2);
+            
+            DrawIconEx(hdc, (ICON_BORDER/2), top, icon->image,
+                       ICON_SIZE, ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL);
+            
+            EndPaint(window, &ps);
+            break;
+	}
+
+
+        case WM_WINDOWPOSCHANGED:
+        {
+            ShowWindow(window, SW_SHOW);
+            return 0;
+        }
+
+
+        case WM_MOUSEMOVE:
+        case WM_LBUTTONDOWN:
+        case WM_LBUTTONUP:
+        case WM_RBUTTONDOWN:
+        case WM_RBUTTONUP:
+        case WM_MBUTTONDOWN:
+        case WM_MBUTTONUP:
+        case WM_LBUTTONDBLCLK:
+        case WM_RBUTTONDBLCLK:
+        case WM_MBUTTONDBLCLK:
+        {
+            /* notify the owner hwnd of the message */
+            WINE_TRACE("relaying 0x%x\n", msg);
+            PostMessage(icon->owner, icon->callback_message, (WPARAM) icon->id, (LPARAM) msg);
+            return 0;
+        }
+    }
+    
+    return DefWindowProc(window, msg, wparam, lparam);
+}
+
+
+/* listener code */
+
+static struct icon *get_icon(HWND owner, UINT id)
+{
+    struct list     *cursor;
+    
+    /* search for the icon */
+    LIST_FOR_EACH( cursor, &tray.icons )
+    {
+        struct icon *this = LIST_ENTRY(cursor, struct icon, entry);
+        
+        if ((this->id == id) && (this->owner = owner)) return this;
+    }
+
+    return NULL;
+}
+
+static void modify_icon(struct request *req)
+{
+    struct icon    *icon;
+    HICON16         hicon;
+    BYTE           *iconbits;
+    CURSORICONINFO *cii;
+    DWORD           ciisize;
+    
+    WINE_TRACE("id=0x%x, hwnd=%p\n", req->id, req->owner);
+    
+    /* demarshal the request from the NID */
+    icon = get_icon(req->owner, req->id);
+    if (!icon)
+    {
+        WINE_WARN("Invalid icon ID (0x%x) for HWND %p\n", req->id, req->owner);
+        return;
+    }
+
+    if ((req->unicode ? req->wide.uFlags : req->ansi.uFlags) & NIF_ICON)
+    {
+        WINE_TRACE("icon is specified\n");
+        
+        /* demarshal the icon */
+        if ((req->unicode  && (req->wide.hIcon != (HICON) -1)) ||
+            (!req->unicode && (req->ansi.hIcon != (HICON) -1)))
+        {
+            WINE_ERR("You cannot use native shell32 at this time for system tray integration\n");
+            return;
+        }
+    
+        cii = (CURSORICONINFO *) (req->copydata + (req->unicode ? req->wide.cbSize : req->ansi.cbSize));
+        ciisize = sizeof(CURSORICONINFO) + ((cii->nHeight * cii->nWidthBytes) * 2);
+
+        WINE_TRACE("cii->nHeight = %d, cii->nWidthBytes = %d, ciisize = %ld\n",
+                   cii->nHeight, cii->nWidthBytes, ciisize);
+        
+        hicon = GlobalAlloc16(GMEM_MOVEABLE, ciisize);
+        /* FIXME: do we need to do a FarSetOwner here? */
+        iconbits = GlobalLock16(hicon);
+        memcpy(iconbits, cii, ciisize);
+        GlobalUnlock16(hicon);
+
+        if (icon->image) DestroyIcon(icon->image);
+        icon->image = HICON_32(hicon);
+
+        RedrawWindow(icon->window, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
+    }
+
+    if ((req->unicode ? req->wide.uFlags : req->ansi.uFlags) & NIF_MESSAGE)
+    {
+        icon->callback_message = req->unicode? req->wide.uCallbackMessage : req->ansi.uCallbackMessage;
+    }
+}
+
+static void add_icon(struct request *req)
+{
+    struct icon  *icon;
+    const static WCHAR  adaptor_windowname[] = /* Wine System Tray Adaptor */ {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',' ','A','d','a','p','t','o','r',0};
+
+    WINE_TRACE("id=0x%x, hwnd=%p\n", req->id, req->owner);    
+
+    if ((icon = get_icon(req->owner, req->id)))
+    {
+        WINE_WARN("duplicate tray icon add, buggy app?\n");
+        return;
+    }
+    
+    icon        = xmalloc(sizeof(struct icon));
+    icon->id    = req->id;    
+    icon->owner = req->owner;
+    icon->image = NULL;
+
+    /* create the adaptor window */
+    icon->window = CreateWindowEx(WS_EX_TRAYWINDOW,
+                                  (LPCWSTR) &adaptor_classname,
+                                  (LPCWSTR) &adaptor_windowname,
+                                  0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                  16, 16,
+                                  0, 0, 0, 0);
+
+    SetWindowLong(icon->window, GWL_USERDATA, (LONG) icon);
+    
+    list_add_tail(&(tray.icons), &icon->entry);
+    
+    modify_icon(req);
+
+    shell_refs++;
+    WINE_TRACE("shell now has %d refs\n", shell_refs);
+}
+
+static void delete_icon(struct request *req)
+{
+    struct icon *icon = get_icon(req->owner, req->id);
+
+    WINE_TRACE("id=0x%x, hwnd=%p\n", req->id, req->owner);    
+   
+    if (!icon)
+    {
+        WINE_ERR("invalid tray icon ID specified: %ud\n", req->id);
+        return;
+    }
+
+    list_remove(&icon->entry);
+
+    DestroyIcon(icon->image);
+    if (!DestroyWindow(icon->window)) WINE_ERR("Could not destroy window!\n");
+
+    HeapFree(GetProcessHeap(), 0, icon);
+
+    shell_refs--;
+    WINE_TRACE("shell now has %d refs\n", shell_refs);    
+}
+
+static void handle_incoming(WPARAM wparam, LPARAM lparam)
+{
+    COPYDATASTRUCT *cds = (COPYDATASTRUCT *) lparam;
+
+    struct protocol
+    {
+        DWORD cookie;
+        DWORD code;
+        DWORD data[1];
+    } *protocol = (struct protocol *) cds->lpData;
+    
+    struct request req;
+
+    WINE_TRACE("cookie = %ld\n", protocol->cookie);
+    WINE_TRACE("notify_code = %ld\n", protocol->code);
+
+    if (((NOTIFYICONDATAW *)&protocol->data[0])->cbSize == sizeof(NOTIFYICONDATAW))
+    {
+        WINE_TRACE("unicode\n");
+        
+        memcpy(&req.wide, &protocol->data[0], sizeof(NOTIFYICONDATAW));
+        req.unicode = TRUE;
+    }
+    else
+    {
+        WINE_TRACE("ansi\n");
+        
+        memcpy(&req.ansi, &protocol->data[0], sizeof(NOTIFYICONDATAA));
+        req.unicode = FALSE;
+    }
+
+    /* these are just convenience accessors, as we'll need them frequently */
+    req.owner    = req.unicode ? req.wide.hWnd : req.ansi.hWnd;
+    req.id       = req.unicode ? req.wide.uID  : req.ansi.uID;
+    req.copydata = (BYTE *) &protocol->data;
+    
+    if (protocol->code == NIM_ADD) add_icon(&req);
+    else if (protocol->code == NIM_DELETE) delete_icon(&req);
+    else if (protocol->code == NIM_MODIFY) modify_icon(&req);
+    else
+    {
+        WINE_WARN("unhandled tray message: %ld\n", protocol->code);
+    }
+    
+}
+
+static LRESULT WINAPI listener_wndproc(HWND window, UINT msg,
+                                       WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+        case WM_COPYDATA:
+            handle_incoming(wparam, lparam);
+            break;
+    }
+    return DefWindowProc(window, msg, wparam, lparam);
+}
+
+
+/* this function creates the the listener window */
+void initialize_systray()
+{
+    WNDCLASSEX class;
+    const static WCHAR classname[] = /* Shell_TrayWnd */ {'S','h','e','l','l','_','T','r','a','y','W','n','d',0};
+    const static WCHAR winname[]   = /* Wine Systray Listener */ {'W','i','n','e',' ','S','y','s','t','r','a','y',' ','L','i','s','t','e','n','e','r',0};
+
+    WINE_TRACE(" :: go go gadget systray!\n");
+    
+    list_init(&(tray.icons));
+
+    /* register the systray listener window class */
+    ZeroMemory(&class, sizeof(class));
+    class.cbSize        = sizeof(class);
+    class.lpfnWndProc   = &listener_wndproc;
+    class.hInstance     = NULL;
+    class.hIcon         = LoadIcon(0, IDI_WINLOGO);
+    class.hCursor       = LoadCursor(0, IDC_ARROW);
+    class.hbrBackground = (HBRUSH) COLOR_WINDOW;
+    class.lpszClassName = (WCHAR *) &classname;
+
+    if (!RegisterClassEx(&class))
+    {
+        WINE_ERR("Could not register SysTray window class\n");
+        return;
+    }
+
+    /* now register the adaptor window class */
+    ZeroMemory(&class, sizeof(class));
+    class.cbSize        = sizeof(class);
+    class.lpfnWndProc   = &adaptor_wndproc;
+    class.hInstance     = NULL;
+    class.hIcon         = LoadIcon(0, IDI_WINLOGO);
+    class.hCursor       = LoadCursor(0, IDC_ARROW);
+    class.hbrBackground = (HBRUSH) COLOR_WINDOW;
+    class.lpszClassName = (WCHAR *) &adaptor_classname;
+    class.style         = CS_SAVEBITS | CS_DBLCLKS;
+
+    if (!RegisterClassEx(&class))
+    {
+        WINE_ERR("Could not register adaptor class\n");
+        return;
+    }
+    
+    tray.window = CreateWindow((WCHAR *)&classname, (WCHAR *)&winname,
+                               WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT,
+                               0, 0, 0, 0, 0, 0);
+
+    if (!tray.window)
+    {
+        WINE_ERR("Could not create tray window\n");
+        return;
+    }
+
+    /* FIXME: implement tooltips */
+}
+
+void shutdown_systray()
+{
+    WINE_TRACE(" :: next time gadget .... next time <muahahahahaha>\n");
+
+    DestroyWindow(tray.window);
+}
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.328
diff -u -p -r1.328 configure.ac
--- configure.ac	23 Nov 2004 17:33:56 -0000	1.328
+++ configure.ac	30 Nov 2004 00:14:19 -0000
@@ -1788,6 +1788,7 @@ programs/winefile/Makefile
 programs/winemenubuilder/Makefile
 programs/winemine/Makefile
 programs/winepath/Makefile
+programs/wineshell/Makefile
 programs/winetest/Makefile
 programs/winevdm/Makefile
 programs/winhelp/Makefile
Index: dlls/shell32/shell32_main.c
===================================================================
RCS file: /home/wine/wine/dlls/shell32/shell32_main.c,v
retrieving revision 1.139
diff -u -p -r1.139 shell32_main.c
--- dlls/shell32/shell32_main.c	7 Oct 2004 03:06:48 -0000	1.139
+++ dlls/shell32/shell32_main.c	30 Nov 2004 14:10:04 -0000
@@ -959,7 +959,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, 
 	    InitCommonControlsEx(NULL);
 
 	    SIC_Initialize();
-	    SYSTRAY_Init();
 	    InitChangeNotifications();
 	    break;
 


More information about the wine-patches mailing list