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);