diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in index 95301f78eb0..4d7df18089f 100644 --- a/dlls/winex11.drv/Makefile.in +++ b/dlls/winex11.drv/Makefile.in @@ -4,6 +4,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = winex11.drv IMPORTS = user32 gdi32 advapi32 imm32 kernel32 ntdll +DELAYIMPORTS = comctl32 EXTRAINCL = @X_CFLAGS@ EXTRALIBS = @X_LIBS@ @X_PRE_LIBS@ @XLIB@ @X_EXTRA_LIBS@ @@ -30,6 +31,7 @@ C_SRCS = \ pen.c \ scroll.c \ settings.c \ + systray.c \ text.c \ window.c \ winpos.c \ diff --git a/dlls/winex11.drv/systray.c b/dlls/winex11.drv/systray.c new file mode 100644 index 00000000000..9deb003e17c --- /dev/null +++ b/dlls/winex11.drv/systray.c @@ -0,0 +1,439 @@ +/* + * X11 system tray management + * + * Copyright (C) 2004 Mike Hearn, for CodeWeavers + * Copyright (C) 2005 Robert Shearman + * Copyright (C) 2008 Alexandre Julliard + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commctrl.h" +#include "shellapi.h" + +#include "x11drv.h" +#include "wine/list.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(systray); + +/* an individual systray icon */ +struct tray_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 */ + HWND tooltip; /* Icon tooltip */ + UINT id; /* the unique id given by the app */ + UINT callback_message; + BOOL hidden; /* icon display state */ + WCHAR tiptext[128]; /* Tooltip text. If empty => tooltip disabled */ +}; + +static struct list icon_list = LIST_INIT( icon_list ); + +static const WCHAR tray_classname[] = {'_','_','w','i','n','e','x','1','1','_','t','r','a','y','_','w','i','n','d','o','w',0}; + +static BOOL delete_icon( struct tray_icon *icon ); + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define XEMBED_MAPPED (1 << 0) + +#define ICON_BORDER 2 + +/* retrieves icon record by owner window and ID */ +static struct tray_icon *get_icon(HWND owner, UINT id) +{ + struct tray_icon *this; + + LIST_FOR_EACH_ENTRY( this, &icon_list, struct tray_icon, entry ) + if ((this->id == id) && (this->owner == owner)) return this; + return NULL; +} + +/* create tooltip window for icon */ +static void create_tooltip(struct tray_icon *icon) +{ + static BOOL tooltips_initialized = FALSE; + + if (!tooltips_initialized) + { + INITCOMMONCONTROLSEX init_tooltip; + + init_tooltip.dwSize = sizeof(INITCOMMONCONTROLSEX); + init_tooltip.dwICC = ICC_TAB_CLASSES; + + InitCommonControlsEx(&init_tooltip); + tooltips_initialized = TRUE; + } + + icon->tooltip = CreateWindowExW( WS_EX_TOPMOST, TOOLTIPS_CLASSW, NULL, + WS_POPUP | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + icon->window, NULL, NULL, NULL); + if (icon->tooltip) + { + TTTOOLINFOW ti; + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(TTTOOLINFOW); + ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND; + ti.hwnd = icon->window; + ti.uId = (UINT_PTR)icon->window; + ti.lpszText = icon->tiptext; + SendMessageW(icon->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&ti); + } +} + +/* synchronize tooltip text with tooltip window */ +static void update_tooltip_text(struct tray_icon *icon) +{ + TTTOOLINFOW ti; + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(TTTOOLINFOW); + ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND; + ti.hwnd = icon->window; + ti.uId = (UINT_PTR)icon->window; + ti.lpszText = icon->tiptext; + + SendMessageW(icon->tooltip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti); +} + +/* window procedure for the tray window */ +static LRESULT WINAPI tray_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + struct tray_icon *icon = NULL; + BOOL ret; + + WINE_TRACE("hwnd=%p, msg=0x%x\n", hwnd, msg); + + /* set the icon data for the window from the data passed into CreateWindow */ + if (msg == WM_NCCREATE) + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)((const CREATESTRUCTW *)lparam)->lpCreateParams); + + icon = (struct tray_icon *) GetWindowLongPtrW(hwnd, GWLP_USERDATA); + + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + RECT rc; + HDC hdc; + int cx = GetSystemMetrics( SM_CXSMICON ); + int cy = GetSystemMetrics( SM_CYSMICON ); + + hdc = BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &rc); + TRACE("painting rect %s\n", wine_dbgstr_rect(&rc)); + DrawIconEx( hdc, (rc.left + rc.right - cx) / 2, (rc.top + rc.bottom - cy) / 2, + icon->image, cx, cy, 0, 0, DI_DEFAULTSIZE|DI_NORMAL ); + 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: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + /* notify the owner hwnd of the message */ + TRACE("relaying 0x%x\n", msg); + ret = PostMessageW(icon->owner, icon->callback_message, (WPARAM) icon->id, (LPARAM) msg); + if (!ret && (GetLastError() == ERROR_INVALID_WINDOW_HANDLE)) + { + WARN( "application window was destroyed, removing icon %u\n", icon->id ); + delete_icon( icon ); + } + break; + + default: + return DefWindowProcW(hwnd, msg, wparam, lparam); + } + return 0; +} + +/* find the X11 window owner the system tray selection */ +static Window get_systray_selection_owner( Display *display ) +{ + static Atom systray_atom; + Window ret; + + if (root_window != DefaultRootWindow( display )) return 0; + + wine_tsx11_lock(); + if (!systray_atom) + { + if (DefaultScreen( display ) == 0) + systray_atom = x11drv_atom(_NET_SYSTEM_TRAY_S0); + else + { + char systray_buffer[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */ + sprintf( systray_buffer, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display ) ); + systray_atom = XInternAtom( display, systray_buffer, False ); + } + } + ret = XGetSelectionOwner( display, systray_atom ); + wine_tsx11_unlock(); + return ret; +} + + +/* dock the given X window with the NETWM system tray */ +static void dock_systray_window( HWND hwnd, Window systray_window ) +{ + Display *display = thread_display(); + struct x11drv_win_data *data; + XEvent ev; + unsigned long info[2]; + + if (!(data = X11DRV_get_win_data( hwnd )) && + !(data = X11DRV_create_win_data( hwnd ))) return; + + TRACE( "icon window %p/%lx managed %u\n", data->hwnd, data->whole_window, data->managed ); + + /* the window _cannot_ be mapped if we intend to dock with an XEMBED tray */ + assert( !data->mapped ); + + /* set XEMBED protocol data on the window */ + info[0] = 0; /* protocol version */ + info[1] = XEMBED_MAPPED; /* flags */ + + wine_tsx11_lock(); + XChangeProperty( display, data->whole_window, x11drv_atom(_XEMBED_INFO), + x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, (unsigned char*)info, 2 ); + + /* send the docking request message */ + 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; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + XSendEvent( display, systray_window, False, NoEventMask, &ev ); + wine_tsx11_unlock(); + + data->mapped = TRUE; + data->wm_state = NormalState; +} + + +/* hide a tray icon */ +static BOOL hide_icon( struct tray_icon *icon ) +{ + TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner ); + + if (!icon->window || icon->hidden) return TRUE; /* already hidden */ + + if (icon->window) + { + DestroyWindow(icon->window); + DestroyWindow(icon->tooltip); + } + icon->hidden = TRUE; + return TRUE; +} + +/* make the icon visible */ +static BOOL show_icon( struct tray_icon *icon ) +{ + RECT rect; + static BOOL class_registered; + Window systray_window; + + TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner ); + + if (icon->window && !icon->hidden) return TRUE; /* already shown */ + + if (!class_registered) + { + WNDCLASSEXW class; + + ZeroMemory( &class, sizeof(class) ); + class.cbSize = sizeof(class); + class.lpfnWndProc = tray_wndproc; + class.hCursor = LoadCursorW( 0, (LPCWSTR)IDC_ARROW ); + class.lpszClassName = tray_classname; + class.hbrBackground = (HBRUSH)COLOR_WINDOW; + class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + + if (!RegisterClassExW(&class) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + WINE_ERR( "Could not register tray window class\n" ); + return FALSE; + } + class_registered = TRUE; + } + + if (!(systray_window = get_systray_selection_owner( thread_display() ))) return FALSE; + + rect.left = 0; + rect.top = 0; + rect.right = GetSystemMetrics( SM_CXSMICON ) + 2*ICON_BORDER; + rect.bottom = GetSystemMetrics( SM_CYSMICON ) + 2*ICON_BORDER; + + icon->window = CreateWindowExW( WS_EX_APPWINDOW, tray_classname, NULL, WS_CLIPSIBLINGS | WS_POPUP, + CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, NULL, icon ); + create_tooltip( icon ); + dock_systray_window( icon->window, systray_window ); + ShowWindow( icon->window, SW_SHOWNA ); + icon->hidden = FALSE; + return TRUE; +} + +/* Modifies an existing icon record */ +static BOOL modify_icon( struct tray_icon *icon, NOTIFYICONDATAW *nid ) +{ + TRACE( "id=0x%x hwnd=%p flags=%x\n", nid->uID, nid->hWnd, nid->uFlags ); + + if ((nid->uFlags & NIF_STATE) && (nid->dwStateMask & NIS_HIDDEN)) + { + if (nid->dwState & NIS_HIDDEN) hide_icon( icon ); + else show_icon( icon ); + } + + /* startup case*/ + if (!icon->window && !icon->hidden) show_icon( icon ); + + if (nid->uFlags & NIF_ICON) + { + if (icon->image) DestroyIcon(icon->image); + icon->image = CopyIcon(nid->hIcon); + + if (!icon->hidden) + RedrawWindow(icon->window, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); + } + + if (nid->uFlags & NIF_MESSAGE) + { + icon->callback_message = nid->uCallbackMessage; + } + if (nid->uFlags & NIF_TIP) + { + lstrcpynW(icon->tiptext, nid->szTip, sizeof(icon->tiptext)/sizeof(WCHAR)); + if (!icon->hidden) + update_tooltip_text(icon); + } + if (nid->uFlags & NIF_INFO && nid->cbSize >= NOTIFYICONDATAA_V2_SIZE) + { + FIXME("balloon tip title %s, message %s\n", wine_dbgstr_w(nid->szInfoTitle), wine_dbgstr_w(nid->szInfo)); + } + return TRUE; +} + +/* Adds a new icon record to the list */ +static BOOL add_icon(NOTIFYICONDATAW *nid) +{ + struct tray_icon *icon; + + WINE_TRACE("id=0x%x, hwnd=%p\n", nid->uID, nid->hWnd); + + if ((icon = get_icon(nid->hWnd, nid->uID))) + { + WINE_WARN("duplicate tray icon add, buggy app?\n"); + return FALSE; + } + + if (!(icon = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*icon)))) + { + WINE_ERR("out of memory\n"); + return FALSE; + } + + ZeroMemory(icon, sizeof(struct tray_icon)); + icon->id = nid->uID; + icon->owner = nid->hWnd; + + list_add_tail(&icon_list, &icon->entry); + + /* + * Both icon->window and icon->hidden are zero. modify_icon function + * will treat this case as a startup, i.e. icon window will be created if + * NIS_HIDDEN flag is not set. + */ + + return modify_icon( icon, nid ); +} + +/* delete tray icon window and icon structure */ +static BOOL delete_icon( struct tray_icon *icon ) +{ + hide_icon( icon ); + list_remove( &icon->entry ); + DestroyIcon( icon->image ); + HeapFree( GetProcessHeap(), 0, icon ); + return TRUE; +} + + +/*********************************************************************** + * wine_notify_icon (X11DRV.@) + * + * Driver-side implementation of Shell_NotifyIcon. + */ +BOOL wine_notify_icon( DWORD msg, NOTIFYICONDATAW *data ) +{ + BOOL ret = FALSE; + struct tray_icon *icon; + Window owner; + + switch (msg) + { + case NIM_ADD: + if ((owner = get_systray_selection_owner( thread_display() ))) ret = add_icon( data ); + break; + case NIM_DELETE: + if ((icon = get_icon( data->hWnd, data->uID ))) ret = delete_icon( icon ); + break; + case NIM_MODIFY: + if ((icon = get_icon( data->hWnd, data->uID ))) ret = modify_icon( icon, data ); + break; + default: + FIXME( "unhandled tray message: %u\n", msg ); + break; + } + return ret; +} diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 09ef8de3a63..6edfbc551ca 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -64,9 +64,6 @@ static const char gl_drawable_prop[] = "__wine_x11_gl_drawable"; static const char pixmap_prop[] = "__wine_x11_pixmap"; static const char managed_prop[] = "__wine_x11_managed"; -/* for XDG systray icons */ -#define SYSTEM_TRAY_REQUEST_DOCK 0 - extern int usexcomposite; /*********************************************************************** @@ -692,93 +689,6 @@ static void set_icon_hints( Display *display, struct x11drv_win_data *data, HICO } } -/*********************************************************************** - * wine_make_systray_window (X11DRV.@) - * - * Docks the given X window with the NETWM system tray. - */ -void X11DRV_make_systray_window( HWND hwnd ) -{ - static Atom systray_atom; - Display *display = thread_display(); - struct x11drv_win_data *data; - Window systray_window; - - if (root_window != DefaultRootWindow(display)) return; - - if (!(data = X11DRV_get_win_data( hwnd )) && - !(data = X11DRV_create_win_data( hwnd ))) return; - - wine_tsx11_lock(); - if (!systray_atom) - { - if (DefaultScreen( display ) == 0) - systray_atom = x11drv_atom(_NET_SYSTEM_TRAY_S0); - else - { - char systray_buffer[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */ - sprintf( systray_buffer, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display ) ); - systray_atom = XInternAtom( display, systray_buffer, False ); - } - } - systray_window = XGetSelectionOwner( display, systray_atom ); - wine_tsx11_unlock(); - - TRACE("Docking tray icon %p\n", data->hwnd); - - if (systray_window != None) - { - XEvent ev; - unsigned long info[2]; - - /* the window _cannot_ be mapped if we intend to dock with an XEMBED tray */ - if (data->mapped) FIXME( "trying to dock mapped window %p\n", data->hwnd ); - - /* set XEMBED protocol data on the window */ - info[0] = 0; /* protocol version */ - info[1] = 1; /* mapped = true */ - - wine_tsx11_lock(); - XChangeProperty( display, data->whole_window, - x11drv_atom(_XEMBED_INFO), - x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, - (unsigned char*)info, 2 ); - - /* send the docking request message */ - 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; - ev.xclient.data.l[3] = 0; - ev.xclient.data.l[4] = 0; - XSendEvent( display, systray_window, False, NoEventMask, &ev ); - wine_tsx11_unlock(); - - data->mapped = TRUE; - } - else - { - int val = 1; - - /* fall back to he KDE hints if the WM doesn't support XEMBED'ed - * systrays */ - - wine_tsx11_lock(); - XChangeProperty( display, data->whole_window, - x11drv_atom(KWM_DOCKWINDOW), - x11drv_atom(KWM_DOCKWINDOW), 32, PropModeReplace, - (unsigned char*)&val, 1 ); - XChangeProperty( display, data->whole_window, - x11drv_atom(_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR), - XA_WINDOW, 32, PropModeReplace, - (unsigned char*)&data->whole_window, 1 ); - wine_tsx11_unlock(); - } -} - /*********************************************************************** * set_size_hints diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index 6d9c1be5876..4ad3f3d7d04 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -129,7 +129,7 @@ @ cdecl wine_create_desktop(long long) X11DRV_create_desktop # System tray -@ cdecl wine_make_systray_window(long) X11DRV_make_systray_window +@ cdecl wine_notify_icon(long ptr) # XIM @ cdecl ForceXIMReset(long) X11DRV_ForceXIMReset diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index f2ef1248179..14cb88919f7 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -582,12 +582,10 @@ enum x11drv_atoms XATOM_WM_DELETE_WINDOW, XATOM_WM_STATE, XATOM_WM_TAKE_FOCUS, - XATOM_KWM_DOCKWINDOW, XATOM_DndProtocol, XATOM_DndSelection, XATOM__ICC_PROFILE, XATOM__MOTIF_WM_HINTS, - XATOM__KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR, XATOM__NET_SUPPORTED, XATOM__NET_SYSTEM_TRAY_OPCODE, XATOM__NET_SYSTEM_TRAY_S0, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index f90efed7ba3..b8f0b458bae 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -125,12 +125,10 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "WM_DELETE_WINDOW", "WM_STATE", "WM_TAKE_FOCUS", - "KWM_DOCKWINDOW", "DndProtocol", "DndSelection", "_ICC_PROFILE", "_MOTIF_WM_HINTS", - "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", "_NET_SUPPORTED", "_NET_SYSTEM_TRAY_OPCODE", "_NET_SYSTEM_TRAY_S0", diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index b01203fa76e..7e83d208d6e 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -45,6 +45,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(systray); #define IS_OPTION_FALSE(ch) \ ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0') +static BOOL (*wine_notify_icon)(DWORD,NOTIFYICONDATAW *); + static const WCHAR adaptor_classname[] = /* Adaptor */ {'A','d','a','p','t','o','r',0}; /* tray state */ @@ -146,7 +148,6 @@ static void update_tooltip_text(struct icon *icon) */ static BOOL display_icon(struct icon *icon, BOOL hide) { - HMODULE x11drv = GetModuleHandleA("winex11.drv"); RECT rect; static const 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}; @@ -182,11 +183,6 @@ static BOOL display_icon(struct icon *icon, BOOL hide) rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, NULL, icon); - if (x11drv) - { - void (*make_systray_window)(HWND) = (void *)GetProcAddress(x11drv, "wine_make_systray_window"); - if (make_systray_window) make_systray_window(icon->window); - } if (!hide_systray) ShowWindow(icon->window, SW_SHOWNA); @@ -355,6 +351,12 @@ static BOOL handle_incoming(HWND hwndSource, COPYDATASTRUCT *cds) buffer, buffer + cbMaskBits); } + if (wine_notify_icon && wine_notify_icon( cds->dwData, &nid )) + { + if (nid.uFlags & NIF_ICON) DestroyIcon( nid.hIcon ); + return TRUE; + } + switch (cds->dwData) { case NIM_ADD: @@ -484,6 +486,7 @@ static BOOL is_systray_hidden(void) /* this function creates the listener window */ void initialize_systray(void) { + HMODULE x11drv; WNDCLASSEX class; static const WCHAR classname[] = /* Shell_TrayWnd */ {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; static const WCHAR winname[] = /* Wine Systray Listener */ @@ -491,6 +494,9 @@ void initialize_systray(void) WINE_TRACE("initiaizing\n"); + if ((x11drv = GetModuleHandleA( "winex11.drv" ))) + wine_notify_icon = (void *)GetProcAddress( x11drv, "wine_notify_icon" ); + hide_systray = is_systray_hidden(); list_init(&tray.icons);