Run system tray window in dedicated thread
Mike Hearn
m.hearn at signal.qinetiq.com
Mon Jun 9 10:19:46 CDT 2003
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 at signal.qinetiq.com>
QinetiQ - Malvern Technology Center
-------------- next part --------------
--- 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 at bigfoot.de>
+ * Copyright 1999 Kai Morich <kai.morich at bigfoot.de>
+ * Copyright 2003 Mike Hearn <mike at 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;
More information about the wine-patches
mailing list