[TRY 2] XEmbed System Tray Support
James Liggett
jrliggett at cox.net
Sun Aug 20 21:57:39 CDT 2006
RESEND: The previous patch wasn't fully correct as we can't trust the adaptor class name to be unique.
I've fallen back on using WS_EX_TRAYWINDOW (as before) for now.
ChangeLog:
Add XEmbed system tray support.
Portions of this patch based on the work of Mike Hearn <mike at
plan99.net> and Rob Shearman <rob at codeweavers.com>.
---
dlls/winex11.drv/window.c | 92 +++++++++++++++++++++++++++++++++++-----
dlls/winex11.drv/x11drv.h | 4 ++
dlls/winex11.drv/x11drv_main.c | 14 ++++++
programs/explorer/systray.c | 16 ++++++-
4 files changed, 113 insertions(+), 13 deletions(-)
d317baa53cc8bd7b13d900d2fc49d25321ec3643
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 2bc90a7..0d27619 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -58,6 +58,9 @@ static const char icon_window_prop[] =
static const char managed_prop[] = "__wine_x11_managed";
static const char visual_id_prop[] = "__wine_x11_visual_id";
+/* for XDG systray icons */
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+
/***********************************************************************
* is_window_managed
*
@@ -291,6 +294,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 +491,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 */
@@ -917,7 +978,7 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE
struct x11drv_win_data *data;
HWND insert_after;
RECT rect;
- DWORD style;
+ DWORD style, ex_style;
CBT_CREATEWNDA cbtc;
CREATESTRUCTA cbcs;
BOOL ret = FALSE;
@@ -1079,6 +1140,15 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE
newPos.right, newPos.bottom, swFlag );
}
+ /* Dock system tray icons */
+ /* We have to dock here instead of in X11DRV_set_wm_hints to ensure that
+ * nothing interferes with the docking process, and that the adaptor window
+ * can appear properly if we don't have a tray, as we must have a fully
+ * created window in order to call SetWindowPos to put it onscreen again. */
+ ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
+ if (ex_style & WS_EX_TRAYWINDOW)
+ systray_dock_window( display, data );
+
return TRUE;
failed:
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/programs/explorer/systray.c b/programs/explorer/systray.c
index 70d29bf..59e5f20 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,12 +218,23 @@ 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,
+ screen_width + 1, screen_height + 1,
rect.right - rect.left,
rect.bottom - rect.top,
NULL, NULL, NULL, icon);
--
1.2.4
More information about the wine-patches
mailing list