Here's the result of todays hacking for the enjoyment of all. ChangeLog: - Run the system tray window in a dedicated thread to avoid potential deadlocks. - Add some more comments to the code. - Don't send WM_MOUSEMOVE, some apps don't seem to be expecting it and screw up if it's sent. -- Mike Hearn <m.hearn@signal.qinetiq.com> QinetiQ - Malvern Technology Center
--- dlls/shell32/systray.c.old 2003-06-09 11:32:26.000000000 +0100 +++ dlls/shell32/systray.c 2003-06-09 16:16:11.000000000 +0100 @@ -1,11 +1,11 @@ /* - * Systray + * System tray handling code (client side) * - * Copyright 1999 Kai Morich <kai.morich@bigfoot.de> + * Copyright 1999 Kai Morich <kai.morich@bigfoot.de> + * Copyright 2003 Mike Hearn <mike@theoretic.com> * - * Manage the systray window. That it actually appears in the docking - * area of KDE or GNOME is delegated to windows/x11drv/wnd.c, - * X11DRV_WND_DockWindow. + * This code creates a window with the WS_EX_TRAYWINDOW style. The actual + * environment integration code is handled inside the X11 driver. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -43,15 +43,15 @@ HWND hWnd; HWND hWndToolTip; NOTIFYICONDATAA notifyIcon; + CRITICAL_SECTION lock; struct SystrayItem *nextTrayItem; } SystrayItem; -static SystrayItem *systray=NULL; -static int firstSystray=TRUE; /* defer creation of window class until first systray item is created */ +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 @@ -75,8 +75,8 @@ { RECT rc; SystrayItem *ptrayItem = systray; - int top, left; - + int top; + EnterCriticalSection(&ptrayItem->lock); while (ptrayItem) { if (ptrayItem->hWnd==hWnd) { if (ptrayItem->notifyIcon.hIcon) { @@ -87,6 +87,7 @@ if (!DrawIconEx(hdc, (ICON_BORDER/2), top, ptrayItem->notifyIcon.hIcon, ICON_SIZE, ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL)) { ERR("Paint(SystrayWindow %p) failed -> removing SystrayItem %p\n", hWnd, ptrayItem); + LeaveCriticalSection(&ptrayItem->lock); SYSTRAY_Delete(&ptrayItem->notifyIcon); } } @@ -95,10 +96,10 @@ ptrayItem = ptrayItem->nextTrayItem; } EndPaint(hWnd, &ps); + LeaveCriticalSection(&ptrayItem->lock); } break; - case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: @@ -108,7 +109,7 @@ { MSG msg; SystrayItem *ptrayItem = systray; - + /* relay the event to the tooltip */ while ( ptrayItem ) { if (ptrayItem->hWnd == hWnd) { msg.hwnd=hWnd; @@ -124,14 +125,14 @@ ptrayItem = ptrayItem->nextTrayItem; } } - /* fall through */ + /* fall through, so the message is sent to the callback as well */ case WM_LBUTTONDBLCLK: case WM_RBUTTONDBLCLK: case WM_MBUTTONDBLCLK: { SystrayItem *ptrayItem = systray; - + /* iterate over the currently active tray items */ while (ptrayItem) { if (ptrayItem->hWnd == hWnd) { if (ptrayItem->notifyIcon.hWnd && ptrayItem->notifyIcon.uCallbackMessage) { @@ -179,26 +180,17 @@ } -BOOL SYSTRAY_ItemInit(SystrayItem *ptrayItem) -{ +DWORD WINAPI SYSTRAY_ThreadProc(LPVOID p1) { + SystrayItem *ptrayItem = (SystrayItem *)p1; + MSG msg; 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; - } - } - + /* 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", @@ -220,31 +212,72 @@ ERR( "CreateWindow(TOOLTIP) failed\n" ); return FALSE; } + + /* Enter the message loop */ + while (GetMessageA (&msg, 0, 0, 0) > 0) { + TranslateMessage (&msg); + DispatchMessageA (&msg); + } + + TRACE("Shutting down system tray thread\n"); + if(ptrayItem->notifyIcon.hIcon) + DestroyIcon(ptrayItem->notifyIcon.hIcon); + if(ptrayItem->hWndToolTip) + DestroyWindow(ptrayItem->hWndToolTip); + + return 0; +} + +BOOL SYSTRAY_ItemInit(SystrayItem *ptrayItem) +{ + DWORD threadID; + + /* Register the class if this is our first tray item. */ + if ( firstSystray ) { + firstSystray = FALSE; + if ( !SYSTRAY_RegisterClass() ) { + ERR( "RegisterClass(WineSystray) failed\n" ); + return FALSE; + } + } + + ZeroMemory( ptrayItem, sizeof(SystrayItem) ); + + /* We need to run the system tray window in a separate thread, as otherwise if the originating thread + stops processing messages, the tray window will hang. If another part of the application then does + for instance a FindWindow call, this can deadlock the application. */ + InitializeCriticalSection(&ptrayItem->lock); + if (!CreateThread(NULL, 0, SYSTRAY_ThreadProc, (LPVOID) ptrayItem, 0, &threadID)) { + ERR("Could not create system tray item thread\n"); + return FALSE; + } return TRUE; } 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); + /* MSDN says we shouldn't do this, but I can't see another way to make GetMessage() return zero */ + PostMessageA(ptrayItem->hWnd, WM_QUIT, 0, 0); + DeleteCriticalSection(&ptrayItem->lock); return; } void SYSTRAY_ItemSetMessage(SystrayItem *ptrayItem, UINT uCallbackMessage) { + EnterCriticalSection(&ptrayItem->lock); ptrayItem->notifyIcon.uCallbackMessage = uCallbackMessage; + LeaveCriticalSection(&ptrayItem->lock); } void SYSTRAY_ItemSetIcon(SystrayItem *ptrayItem, HICON hIcon) { + EnterCriticalSection(&ptrayItem->lock); ptrayItem->notifyIcon.hIcon = CopyIcon(hIcon); + LeaveCriticalSection(&ptrayItem->lock); + InvalidateRect(ptrayItem->hWnd, NULL, TRUE); } @@ -253,9 +286,11 @@ { TTTOOLINFOA ti; - strncpy(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip)); + EnterCriticalSection(&ptrayItem->lock); + strncpy(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip)); ptrayItem->notifyIcon.szTip[sizeof(ptrayItem->notifyIcon.szTip)-1]=0; - + LeaveCriticalSection(&ptrayItem->lock); + ti.cbSize = sizeof(TTTOOLINFOA); ti.uFlags = 0; ti.hwnd = ptrayItem->hWnd;