System tray integration

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

 



The code that caused the patch to be dropped last time has been removed
- the tray applet in Red Hat 9 doesn't appear to need it, as it always
sets the height of the window to roughly what was asked for. Or at
least, I can't make it grow vertically again.

Known issues:
- Heisenbug where it sometimes won't dock correctly. Unfortunately, I
can't duplicate this with any reliability.
- Background colour doesn't match the tray applet, but I think this is
just a generic problem with our tray protocol sucking :)
- Apparently on Windows if you click the desktop any popped up tray
menus disappear. I can quite believe this, but I don't know how to
replicate it in this code, as unlike in Windows the desktop is not
controlled by the same code that runs the tray.

Probably some others I also forgot.

ChangeLog:
Add support for XEMBED based system tray protocol

thanks -mike
Index: dlls/x11drv/window.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/window.c,v
retrieving revision 1.54
diff -u -b -w -r1.54 window.c
--- dlls/x11drv/window.c	20 Jun 2003 23:29:06 -0000	1.54
+++ dlls/x11drv/window.c	13 Jul 2003 12:47:12 -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,66 @@
             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);
+  wine_tsx11_unlock();
+  
+  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 +439,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 +452,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);
@@ -534,6 +611,7 @@
                 prev = GetWindow( prev, GW_HWNDPREV );
             if (!prev)  /* top child */
             {
+		TRACE("Top Child\n");
                 changes.stack_mode = Above;
                 mask |= CWStackMode;
             }
@@ -542,6 +620,9 @@
                 /* should use stack_mode Below but most window managers don't get it right */
                 /* so move it above the next one in Z order */
                 HWND next = GetWindow( win->hwndSelf, GW_HWNDNEXT );
+
+		TRACE("Getting next window\n");
+		
                 while (next && !(GetWindowLongW( next, GWL_STYLE ) & WS_VISIBLE))
                     next = GetWindow( next, GW_HWNDNEXT );
                 if (next)
@@ -557,6 +638,7 @@
             HWND next = GetWindow( win->hwndSelf, GW_HWNDNEXT );
             if (!next)  /* bottom child */
             {
+		TRACE("bottom child\n");
                 changes.stack_mode = Below;
                 mask |= CWStackMode;
             }
@@ -564,6 +646,7 @@
             {
                 changes.stack_mode = Above;
                 changes.sibling = X11DRV_get_whole_window(next);
+		TRACE("0x%x is above something else, next=0x%x, sibling=0x%x\n", win->hwndSelf, next, changes.sibling);
                 mask |= CWStackMode | CWSibling;
             }
         }
@@ -651,6 +734,7 @@
 static void create_desktop( Display *display, WND *wndPtr )
 {
     X11DRV_WND_DATA *data = wndPtr->pDriverData;
+    char* buffer;
 
     wine_tsx11_lock();
     winContext     = XUniqueContext();
@@ -665,6 +749,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 +776,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 +871,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 ),
@@ -966,7 +1066,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;
 
@@ -1061,7 +1161,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*/
@@ -1081,6 +1180,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;
 
@@ -1248,8 +1355,9 @@
 
         /* we must not use CurrentTime (ICCCM), so try to use last message time instead */
         /* FIXME: this is not entirely correct */
+        /* NOTE - not using CurrentTime here causes focus problems with some fullscreen apps */
         XSetInputFocus( display, win, RevertToParent,
-                        /*CurrentTime*/ GetMessageTime() + X11DRV_server_startticks );
+                        CurrentTime /*GetMessageTime() + X11DRV_server_startticks*/ );
         if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE)
             XInstallColormap( display, X11DRV_PALETTE_PaletteXColormap );
     }
Index: dlls/x11drv/event.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/event.c,v
retrieving revision 1.21
diff -u -b -w -r1.21 event.c
--- dlls/x11drv/event.c	23 Jun 2003 23:02:03 -0000	1.21
+++ dlls/x11drv/event.c	13 Jul 2003 12:47:13 -0000
@@ -34,6 +34,7 @@
 
 #include <assert.h>
 #include <string.h>
+#include <stdio.h>
 #include "wine/winuser16.h"
 #include "shlobj.h"  /* DROPFILES */
 
@@ -47,6 +48,7 @@
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(event);
+WINE_DECLARE_DEBUG_CHANNEL(systray);
 WINE_DECLARE_DEBUG_CHANNEL(clipboard);
 
 /* X context to associate a hwnd to an X window */
@@ -54,9 +56,14 @@
 
 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
@@ -106,6 +113,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,10 @@
   wine_tsx11_unlock();
   if (!hWnd && event->xany.window == root_window) hWnd = GetDesktopWindow();
 
-  if (!hWnd && event->type != PropertyNotify && event->type != MappingNotify)
+  if ( !hWnd && event->xany.window != root_window
+             && event->type != PropertyNotify
+             && 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 +349,6 @@
       break;
 
     case ClientMessage:
-      if (!hWnd) return;
       EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
       break;
 
@@ -1201,8 +1211,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 -w -r1.20 systray.c
--- dlls/shell32/systray.c	24 Nov 2002 22:16:29 -0000	1.20
+++ dlls/shell32/systray.c	13 Jul 2003 12:47:14 -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;
 
     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 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