Second try at NETWM system tray support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



With added sugar, here is another attempt at the patch, but with fewer
bugs and that strips out less of other peoples code this time :)

ChangeLog:
Implement standardised system tray support
Index: ./dlls/x11drv/window.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/window.c,v
retrieving revision 1.53
diff -u -b -r1.53 window.c
--- ./dlls/x11drv/window.c	19 May 2003 19:08:57 -0000	1.53
+++ ./dlls/x11drv/window.c	5 Jun 2003 21:55:16 -0000
@@ -24,6 +24,7 @@
 
 #include <stdlib.h>
 #include <unistd.h>
+#include <stdio.h>
 
 #include "ts_xlib.h"
 #include <X11/Xresource.h>
@@ -42,6 +43,7 @@
 #include "mwm.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
+WINE_DECLARE_DEBUG_CHANNEL(systray);
 
 extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP );
 
@@ -55,6 +57,7 @@
 Atom wmProtocols = None;
 Atom wmDeleteWindow = None;
 Atom wmTakeFocus = None;
+Atom wmManager = None;
 Atom dndProtocol = None;
 Atom dndSelection = None;
 Atom wmChangeState = None;
@@ -62,12 +65,28 @@
 Atom kwmDockWindow = None;
 Atom netwmPid = None;
 Atom netwmPing = None;
+Atom netSysTraySelection = None;
+Atom netSysTrayOpcode = None;
+Atom xembedInfo = None;
+Atom xembed = None;
 Atom _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */
 
+Window systrayWindow;
+
 static LPCSTR whole_window_atom;
 static LPCSTR client_window_atom;
 static LPCSTR icon_window_atom;
 
+#define MAX_TRAY_ICONS 256
+int undockedTrayIconsCount = 0;
+static HWND undockedTrayIcons[MAX_TRAY_ICONS]; /* stores HWNDs of undocked tray windows when a
+						  NETWM tray window appears, we can dock */
+
+#define SYSTEM_TRAY_REQUEST_DOCK    0
+#define SYSTEM_TRAY_BEGIN_MESSAGE   1
+#define SYSTEM_TRAY_CANCEL_MESSAGE  2
+
+
 /***********************************************************************
  *		is_window_managed
  *
@@ -315,11 +334,81 @@
             size_hints->min_height = size_hints->max_height;
             size_hints->flags |= PMinSize | PMaxSize;
         }
+	if (win->dwExStyle & WS_EX_TRAYWINDOW) {
+	  /* force the window to be the correct width */
+	  size_hints->min_width = GetSystemMetrics(SM_CXSMICON) + 5; /* give some padding to make icons not bunched up */
+	}
         XSetWMNormalHints( display, data->whole_window, size_hints );
         XFree( size_hints );
     }
 }
 
+/***********************************************************************
+ *              X11DRV_systray_dock_window
+ *
+ * Docks the given X window with the NETWM system tray
+ */
+void X11DRV_systray_dock_window( Display *display, HWND hwnd ) {
+  WND* win = WIN_GetPtr(hwnd);
+  struct x11drv_win_data *data = win->pDriverData;  
+  XEvent ev;
+  Window child;
+  XWindowAttributes attrs;
+  int x, y;
+  RECT r;
+  unsigned long info[2];
+
+  TRACE("Docking tray icon 0x%x\n", (int)hwnd);
+  wine_tsx11_lock();
+  
+  /* set XEMBED protocol data on the window */
+  info[0] = 0; /* protocol version */
+  info[1] = 1; /* mapped = true */
+  XChangeProperty(display, data->whole_window, xembedInfo, xembedInfo, 32, PropModeReplace, (unsigned char*)info, 2);
+  
+  /* send the docking request message */
+  memset(&ev, 0, sizeof(ev));
+  ev.xclient.type = ClientMessage;
+  ev.xclient.window = systrayWindow;
+  ev.xclient.message_type = netSysTrayOpcode;
+  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;
+  XSendEvent(display, systrayWindow, False, NoEventMask, &ev);
+  XSync(display, False);
+
+  /* now set sync the X11 window rects to the win32 window rects,
+     so the shell tray code can properly align the icon */
+  
+
+  XTranslateCoordinates(display, data->whole_window, root_window,
+			0, 0, &x, &y, &child);
+  XGetWindowAttributes(display, data->whole_window, &attrs);
+
+  wine_tsx11_unlock();
+
+  r.left = x;    
+  r.top = y;
+  r.right = r.left + attrs.width;
+  r.bottom = r.top + attrs.height;
+
+  WIN_SetRectangles(hwnd, &r, &r);
+  WIN_ReleasePtr(win);
+}
+
+/***********************************************************************
+ *              X11DRV_systray_dock_window
+ *
+ * Docks all undocked tray icons. Usually called when a tray manager appears.
+ */
+void X11DRV_systray_dock_all(Display* display) {
+  TRACE_(systray)("called with %d undocked tray icon(s)\n", undockedTrayIconsCount);
+  while (undockedTrayIconsCount > 0) {
+    X11DRV_systray_dock_window(display, undockedTrayIcons[undockedTrayIconsCount-1]);
+    undockedTrayIconsCount--;
+  }
+}
 
 /***********************************************************************
  *              X11DRV_set_wm_hints
@@ -365,10 +454,12 @@
     /* size hints */
     set_size_hints( display, win );
 
-    /* systray properties (KDE only for now) */
+    /* systray properties */
     if (win->dwExStyle & WS_EX_TRAYWINDOW)
     {
         int val = 1;
+
+	if (systrayWindow == None) {
         if (kwmDockWindow != None)
             XChangeProperty( display, data->whole_window, kwmDockWindow, kwmDockWindow,
                              32, PropModeReplace, (char*)&val, 1 );
@@ -376,6 +467,7 @@
             XChangeProperty( display, data->whole_window, _kde_net_wm_system_tray_window_for,
                              XA_WINDOW, 32, PropModeReplace, (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);
@@ -651,6 +743,7 @@
 static void create_desktop( Display *display, WND *wndPtr )
 {
     X11DRV_WND_DATA *data = wndPtr->pDriverData;
+    char* buffer;
 
     wine_tsx11_lock();
     winContext     = XUniqueContext();
@@ -665,6 +758,19 @@
     _kde_net_wm_system_tray_window_for = XInternAtom( display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False );
     netwmPid = XInternAtom( display, "_NET_WM_PID", False );
     netwmPing = XInternAtom( display, "_NET_WM_PING", False );
+    xembedInfo = XInternAtom( display, "_XEMBED_INFO", False );
+    xembed = XInternAtom( display, "_XEMBED", False );
+    systrayWindow = None;
+    
+    /* get system tray atoms */
+    buffer = malloc(sizeof(char)*20);
+    sprintf(buffer, "_NET_SYSTEM_TRAY_S%d", DefaultScreen(display));
+    TRACE_(systray)("%s\n", buffer);
+    netSysTraySelection = XInternAtom(display, buffer, False);
+    free(buffer);
+    netSysTrayOpcode = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False);
+    wmManager = XInternAtom(display, "MANAGER", False);
+    
     wine_tsx11_unlock();
 
     whole_window_atom  = MAKEINTATOMA( GlobalAddAtomA( "__wine_x11_whole_window" ));
@@ -679,6 +785,8 @@
     SetPropA( wndPtr->hwndSelf, "__wine_x11_visual_id", (HANDLE)XVisualIDFromVisual(visual) );
 
     if (root_window != DefaultRootWindow(display)) X11DRV_create_desktop_thread();
+    /* notify us of manager events, so we can monitor for system tray managers */
+    if (root_window == DefaultRootWindow(display)) XSelectInput(display, root_window, StructureNotifyMask);
 }
 
 
@@ -772,6 +880,7 @@
     attr.backing_store = NotUseful/*WhenMapped*/;
 
     wine_tsx11_lock();
+
     data->client_window = XCreateWindow( display, data->whole_window, 0, 0,
                                          max( rect.right - rect.left, 1 ),
                                          max( rect.bottom - rect.top, 1 ),
@@ -961,7 +1070,7 @@
     }
 
     /* Send the WM_GETMINMAXINFO message and fix the size if needed */
-    if ((cs->style & WS_THICKFRAME) || !(cs->style & (WS_POPUP | WS_CHILD)))
+    if (((cs->style & WS_THICKFRAME) || !(cs->style & (WS_POPUP | WS_CHILD))) && !(cs->dwExStyle & WS_EX_TRAYWINDOW))
     {
         POINT maxSize, maxPos, minTrack, maxTrack;
 
@@ -1056,7 +1165,6 @@
     }
 
     /* Show the window, maximizing or minimizing if needed */
-
     if (wndPtr->dwStyle & (WS_MINIMIZE | WS_MAXIMIZE))
     {
         extern UINT WINPOS_MinMaximize( HWND hwnd, UINT cmd, LPRECT rect ); /*FIXME*/
@@ -1076,6 +1184,14 @@
      * we do a proper ShowWindow later on */
     if (wndPtr->dwStyle & WS_VISIBLE) cs->style |= WS_VISIBLE;
 
+    /* if it's a tray window, dock it */
+    if (wndPtr->dwExStyle & WS_EX_TRAYWINDOW) {
+      /* get the tray window if present */
+      systrayWindow = XGetSelectionOwner(display, netSysTraySelection);
+      if (systrayWindow == None)
+	undockedTrayIcons[undockedTrayIconsCount++] = hwnd;
+      else X11DRV_systray_dock_window(display, hwnd);
+    }
     WIN_ReleaseWndPtr( wndPtr );
     return TRUE;
 
Index: ./dlls/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/event.c,v
retrieving revision 1.20
diff -u -b -r1.20 event.c
--- ./dlls/x11drv/event.c	19 May 2003 19:07:32 -0000	1.20
+++ ./dlls/x11drv/event.c	5 Jun 2003 21:55:18 -0000
@@ -34,6 +34,7 @@
 
 #include <assert.h>
 #include <string.h>
+#include <stdio.h>
 #include "wine/winuser16.h"
 #include "shlobj.h"  /* DROPFILES */
 
@@ -47,15 +48,21 @@
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(event);
+WINE_DECLARE_DEBUG_CHANNEL(systray);
 
 /* X context to associate a hwnd to an X window */
 extern XContext winContext;
 
 extern Atom wmProtocols;
 extern Atom wmDeleteWindow;
+extern Atom wmManager;
 extern Atom dndProtocol;
 extern Atom dndSelection;
 extern Atom netwmPing;
+extern Atom netSysTraySelection;
+extern Atom xembed;
+
+extern Window systrayWindow;
 
 #define DndNotDnd       -1    /* OffiX drag&drop */
 #define DndUnknown      0
@@ -105,6 +112,7 @@
 extern void X11DRV_UnmapNotify( HWND hwnd, XUnmapEvent *event );
 extern void X11DRV_ConfigureNotify( HWND hwnd, XConfigureEvent *event );
 extern void X11DRV_MappingNotify( XMappingEvent *event );
+extern void X11DRV_systray_dock_all( Display* display );
 
 #ifdef HAVE_LIBXXF86DGA2
 static int DGAMotionEventType;
@@ -275,7 +283,8 @@
 
   if ( !hWnd && event->xany.window != root_window
              && event->type != PropertyNotify
-             && event->type != MappingNotify)
+             && event->type != MappingNotify
+             && event->type != ClientMessage)
       WARN( "Got event %s for unknown Window %08lx\n",
             event_names[event->type], event->xany.window );
   else
@@ -338,7 +347,6 @@
       break;
 
     case ClientMessage:
-      if (!hWnd) return;
       EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
       break;
 
@@ -1431,8 +1439,39 @@
  */
 static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
 {
+  TRACE("called\n");
   if (event->message_type != None && event->format == 32) {
-    if (event->message_type == wmProtocols)
+    if (event->message_type == wmManager) {
+	if (event->data.l[1] == netSysTraySelection) {
+	    TRACE_(systray)("New NETWM systray manager detected, id=%ld\n", event->data.l[2]);
+	    if (systrayWindow == None) {
+	      /* only update when there was no previous tray manager, if it's just a replacement ignore the event */
+	      systrayWindow = event->data.l[2];
+	      TRACE_(systray)("docking all to %d\n", (int)systrayWindow);
+	      X11DRV_systray_dock_all(event->display);
+	    }
+	}
+    } else if (event->message_type == xembed) {
+        char* opcode;
+	switch (event->data.l[1]) {
+	  case 0: opcode = "XEMBED_EMBEDDED_NOTIFY"; break;
+	  case 1: opcode = "XEMBED_WINDOW_ACTIVATE"; break;
+	  case 2: opcode = "XEMBED_WINDOW_DEACTIVATE"; break;
+	  case 3: opcode = "XEMBED_REQUEST_FOCUS"; break;
+	  case 4: opcode = "XEMBED_FOCUS_IN"; break;
+	  case 5: opcode = "XEMBED_FOCUS_OUT"; break;
+	  case 6: opcode = "XEMBED_FOCUS_NEXT"; break;
+	  case 7: opcode = "XEMEBD_FOCUS_PREV"; break;
+	  case 10: opcode = "XEMBED_MODALITY_ON"; break;
+	  case 11: opcode = "XEMBED_MODALITY_OFF"; break;
+	  case 12: opcode = "XEMBED_REGISTER_ACCELERATOR"; break;
+	  case 13: opcode = "XEMBED_UNREGISTER_ACCELERATOR"; break;
+	  case 14: opcode = "XEMBED_ACTIVATE_ACCELERATOR"; break;
+	  default: opcode = "[Unknown opcode]"; break;
+	}
+        TRACE_(systray)("XEmbed message, opcode is %s : %ld\n", opcode, event->data.l[1]);
+	/* we currently don't handle these messages */
+    } else if (event->message_type == wmProtocols)
         handle_wm_protocols_message( hWnd, event );
     else if (event->message_type == dndProtocol)
     {
Index: ./dlls/shell32/systray.c
===================================================================
RCS file: /home/wine/wine/dlls/shell32/systray.c,v
retrieving revision 1.20
diff -u -b -r1.20 systray.c
--- ./dlls/shell32/systray.c	24 Nov 2002 22:16:29 -0000	1.20
+++ ./dlls/shell32/systray.c	5 Jun 2003 21:55:18 -0000
@@ -37,7 +37,7 @@
 #include "commctrl.h"
 #include "wine/debug.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(shell);
+WINE_DEFAULT_DEBUG_CHANNEL(systray);
 
 typedef struct SystrayItem {
   HWND                  hWnd;
@@ -75,13 +75,16 @@
   {
     RECT rc;
     SystrayItem  *ptrayItem = systray;
+    int top, left;
 
     while (ptrayItem) {
       if (ptrayItem->hWnd==hWnd) {
 	if (ptrayItem->notifyIcon.hIcon) {
 	  hdc = BeginPaint(hWnd, &ps);
 	  GetClientRect(hWnd, &rc);
-	  if (!DrawIconEx(hdc, rc.left+ICON_BORDER, rc.top+ICON_BORDER, ptrayItem->notifyIcon.hIcon,
+	  /* calculate top and left so we can deal with arbitrary sized trays */
+	  top = ((rc.bottom-rc.top)/2) - ((ICON_SIZE)/2);
+	  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);
 	    SYSTRAY_Delete(&ptrayItem->notifyIcon);

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux