XEmbed System Tray Support

James Liggett jrliggett at cox.net
Thu Aug 17 21:39:53 CDT 2006


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>.

James Liggett

---

 dlls/winex11.drv/window.c      |  102 +++++++++++++++++++++++++++++++++++-----
 dlls/winex11.drv/x11drv.h      |    4 ++
 dlls/winex11.drv/x11drv_main.c |   14 +++++
 include/winuser.h              |    3 -
 programs/explorer/systray.c    |   30 +++++++++---
 5 files changed, 129 insertions(+), 24 deletions(-)

5a700dff2b8f9843b3673a2e51a675961d48188e
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 2bc90a7..0741bf7 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -58,6 +58,14 @@ static const char icon_window_prop[]  = 
 static const char managed_prop[]      = "__wine_x11_managed";
 static const char visual_id_prop[]    = "__wine_x11_visual_id";
 
+/* System tray icon window class name */
+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,17 +74,19 @@ static const char visual_id_prop[]    = 
 inline static BOOL is_window_managed( HWND hwnd )
 {
     DWORD style, ex_style;
-
+    WCHAR 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;
+    GetClassNameW( hwnd, classname, 80 );
+    if (strcmpW( classname, adaptor_classname )  == 0) return TRUE;
     /* child windows are not managed */
     style = GetWindowLongW( hwnd, GWL_STYLE );
     if (style & WS_CHILD) return FALSE;
     /* windows with caption are managed */
     if ((style & WS_CAPTION) == WS_CAPTION) return TRUE;
     /* tool windows are not managed  */
+    ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
     if (ex_style & WS_EX_TOOLWINDOW) return FALSE;
     /* windows with thick frame are managed */
     if (style & WS_THICKFRAME) return TRUE;
@@ -291,6 +301,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 +498,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 +988,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 +1146,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..33c7a1c 100644
--- a/programs/explorer/systray.c
+++ b/programs/explorer/systray.c
@@ -44,6 +44,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(systray);
 #define IS_OPTION_FALSE(ch) \
     ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0')
 
+/* Note: If you change this, make sure to change the one in 
+ * dlls/winex11.drv/window.c as well, or icon docking will not work. */
 static const WCHAR adaptor_classname[] = /* Adaptor */ {'A','d','a','p','t','o','r',0};
 
 /* tray state */
@@ -193,7 +195,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 +220,26 @@ 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 + 1), (screen_height + 1),
+                                rect.right - rect.left,
+                                rect.bottom - rect.top,
+                                NULL, NULL, NULL, icon);
 
     if (!hide_systray)
         ShowWindow(icon->window, SW_SHOWNA);
-- 
1.2.4






More information about the wine-patches mailing list