? dlls/x11drv/wineclipsrv Index: dlls/x11drv/event.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/event.c,v retrieving revision 1.37 diff -u -p -r1.37 event.c --- dlls/x11drv/event.c 29 Sep 2004 21:11:28 -0000 1.37 +++ dlls/x11drv/event.c 30 Nov 2004 00:12:52 -0000 @@ -3,6 +3,7 @@ * * Copyright 1993 Alexandre Julliard * 1999 Noel Borthwick + * 2003 Mike Hearn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,10 +51,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(event); WINE_DECLARE_DEBUG_CHANNEL(clipboard); +WINE_DECLARE_DEBUG_CHANNEL(systray); /* X context to associate a hwnd to an X window */ extern XContext winContext; +extern Atom systray_selection; +extern Window systray_window; + extern BOOL ximInComposeMode; #define DndNotDnd -1 /* OffiX drag&drop */ @@ -1248,7 +1253,57 @@ static void EVENT_DropURLs( HWND hWnd, X static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event ) { if (event->message_type != None && event->format == 32) { - if (event->message_type == x11drv_atom(WM_PROTOCOLS)) + if (event->message_type == x11drv_atom(MANAGER)) { + if (event->data.l[1] == systray_selection) { + TRACE_(systray)("New NETWM systray manager detected, id=%ld\n", event->data.l[2]); + + /* NOTE: It turns out that the ability to detect when a + * new tray applet joins the desktop is not as helpful as + * you might think. In order to do something useful with + * it, we would need to be able to "store" icons unmapped + * as children of the root window while no tray applet is + * available. + * + * The basic problem is that tray icons are always + * destroyed when the applet is removed. This is + * apparently an issue with X itself, which the upcoming + * XFIXES extension should hopefully address. The + * EggTrayIcon code which will be soon moving into GTK+ + * doesn't attempt to handle this situation, so for now + * neither do we. + * + * This is theoretically fixable in Wine with enough work, + * we just have to modify the code in shell32/systray.c to + * save the image and recreate the window on + * demand. Exactly how the communication between the + * x11drv and shell32 takes place is left as an excercise + * for the reader. + * + * -mike (3rd August 2003) + */ + } + } else if (event->message_type == x11drv_atom(_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 == x11drv_atom(WM_PROTOCOLS)) handle_wm_protocols_message( hWnd, event ); else if (event->message_type == x11drv_atom(DndProtocol)) { Index: dlls/x11drv/window.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/window.c,v retrieving revision 1.81 diff -u -p -r1.81 window.c --- dlls/x11drv/window.c 22 Sep 2004 19:14:19 -0000 1.81 +++ dlls/x11drv/window.c 30 Nov 2004 00:12:54 -0000 @@ -4,6 +4,7 @@ * Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard * Copyright 1993 David Metcalfe * Copyright 1995, 1996 Alex Korobka + * Copyright 2003 Mike Hearn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,6 +25,7 @@ #include #include +#include #ifdef HAVE_UNISTD_H # include #endif @@ -47,6 +49,7 @@ #include "mwm.h" WINE_DEFAULT_DEBUG_CHANNEL(x11drv); +WINE_DECLARE_DEBUG_CHANNEL(systray); extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP ); @@ -70,6 +73,7 @@ static const char * const atom_names[NB_ "WM_PROTOCOLS", "WM_DELETE_WINDOW", "WM_TAKE_FOCUS", + "MANAGER", "KWM_DOCKWINDOW", "DndProtocol", "DndSelection", @@ -79,6 +83,9 @@ static const char * const atom_names[NB_ "_NET_WM_PID", "_NET_WM_PING", "_NET_WM_NAME", + "_XEMBED_INFO", + "_XEMBED", + "_NET_SYSTEM_TRAY_OPCODE", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_UTILITY", "XdndAware", @@ -104,6 +111,14 @@ static const char * const atom_names[NB_ "text/richtext" }; +/* for XDG systray icons */ +Atom systray_selection; +Window systray_window; +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + + static LPCSTR whole_window_atom; static LPCSTR client_window_atom; static LPCSTR icon_window_atom; @@ -382,11 +397,72 @@ static void set_size_hints( Display *dis 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 ); } } +/*********************************************************************** + * systray_dock_window + * + * Docks the given X window with the NETWM system tray. + */ +static BOOL systray_dock_window( HWND hwnd, Display *display ) +{ + WND *win = WIN_GetPtr(hwnd); + struct x11drv_win_data *data = win->pDriverData; + XEvent ev; + unsigned long info[2]; + LONG exstyle; + + /* is the window a tray window? */ + if (IsWindowUnicode(hwnd)) + { + exstyle = GetWindowLongW(hwnd, GWL_EXSTYLE); + } + else + { + exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE); + } + + if ( !(exstyle & WS_EX_TRAYWINDOW) ) + { + WIN_ReleasePtr(win); + return TRUE; + } + + TRACE_(systray)("Docking tray icon %p\n", hwnd); + + /* set XEMBED protocol data on the window */ + info[0] = 0; /* protocol version */ + info[1] = 0; /* mapped = true */ + XChangeProperty(display, data->whole_window, x11drv_atom(_XEMBED_INFO), + x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, + (unsigned char*)info, 2); + + /* 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; + XSendEvent(display, systray_window, False, NoEventMask, &ev); + XSync(display, False); + + WIN_ReleasePtr(win); + return TRUE; +} + + /*********************************************************************** * X11DRV_set_wm_hints @@ -436,7 +512,7 @@ void X11DRV_set_wm_hints( Display *displ set_size_hints( display, win ); /* systray properties (KDE only for now) */ - if (win->dwExStyle & WS_EX_TRAYWINDOW) + if ((win->dwExStyle & WS_EX_TRAYWINDOW) && (systray_window == None)) { int val = 1; XChangeProperty( display, data->whole_window, x11drv_atom(KWM_DOCKWINDOW), @@ -789,10 +865,19 @@ void X11DRV_set_window_rectangles( HWND static void create_desktop( Display *display, WND *wndPtr ) { X11DRV_WND_DATA *data = wndPtr->pDriverData; - + char *systray_buffer; + wine_tsx11_lock(); winContext = XUniqueContext(); XInternAtoms( display, (char **)atom_names, NB_XATOMS - FIRST_XATOM, False, X11DRV_Atoms ); + + /* we can't intern this with the rest as it depends on the screen we are connecting to */ + systray_buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(char)*20); + sprintf(systray_buffer, "_NET_SYSTEM_TRAY_S%d", DefaultScreen(display)); + systray_selection = XInternAtom(display, systray_buffer, False); + HeapFree(GetProcessHeap(), 0, systray_buffer); + + wine_tsx11_unlock(); whole_window_atom = MAKEINTATOMA( GlobalAddAtomA( "__wine_x11_whole_window" )); @@ -1179,6 +1264,14 @@ BOOL X11DRV_CreateWindow( HWND hwnd, CRE newPos.right, newPos.bottom, swFlag ); } + /* if it's a tray window, dock it */ + if (wndPtr->dwExStyle & WS_EX_TRAYWINDOW) + { + /* get the tray window if present */ + systray_window = XGetSelectionOwner(display, systray_selection); + if (systray_window != None) systray_dock_window(hwnd, display); + } + WIN_ReleaseWndPtr( wndPtr ); return TRUE; Index: dlls/x11drv/x11drv.h =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11drv.h,v retrieving revision 1.36 diff -u -p -r1.36 x11drv.h --- dlls/x11drv/x11drv.h 16 Sep 2004 19:10:14 -0000 1.36 +++ dlls/x11drv/x11drv.h 30 Nov 2004 00:12:55 -0000 @@ -416,6 +416,7 @@ enum x11drv_atoms XATOM_WM_PROTOCOLS, XATOM_WM_DELETE_WINDOW, XATOM_WM_TAKE_FOCUS, + XATOM_MANAGER, XATOM_KWM_DOCKWINDOW, XATOM_DndProtocol, XATOM_DndSelection, @@ -425,6 +426,9 @@ enum x11drv_atoms XATOM__NET_WM_PID, XATOM__NET_WM_PING, XATOM__NET_WM_NAME, + XATOM__XEMBED_INFO, + XATOM__XEMBED, + XATOM__NET_SYSTEM_TRAY_OPCODE, XATOM__NET_WM_WINDOW_TYPE, XATOM__NET_WM_WINDOW_TYPE_UTILITY, XATOM_XdndAware, --- /dev/null 2004-11-29 17:35:34.335853480 +0000 +++ programs/wineshell/Makefile.in 2004-11-27 22:12:01.000000000 +0000 @@ -0,0 +1,16 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wineshell.exe +APPMODE = -mconsole +IMPORTS = advapi32 kernel32 user32 gdi32 + +C_SRCS = \ + wineshell.c \ + systray.c + + +@MAKE_PROG_RULES@ + +### Dependencies: --- /dev/null 2004-11-29 17:35:34.335853480 +0000 +++ programs/wineshell/wineshell.c 2004-11-29 23:59:37.613375672 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2004 Mike Hearn, for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This program is a general purpose bridge between the Windows + * environment and native. Currently it deals with system tray + * windows, in future it may take on other functions that the standard + * windows shell (explorer) deals with, for instance responding to DDE + * requests and syncing the wallpaper with native. + */ + +#include +#include + +#include +#include + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(wineshell); + +unsigned int shell_refs = 0; + +void *xmalloc(unsigned int size) +{ + void *p = HeapAlloc(GetProcessHeap(), 0, size); + + if (!p) + { + fprintf(stderr, "wineshell: virtual memory exhausted\n"); + exit(1); + } + + ZeroMemory(p, size); + + return p; +} + +int main(int argc, char *argv[]) +{ + initialize_systray(); + + while (TRUE) + { + const int timeout = 5; + MSG message; + DWORD res; + + res = MsgWaitForMultipleObjectsEx(0, NULL, shell_refs ? INFINITE : timeout * 1000, + QS_ALLINPUT, MWMO_WAITALL); + if (res == WAIT_TIMEOUT) break; + + res = PeekMessage(&message, 0, 0, 0, PM_REMOVE); + if (!res) continue; + + if (message.message == WM_QUIT) + { + WINE_FIXME("Somebody sent the shell a WM_QUIT message, should we reboot?"); + + /* Sending the tray window a WM_QUIT message is actually a + * tip given by some programming websites as a way of + * forcing a reboot! let's delay implementing this hack + * until we find a program that really needs it. for now + * just bail out. + */ + + break; + } + + TranslateMessage(&message); + DispatchMessage(&message); + } + + shutdown_systray(); + + return 0; +} --- /dev/null 2004-11-29 17:35:34.335853480 +0000 +++ programs/wineshell/wineshell.h 2004-11-29 23:21:15.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2004 Mike Hearn, for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void initialize_systray(); +void shutdown_systray(); +void *xmalloc(unsigned int size); + +/* when this drops to zero, a few seconds later the shell will shut down */ +extern unsigned int shell_refs; --- /dev/null 2004-11-29 17:35:34.335853480 +0000 +++ programs/wineshell/systray.c 2004-11-30 00:15:16.543636576 +0000 @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2004 Mike Hearn, for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* There are two types of window involved here. The first is the + * listener window. This is like the taskbar in Windows. It doesn't + * ever appear on-screen in our implementation, instead we create + * individual mini "adaptor" windows which are docked by the native + * systray host. + * + * In future for those who don't have a systray we could make the + * listener window more clever so it can draw itself like the Windows + * tray area does (with a clock and stuff). + */ + +#include + +#define UNICODE +#define _WIN32_IE 0x500 +#include +#include + +#include +#include +#include + +#include "wineshell.h" + +WINE_DEFAULT_DEBUG_CHANNEL(systray); + +const static WCHAR adaptor_classname[] = /* Adaptor */ {'A','d','a','p','t','o','r',0}; + +struct request { + NOTIFYICONDATAA ansi; + NOTIFYICONDATAW wide; + BOOL unicode; + BYTE *copydata; + + /* some convenience fields */ + HWND owner; + UINT id; + DWORD flags; +}; + +/* tray state */ +struct tray +{ + HWND window; + struct list icons; +}; + +/* an individual systray icon, unpacked from the NOTIFYICONDATA and always in unicode */ +struct icon +{ + struct list entry; + HICON image; /* the image to render */ + HWND owner; /* the HWND passed in to the Shell_NotifyIcon call */ + HWND window; /* the adaptor window */ + UINT id; /* the unique id given by the app */ + UINT callback_message; +}; + +static struct tray tray; + + + +/* adaptor code */ + +#define ICON_SIZE GetSystemMetrics(SM_CXSMICON) +/* space around icon (forces icon to center of KDE systray area) */ +#define ICON_BORDER 4 + +static LRESULT WINAPI adaptor_wndproc(HWND window, UINT msg, + WPARAM wparam, LPARAM lparam) +{ + struct icon *icon = NULL; + + WINE_TRACE("hwnd=%p, msg=0x%x\n", window, msg); + + icon = (struct icon *) GetWindowLong(window, GWL_USERDATA); + + if (!icon) /* not initialized yet */ + return DefWindowProc(window, msg, wparam, lparam); + + switch (msg) + { + case WM_MOVE: + case WM_SIZE: + case WM_WINDOWPOSCHANGING: + /* ignore */ + return 0; + + case WM_PAINT: + { + RECT rc; + int top; + PAINTSTRUCT ps; + HDC hdc; + + WINE_TRACE("painting\n"); + + hdc = BeginPaint(window, &ps); + GetClientRect(window, &rc); + + /* calculate top so we can deal with arbitrary sized trays */ + top = ((rc.bottom-rc.top)/2) - ((ICON_SIZE)/2); + + DrawIconEx(hdc, (ICON_BORDER/2), top, icon->image, + ICON_SIZE, ICON_SIZE, 0, 0, DI_DEFAULTSIZE|DI_NORMAL); + + EndPaint(window, &ps); + break; + } + + + case WM_WINDOWPOSCHANGED: + { + ShowWindow(window, SW_SHOW); + return 0; + } + + + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + { + /* notify the owner hwnd of the message */ + WINE_TRACE("relaying 0x%x\n", msg); + PostMessage(icon->owner, icon->callback_message, (WPARAM) icon->id, (LPARAM) msg); + return 0; + } + } + + return DefWindowProc(window, msg, wparam, lparam); +} + + +/* listener code */ + +static struct icon *get_icon(HWND owner, UINT id) +{ + struct list *cursor; + + /* search for the icon */ + LIST_FOR_EACH( cursor, &tray.icons ) + { + struct icon *this = LIST_ENTRY(cursor, struct icon, entry); + + if ((this->id == id) && (this->owner = owner)) return this; + } + + return NULL; +} + +static void modify_icon(struct request *req) +{ + struct icon *icon; + HICON16 hicon; + BYTE *iconbits; + CURSORICONINFO *cii; + DWORD ciisize; + + WINE_TRACE("id=0x%x, hwnd=%p\n", req->id, req->owner); + + /* demarshal the request from the NID */ + icon = get_icon(req->owner, req->id); + if (!icon) + { + WINE_WARN("Invalid icon ID (0x%x) for HWND %p\n", req->id, req->owner); + return; + } + + if ((req->unicode ? req->wide.uFlags : req->ansi.uFlags) & NIF_ICON) + { + WINE_TRACE("icon is specified\n"); + + /* demarshal the icon */ + if ((req->unicode && (req->wide.hIcon != (HICON) -1)) || + (!req->unicode && (req->ansi.hIcon != (HICON) -1))) + { + WINE_ERR("You cannot use native shell32 at this time for system tray integration\n"); + return; + } + + cii = (CURSORICONINFO *) (req->copydata + (req->unicode ? req->wide.cbSize : req->ansi.cbSize)); + ciisize = sizeof(CURSORICONINFO) + ((cii->nHeight * cii->nWidthBytes) * 2); + + WINE_TRACE("cii->nHeight = %d, cii->nWidthBytes = %d, ciisize = %ld\n", + cii->nHeight, cii->nWidthBytes, ciisize); + + hicon = GlobalAlloc16(GMEM_MOVEABLE, ciisize); + /* FIXME: do we need to do a FarSetOwner here? */ + iconbits = GlobalLock16(hicon); + memcpy(iconbits, cii, ciisize); + GlobalUnlock16(hicon); + + if (icon->image) DestroyIcon(icon->image); + icon->image = HICON_32(hicon); + + RedrawWindow(icon->window, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW); + } + + if ((req->unicode ? req->wide.uFlags : req->ansi.uFlags) & NIF_MESSAGE) + { + icon->callback_message = req->unicode? req->wide.uCallbackMessage : req->ansi.uCallbackMessage; + } +} + +static void add_icon(struct request *req) +{ + struct icon *icon; + const static 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}; + + WINE_TRACE("id=0x%x, hwnd=%p\n", req->id, req->owner); + + if ((icon = get_icon(req->owner, req->id))) + { + WINE_WARN("duplicate tray icon add, buggy app?\n"); + return; + } + + icon = xmalloc(sizeof(struct icon)); + icon->id = req->id; + icon->owner = req->owner; + icon->image = NULL; + + /* create the adaptor window */ + icon->window = CreateWindowEx(WS_EX_TRAYWINDOW, + (LPCWSTR) &adaptor_classname, + (LPCWSTR) &adaptor_windowname, + 0, CW_USEDEFAULT, CW_USEDEFAULT, + 16, 16, + 0, 0, 0, 0); + + SetWindowLong(icon->window, GWL_USERDATA, (LONG) icon); + + list_add_tail(&(tray.icons), &icon->entry); + + modify_icon(req); + + shell_refs++; + WINE_TRACE("shell now has %d refs\n", shell_refs); +} + +static void delete_icon(struct request *req) +{ + struct icon *icon = get_icon(req->owner, req->id); + + WINE_TRACE("id=0x%x, hwnd=%p\n", req->id, req->owner); + + if (!icon) + { + WINE_ERR("invalid tray icon ID specified: %ud\n", req->id); + return; + } + + list_remove(&icon->entry); + + DestroyIcon(icon->image); + if (!DestroyWindow(icon->window)) WINE_ERR("Could not destroy window!\n"); + + HeapFree(GetProcessHeap(), 0, icon); + + shell_refs--; + WINE_TRACE("shell now has %d refs\n", shell_refs); +} + +static void handle_incoming(WPARAM wparam, LPARAM lparam) +{ + COPYDATASTRUCT *cds = (COPYDATASTRUCT *) lparam; + + struct protocol + { + DWORD cookie; + DWORD code; + DWORD data[1]; + } *protocol = (struct protocol *) cds->lpData; + + struct request req; + + WINE_TRACE("cookie = %ld\n", protocol->cookie); + WINE_TRACE("notify_code = %ld\n", protocol->code); + + if (((NOTIFYICONDATAW *)&protocol->data[0])->cbSize == sizeof(NOTIFYICONDATAW)) + { + WINE_TRACE("unicode\n"); + + memcpy(&req.wide, &protocol->data[0], sizeof(NOTIFYICONDATAW)); + req.unicode = TRUE; + } + else + { + WINE_TRACE("ansi\n"); + + memcpy(&req.ansi, &protocol->data[0], sizeof(NOTIFYICONDATAA)); + req.unicode = FALSE; + } + + /* these are just convenience accessors, as we'll need them frequently */ + req.owner = req.unicode ? req.wide.hWnd : req.ansi.hWnd; + req.id = req.unicode ? req.wide.uID : req.ansi.uID; + req.copydata = (BYTE *) &protocol->data; + + if (protocol->code == NIM_ADD) add_icon(&req); + else if (protocol->code == NIM_DELETE) delete_icon(&req); + else if (protocol->code == NIM_MODIFY) modify_icon(&req); + else + { + WINE_WARN("unhandled tray message: %ld\n", protocol->code); + } + +} + +static LRESULT WINAPI listener_wndproc(HWND window, UINT msg, + WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_COPYDATA: + handle_incoming(wparam, lparam); + break; + } + return DefWindowProc(window, msg, wparam, lparam); +} + + +/* this function creates the the listener window */ +void initialize_systray() +{ + WNDCLASSEX class; + const static WCHAR classname[] = /* Shell_TrayWnd */ {'S','h','e','l','l','_','T','r','a','y','W','n','d',0}; + const static WCHAR winname[] = /* Wine Systray Listener */ {'W','i','n','e',' ','S','y','s','t','r','a','y',' ','L','i','s','t','e','n','e','r',0}; + + WINE_TRACE(" :: go go gadget systray!\n"); + + list_init(&(tray.icons)); + + /* register the systray listener window class */ + ZeroMemory(&class, sizeof(class)); + class.cbSize = sizeof(class); + class.lpfnWndProc = &listener_wndproc; + class.hInstance = NULL; + class.hIcon = LoadIcon(0, IDI_WINLOGO); + class.hCursor = LoadCursor(0, IDC_ARROW); + class.hbrBackground = (HBRUSH) COLOR_WINDOW; + class.lpszClassName = (WCHAR *) &classname; + + if (!RegisterClassEx(&class)) + { + WINE_ERR("Could not register SysTray window class\n"); + return; + } + + /* now register the adaptor window class */ + ZeroMemory(&class, sizeof(class)); + class.cbSize = sizeof(class); + class.lpfnWndProc = &adaptor_wndproc; + class.hInstance = NULL; + class.hIcon = LoadIcon(0, IDI_WINLOGO); + class.hCursor = LoadCursor(0, IDC_ARROW); + class.hbrBackground = (HBRUSH) COLOR_WINDOW; + class.lpszClassName = (WCHAR *) &adaptor_classname; + class.style = CS_SAVEBITS | CS_DBLCLKS; + + if (!RegisterClassEx(&class)) + { + WINE_ERR("Could not register adaptor class\n"); + return; + } + + tray.window = CreateWindow((WCHAR *)&classname, (WCHAR *)&winname, + WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, 0, 0, 0, 0); + + if (!tray.window) + { + WINE_ERR("Could not create tray window\n"); + return; + } + + /* FIXME: implement tooltips */ +} + +void shutdown_systray() +{ + WINE_TRACE(" :: next time gadget .... next time \n"); + + DestroyWindow(tray.window); +} Index: configure.ac =================================================================== RCS file: /home/wine/wine/configure.ac,v retrieving revision 1.328 diff -u -p -r1.328 configure.ac --- configure.ac 23 Nov 2004 17:33:56 -0000 1.328 +++ configure.ac 30 Nov 2004 00:14:19 -0000 @@ -1788,6 +1788,7 @@ programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile +programs/wineshell/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile Index: dlls/shell32/shell32_main.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shell32_main.c,v retrieving revision 1.139 diff -u -p -r1.139 shell32_main.c --- dlls/shell32/shell32_main.c 7 Oct 2004 03:06:48 -0000 1.139 +++ dlls/shell32/shell32_main.c 30 Nov 2004 14:10:04 -0000 @@ -959,7 +959,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, InitCommonControlsEx(NULL); SIC_Initialize(); - SYSTRAY_Init(); InitChangeNotifications(); break; Index: dlls/shell32/systray.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/systray.c,v retrieving revision 1.28 diff -u -p -r1.28 systray.c --- dlls/shell32/systray.c 6 Dec 2004 11:51:29 -0000 1.28 +++ dlls/shell32/systray.c 6 Dec 2004 19:21:37 -0000 @@ -1,11 +1,8 @@ /* - * Systray + * Systray handling * - * Copyright 1999 Kai Morich - * - * Manage the systray window. That it actually appears in the docking - * area of KDE is handled in dlls/x11drv/window.c, - * X11DRV_set_wm_hints using KWM_DOCKWINDOW. + * Copyright 1999 Kai Morich + * Copyright 2004 Mike Hearn, for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,336 +20,116 @@ */ #include "config.h" +#include -#ifdef HAVE_UNISTD_H -# include -#endif -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "winnls.h" -#include "wingdi.h" -#include "winuser.h" -#include "shlobj.h" -#include "shellapi.h" -#include "shell32_main.h" -#include "commctrl.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(shell); - -typedef struct SystrayItem { - HWND hWnd; - HWND hWndToolTip; - NOTIFYICONDATAA notifyIcon; - struct SystrayItem *nextTrayItem; -} SystrayItem; - -static SystrayItem *systray=NULL; -static int firstSystray=TRUE; /* defer creation of window class until first systray item is created */ - -static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid); - - -#define ICON_SIZE GetSystemMetrics(SM_CXSMICON) -/* space around icon (forces icon to center of KDE systray area) */ -#define ICON_BORDER 4 +#include +#include +#include +#include +#include +#include +WINE_DEFAULT_DEBUG_CHANNEL(systray); +const static WCHAR classname[] = /* Shell_TrayWnd */ {'S','h','e','l','l','_','T','r','a','y','W','n','d','\0'}; -static BOOL SYSTRAY_ItemIsEqual(PNOTIFYICONDATAA pnid1, PNOTIFYICONDATAA pnid2) +struct protocol { - if (pnid1->hWnd != pnid2->hWnd) return FALSE; - if (pnid1->uID != pnid2->uID) return FALSE; - return TRUE; -} - -static LRESULT CALLBACK SYSTRAY_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - HDC hdc; - PAINTSTRUCT ps; - - switch (message) { - case WM_PAINT: - { - RECT rc; - SystrayItem *ptrayItem = systray; - - 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, - 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); - } - } - break; - } - ptrayItem = ptrayItem->nextTrayItem; - } - EndPaint(hWnd, &ps); - } - break; - - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - { - MSG msg; - SystrayItem *ptrayItem = systray; - - while ( ptrayItem ) { - if (ptrayItem->hWnd == hWnd) { - msg.hwnd=hWnd; - msg.message=message; - msg.wParam=wParam; - msg.lParam=lParam; - msg.time = GetMessageTime (); - msg.pt.x = LOWORD(GetMessagePos ()); - msg.pt.y = HIWORD(GetMessagePos ()); - - SendMessageA(ptrayItem->hWndToolTip, TTM_RELAYEVENT, 0, (LPARAM)&msg); - } - ptrayItem = ptrayItem->nextTrayItem; - } - } - /* fall through */ - - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDBLCLK: - case WM_MBUTTONDBLCLK: - { - SystrayItem *ptrayItem = systray; - - while (ptrayItem) { - if (ptrayItem->hWnd == hWnd) { - if (ptrayItem->notifyIcon.hWnd && ptrayItem->notifyIcon.uCallbackMessage) { - if (!PostMessageA(ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.uCallbackMessage, - (WPARAM)ptrayItem->notifyIcon.uID, (LPARAM)message)) { - ERR("PostMessage(SystrayWindow %p) failed -> removing SystrayItem %p\n", hWnd, ptrayItem); - SYSTRAY_Delete(&ptrayItem->notifyIcon); - } - } - break; - } - ptrayItem = ptrayItem->nextTrayItem; - } - } - break; - - default: - return (DefWindowProcA(hWnd, message, wParam, lParam)); - } - return (0); - -} - + DWORD cookie; + DWORD code; + BYTE data[1]; +}; -BOOL SYSTRAY_RegisterClass(void) +static void send_to_tray(DWORD message, void *data, BOOL unicode) { - WNDCLASSA wc; + HWND tray; + COPYDATASTRUCT transfer; + DWORD size; + struct protocol *protocol; + CURSORICONINFO *icon; + DWORD iconsize = 0; + NOTIFYICONDATAW *nid; - wc.style = CS_SAVEBITS|CS_DBLCLKS; - wc.lpfnWndProc = SYSTRAY_WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = 0; - wc.hIcon = 0; - wc.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wc.lpszMenuName = NULL; - wc.lpszClassName = "WineSystray"; - - if (!RegisterClassA(&wc)) { - ERR("RegisterClass(WineSystray) failed\n"); - return FALSE; - } - return TRUE; -} + TRACE("message=%ld\n", message); + size = unicode ? sizeof(NOTIFYICONDATAW) : sizeof(NOTIFYICONDATAA); -BOOL SYSTRAY_ItemInit(SystrayItem *ptrayItem) -{ - RECT rect; - - /* Register the class if this is our first tray item. */ - if ( firstSystray ) { - firstSystray = FALSE; - if ( !SYSTRAY_RegisterClass() ) { - ERR( "RegisterClass(WineSystray) failed\n" ); - return FALSE; + nid = (NOTIFYICONDATAW *) data; + if (nid->uFlags & NIF_ICON) + { + iconsize = GlobalSize16(HICON_16(nid->hIcon)); + TRACE("iconsize = %ld, icon = %p\n", iconsize, nid->hIcon); } - } - - /* Initialize the window size. */ - rect.left = 0; - rect.top = 0; - rect.right = ICON_SIZE+2*ICON_BORDER; - rect.bottom = ICON_SIZE+2*ICON_BORDER; - - ZeroMemory( ptrayItem, sizeof(SystrayItem) ); - /* Create tray window for icon. */ - ptrayItem->hWnd = CreateWindowExA( WS_EX_TRAYWINDOW, - "WineSystray", "Wine-Systray", - WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, - rect.right-rect.left, rect.bottom-rect.top, - 0, 0, 0, 0 ); - if ( !ptrayItem->hWnd ) { - ERR( "CreateWindow(WineSystray) failed\n" ); - return FALSE; - } - - /* Create tooltip for icon. */ - ptrayItem->hWndToolTip = CreateWindowA( TOOLTIPS_CLASSA,NULL,TTS_ALWAYSTIP, - CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, - ptrayItem->hWnd, 0, 0, 0 ); - if ( !ptrayItem->hWndToolTip ) { - ERR( "CreateWindow(TOOLTIP) failed\n" ); - return FALSE; - } - return TRUE; -} - - -static void SYSTRAY_ItemTerm(SystrayItem *ptrayItem) -{ - if(ptrayItem->notifyIcon.hIcon) - DestroyIcon(ptrayItem->notifyIcon.hIcon); - if(ptrayItem->hWndToolTip) - DestroyWindow(ptrayItem->hWndToolTip); - if(ptrayItem->hWnd) - DestroyWindow(ptrayItem->hWnd); - return; -} + protocol = HeapAlloc(GetProcessHeap(), 0, sizeof(struct protocol) - sizeof(DWORD) + size + iconsize); + protocol->cookie = 1; + protocol->code = message; + memcpy(&protocol->data, data, size); + ((NOTIFYICONDATAW *)&protocol->data)->hIcon = (HICON) -1; -void SYSTRAY_ItemSetMessage(SystrayItem *ptrayItem, UINT uCallbackMessage) -{ - ptrayItem->notifyIcon.uCallbackMessage = uCallbackMessage; -} - - -void SYSTRAY_ItemSetIcon(SystrayItem *ptrayItem, HICON hIcon) -{ - if(ptrayItem->notifyIcon.hIcon) - DestroyIcon(ptrayItem->notifyIcon.hIcon); - ptrayItem->notifyIcon.hIcon = CopyIcon(hIcon); - InvalidateRect(ptrayItem->hWnd, NULL, TRUE); -} + if (nid->uFlags & NIF_ICON) + { + /* we currently don't just pass the HICON directly as Wine doesn't + implement shared images, so marshal the icon data onto the end + of the request and set the HICON to be -1 */ + icon = GlobalLock16(HICON_16(nid->hIcon)); + memcpy(&protocol->data[0] + size, icon, iconsize); + GlobalUnlock16(HICON_16(nid->hIcon)); -void SYSTRAY_ItemSetTip(SystrayItem *ptrayItem, CHAR* szTip, int modify) -{ - TTTOOLINFOA ti; - - strncpy(ptrayItem->notifyIcon.szTip, szTip, sizeof(ptrayItem->notifyIcon.szTip)); - ptrayItem->notifyIcon.szTip[sizeof(ptrayItem->notifyIcon.szTip)-1]=0; - - ti.cbSize = sizeof(TTTOOLINFOA); - ti.uFlags = 0; - ti.hwnd = ptrayItem->hWnd; - ti.hinst = 0; - ti.uId = 0; - ti.lpszText = ptrayItem->notifyIcon.szTip; - ti.rect.left = 0; - ti.rect.top = 0; - ti.rect.right = ICON_SIZE+2*ICON_BORDER; - ti.rect.bottom = ICON_SIZE+2*ICON_BORDER; - - if(modify) - SendMessageA(ptrayItem->hWndToolTip, TTM_UPDATETIPTEXTA, 0, (LPARAM)&ti); - else - SendMessageA(ptrayItem->hWndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti); -} - - -static BOOL SYSTRAY_Add(PNOTIFYICONDATAA pnid) -{ - SystrayItem **ptrayItem = &systray; - - /* Find last element. */ - while( *ptrayItem ) { - if ( SYSTRAY_ItemIsEqual(pnid, &(*ptrayItem)->notifyIcon) ) - return FALSE; - ptrayItem = &((*ptrayItem)->nextTrayItem); - } - /* Allocate SystrayItem for element and add to end of list. */ - (*ptrayItem) = HeapAlloc(GetProcessHeap(),0,sizeof(SystrayItem)); - - /* Initialize and set data for the tray element. */ - SYSTRAY_ItemInit( (*ptrayItem) ); - (*ptrayItem)->notifyIcon.uID = pnid->uID; /* only needed for callback message */ - (*ptrayItem)->notifyIcon.hWnd = pnid->hWnd; /* only needed for callback message */ - SYSTRAY_ItemSetIcon (*ptrayItem, (pnid->uFlags&NIF_ICON) ?pnid->hIcon :0); - SYSTRAY_ItemSetMessage(*ptrayItem, (pnid->uFlags&NIF_MESSAGE)?pnid->uCallbackMessage:0); - SYSTRAY_ItemSetTip (*ptrayItem, (pnid->uFlags&NIF_TIP) ?pnid->szTip :"", FALSE); - - TRACE("%p: %p %s\n", (*ptrayItem), (*ptrayItem)->notifyIcon.hWnd, - (*ptrayItem)->notifyIcon.szTip); - return TRUE; -} + } + transfer.dwData = 1; + transfer.cbData = (sizeof(DWORD)*2) + size + iconsize; + transfer.lpData = protocol; -static BOOL SYSTRAY_Modify(PNOTIFYICONDATAA pnid) -{ - SystrayItem *ptrayItem = systray; + tray = FindWindowExW(0, NULL, classname, NULL); - while ( ptrayItem ) { - if ( SYSTRAY_ItemIsEqual(pnid, &ptrayItem->notifyIcon) ) { - if (pnid->uFlags & NIF_ICON) - SYSTRAY_ItemSetIcon(ptrayItem, pnid->hIcon); - if (pnid->uFlags & NIF_MESSAGE) - SYSTRAY_ItemSetMessage(ptrayItem, pnid->uCallbackMessage); - if (pnid->uFlags & NIF_TIP) - SYSTRAY_ItemSetTip(ptrayItem, pnid->szTip, TRUE); + if (!tray) + { + STARTUPINFOA sinfo; + PROCESS_INFORMATION pinfo; + int timeout = 5; - TRACE("%p: %p %s\n", ptrayItem, ptrayItem->notifyIcon.hWnd, ptrayItem->notifyIcon.szTip); - return TRUE; - } - ptrayItem = ptrayItem->nextTrayItem; - } - return FALSE; /* not found */ -} + TRACE("No tray window found, starting wineshell\n"); + ZeroMemory(&sinfo, sizeof(STARTUPINFOA)); + sinfo.cb = sizeof(STARTUPINFOA); -static BOOL SYSTRAY_Delete(PNOTIFYICONDATAA pnid) -{ - SystrayItem **ptrayItem = &systray; + if (CreateProcessA(NULL, "wineshell", NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo) == 0) + { + ERR("Could not start wineshell, error 0x%lx\n", GetLastError()); + return; + } - while (*ptrayItem) { - if (SYSTRAY_ItemIsEqual(pnid, &(*ptrayItem)->notifyIcon)) { - SystrayItem *next = (*ptrayItem)->nextTrayItem; - TRACE("%p: %p %s\n", *ptrayItem, (*ptrayItem)->notifyIcon.hWnd, (*ptrayItem)->notifyIcon.szTip); - SYSTRAY_ItemTerm(*ptrayItem); + CloseHandle(pinfo.hThread); + CloseHandle(pinfo.hProcess); - HeapFree(GetProcessHeap(),0,*ptrayItem); - *ptrayItem = next; + /* wait for at least 5 seconds for the wineshell to start, checking once a second */ + do + { + tray = FindWindowExW(0, NULL, classname, NULL); + if (tray) break; + + timeout--; + Sleep(1000); + } while (timeout); + + if (!tray) + { + ERR("Timeout waiting for wineshell to start, ignoring tray request\n"); + return; + } - return TRUE; + TRACE("synced with wineshell OK\n"); } - ptrayItem = &((*ptrayItem)->nextTrayItem); - } - return FALSE; /* not found */ -} + TRACE("sending %s opcode %ld to tray window %p\n", unicode ? "unicode" : "ansi", message, tray); -/************************************************************************* - * - */ -BOOL SYSTRAY_Init(void) -{ - return TRUE; + if (unicode) + SendMessageW(tray, WM_COPYDATA, (WPARAM)((NOTIFYICONDATAW *)data)->hWnd, (LPARAM)&transfer); + else + SendMessageA(tray, WM_COPYDATA, (WPARAM)((NOTIFYICONDATAW *)data)->hWnd, (LPARAM)&transfer); } /************************************************************************* @@ -361,37 +138,15 @@ BOOL SYSTRAY_Init(void) */ BOOL WINAPI Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA pnid ) { - BOOL flag=FALSE; - TRACE("enter %p %d %ld\n", pnid->hWnd, pnid->uID, dwMessage); - switch(dwMessage) { - case NIM_ADD: - flag = SYSTRAY_Add(pnid); - break; - case NIM_MODIFY: - flag = SYSTRAY_Modify(pnid); - break; - case NIM_DELETE: - flag = SYSTRAY_Delete(pnid); - break; - } - TRACE("leave %p %d %ld=%d\n", pnid->hWnd, pnid->uID, dwMessage, flag); - return flag; + send_to_tray(dwMessage, pnid, FALSE); + return TRUE; } /************************************************************************* * Shell_NotifyIconW [SHELL32.298] */ -BOOL WINAPI Shell_NotifyIconW (DWORD dwMessage, PNOTIFYICONDATAW pnid ) +BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW pnid ) { - BOOL ret; - - PNOTIFYICONDATAA p = HeapAlloc(GetProcessHeap(),0,sizeof(NOTIFYICONDATAA)); - memcpy(p, pnid, sizeof(NOTIFYICONDATAA)); - WideCharToMultiByte( CP_ACP, 0, pnid->szTip, -1, p->szTip, sizeof(p->szTip), NULL, NULL ); - p->szTip[sizeof(p->szTip)-1] = 0; - - ret = Shell_NotifyIconA(dwMessage, p ); - - HeapFree(GetProcessHeap(),0,p); - return ret; + send_to_tray(dwMessage, pnid, TRUE); + return TRUE; }