desktop mode again

Alex Pasadyn ajp at mail.utexas.edu
Wed Jan 28 11:47:43 CST 2004


ChangeLog:
- Make a separate process for the Wine desktop window
- In desktop mode, all applications share one desktop window
- Ensure system metrics are accurate for all processes after resolution 
changes


This is an updated version of this patch that I originally sent a couple 
months ago.  To test it, you must run both autoconf and 
tools/make_requests.  While several applications work fine, there are 
some issues I have not been able to resolve.  The problems have to do 
with trying to call WIN_FindWndPtr across processes, especially from the 
windows/painting.c file.

I am sending this now as several people were interested in it, and the 
old version I posted no longer applies cleanly to the CVS Wine version. 
  I am hoping someone with more knowledge of the painting and region 
code could spot some obvious way to get around the issue.  Otherwise it 
will be tough.  I had tried replacing some of the WND* uses with server 
calls to get rectangles and style flags, but that does not handle 
everything that's being read out of those structures.  Unless there's 
some shortcut I don't see how to do it without moving more of that data 
into the server.  The other problem related to this is that some 
functions are supposed to fail if the window is in another process, 
while others would work better if they got the data from the server, and 
it's not always obvious which way a particular function should work.

-ajp

-------------- next part --------------
? programs/winedesktop
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.234
diff -u -r1.234 configure.ac
--- configure.ac	26 Jan 2004 21:29:05 -0000	1.234
+++ configure.ac	28 Jan 2004 16:57:02 -0000
@@ -1640,6 +1640,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.32
diff -u -r1.32 desktop.c
--- controls/desktop.c	10 Dec 2003 04:14:35 -0000	1.32
+++ controls/desktop.c	28 Jan 2004 16:57:02 -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.18
diff -u -r1.18 desktop.c
--- dlls/x11drv/desktop.c	21 Nov 2003 21:50:59 -0000	1.18
+++ dlls/x11drv/desktop.c	28 Jan 2004 16:57:04 -0000
@@ -32,88 +32,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;
-    Atom atom = x11drv_atom(WM_DELETE_WINDOW);
-
-    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();
-    XChangeProperty ( display, root_window, x11drv_atom(WM_PROTOCOLS),
-                      XA_ATOM, 32, PropModeReplace, (char *)&atom, 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;
@@ -150,6 +68,8 @@
     }
 }
 
+#define WM_WINEDESKTOP_SIZE  WM_USER+1
+
 /***********************************************************************
  *		X11DRV_resize_desktop
  *
@@ -157,39 +77,11 @@
  */
 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 );
+    TRACE("posting WM_WINEDESKTOP_SIZE to %p\n", GetDesktopWindow());
+    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;
 }
 
@@ -226,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_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.69
diff -u -r1.69 window.c
--- dlls/x11drv/window.c	21 Jan 2004 02:22:26 -0000	1.69
+++ dlls/x11drv/window.c	28 Jan 2004 16:57:05 -0000
@@ -611,22 +611,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;
@@ -742,7 +726,7 @@
 
     X11DRV_InitClipboard();
 
-    if (root_window != DefaultRootWindow(display)) X11DRV_create_desktop_thread();
+    XSelectInput(display, root_window, StructureNotifyMask);
 }
 
 
Index: dlls/x11drv/winpos.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/winpos.c,v
retrieving revision 1.74
diff -u -r1.74 winpos.c
--- dlls/x11drv/winpos.c	20 Jan 2004 22:48:57 -0000	1.74
+++ dlls/x11drv/winpos.c	28 Jan 2004 16:57:07 -0000
@@ -1551,6 +1551,44 @@
 }
 
 
+ /***********************************************************************
+ *		USER_EnumCallbackCDS (USER32.@)
+ *
+ * Callback function so we can update all existing windows
+ */
+static int new_bpp, new_width, new_height;
+static BOOL CALLBACK USER_EnumCallbackCDS(HWND hwnd, LPARAM ignored)
+{
+    WND *win;
+    WPARAM wParam = new_bpp;
+    LPARAM lParam = (new_height<<16) | new_width;
+    /* only send to windows in the current process */
+    win = WIN_GetPtr( hwnd );
+    if ( (!win) || (win == WND_OTHER_PROCESS)) return TRUE;
+    WIN_ReleasePtr( win );
+    TRACE("sending WM_DISPLAYCHANGE to %p\n", hwnd);
+    SendMessageA( hwnd, WM_DISPLAYCHANGE, wParam, lParam );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		X11DRV_handle_desktop_resize
+ */
+void X11DRV_handle_desktop_resize( unsigned int width, unsigned int height )
+{
+    RECT rect;
+    HWND hwnd = GetDesktopWindow();
+    screen_width  = width;
+    screen_height = height;
+    TRACE("HWND: %p change to (%dx%d)\n", hwnd, width, height);
+    SetRect( &rect, 0, 0, width, height );
+    WIN_SetRectangles( hwnd, &rect, &rect );
+    EnumWindows(USER_EnumCallbackCDS, 0);
+}
+
+
+
 /***********************************************************************
  *		X11DRV_ConfigureNotify
  */
@@ -1562,6 +1600,8 @@
     RECT rect;
     WINDOWPOS winpos;
     int x = event->x, y = event->y;
+
+    if (hwnd == GetDesktopWindow()) X11DRV_handle_desktop_resize( event->width, event->height );
 
     if (!(win = WIN_GetPtr( hwnd ))) return;
     data = win->pDriverData;
Index: dlls/x11drv/x11drv.h
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/x11drv.h,v
retrieving revision 1.20
diff -u -r1.20 x11drv.h
--- dlls/x11drv/x11drv.h	21 Jan 2004 02:22:26 -0000	1.20
+++ dlls/x11drv/x11drv.h	28 Jan 2004 16:57:07 -0000
@@ -222,6 +222,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);
@@ -544,13 +545,13 @@
 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 );
 extern void X11DRV_set_wm_hints( Display *display, WND *win );
 
+extern void X11DRV_handle_desktop_resize(unsigned int width, unsigned int height);
 extern void X11DRV_Settings_AddDepthModes(void);
 extern void X11DRV_Settings_AddOneMode(unsigned int width, unsigned int height, unsigned int bpp, unsigned int freq);
 extern int X11DRV_Settings_CreateDriver(LPDDHALINFO info);
Index: dlls/x11drv/x11drv_main.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/x11drv_main.c,v
retrieving revision 1.86
diff -u -r1.86 x11drv_main.c
--- dlls/x11drv/x11drv_main.c	21 Jan 2004 02:22:26 -0000	1.86
+++ dlls/x11drv/x11drv_main.c	28 Jan 2004 16:57:08 -0000
@@ -90,6 +90,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;
 
@@ -319,6 +320,36 @@
     RegCloseKey( hkey );
 }
 
+/***********************************************************************
+ *           detect_desktop_mode
+ *
+ * returns the HWND if found
+ */
+static int detect_desktop_mode(unsigned int *w, unsigned int *h)
+{
+    int res = 0;
+    RECT r;
+    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" );
+            GetClientRect(reply->cur_handle, &r);
+            *w = r.right-r.left;
+            *h = r.bottom-r.top;
+        }
+        else
+        {
+            TRACE("No desktop window thread -- using X root window\n");
+        }
+    }
+    SERVER_END_REQ;
+    return res;
+}
+
 
 /***********************************************************************
  *           X11DRV process initialisation routine
@@ -326,7 +357,7 @@
 static void process_attach(void)
 {
     Display *display;
-
+    unsigned int desktop_w, desktop_h;
     get_server_startup();
     setup_options();
 
@@ -378,8 +409,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.4
diff -u -r1.4 xrandr.c
--- dlls/x11drv/xrandr.c	27 Jan 2004 00:01:43 -0000	1.4
+++ dlls/x11drv/xrandr.c	28 Jan 2004 16:57:08 -0000
@@ -56,7 +56,6 @@
     return 1;
 }
 
-static Bool in_desktop_mode;
 
 /* create the mode structures */
 static void make_modes(void)
@@ -154,8 +153,6 @@
                               dd_modes[mode].dwWidth, dd_modes[mode].dwHeight, rate);
                         stat = XRRSetScreenConfigAndRate (gdi_display, sc, root, 
                                                           size, rot, rate, CurrentTime);
-                        FIXME("Need to update SYSMETRICS after resizing display (now %ldx%ld)\n",
-                              dd_modes[mode].dwWidth, dd_modes[mode].dwHeight);
                     }
                 }
             }
@@ -165,12 +162,15 @@
                       dd_modes[mode].dwWidth, dd_modes[mode].dwHeight);
                 stat = XRRSetScreenConfig (gdi_display, sc, root, 
                                            size, rot, CurrentTime);
-                FIXME("Need to update SYSMETRICS after resizing display (now %ldx%ld)\n",
-                      dd_modes[mode].dwWidth, dd_modes[mode].dwHeight);
             }
         }
     }
-    if (stat != RRSetConfigSuccess)
+    if (stat == RRSetConfigSuccess)
+    {
+        screen_width  = dd_modes[mode].dwWidth;
+        screen_height = dd_modes[mode].dwHeight;
+    }
+    else
     {
         ERR("Resolution change not successful -- perhaps display has changed?\n");
     }
@@ -183,11 +183,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();
@@ -222,6 +221,7 @@
             }
         }
     }
+    /* XRRSelectInput(gdi_display, root_window, RRScreenChangeNotifyMask); */
     wine_tsx11_unlock();
     if (!ok) return;
 
Index: dlls/x11drv/xvidmode.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/xvidmode.c,v
retrieving revision 1.27
diff -u -r1.27 xvidmode.c
--- dlls/x11drv/xvidmode.c	20 Jan 2004 22:48:57 -0000	1.27
+++ dlls/x11drv/xvidmode.c	28 Jan 2004 16:57:08 -0000
@@ -93,8 +93,6 @@
     return 1;
 }
 
-static Bool in_desktop_mode;
-
 int X11DRV_XF86VM_GetCurrentMode(void)
 {
   XF86VidModeModeLine line;
@@ -132,13 +130,8 @@
   TRACE("Resizing X display to %dx%d\n", 
         real_xf86vm_modes[mode]->hdisplay, real_xf86vm_modes[mode]->vdisplay);
   XF86VidModeSwitchToMode(gdi_display, DefaultScreen(gdi_display), real_xf86vm_modes[mode]);
-#if 0 /* FIXME */
-  SYSMETRICS_Set( SM_CXSCREEN, real_xf86vm_modes[mode]->hdisplay );
-  SYSMETRICS_Set( SM_CYSCREEN, real_xf86vm_modes[mode]->vdisplay );
-#else
-  FIXME("Need to update SYSMETRICS after resizing display (now %dx%d)\n",
-        real_xf86vm_modes[mode]->hdisplay, real_xf86vm_modes[mode]->vdisplay);
-#endif
+  screen_width  = real_xf86vm_modes[mode]->hdisplay;
+  screen_height = real_xf86vm_modes[mode]->vdisplay;
 #if 0 /* it is said that SetViewPort causes problems with some X servers */
   XF86VidModeSetViewPort(gdi_display, DefaultScreen(gdi_display), 0, 0);
 #else
@@ -155,8 +148,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;
@@ -183,13 +174,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: programs/Makefile.in
===================================================================
RCS file: /home/wine/wine/programs/Makefile.in,v
retrieving revision 1.43
diff -u -r1.43 Makefile.in
--- programs/Makefile.in	6 Jan 2004 20:49:59 -0000	1.43
+++ programs/Makefile.in	28 Jan 2004 16:57:09 -0000
@@ -26,6 +26,7 @@
 	winecfg \
 	wineconsole \
 	winedbg \
+	winedesktop \
 	winefile \
 	winemenubuilder \
 	winemine \
@@ -55,6 +56,7 @@
 	winecfg \
 	wineconsole \
 	winedbg \
+	winedesktop \
 	winefile \
 	winemenubuilder \
 	winemine \
@@ -77,6 +79,7 @@
 	winecfg \
 	wineconsole \
 	winedbg \
+	winedesktop \
 	winefile \
 	winemine \
 	winepath \
@@ -106,6 +109,7 @@
 	winecfg.exe \
 	wineconsole.exe \
 	winedbg.exe \
+	winedesktop.exe \
 	winefile.exe \
 	winemenubuilder.exe \
 	winemine.exe \
@@ -224,6 +228,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) $@
+
 winefile.exe$(DLLEXT): winefile/winefile.exe$(DLLEXT)
 	$(RM) $@ && $(LN_S) winefile/winefile.exe$(DLLEXT) $@
 
@@ -270,6 +277,7 @@
 winecfg/winecfg.exe$(DLLEXT): winecfg
 wineconsole/wineconsole.exe$(DLLEXT): wineconsole
 winedbg/winedbg.exe$(DLLEXT): winedbg
+winedesktop/winedesktop.exe$(DLLEXT): winedesktop
 winefile/winefile.exe$(DLLEXT): winefile
 winemenubuilder/winemenubuilder.exe$(DLLEXT): winemenubuilder
 winemine/winemine.exe$(DLLEXT): winemine
Index: server/protocol.def
===================================================================
RCS file: /home/wine/wine/server/protocol.def,v
retrieving revision 1.92
diff -u -r1.92 protocol.def
--- server/protocol.def	3 Jan 2004 00:38:30 -0000	1.92
+++ server/protocol.def	28 Jan 2004 16:57:11 -0000
@@ -2236,3 +2236,11 @@
 #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 */
+ at REQ(set_desktop_window)
+    user_handle_t  handle;      /* desktop window */
+ at REPLY
+    user_handle_t  cur_handle;  /* current desktop window */
+    thread_id_t    cur_tid;     /* current owner thread id */
+ at END
Index: server/window.c
===================================================================
RCS file: /home/wine/wine/server/window.c,v
retrieving revision 1.29
diff -u -r1.29 window.c
--- server/window.c	11 Dec 2003 05:34:53 -0000	1.29
+++ server/window.c	28 Jan 2004 16:57:12 -0000
@@ -82,7 +82,8 @@
     char             extra_bytes[1];  /* extra bytes storage */
 };
 
-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 */
 
 /* global window pointers */
 static struct window *shell_window;
@@ -242,6 +243,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 */
@@ -603,7 +610,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;
@@ -930,4 +937,43 @@
     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 && !orig_top_window)
+    {
+        /* set a new desktop window */
+        struct window *the_window = get_window(req->handle);
+        struct window *ptr;
+        int total = 0;
+        int okay = 1;
+        assert( the_window );
+        assert( the_window->thread );
+        /* make sure only the requesting process has windows */
+        for (ptr = top_window->first_child, total = 0; ptr; ptr = ptr->next)
+        {
+            total++;
+            if (ptr->thread != current) okay = 0;
+        }
+        if (okay)
+        {
+            orig_top_window = top_window;
+            top_window = the_window;
+            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);
+    }
+    else
+    {
+        reply->cur_handle = NULL;
+        reply->cur_tid = 0;
+    }
 }
Index: windows/sysmetrics.c
===================================================================
RCS file: /home/wine/wine/windows/sysmetrics.c,v
retrieving revision 1.35
diff -u -r1.35 sysmetrics.c
--- windows/sysmetrics.c	5 Sep 2003 23:15:39 -0000	1.35
+++ windows/sysmetrics.c	28 Jan 2004 16:57:12 -0000
@@ -298,6 +298,40 @@
  */
 INT WINAPI GetSystemMetrics( INT index )
 {
+    HDC hdc;
+    /* return 0 if out of range */
     if ((index < 0) || (index > SM_WINE_CMETRICS)) return 0;
+    /* obtain and update the screen resolution if we need it */
+    switch (index)
+    {
+    case SM_CXFULLSCREEN:
+    case SM_CYFULLSCREEN:
+    case SM_CXMAXTRACK:
+    case SM_CYMAXTRACK:
+    case SM_CXSCREEN:
+    case SM_CYSCREEN:
+    case SM_CXVIRTUALSCREEN:
+    case SM_CYVIRTUALSCREEN:
+    case SM_CXMAXIMIZED:
+    case SM_CYMAXIMIZED:
+        hdc = CreateDCA( "DISPLAY", NULL, NULL, NULL );
+	sysMetrics[SM_CXSCREEN] = GetDeviceCaps( hdc, HORZRES );
+	sysMetrics[SM_CYSCREEN] = GetDeviceCaps( hdc, VERTRES );
+	DeleteDC( hdc );
+
+	sysMetrics[SM_CXFULLSCREEN] = sysMetrics[SM_CXSCREEN];
+	sysMetrics[SM_CYFULLSCREEN] = sysMetrics[SM_CYSCREEN] - sysMetrics[SM_CYCAPTION];
+
+	/* FIXME: How do I calculate these? */
+	sysMetrics[SM_CXMAXTRACK] = sysMetrics[SM_CXSCREEN] + 4 + 2 * sysMetrics[SM_CXFRAME];
+	sysMetrics[SM_CYMAXTRACK] = sysMetrics[SM_CYSCREEN] + 4 + 2 * sysMetrics[SM_CYFRAME];
+	sysMetrics[SM_CXMAXIMIZED] = sysMetrics[SM_CXSCREEN] + 2 * sysMetrics[SM_CXFRAME];
+	sysMetrics[SM_CYMAXIMIZED] = sysMetrics[SM_CYSCREEN] + 2 * sysMetrics[SM_CXFRAME];
+
+	sysMetrics[SM_CXVIRTUALSCREEN] = sysMetrics[SM_CXSCREEN];
+	sysMetrics[SM_CYVIRTUALSCREEN] = sysMetrics[SM_CYSCREEN];
+
+	break;
+    }
     return sysMetrics[index];
 }
Index: windows/win.c
===================================================================
RCS file: /home/wine/wine/windows/win.c,v
retrieving revision 1.233
diff -u -r1.233 win.c
--- windows/win.c	20 Jan 2004 04:29:20 -0000	1.233
+++ windows/win.c	28 Jan 2004 16:57:14 -0000
@@ -749,6 +749,18 @@
     pWndDesktop->flags             = 0;
     pWndDesktop->hSysMenu          = 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");
+            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	2004-01-28 10:53:34.000000000 -0600
@@ -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
+
+ at MAKE_PROG_RULES@
+
+### Dependencies:
--- /dev/null	2003-01-30 04:24:37.000000000 -0600
+++ programs/winedesktop/winedesktop.h	2004-01-28 10:53:34.000000000 -0600
@@ -0,0 +1,35 @@
+/*
+ * Wine Desktop
+ *
+ * Copyright 2004 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    hChildWnd;
+    HWND    hDesktopWindow;
+    DWORD   dwStyle;
+    char* szDesktopClassName;
+    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	2004-01-28 10:53:34.000000000 -0600
@@ -0,0 +1,375 @@
+/*
+ * Wine Desktop
+ *
+ * Copyright 2004 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
+ *  - Decide what command-line arguments would be useful
+ */
+
+#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;
+
+/***********************************************************************
+ *           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);
+}
+
+/***********************************************************************
+ *           EnumCallbackQES
+ *
+ * Callback for sending QUERYENDSESSION messages
+ */
+static BOOL CALLBACK EnumCallbackQES(HWND hwnd, LPARAM lParam)
+{
+    int *okptr = (int *) lParam;
+    if (hwnd == Globals.hChildWnd) return TRUE;
+    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;
+    if (hwnd == Globals.hChildWnd) return TRUE;
+    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.  
+ */
+static BOOL BecomeDesktop()
+{
+    BOOL res = TRUE;
+    SERVER_START_REQ( set_desktop_window )
+    {
+        req->handle = Globals.hChildWnd;
+        wine_server_call( req );
+        Globals.hDesktopWindow = reply->cur_handle;
+        if (Globals.hDesktopWindow != Globals.hChildWnd)
+        {
+            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;
+}
+
+/***********************************************************************
+ *           WrapperWndProc
+ *
+ * Window procedure
+ */
+static LRESULT WINAPI WrapperWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+    switch (message) {
+    case WM_CLOSE:
+        if (QueryEndSession(hwnd)) PostQuitMessage (0);
+        break;
+    case WM_CREATE: {
+        RECT rc;
+        GetClientRect(hwnd, &rc);
+        Globals.hChildWnd = CreateWindow(Globals.szDesktopClassName, NULL,
+                                         WS_CHILD | WS_VISIBLE,
+                                         rc.left, rc.top, rc.right, rc.bottom, hwnd,
+                                         NULL, Globals.hInstance, NULL);
+        WINE_TRACE("setting %08lx to top_window\n", (DWORD) Globals.hChildWnd);
+        if (!BecomeDesktop())
+        {
+            if (IsWindow(Globals.hMainWnd)) DestroyWindow(Globals.hMainWnd);
+            Bail("Could not become WineDesktop window");
+        }
+        break;
+    }
+    case WM_DESTROY:
+        PostQuitMessage (0);
+        break;
+    case WM_NCHITTEST:
+        return HTCLIENT;
+    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: {
+        WINE_ERR("got WM_WINEDESKTOP_SIZE should have gone to child!\n");
+    }
+    default:
+        return DefWindowProc (hwnd, message, wParam, lParam);
+    }
+    return 0;
+}
+
+/***********************************************************************
+ *           DesktopWndProc
+ *
+ * Window procedure
+ */
+static LRESULT WINAPI DesktopWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+    switch (message) {
+    case WM_CREATE:
+        SystemParametersInfoA( SPI_SETDESKPATTERN, -1, NULL, FALSE );
+        SetDeskWallPaper( (LPSTR)-1 );
+        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_WINEDESKTOP_SIZE: {
+        int width, height;
+        Globals.width = width = LOWORD(lParam);
+        Globals.height = height = HIWORD(lParam);
+        WINE_TRACE("request to resize to %dx%d\n", width, height);
+        AdjustSize( &width, &height );
+        SetWindowPos(Globals.hMainWnd, NULL, 0, 0, width, height, SWP_NOMOVE|SWP_NOZORDER);
+        SetWindowPos(Globals.hChildWnd, NULL, 0, 0, Globals.width, Globals.height,
+                     SWP_NOOWNERZORDER | SWP_NOZORDER);
+        /* FIXME
+        InvalidateRect( hwnd, NULL, TRUE );
+        */
+        break;
+    }
+    default:
+        return DefWindowProc (hwnd, message, wParam, lParam);
+    }
+    return 0;
+}
+
+/***********************************************************************
+ *           ParseGeometry
+ *
+ * Simple routine 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
+ *
+ */
+int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
+{
+    MSG msg;
+    WNDCLASS class;
+    char szWrapperClassName[] = "WineDesktopWrapperClass";
+    char szWinName[] = "Wine Desktop";
+    DWORD tidDesktop;
+    HKEY hkey = NULL;
+    char buffer[100];
+    int width, height;
+
+    ZeroMemory(&Globals, sizeof(Globals));
+
+    Globals.szDesktopClassName = "WineDesktopClass";
+    Globals.hDesktopWindow = GetDesktopWindow();
+    Globals.width = 640;
+    Globals.height = 480;
+    Globals.hChildWnd = NULL;
+    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   = WrapperWndProc;
+    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 = szWrapperClassName;
+    
+    if (!RegisterClass (&class)) return FALSE;
+
+    ZeroMemory(&class, sizeof(class));
+    class.style         = CS_GLOBALCLASS;
+    class.lpfnWndProc   = DesktopWndProc;
+    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 = Globals.szDesktopClassName;
+    
+    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 (szWrapperClassName, szWinName, 
+                                     Globals.dwStyle,
+                                     CW_USEDEFAULT, CW_USEDEFAULT,
+                                     width, height, 0,
+                                     0, Globals.hInstance, 0);
+
+    ShowWindow (Globals.hMainWnd, show);
+    UpdateWindow (Globals.hMainWnd);
+
+    while (GetMessage(&msg, NULL, 0, 0)) {
+        if (msg.hwnd == Globals.hMainWnd || msg.hwnd == Globals.hChildWnd)
+        {
+            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;
+}
+


More information about the wine-patches mailing list