bump ChangeLog: Implement XEMBED based system tray support for KDE 3.1+ and GNOME 2.2+
Index: dlls/shell32/systray.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/systray.c,v retrieving revision 1.20 diff -u -r1.20 systray.c --- dlls/shell32/systray.c 24 Nov 2002 22:16:29 -0000 1.20 +++ dlls/shell32/systray.c 23 Jun 2003 19:37: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; + 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: ./dlls/x11drv/window.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/window.c,v retrieving revision 1.54 diff -u -r1.54 window.c --- ./dlls/x11drv/window.c 20 Jun 2003 23:29:06 -0000 1.54 +++ ./dlls/x11drv/window.c 23 Jun 2003 19:37:20 -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,16 +454,19 @@ /* 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 (kwmDockWindow != None) + + if (systrayWindow == None) { + if (kwmDockWindow != None) XChangeProperty( display, data->whole_window, kwmDockWindow, kwmDockWindow, 32, PropModeReplace, (char*)&val, 1 ); - if (_kde_net_wm_system_tray_window_for != None) + if (_kde_net_wm_system_tray_window_for != None) 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 */ @@ -403,7 +495,7 @@ if (win->dwStyle & WS_SYSMENU) mwm_hints.decorations |= MWM_DECOR_MENU; if (win->dwStyle & WS_MINIMIZEBOX) mwm_hints.decorations |= MWM_DECOR_MINIMIZE; if (win->dwStyle & WS_MAXIMIZEBOX) mwm_hints.decorations |= MWM_DECOR_MAXIMIZE; - + XChangeProperty( display, data->whole_window, mwmHints, mwmHints, 32, PropModeReplace, (char*)&mwm_hints, sizeof(mwm_hints)/sizeof(long) ); } @@ -651,7 +743,8 @@ static void create_desktop( Display *display, WND *wndPtr ) { X11DRV_WND_DATA *data = wndPtr->pDriverData; - + char* buffer; + wine_tsx11_lock(); winContext = XUniqueContext(); wmProtocols = XInternAtom( display, "WM_PROTOCOLS", False ); @@ -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 ), @@ -966,7 +1075,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 +1170,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 +1189,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 +1364,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.20 diff -u -r1.20 event.c --- ./dlls/x11drv/event.c 19 May 2003 19:07:32 -0000 1.20 +++ ./dlls/x11drv/event.c 23 Jun 2003 19:37:22 -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) {