Attached is the fourth update to my XEmbed system tray patches. For this version I made some fairly major changes:
1. Did away with the low-level checks for systrays, and instead had the systray icon windows created off-screen so they aren't mapped, as per Alexandre's suggestion. 2. systray_dock_window will put the icon windows back on the screen if there isn't an XEmbed tray to dock with. 3. Eliminated the WS_EX_TRAYWINDOW style hack, and moved the call to systray_dock_window to X11DRV_CreateWindow. I used the window class name to see if it was a tray window being created or not. 4. Consolidated all patches into one, as the individual patches weren't really individual changes. (Thanks to Mike McCormack for this advice.) As usual, comments/suggestions appreciated. Thanks again, James Liggett
>From nobody Mon Sep 17 00:00:00 2001 From: James Liggett <[EMAIL PROTECTED]> Date: Wed Aug 16 17:06:15 2006 -0700 Subject: [PATCH] Add support for XEmbed-compliant system trays. Portions of this patch based on the work of Mike Hearn <[EMAIL PROTECTED]> and Rob Shearman <[EMAIL PROTECTED]>. --- dlls/winex11.drv/window.c | 103 +++++++++++++++++++++++++++++++++++----- dlls/winex11.drv/x11drv.h | 4 ++ dlls/winex11.drv/x11drv_main.c | 14 +++++ include/winuser.h | 3 - programs/explorer/systray.c | 29 ++++++++--- 5 files changed, 129 insertions(+), 24 deletions(-) 35decf77a7d1ed28098fb962653484da3661183d diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 2bc90a7..9da41e3 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -58,6 +58,13 @@ static const char icon_window_prop[] = static const char managed_prop[] = "__wine_x11_managed"; static const char visual_id_prop[] = "__wine_x11_visual_id"; +static const WCHAR adaptor_classname[] = /* Adaptor */ {'A','d','a','p','t','o','r',0}; + +/* for XDG systray icons */ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + /*********************************************************************** * is_window_managed * @@ -66,11 +73,15 @@ static const char visual_id_prop[] = inline static BOOL is_window_managed( HWND hwnd ) { DWORD style, ex_style; - + WCHAR classname[80]; + + ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE ); + GetClassNameW( hwnd, classname, 80 ); + if (!managed_mode) return FALSE; /* tray window is always managed */ - ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE ); - if (ex_style & WS_EX_TRAYWINDOW) return TRUE; + if (strcmpW( classname, adaptor_classname ) == 0) + return TRUE; /* child windows are not managed */ style = GetWindowLongW( hwnd, GWL_STYLE ); if (style & WS_CHILD) return FALSE; @@ -291,6 +302,73 @@ static void set_icon_hints( Display *dis } } +/*********************************************************************** + * systray_dock_window + * + * Docks the given X window with the NETWM system tray. + */ +static void systray_dock_window( Display *display, struct x11drv_win_data *data ) +{ + Window systray_window = XGetSelectionOwner( display, systray_atom ); + + TRACE("Docking tray icon %p\n", data->hwnd); + + if (systray_window != None) + { + XEvent ev; + unsigned long info[2]; + + /* 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 ); + wine_tsx11_unlock(); + + /* 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; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + wine_tsx11_lock(); + XSendEvent( display, systray_window, False, NoEventMask, &ev ); + wine_tsx11_unlock(); + + } + else + { + int val = 1; + + /* fall back to he KDE hints if the WM doesn't support XEMBED'ed + * systrays */ + + /* Put the window back within the screen so it will be mapped */ + SetWindowPos( data->hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE ); + + 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 @@ -421,16 +499,6 @@ void X11DRV_set_wm_hints( Display *displ /* size hints */ set_size_hints( display, data, style ); - /* systray properties (KDE only for now) */ - if (ex_style & WS_EX_TRAYWINDOW) - { - int val = 1; - 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 ); - } - /* set the WM_CLIENT_MACHINE and WM_LOCALE_NAME properties */ XSetWMProperties(display, data->whole_window, NULL, NULL, NULL, 0, NULL, NULL, NULL); /* set the pid. together, these properties are needed so the window manager can kill us if we freeze */ @@ -921,6 +989,7 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE CBT_CREATEWNDA cbtc; CREATESTRUCTA cbcs; BOOL ret = FALSE; + WCHAR classname[80]; if (!(data = alloc_win_data( display, hwnd ))) return FALSE; @@ -1078,6 +1147,14 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE SetWindowPos( hwnd, 0, newPos.left, newPos.top, newPos.right, newPos.bottom, swFlag ); } + + /* Dock tray windows */ + /* Use GetClassNameW here to make sure we always deal with one type of + * string so that we make proper comparisons regardless if the window is + * ANSI or Unicode */ + GetClassNameW( hwnd, classname, 80 ); + if (strcmpW( classname, adaptor_classname ) == 0) + systray_dock_window( display, data ); return TRUE; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index e5027c4..5708588 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -562,6 +562,8 @@ enum x11drv_atoms XATOM_DndSelection, XATOM__MOTIF_WM_HINTS, XATOM__KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR, + XATOM__NET_SYSTEM_TRAY_OPCODE, + XATOM__NET_SYSTEM_TRAY_S0, XATOM__NET_WM_MOVERESIZE, XATOM__NET_WM_NAME, XATOM__NET_WM_PID, @@ -570,6 +572,7 @@ enum x11drv_atoms XATOM__NET_WM_STATE_FULLSCREEN, XATOM__NET_WM_WINDOW_TYPE, XATOM__NET_WM_WINDOW_TYPE_UTILITY, + XATOM__XEMBED_INFO, XATOM_XdndAware, XATOM_XdndEnter, XATOM_XdndPosition, @@ -595,6 +598,7 @@ enum x11drv_atoms }; extern Atom X11DRV_Atoms[NB_XATOMS - FIRST_XATOM]; +extern Atom systray_atom; #define x11drv_atom(name) (X11DRV_Atoms[XATOM_##name - FIRST_XATOM]) diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 41d45a6..9d375e9 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -104,6 +104,7 @@ static char input_style[20]; ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0') Atom X11DRV_Atoms[NB_XATOMS - FIRST_XATOM]; +Atom systray_atom; static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = { @@ -125,6 +126,8 @@ static const char * const atom_names[NB_ "DndSelection", "_MOTIF_WM_HINTS", "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", + "_NET_SYSTEM_TRAY_OPCODE", + "_NET_SYSTEM_TRAY_S0", "_NET_WM_MOVERESIZE", "_NET_WM_NAME", "_NET_WM_PID", @@ -133,6 +136,7 @@ static const char * const atom_names[NB_ "_NET_WM_STATE_FULLSCREEN", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_UTILITY", + "_XEMBED_INFO", "XdndAware", "XdndEnter", "XdndPosition", @@ -416,6 +420,16 @@ static BOOL process_attach(void) } XInternAtoms( display, (char **)atom_names, NB_XATOMS - FIRST_XATOM, False, X11DRV_Atoms ); + + 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%d", DefaultScreen( display ) ); + systray_atom = XInternAtom( display, systray_buffer, False ); + } + if (TRACE_ON(synchronous)) XSynchronize( display, True ); diff --git a/include/winuser.h b/include/winuser.h index cd83c5a..ed38579 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -3043,9 +3043,6 @@ typedef struct tagMINIMIZEDMETRICS { #define WS_EX_OVERLAPPEDWINDOW (WS_EX_WINDOWEDGE|WS_EX_CLIENTEDGE) #define WS_EX_PALETTEWINDOW (WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW|WS_EX_TOPMOST) -/* WINE internal... */ -#define WS_EX_TRAYWINDOW 0x80000000L - /* Window scrolling */ #define SW_SCROLLCHILDREN 0x0001 #define SW_INVALIDATE 0x0002 diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c index 70d29bf..32b88cd 100644 --- a/programs/explorer/systray.c +++ b/programs/explorer/systray.c @@ -193,7 +193,8 @@ static void add_icon(const NOTIFYICONDAT RECT rect; struct icon *icon; 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}; - + int screen_width, screen_height; + WINE_TRACE("id=0x%x, hwnd=%p\n", nid->uID, nid->hWnd); if ((icon = get_icon(nid->hWnd, nid->uID))) @@ -217,15 +218,27 @@ static void add_icon(const NOTIFYICONDAT rect.right = GetSystemMetrics(SM_CXSMICON) + ICON_BORDER; rect.bottom = GetSystemMetrics(SM_CYSMICON) + ICON_BORDER; AdjustWindowRect(&rect, WS_CLIPSIBLINGS | WS_CAPTION, FALSE); + + /* Initially make the adaptor window offscreen so it isn't mapped. + * If we're going to dock with an XEmbed compliant system tray, we _cannot_ + * map the adaptor window ourselves; the tray itself has to handle that. + * If we try to map the adaptor, it may become visible as a child of the + * root window after it docks, which isn't the proper behavior. + * For more information, see: + * + * http://standards.freedesktop.org/xembed-spec/latest/ar01s04.html. */ + screen_width = GetSystemMetrics(SM_CXSCREEN); + screen_height = GetSystemMetrics(SM_CYSCREEN); /* create the adaptor window */ - icon->window = CreateWindowEx(WS_EX_TRAYWINDOW, adaptor_classname, - adaptor_windowname, - WS_CLIPSIBLINGS | WS_CAPTION, - CW_USEDEFAULT, CW_USEDEFAULT, - rect.right - rect.left, - rect.bottom - rect.top, - NULL, NULL, NULL, icon); + icon->window = CreateWindow(adaptor_classname, + adaptor_windowname, + WS_CLIPSIBLINGS | WS_CAPTION, + (screen_width + (rect.right - rect.left)), + (screen_height + (rect.bottom - rect.top)), + rect.right - rect.left, + rect.bottom - rect.top, + NULL, NULL, NULL, icon); if (!hide_systray) ShowWindow(icon->window, SW_SHOWNA); -- 1.2.4