[User] Fix clip cursor handling on resolution change

Lionel Ulmer lionel.ulmer at free.fr
Tue Nov 1 15:03:51 CST 2005


This is the patch which fixes the mouse on DungeonKeeper (resent after 1.5
years :-) ). But this time I added some tests so I hope it will get in :-)

          Lionel

Changelog:
 - fix ClipCursor handling on resolution change
 - add tests for other corner cases

-- 
		 Lionel Ulmer - http://www.bbrox.org/
-------------- next part --------------
Index: dlls/x11drv/settings.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/settings.c,v
retrieving revision 1.3
diff -u -r1.3 settings.c
--- dlls/x11drv/settings.c	18 Jul 2005 13:20:18 -0000	1.3
+++ dlls/x11drv/settings.c	1 Nov 2005 21:01:53 -0000
@@ -239,6 +239,7 @@
 {
     DWORD i;
     DEVMODEW dm;
+    int cur_mode;
 
     TRACE("(%s,%p,%p,0x%08lx,%p)\n",debugstr_w(devname),devmode,hwnd,flags,lpvoid);
     TRACE("flags=%s\n",_CDS_flags(flags));
@@ -260,6 +261,8 @@
         devmode = &dm;
     }
 
+    cur_mode = pGetCurrentMode();
+
     for (i = 0; i < dd_mode_count; i++)
     {
         if (devmode->dmFields & DM_BITSPERPEL)
@@ -284,11 +287,33 @@
         }
         /* we have a valid mode */
         TRACE("Requested display settings match mode %ld (%s)\n", i, handler_name);
-        if (!(flags & CDS_TEST))
+        if (!(flags & CDS_TEST)) {
             pSetCurrentMode(i);
+	    
+	    if (i != cur_mode) {
+		/* This means that we choose another resolution .. Update the clip cursor accordingly.
+		 *
+		 * Note: at this point, as 'pSetCurrentMode' has been called, we suppose that the System
+		 *       metrics have been updated properly and that the sanity checks in ClipCursor
+		 *       won't give us any problems.
+		 */
+		RECT rect;
+		
+		TRACE("New mode (%ld) different from current mode (%d) - updating clip cursor.\n", i, cur_mode);
+		
+		rect.top    = 0;
+		rect.bottom = dd_modes[i].dwHeight;
+		
+		rect.left  = 0;
+		rect.right = dd_modes[i].dwWidth;
+		
+		ClipCursor(&rect);
+	    }
+	}
+	
         return DISP_CHANGE_SUCCESSFUL;
     }
-
+    
     /* no valid modes found */
     ERR("No matching mode found! (%s)\n", handler_name);
     return DISP_CHANGE_BADMODE;
Index: dlls/user/cursoricon.c
===================================================================
RCS file: /home/wine/wine/dlls/user/cursoricon.c,v
retrieving revision 1.17
diff -u -r1.17 cursoricon.c
--- dlls/user/cursoricon.c	12 Sep 2005 10:30:06 -0000	1.17
+++ dlls/user/cursoricon.c	1 Nov 2005 21:01:54 -0000
@@ -1553,7 +1553,29 @@
 BOOL WINAPI ClipCursor( const RECT *rect )
 {
     if (!rect) SetRectEmpty( &CURSOR_ClipRect );
-    else CopyRect( &CURSOR_ClipRect, rect );
+    else {
+	/* Some sanity checks (verified on Win2K) */
+	RECT screen_rect;
+	
+	screen_rect.top = screen_rect.left = 0;
+	screen_rect.right = GetSystemMetrics(SM_CXSCREEN);
+	screen_rect.bottom = GetSystemMetrics(SM_CYSCREEN);
+
+	/* If the rectangle is bad or out of the screen rectangle, the clip cursor is set
+	 * to the whole screen.
+	 */
+	if ((rect->top > rect->bottom) || (rect->left > rect->right) ||
+	    (rect->top >= screen_rect.bottom) ||
+	    (rect->bottom < screen_rect.top) ||
+	    (rect->left >= screen_rect.right) ||
+	    (rect->right < screen_rect.left))
+	    rect = &screen_rect;
+	
+	/* If the rectangle is empty, keep the same rectangle */
+	if (IsRectEmpty(rect)) CopyRect(&CURSOR_ClipRect, rect);
+	/* Otherwise, it's the intersection of the given rectangle with the screen */
+	else IntersectRect(&CURSOR_ClipRect, rect, &screen_rect);
+    }
     return TRUE;
 }
 
Index: dlls/user/user_main.c
===================================================================
RCS file: /home/wine/wine/dlls/user/user_main.c,v
retrieving revision 1.97
diff -u -r1.97 user_main.c
--- dlls/user/user_main.c	17 Sep 2005 14:28:44 -0000	1.97
+++ dlls/user/user_main.c	1 Nov 2005 21:01:54 -0000
@@ -158,6 +158,7 @@
 static BOOL process_attach(void)
 {
     HINSTANCE16 instance;
+    RECT rect;
 
     /* Create USER heap */
     if ((instance = LoadLibrary16( "USER.EXE" )) >= 32) USER_HeapSel = instance | 7;
@@ -173,6 +174,13 @@
     /* Initialize system colors and metrics */
     SYSPARAMS_Init();
 
+    /* Now that system metrics are set-up, we can initialize the clip rectangle */
+    rect.left   = 0;
+    rect.right  = GetSystemMetrics(SM_CXSCREEN);
+    rect.top    = 0;
+    rect.bottom = GetSystemMetrics(SM_CYSCREEN);
+    ClipCursor(&rect);
+
     /* Setup palette function pointers */
     palette_init();
 
Index: dlls/user/tests/monitor.c
===================================================================
RCS file: /home/wine/wine/dlls/user/tests/monitor.c,v
retrieving revision 1.2
diff -u -r1.2 monitor.c
--- dlls/user/tests/monitor.c	11 Oct 2005 20:27:27 -0000	1.2
+++ dlls/user/tests/monitor.c	1 Nov 2005 21:01:54 -0000
@@ -86,9 +86,104 @@
     }
 }
 
+static void test_changedisplaysettings(void)
+{
+    RECT rect;
+    DEVMODE mode;
+
+    EnumDisplaySettingsEx(NULL, ENUM_CURRENT_SETTINGS, &mode, 0);
+    
+    /* Basic test */
+    rect.left  = 0;    rect.top    = 0;
+    rect.right = 400;  rect.bottom = 400;
+    ClipCursor(&rect);
+    memset(&rect, 0, sizeof(rect));
+    ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+    ok((rect.left == 0) && (rect.top == 0) && (rect.right == 400) && (rect.bottom == 400), "ClipCursor not properly set\n");
+
+    /* Test various 'broken' clipping cursors */
+    rect.left  = 50;    rect.top    = 50;
+    rect.right = 3000;  rect.bottom = 3000;
+    ClipCursor(&rect);
+    memset(&rect, 0, sizeof(rect));
+    ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+    ok((rect.left == 50) && (rect.top == 50) && (rect.right == mode.dmPelsWidth) && (rect.bottom == mode.dmPelsHeight), "ClipCursor not properly set (clip 1)\n");
+    
+    rect.left  = -50;  rect.top    = -50;
+    rect.right = 400;  rect.bottom = 400;
+    ClipCursor(&rect);
+    memset(&rect, 0, sizeof(rect));
+    ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+    ok((rect.left == 0) && (rect.top == 0) && (rect.right == 400) && (rect.bottom == 400), "ClipCursor not properly set (clip 2)\n");
+    
+    rect.left  = 3000;  rect.top    = 3000;
+    rect.right = 4000;  rect.bottom = 4000;
+    ClipCursor(&rect);
+    memset(&rect, 0, sizeof(rect));
+    ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+    ok((rect.left == 0) && (rect.top == 0) && (rect.right == mode.dmPelsWidth) && (rect.bottom == mode.dmPelsHeight), "ClipCursor not properly set (out of screen)\n");
+
+    rect.left  = 50;  rect.top    = 50;
+    rect.right = 50;  rect.bottom = 50;
+    ClipCursor(&rect);
+    memset(&rect, 0, sizeof(rect));
+    ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+    ok((rect.left == 50) && (rect.top == 50) && (rect.right == 50) && (rect.bottom == 50), "ClipCursor not properly set (point in screen)\n");
+
+    rect.left  = 3000;  rect.top    = 3000;
+    rect.right = 3000;  rect.bottom = 3000;
+    ClipCursor(&rect);
+    memset(&rect, 0, sizeof(rect));
+    ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+    ok((rect.left == 0) && (rect.top == 0) && (rect.right == mode.dmPelsWidth) && (rect.bottom == mode.dmPelsHeight), "ClipCursor not properly set (point out of screen)\n");
+
+    /* Tests with resolution / depth change */
+    if ((mode.dmPelsWidth != 640) || (mode.dmPelsHeight != 480)) {
+	/* I am lazy here... Instead of searching for a given mode in the list, I use 640x480 if not already the current
+	 * mode and just exit the test if it cannot be set.
+	 */
+	rect.left  = 0;    rect.top    = 0;
+	rect.right = 400;  rect.bottom = 400;
+	ClipCursor(&rect);
+	memset(&rect, 0, sizeof(rect));
+	
+	mode.dmSize = sizeof(mode);
+	mode.dmDriverExtra = 0;
+	mode.dmPelsWidth = 640;
+	mode.dmPelsHeight = 480;
+	mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
+	if (ChangeDisplaySettingsEx(NULL, &mode, NULL, 0, NULL) != DISP_CHANGE_SUCCESSFUL) {
+	    return;
+	}
+	memset(&rect, 0, sizeof(rect));
+	ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+	ok((rect.left == 0) && (rect.top == 0) && (rect.right == 640) && (rect.bottom == 480), "ClipCursor not properly set (after resolution change)\n");
+
+	rect.left  = 0;    rect.top    = 0;
+	rect.right = 400;  rect.bottom = 400;
+	ClipCursor(&rect);
+	memset(&rect, 0, sizeof(rect));
+	
+	mode.dmSize = sizeof(mode);
+	mode.dmDriverExtra = 0;
+	mode.dmPelsWidth = 640;
+	mode.dmPelsHeight = 480;
+	mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
+	if (ChangeDisplaySettingsEx(NULL, &mode, NULL, 0, NULL) != DISP_CHANGE_SUCCESSFUL) {
+	    return;
+	}
+	memset(&rect, 0, sizeof(rect));
+	ok(GetClipCursor(&rect), "GetClipCursor failed\n");
+	ok((rect.left == 0) && (rect.top == 0) && (rect.right == 400) && (rect.bottom == 400), "ClipCursor not properly set (after dummy resolution change)\n");	
+	
+	/* Go back to original mode */
+	ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
+    }
+}
 
 START_TEST(monitor)
 {
     init_function_pointers();
     test_enumdisplaydevices();
+    test_changedisplaysettings();
 }


More information about the wine-patches mailing list