I tried to send this patch twice before, and it didn't go through. The only thing I could tell that was different from my previous patches was that my mailer switched it to a different encoding due to an extremely long line. That line was in "configure" so I removed that file and you will need to run autoconf after applying the patch.
This is a first attempt at "fixing" desktop mode. The Winelib program winedesktop creates and maintains one Wine desktop window that is shared across all applications.
How it works right now:
The winedesktop program, when started, attempts to register a window it creates as the desktop window. If it succeeds, all Wine apps started from then on will be confined in its desktop window. If there are any Wine apps running when winedesktop is run, it cannot become the desktop window because one already exists (the X root window). If there are no Wine apps running, you can also say "winedesktop MyApp.exe" to start one in the desktop. I tested this with several installers, and they all put the correct windows on top. :-) Apps can resize the desktop window, and the background bitmap is drawn if the appropriate entries are in the win.ini file. The default size of the desktop is read from HKLM\Software\Wine\Wine\Config\x11drv, which probably should be changed, but that's where most users are likely to have it right now.
What doesn't work right now:
Any app that tries to run winhelp (pointing at a nonexistent help file) currently causes the input focus to get confused somehow. The app is waiting for its SendMessage to winhelp to return, while winhelp is showing a modal message box that can't seem to get the input focus (although the message box is correctly painting its window). Killing either the app or winhelp restores things to normal.
What maybe should be added in the future:
- support a full-screen mode, where winedesktop changes the real resolution to match its size and draws without WM decorations
- support multiple desktop windows and coexisting with managed apps
ChangeLog - Moved management the Wine desktop window to a separate process
? programs/winedesktop Index: configure.ac =================================================================== RCS file: /home/wine/wine/configure.ac,v retrieving revision 1.192 diff -u -r1.192 configure.ac --- configure.ac 16 Oct 2003 05:16:05 -0000 1.192 +++ configure.ac 21 Oct 2003 02:02:05 -0000 @@ -1576,6 +1576,7 @@ programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile +programs/winedesktop/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile Index: controls/desktop.c =================================================================== RCS file: /home/wine/wine/controls/desktop.c,v retrieving revision 1.31 diff -u -r1.31 desktop.c --- controls/desktop.c 10 Sep 2003 03:56:49 -0000 1.31 +++ controls/desktop.c 21 Oct 2003 02:02:06 -0000 @@ -33,6 +33,7 @@ #include "user.h" #include "controls.h" #include "wine/winuser16.h" +#include "wine/server.h" static HBRUSH hbrushPattern; static HBITMAP hbitmapWallPaper; @@ -128,7 +129,14 @@ */ BOOL WINAPI PaintDesktop(HDC hdc) { - HWND hwnd = GetDesktopWindow(); + HWND hwnd; + SERVER_START_REQ( set_desktop_window ) + { + req->handle = 0; + wine_server_call( req ); + hwnd = reply->cur_handle; + } + SERVER_END_REQ; /* check for an owning thread; otherwise don't paint anything (non-desktop mode) */ if (GetWindowThreadProcessId( hwnd, NULL )) Index: dlls/x11drv/desktop.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/desktop.c,v retrieving revision 1.16 diff -u -r1.16 desktop.c --- dlls/x11drv/desktop.c 16 Oct 2003 00:21:42 -0000 1.16 +++ dlls/x11drv/desktop.c 21 Oct 2003 02:02:08 -0000 @@ -33,86 +33,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(x11drv); -/* desktop window procedure */ -static LRESULT WINAPI desktop_winproc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) -{ - switch(message) - { - case WM_NCCREATE: - SystemParametersInfoA( SPI_SETDESKPATTERN, -1, NULL, FALSE ); - SetDeskWallPaper( (LPSTR)-1 ); - return TRUE; - - case WM_ERASEBKGND: - PaintDesktop( (HDC)wParam ); - ValidateRect( hwnd, NULL ); - break; - - case WM_SYSCOMMAND: - if ((wParam & 0xfff0) == SC_CLOSE) ExitWindows( 0, 0 ); - break; - - case WM_SETCURSOR: - return (LRESULT)SetCursor( LoadCursorA( 0, (LPSTR)IDC_ARROW ) ); - - case WM_NCHITTEST: - return HTCLIENT; - } - return 0; -} - - -/* desktop window manager thread */ -static DWORD CALLBACK desktop_thread( LPVOID driver_data ) -{ - Display *display; - MSG msg; - HWND hwnd; - WND *win; - - NtCurrentTeb()->driver_data = driver_data; - display = thread_display(); - hwnd = GetDesktopWindow(); - - /* patch the desktop window queue to point to our queue */ - win = WIN_GetPtr( hwnd ); - win->tid = GetCurrentThreadId(); - X11DRV_register_window( display, hwnd, win->pDriverData ); - WIN_ReleasePtr( win ); - - SetWindowLongW( hwnd, GWL_WNDPROC, (LONG)desktop_winproc ); - wine_tsx11_lock(); - XSetWMProtocols( display, root_window, &wmDeleteWindow, 1 ); - XMapWindow( display, root_window ); - wine_tsx11_unlock(); - - SendMessageW( hwnd, WM_NCCREATE, 0, 0 /* should be CREATESTRUCT */ ); - - while (GetMessageW( &msg, hwnd, 0, 0 )) DispatchMessageW( &msg ); - return 0; -} - - -/*********************************************************************** - * X11DRV_create_desktop_thread - * - * Create the thread that manages the desktop window - */ -void X11DRV_create_desktop_thread(void) -{ - HANDLE handle = CreateThread( NULL, 0, desktop_thread, NtCurrentTeb()->driver_data, - 0, &desktop_tid ); - if (!handle) - { - MESSAGE( "Could not create desktop thread\n" ); - ExitProcess(1); - } - /* we transferred our driver data to the new thread */ - NtCurrentTeb()->driver_data = NULL; - CloseHandle( handle ); -} - - /* data for resolution changing */ static LPDDHALMODEINFO dd_modes; static unsigned int dd_mode_count; @@ -149,6 +69,8 @@ } } +#define WM_WINEDESKTOP_SIZE WM_USER+1 + /*********************************************************************** * X11DRV_resize_desktop * @@ -156,39 +78,10 @@ */ int X11DRV_resize_desktop( unsigned int width, unsigned int height ) { - XSizeHints *size_hints; - Display *display = thread_display(); - Window w = root_window; - /* set up */ - wine_tsx11_lock(); - size_hints = XAllocSizeHints(); - if (!size_hints) - { - ERR("Not enough memory for window manager hints.\n" ); - wine_tsx11_unlock(); - return 0; - } - size_hints->min_width = size_hints->max_width = width; - size_hints->min_height = size_hints->max_height = height; - size_hints->flags = PMinSize | PMaxSize | PSize; - /* do the work */ - XSetWMNormalHints( display, w, size_hints ); - XResizeWindow( display, w, width, height ); + PostMessageW(GetDesktopWindow(), WM_WINEDESKTOP_SIZE, 0, width|(height<<16)); screen_width = width; screen_height = height; -#if 0 /* FIXME */ - SYSMETRICS_Set( SM_CXSCREEN, width ); - SYSMETRICS_Set( SM_CYSCREEN, height ); -#else - FIXME("Need to update SYSMETRICS after resizing display (now %dx%d)\n", - width, height); -#endif - - /* clean up */ - XFree( size_hints ); - XFlush( display ); - wine_tsx11_unlock(); return 1; } @@ -225,72 +118,15 @@ * * Create the X11 desktop window for the desktop mode. */ -Window X11DRV_create_desktop( XVisualInfo *desktop_vi, const char *geometry ) +Window X11DRV_create_desktop( XVisualInfo *desktop_vi, unsigned int width, unsigned int height ) { - int x = 0, y = 0, flags; - unsigned int width = 640, height = 480; /* Default size = 640x480 */ - char *name = GetCommandLineA(); - XSizeHints *size_hints; - XWMHints *wm_hints; - XClassHint *class_hints; - XSetWindowAttributes win_attr; - XTextProperty window_name; - Window win; - Display *display = thread_display(); + Window win = using_wine_desktop; - wine_tsx11_lock(); - flags = XParseGeometry( geometry, &x, &y, &width, &height ); max_width = screen_width; max_height = screen_height; - screen_width = width; + screen_width = width; screen_height = height; - /* Create window */ - win_attr.background_pixel = BlackPixel(display, 0); - win_attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask; - win_attr.cursor = XCreateFontCursor( display, XC_top_left_arrow ); - - if (desktop_vi) - win_attr.colormap = XCreateColormap( display, DefaultRootWindow(display), - visual, AllocNone ); - else - win_attr.colormap = None; - - win = XCreateWindow( display, DefaultRootWindow(display), - x, y, width, height, 0, screen_depth, InputOutput, visual, - CWBackPixel | CWEventMask | CWCursor | CWColormap, &win_attr ); - - /* Set window manager properties */ - size_hints = XAllocSizeHints(); - wm_hints = XAllocWMHints(); - class_hints = XAllocClassHint(); - if (!size_hints || !wm_hints || !class_hints) - { - MESSAGE("Not enough memory for window manager hints.\n" ); - ExitProcess(1); - } - size_hints->min_width = size_hints->max_width = width; - size_hints->min_height = size_hints->max_height = height; - size_hints->flags = PMinSize | PMaxSize; - if (flags & (XValue | YValue)) size_hints->flags |= USPosition; - if (flags & (WidthValue | HeightValue)) size_hints->flags |= USSize; - else size_hints->flags |= PSize; - - wm_hints->flags = InputHint | StateHint; - wm_hints->input = True; - wm_hints->initial_state = NormalState; - class_hints->res_name = "wine"; - class_hints->res_class = "Wine"; - - XStringListToTextProperty( &name, 1, &window_name ); - XSetWMProperties( display, win, &window_name, &window_name, - NULL, 0, size_hints, wm_hints, class_hints ); - XFree( size_hints ); - XFree( wm_hints ); - XFree( class_hints ); - XFlush( display ); - wine_tsx11_unlock(); /* initialize the available resolutions */ dd_modes = X11DRV_Settings_SetHandlers("desktop", X11DRV_desktop_GetCurrentMode, Index: dlls/x11drv/window.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/window.c,v retrieving revision 1.59 diff -u -r1.59 window.c --- dlls/x11drv/window.c 3 Oct 2003 03:38:05 -0000 1.59 +++ dlls/x11drv/window.c 21 Oct 2003 02:02:09 -0000 @@ -560,22 +560,6 @@ { HWND next = GetWindow( win->hwndSelf, GW_HWNDNEXT ); - if (win->parent == GetDesktopWindow() && - root_window != DefaultRootWindow(display)) - { - /* in desktop mode we need the sibling to belong to the same process */ - while (next) - { - WND *ptr = WIN_GetPtr( next ); - if (ptr != WND_OTHER_PROCESS) - { - WIN_ReleasePtr( ptr ); - break; - } - next = GetWindow( next, GW_HWNDNEXT ); - } - } - if (!next) /* bottom child */ { changes.stack_mode = Below; @@ -699,7 +683,6 @@ SetPropA( wndPtr->hwndSelf, client_window_atom, (HANDLE)root_window ); SetPropA( wndPtr->hwndSelf, "__wine_x11_visual_id", (HANDLE)XVisualIDFromVisual(visual) ); - if (root_window != DefaultRootWindow(display)) X11DRV_create_desktop_thread(); } Index: dlls/x11drv/x11drv.h =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11drv.h,v retrieving revision 1.6 diff -u -r1.6 x11drv.h --- dlls/x11drv/x11drv.h 16 Oct 2003 00:21:42 -0000 1.6 +++ dlls/x11drv/x11drv.h 21 Oct 2003 02:02:11 -0000 @@ -219,6 +219,7 @@ extern int client_side_antialias_with_core; extern int client_side_antialias_with_render; extern int using_client_side_fonts; +extern int using_wine_desktop; extern void X11DRV_XRender_Init(void); extern void X11DRV_XRender_Finalize(void); extern BOOL X11DRV_XRender_SelectFont(X11DRV_PDEVICE*, HFONT); @@ -489,8 +490,7 @@ extern void X11DRV_set_iconic_state( WND *win ); extern void X11DRV_window_to_X_rect( WND *win, RECT *rect ); extern void X11DRV_X_to_window_rect( WND *win, RECT *rect ); -extern void X11DRV_create_desktop_thread(void); -extern Window X11DRV_create_desktop( XVisualInfo *desktop_vi, const char *geometry ); +extern Window X11DRV_create_desktop( XVisualInfo *desktop_vi, unsigned int width, unsigned int height ); extern void X11DRV_sync_window_style( Display *display, WND *win ); extern int X11DRV_sync_whole_window_position( Display *display, WND *win, int zorder ); extern int X11DRV_sync_client_window_position( Display *display, WND *win ); Index: dlls/x11drv/x11drv_main.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/x11drv_main.c,v retrieving revision 1.75 diff -u -r1.75 x11drv_main.c --- dlls/x11drv/x11drv_main.c 16 Oct 2003 00:21:42 -0000 1.75 +++ dlls/x11drv/x11drv_main.c 21 Oct 2003 02:02:12 -0000 @@ -79,6 +79,7 @@ int client_side_with_render = 1; int client_side_antialias_with_core = 1; int client_side_antialias_with_render = 1; +int using_wine_desktop = 0; unsigned int X11DRV_server_startticks; @@ -294,6 +295,34 @@ RegCloseKey( hkey ); } +/*********************************************************************** + * detect_desktop_mode + * + * returns the HWND if found + */ +static int detect_desktop_mode(unsigned int *w, unsigned int *h) +{ + int res = 0; + SERVER_START_REQ( set_desktop_window ) + { + req->handle = 0; + wine_server_call( req ); + if (reply->cur_tid) + { + TRACE("Desktop window is already managed by a thread\n"); + res = (int) GetPropA(reply->cur_handle , "__wine_x11_client_window" ); + *w = reply->cur_width; + *h = reply->cur_height; + } + else + { + TRACE("No desktop window thread -- using X root window\n"); + } + } + SERVER_END_REQ; + return res; +} + /*********************************************************************** * X11DRV process initialisation routine @@ -301,7 +330,7 @@ static void process_attach(void) { Display *display; - + unsigned int desktop_w, desktop_h; get_server_startup(); setup_options(); @@ -367,8 +396,10 @@ X11DRV_Settings_Init(); - if (desktop_geometry) - root_window = X11DRV_create_desktop( desktop_vi, desktop_geometry ); + if ((using_wine_desktop = detect_desktop_mode( &desktop_w, &desktop_h ))) + { + root_window = X11DRV_create_desktop( desktop_vi, desktop_w, desktop_h ); + } /* initialize GDI */ if(!X11DRV_GDI_Initialize( display )) Index: dlls/x11drv/xrandr.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/xrandr.c,v retrieving revision 1.1 diff -u -r1.1 xrandr.c --- dlls/x11drv/xrandr.c 16 Oct 2003 00:21:42 -0000 1.1 +++ dlls/x11drv/xrandr.c 21 Oct 2003 02:02:12 -0000 @@ -57,7 +57,6 @@ return 1; } -static Bool in_desktop_mode; static const unsigned int depths[] = {8, 16, 32}; /* create the mode structures */ @@ -185,11 +184,10 @@ Bool ok; int nmodes = 0; int i; - in_desktop_mode = (root_window != DefaultRootWindow(gdi_display)); if (xrandr_major) return; /* already initialized? */ if (!usexrandr) return; /* disabled in config */ - if (in_desktop_mode) return; /* not compatible with desktop mode */ + if (using_wine_desktop) return; /* not compatible with desktop mode */ /* see if Xrandr is available */ wine_tsx11_lock(); Index: dlls/x11drv/xvidmode.c =================================================================== RCS file: /home/wine/wine/dlls/x11drv/xvidmode.c,v retrieving revision 1.24 diff -u -r1.24 xvidmode.c --- dlls/x11drv/xvidmode.c 16 Oct 2003 00:21:42 -0000 1.24 +++ dlls/x11drv/xvidmode.c 21 Oct 2003 02:02:12 -0000 @@ -89,8 +89,6 @@ return 1; } -static Bool in_desktop_mode; - int X11DRV_XF86VM_GetCurrentMode(void) { XF86VidModeModeLine line; @@ -151,8 +149,6 @@ DWORD dwBpp = screen_depth; if (dwBpp == 24) dwBpp = 32; - in_desktop_mode = (root_window != DefaultRootWindow(gdi_display)); - if (xf86vm_major) return; /* already initialized? */ if (!usexvidmode) return; @@ -179,13 +175,13 @@ #endif /* X_XF86VidModeSetGammaRamp */ /* retrieve modes */ - if (!in_desktop_mode) ok = XF86VidModeGetAllModeLines(gdi_display, DefaultScreen(gdi_display), &nmodes, &real_xf86vm_modes); + if (!using_wine_desktop) ok = XF86VidModeGetAllModeLines(gdi_display, DefaultScreen(gdi_display), &nmodes, &real_xf86vm_modes); } wine_tsx11_unlock(); if (!ok) return; /* In desktop mode, do not switch resolution... But still use the Gamma ramp stuff */ - if (in_desktop_mode) return; + if (using_wine_desktop) return; TRACE("XVidMode modes: count=%d\n", nmodes); Index: include/wine/server_protocol.h =================================================================== RCS file: /home/wine/wine/include/wine/server_protocol.h,v retrieving revision 1.88 diff -u -r1.88 server_protocol.h --- include/wine/server_protocol.h 14 Oct 2003 01:30:42 -0000 1.88 +++ include/wine/server_protocol.h 21 Oct 2003 02:02:14 -0000 @@ -3114,6 +3114,23 @@ #define SET_GLOBAL_TASKMAN_WINDOW 0x04 +struct set_desktop_window_request +{ + struct request_header __header; + user_handle_t handle; + unsigned int width; + unsigned int height; +}; +struct set_desktop_window_reply +{ + struct reply_header __header; + user_handle_t cur_handle; + thread_id_t cur_tid; + unsigned int cur_width; + unsigned int cur_height; +}; + + enum request { REQ_new_process, @@ -3295,6 +3312,7 @@ REQ_set_clipboard_info, REQ_open_token, REQ_set_global_windows, + REQ_set_desktop_window, REQ_NB_REQUESTS }; @@ -3481,6 +3499,7 @@ struct set_clipboard_info_request set_clipboard_info_request; struct open_token_request open_token_request; struct set_global_windows_request set_global_windows_request; + struct set_desktop_window_request set_desktop_window_request; }; union generic_reply { @@ -3665,8 +3684,9 @@ struct set_clipboard_info_reply set_clipboard_info_reply; struct open_token_reply open_token_reply; struct set_global_windows_reply set_global_windows_reply; + struct set_desktop_window_reply set_desktop_window_reply; }; -#define SERVER_PROTOCOL_VERSION 126 +#define SERVER_PROTOCOL_VERSION 127 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ Index: programs/Makefile.in =================================================================== RCS file: /home/wine/wine/programs/Makefile.in,v retrieving revision 1.39 diff -u -r1.39 Makefile.in --- programs/Makefile.in 23 Jun 2003 19:51:21 -0000 1.39 +++ programs/Makefile.in 21 Oct 2003 02:02:15 -0000 @@ -27,6 +27,7 @@ winecfg \ wineconsole \ winedbg \ + winedesktop \ winefile \ winemenubuilder \ winemine \ @@ -54,6 +55,7 @@ winecfg \ wineconsole \ winedbg \ + winedesktop \ winefile \ winemenubuilder \ winemine \ @@ -75,6 +77,7 @@ winecfg \ wineconsole \ winedbg \ + winedesktop \ winefile \ winemine \ winepath \ @@ -86,6 +89,7 @@ wcmd.exe \ wineconsole.exe \ winedbg.exe \ + winedesktop.exe \ winemenubuilder.exe \ winevdm.exe \ winhelp.exe @@ -145,6 +149,9 @@ winedbg.exe$(DLLEXT): winedbg/winedbg.exe$(DLLEXT) $(RM) $@ && $(LN_S) winedbg/winedbg.exe$(DLLEXT) $@ +winedesktop.exe$(DLLEXT): winedesktop/winedesktop.exe$(DLLEXT) + $(RM) $@ && $(LN_S) winedesktop/winedesktop.exe$(DLLEXT) $@ + winemenubuilder.exe$(DLLEXT): winemenubuilder/winemenubuilder.exe$(DLLEXT) $(RM) $@ && $(LN_S) winemenubuilder/winemenubuilder.exe$(DLLEXT) $@ @@ -157,6 +164,7 @@ wcmd/wcmd.exe$(DLLEXT): wcmd wineconsole/wineconsole.exe$(DLLEXT): wineconsole winedbg/winedbg.exe$(DLLEXT): winedbg +winedesktop/winedesktop.exe$(DLLEXT): winedesktop winemenubuilder/winemenubuilder.exe$(DLLEXT): winemenubuilder winevdm/winevdm.exe$(DLLEXT): winevdm winhelp/winhelp.exe$(DLLEXT): winhelp Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.87 diff -u -r1.87 protocol.def --- server/protocol.def 14 Oct 2003 01:30:42 -0000 1.87 +++ server/protocol.def 21 Oct 2003 02:02:17 -0000 @@ -2175,3 +2175,15 @@ #define SET_GLOBAL_SHELL_WINDOWS 0x01 /* set both main shell and listview windows */ #define SET_GLOBAL_PROGMAN_WINDOW 0x02 #define SET_GLOBAL_TASKMAN_WINDOW 0x04 + +/* Specify a window to be the Wine desktop */ +@REQ(set_desktop_window) + user_handle_t handle; /* desktop window */ + unsigned int width; /* new width */ + unsigned int height; /* new height */ +@REPLY + user_handle_t cur_handle; /* current desktop window */ + thread_id_t cur_tid; /* current owner thread id */ + unsigned int cur_width; /* current width */ + unsigned int cur_height; /* current height */ +@END Index: server/request.h =================================================================== RCS file: /home/wine/wine/server/request.h,v retrieving revision 1.89 diff -u -r1.89 request.h --- server/request.h 7 Oct 2003 03:40:23 -0000 1.89 +++ server/request.h 21 Oct 2003 02:02:17 -0000 @@ -282,6 +282,7 @@ DECL_HANDLER(set_clipboard_info); DECL_HANDLER(open_token); DECL_HANDLER(set_global_windows); +DECL_HANDLER(set_desktop_window); #ifdef WANT_REQUEST_HANDLERS @@ -467,6 +468,7 @@ (req_handler)req_set_clipboard_info, (req_handler)req_open_token, (req_handler)req_set_global_windows, + (req_handler)req_set_desktop_window, }; #endif /* WANT_REQUEST_HANDLERS */ Index: server/trace.c =================================================================== RCS file: /home/wine/wine/server/trace.c,v retrieving revision 1.185 diff -u -r1.185 trace.c --- server/trace.c 14 Oct 2003 01:30:42 -0000 1.185 +++ server/trace.c 21 Oct 2003 02:02:20 -0000 @@ -2544,6 +2544,21 @@ fprintf( stderr, " old_taskman_window=%p", req->old_taskman_window ); } +static void dump_set_desktop_window_request( const struct set_desktop_window_request *req ) +{ + fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " width=%08x,", req->width ); + fprintf( stderr, " height=%08x", req->height ); +} + +static void dump_set_desktop_window_reply( const struct set_desktop_window_reply *req ) +{ + fprintf( stderr, " cur_handle=%p,", req->cur_handle ); + fprintf( stderr, " cur_tid=%04x,", req->cur_tid ); + fprintf( stderr, " cur_width=%08x,", req->cur_width ); + fprintf( stderr, " cur_height=%08x", req->cur_height ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -2724,6 +2739,7 @@ (dump_func)dump_set_clipboard_info_request, (dump_func)dump_open_token_request, (dump_func)dump_set_global_windows_request, + (dump_func)dump_set_desktop_window_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -2906,6 +2922,7 @@ (dump_func)dump_set_clipboard_info_reply, (dump_func)dump_open_token_reply, (dump_func)dump_set_global_windows_reply, + (dump_func)dump_set_desktop_window_reply, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -3088,6 +3105,7 @@ "set_clipboard_info", "open_token", "set_global_windows", + "set_desktop_window", }; /* ### make_requests end ### */ Index: server/window.c =================================================================== RCS file: /home/wine/wine/server/window.c,v retrieving revision 1.24 diff -u -r1.24 window.c --- server/window.c 7 Oct 2003 03:40:23 -0000 1.24 +++ server/window.c 21 Oct 2003 02:02:20 -0000 @@ -79,7 +79,10 @@ struct property *properties; /* window properties array */ }; -static struct window *top_window; /* top-level (desktop) window */ +static struct window *top_window = NULL; /* top-level (desktop) window */ +static struct window *orig_top_window = NULL; /* original desktop window */ +static unsigned int top_width = 0; /* width of desktop window */ +static unsigned int top_height = 0; /* height of desktop window */ /* global window pointers */ static struct window *shell_window; @@ -239,6 +242,12 @@ /* destroy a window */ static void destroy_window( struct window *win ) { + if (win == top_window) + { + assert ( orig_top_window ); + top_window = orig_top_window; + orig_top_window = NULL; + } assert( win != top_window ); /* destroy all children */ @@ -571,7 +580,7 @@ struct window *win = get_window( req->handle ); if (!win) return; - if (req->flags && win == top_window) + if (req->flags && win == top_window && win->thread != current) { set_error( STATUS_ACCESS_DENIED ); return; @@ -880,4 +889,53 @@ shell_listview = new_shell_listview; progman_window = new_progman_window; taskman_window = new_taskman_window; +} + +/* Specify a window to be the Wine desktop */ +DECL_HANDLER(set_desktop_window) +{ + if (req->handle && top_window && (req->handle == top_window->handle)) + { + /* just changing the size */ + top_width = req->width; + top_height = req->height; + } + else if (req->handle && !orig_top_window) + { + /* set a new desktop window */ + struct window *the_window = get_window(req->handle); + struct window *ptr; + int total = 0; + assert( the_window ); + assert( the_window->thread ); + /* make sure the window requested is the only window */ + for (ptr = top_window->first_child, total = 0; ptr; ptr = ptr->next) + { + total++; + } + if ((total == 1) && (top_window->first_child == the_window)) + { + orig_top_window = top_window; + top_window = the_window; + top_width = req->width; + top_height = req->height; + reply->cur_handle = req->handle; + reply->cur_tid = get_thread_id( top_window->thread ); + } + } + /* return status */ + if (top_window) + { + reply->cur_handle = top_window->handle; + reply->cur_tid = (top_window->thread ? get_thread_id( top_window->thread ) : 0); + reply->cur_width = top_width; + reply->cur_height = top_height; + } + else + { + reply->cur_handle = NULL; + reply->cur_tid = 0; + reply->cur_width = 0; + reply->cur_height = 0; + } } Index: windows/win.c =================================================================== RCS file: /home/wine/wine/windows/win.c,v retrieving revision 1.224 diff -u -r1.224 win.c --- windows/win.c 5 Sep 2003 23:15:39 -0000 1.224 +++ windows/win.c 21 Oct 2003 02:02:23 -0000 @@ -739,6 +739,18 @@ pWndDesktop->winproc = winproc; pWndDesktop->cbWndExtra = wndExtra; + SERVER_START_REQ( set_desktop_window ) + { + req->handle = 0; + wine_server_call( req ); + if (reply->cur_tid) + { + TRACE("Desktop window is already managed by a thread\n"); + pWndDesktop->tid = reply->cur_tid; /* maybe someone does own it! */ + } + } + SERVER_END_REQ; + cs.lpCreateParams = NULL; cs.hInstance = 0; cs.hMenu = 0; --- /dev/null 2003-01-30 04:24:37.000000000 -0600 +++ programs/winedesktop/Makefile.in 2003-10-20 12:39:55.000000000 -0500 @@ -0,0 +1,13 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = winedesktop.exe +APPMODE = gui +IMPORTS = advapi32 shell32 user32 gdi32 kernel32 ntdll + +C_SRCS = main.c + +@MAKE_PROG_RULES@ + +### Dependencies: --- /dev/null 2003-01-30 04:24:37.000000000 -0600 +++ programs/winedesktop/winedesktop.h 2003-10-20 20:19:18.000000000 -0500 @@ -0,0 +1,33 @@ +/* + * Wine Desktop + * + * Copyright 2003 Alexander James Pasadyn + * + * 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 + */ + +typedef struct +{ + HANDLE hInstance; + HWND hMainWnd; + HWND hDesktopWindow; + DWORD dwStyle; + unsigned int width; + unsigned int height; +} WINE_DESKTOP_GLOBALS; + +extern WINE_DESKTOP_GLOBALS Globals; + +#define WM_WINEDESKTOP_SIZE WM_USER+1 --- /dev/null 2003-01-30 04:24:37.000000000 -0600 +++ programs/winedesktop/main.c 2003-10-20 20:51:36.000000000 -0500 @@ -0,0 +1,339 @@ +/* + * Wine Desktop + * + * Copyright 2003 Alexander James Pasadyn + * + * 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 + */ + +/* + * TODO: + * - Support full-screen mode + * - Move registry configuration to a more appropriate place + */ + +#include "config.h" +#include "windows.h" +#include "wine/server.h" +#include <wine/debug.h> +#include <stdio.h> + +#include "winedesktop.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winedesktop); + +WINE_DESKTOP_GLOBALS Globals; + +/*********************************************************************** + * EnumCallbackQES + * + * Callback for sending QUERYENDSESSION messages + */ +static BOOL CALLBACK EnumCallbackQES(HWND hwnd, LPARAM lParam) +{ + int *okptr = (int *) lParam; + if (!SendMessage( hwnd, WM_QUERYENDSESSION, 0, 0 )) *okptr = 0; + return TRUE; +} + +/*********************************************************************** + * EnumCallbackES + * + * Callback for killing the processes + */ +static BOOL CALLBACK EnumCallbackES(HWND hwnd, LPARAM okay) +{ + DWORD pid; + HANDLE p; + SendMessage( hwnd, WM_ENDSESSION, okay, 0 ); + if (okay) + { + GetWindowThreadProcessId( hwnd, &pid ); + WINE_WARN("Terminating Wine process %ld\n", pid); + p = OpenProcess (PROCESS_TERMINATE, 0, pid); + TerminateProcess( p, 0 ); + CloseHandle( p ); + } + return TRUE; +} + +/*********************************************************************** + * QueryEndSession + * + * ExitWindows-like functionality + */ +static BOOL QueryEndSession(HWND hwnd) +{ + int okay = 1; + + /* Send a WM_QUERYENDSESSION message to every window */ + EnumChildWindows(hwnd, EnumCallbackQES, (LPARAM) &okay); + + /* Now notify all windows that got a WM_QUERYENDSESSION of the result */ + EnumChildWindows(hwnd, EnumCallbackES, okay); + + return okay; +} + +/*********************************************************************** + * BecomeDesktop + * + * Tell the server to make our window the desktop. + * We must send the dimensions because we might be minimized when a + * new process starts, making our window size misleading. + * This should be called again any time the dimensions change. + */ +static BOOL BecomeDesktop() +{ + BOOL res = TRUE; + SERVER_START_REQ( set_desktop_window ) + { + req->handle = Globals.hMainWnd; + req->width = Globals.width; + req->height = Globals.height; + wine_server_call( req ); + Globals.hDesktopWindow = reply->cur_handle; + if (Globals.hDesktopWindow != Globals.hMainWnd) + { + res = FALSE; + } + + } + SERVER_END_REQ; + return res; +} + +/*********************************************************************** + * AdjustSize + * + * Compute the required window dimensions + */ +static void AdjustSize( int *width, int *height) +{ + RECT r; + r.left = 0; + r.top = 0; + r.right = *width; + r.bottom = *height; + AdjustWindowRect(&r, Globals.dwStyle, FALSE); + *width = r.right - r.left; + *height = r.bottom - r.top; +} + +/*********************************************************************** + * WndProc + * + * Window procedure + */ +static LRESULT WINAPI WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch (message) { + case WM_CLOSE: + if (QueryEndSession(hwnd)) PostQuitMessage (0); + break; + case WM_CREATE: + SystemParametersInfoA( SPI_SETDESKPATTERN, -1, NULL, FALSE ); + SetDeskWallPaper( (LPSTR)-1 ); + break; + case WM_DESTROY: + PostQuitMessage (0); + break; + case WM_ERASEBKGND: + PaintDesktop( (HDC)wParam ); + ValidateRect( hwnd, NULL ); + break; + case WM_NCHITTEST: + return HTCLIENT; + case WM_PAINT: { + PAINTSTRUCT ps; + HDC context; + context = BeginPaint(hwnd, &ps); + EndPaint(hwnd, &ps); + break; + } + case WM_SETCURSOR: + return (LRESULT)SetCursor( LoadCursorA( 0, (LPSTR)IDC_ARROW ) ); + case WM_SYSCOMMAND: + if ((wParam & 0xfff0) == SC_CLOSE) + { + if (QueryEndSession(hwnd)) PostQuitMessage (0); + } + break; + case WM_WINEDESKTOP_SIZE: { + int width, height; + Globals.width = width = LOWORD(lParam); + Globals.height = height = HIWORD(lParam); + AdjustSize( &width, &height ); + SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOMOVE|SWP_NOZORDER); + BecomeDesktop(); + break; + } + default: + return DefWindowProc (hwnd, message, wParam, lParam); + } + return 0; +} + +/*********************************************************************** + * Bail + * + * We hit an error, and it is time to give up + */ +static void Bail (const char *message) +{ + WINE_ERR("%s\n", message); + MessageBox(NULL, message, "Wine Desktop", MB_OK | MB_ICONSTOP); + ExitProcess(1); +} + +/*********************************************************************** + * ParseGeometry + * + * Simple rotine to extract dimensions from wXh+x+y + */ +static int ParseGeometry(char *buffer, unsigned int *w, unsigned int *h) +{ + unsigned int i; + i = 0; + while (buffer[i]) + { + switch (buffer[i]) { + case '=': + case 'x': + case 'X': + case '+': + case '-': + buffer[i] = ' '; + } + i++; + } + return sscanf(buffer, " %u %u", w, h); +} + +/*********************************************************************** + * WinMain + * + * Entry point + * + * Run as follows: + * winedesktop [wine-opts] -- MyApp.exe arg1 arg2 ... + */ +int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show) +{ + MSG msg; + WNDCLASS class; + char szClassName[] = "WineDesktopClass"; + char szWinName[] = "Wine Desktop"; + DWORD tidDesktop; + HKEY hkey = NULL; + char buffer[100]; + int width, height; + + ZeroMemory(&Globals, sizeof(Globals)); + + Globals.hDesktopWindow = GetDesktopWindow(); + Globals.width = 640; + Globals.height = 480; + WINE_TRACE("Current desktop HWND is %08lx\n", (DWORD) Globals.hDesktopWindow); + tidDesktop = GetWindowThreadProcessId( Globals.hDesktopWindow, NULL ); + WINE_TRACE("Current desktop thread is %08lx\n", tidDesktop); + + if (tidDesktop) + { + Bail("There is already a WineDesktop process running!"); + } + + ZeroMemory(&class, sizeof(class)); + class.style = CS_GLOBALCLASS; + class.lpfnWndProc = WndProc; + class.cbClsExtra = 0; + class.cbWndExtra = 0; + class.hInstance = hInstance; + class.hCursor = LoadCursor(0, IDC_ARROW); + class.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1); + class.lpszMenuName = 0; + class.lpszClassName = szClassName; + + if (!RegisterClass (&class)) return FALSE; + + Globals.dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + /* + * This key is not the most appropriate location for this information, + * but it is where most users will currently have it set. + */ + if (!RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", 0, NULL, + REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL )) + { + DWORD size = sizeof(buffer); + if (!RegQueryValueExA( hkey, "Desktop", 0, NULL, buffer, &size )) + { + int n = ParseGeometry(buffer, &(Globals.width), &(Globals.height)); + if (n == 2) + { + WINE_TRACE("Found specified size in registry: %dx%d\n", + Globals.width, Globals.height); + } + } + RegCloseKey( hkey ); + } + + width = Globals.width; + height = Globals.height; + AdjustSize( &width, &height ); + + Globals.hMainWnd = CreateWindow (szClassName, szWinName, + Globals.dwStyle, + CW_USEDEFAULT, CW_USEDEFAULT, + width, height, 0, + 0, Globals.hInstance, 0); + + WINE_TRACE("setting %08lx to top_window\n", (DWORD) Globals.hMainWnd); + + if (!BecomeDesktop()) + { + if (IsWindow(Globals.hMainWnd)) DestroyWindow(Globals.hMainWnd); + Bail("Could not become WineDesktop window"); + } + + ShowWindow (Globals.hMainWnd, show); + UpdateWindow (Globals.hMainWnd); + + if (strlen(cmdline)) + { + if (WinExec(cmdline, SW_SHOWNORMAL) < 32) + { + WINE_ERR("Unable to exec: %s\n", cmdline); + } + } + + while (GetMessage(&msg, NULL, 0, 0)) { + if (msg.hwnd == Globals.hMainWnd) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + /* sometimes we get messages for our children */ + WINE_WARN("Sending message of type %x to HWND %p\n", msg.message, msg.hwnd); + PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + } + + return 0; +} +