Darrell's fix for Quartz mouse motion
authorSam Lantinga <slouken@libsdl.org>
Mon, 12 Aug 2002 22:43:58 +0000
changeset 435 140798e1e7a6
parent 434 ed58b98c0d9d
child 436 3691cc3e14b3
Darrell's fix for Quartz mouse motion
src/video/quartz/SDL_QuartzEvents.m
src/video/quartz/SDL_QuartzVideo.h
src/video/quartz/SDL_QuartzVideo.m
--- a/src/video/quartz/SDL_QuartzEvents.m	Mon Aug 12 14:54:38 2002 +0000
+++ b/src/video/quartz/SDL_QuartzEvents.m	Mon Aug 12 22:43:58 2002 +0000
@@ -23,6 +23,8 @@
 
 #include "SDL_QuartzKeys.h"
 
+
+
 static SDLKey keymap[256];
 static unsigned int currentMods = 0; /* Current keyboard modifiers, to track modifier state */
 static int last_virtual_button = 0; /* Last virtual mouse button pressed */
@@ -305,9 +307,7 @@
 
 static void QZ_PumpEvents (_THIS)
 {
-    static NSPoint lastMouse;
-    NSPoint mouse, saveMouse;
-    Point qdMouse;
+    int firstMouseEvent;
     CGMouseDelta dx, dy;
 
     NSDate *distantPast;
@@ -320,33 +320,13 @@
     distantPast = [ NSDate distantPast ];
 
     winRect = NSMakeRect (0, 0, SDL_VideoSurface->w, SDL_VideoSurface->h);
-    titleBarRect = NSMakeRect ( 0, SDL_VideoSurface->h, SDL_VideoSurface->w,
-                                SDL_VideoSurface->h + 22 );
-
-    if (currentGrabMode != SDL_GRAB_ON) { /* if grabbed, the cursor can't move! (see fallback below) */
-
-        /* 1/2 second after a warp, the mouse cannot move (don't ask me why) */
-        /* So, approximate motion with CGGetLastMouseDelta, which still works, somehow */
-        if (! warp_flag) {
-
-            GetGlobalMouse (&qdMouse);  /* use Carbon since [ NSEvent mouseLocation ] is broken */
-            mouse = NSMakePoint (qdMouse.h, qdMouse.v);
-            saveMouse = mouse;
-
-            if (mouse.x != lastMouse.x || mouse.y != lastMouse.y) {
-
-                QZ_PrivateCGToSDL (this, &mouse);
-                /* -note- we now generate mouse motion events if the mouse isn't over the window */
-                if (inForeground /* && NSPointInRect (mouse, winRect)*/) {
-                    //printf ("Mouse Loc: (%f, %f)\n", mouse.x, mouse.y);
-                    SDL_PrivateMouseMotion (0, 0, mouse.x, mouse.y);
-                }
-            }
-            lastMouse = saveMouse;
-        }
-    }
-
-    /* accumulate any mouse events into one SDL mouse event */
+    titleBarRect = NSMakeRect (0, SDL_VideoSurface->h, SDL_VideoSurface->w,
+                                SDL_VideoSurface->h + 22);
+    
+    /* send the first mouse event in absolute coordinates */
+    firstMouseEvent = 1;
+    
+    /* accumulate any additional mouse moved events into one SDL mouse event */
     dx = 0;
     dy = 0;
     
@@ -419,14 +399,14 @@
                     break;
                 case NSLeftMouseDragged:
                 case NSRightMouseDragged:
-                case 27:
+                case NSOtherMouseDragged: /* usually middle mouse dragged */
                 case NSMouseMoved:
                     if (currentGrabMode == SDL_GRAB_ON) {
                 
                         /**
-                         *  If input is grabbed, we'll wing it and try to send some mouse
-                         *  moved events with CGGetLastMouseDelta(). Not optimal, but better
-                         *  than nothing.
+                         *  If input is grabbed, the cursor doesn't move,
+                         *  so we have to call the lowlevel window server
+                         *  function. This is less accurate but works OK.                         
                          **/
                         CGMouseDelta dx1, dy1;
                         CGGetLastMouseDelta (&dx1, &dy1);
@@ -435,6 +415,12 @@
                     }
                     else if (warp_flag) {
                 
+                        /**
+                         * If we just warped the mouse, the cursor is frozen for a while.
+                         * So we have to use the lowlevel function until it
+                         * unfreezes. This really helps apps that continuously
+                         * warp the mouse to keep it in the game window.
+                         **/
                         Uint32 ticks;
                 
                         ticks = SDL_GetTicks();
@@ -450,6 +436,30 @@
                             warp_flag = 0;
                         }
                     }
+                    else if (firstMouseEvent) {
+                        
+                        /**
+                         * Get the first mouse event in a possible
+                         * sequence of mouse moved events. Since we
+                         * use absolute coordinates, this serves to
+                         * compensate any inaccuracy in deltas, and
+                         * provides the first known mouse position,
+                         * since everything after this uses deltas
+                         **/
+                        NSPoint p = [ event locationInWindow ];
+                        QZ_PrivateCocoaToSDL(this, &p);
+                        
+                        firstMouseEvent = 0;
+                    }
+                    else {
+                    
+                       /**
+                        * Get the amount moved since the last drag or move event,
+                        * add it on for one big move event at the end.
+                        **/
+                       dx += [ event deltaX ];
+                       dy += [ event deltaY ];
+                    }
                     break;
                 case NSScrollWheel:
                     if (NSPointInRect([ event locationInWindow ], winRect)) {
@@ -490,9 +500,9 @@
         }
     } while (event != nil);
     
-    /* check for accumulated mouse events */
+    /* handle accumulated mouse moved events */
     if (dx != 0 || dy != 0)
-    SDL_PrivateMouseMotion (0, 1, dx, dy);
+        SDL_PrivateMouseMotion (0, 1, dx, dy);
     
     [ pool release ];
 }
--- a/src/video/quartz/SDL_QuartzVideo.h	Mon Aug 12 14:54:38 2002 +0000
+++ b/src/video/quartz/SDL_QuartzVideo.h	Mon Aug 12 22:43:58 2002 +0000
@@ -61,6 +61,24 @@
 #include "SDL_pixels_c.h"
 #include "SDL_events_c.h"
 
+/* 
+   Add methods to get at private members of NSScreen. 
+   Since there is a bug in Apple's screen switching code
+   that does not update this variable when switching
+   to fullscreen, we'll set it manually (but only for the
+   main screen).
+*/
+@interface NSScreen (NSScreenAccess)
+- (void) setFrame:(NSRect)frame;
+@end
+
+@implementation NSScreen (NSScreenAccess)
+- (void) setFrame:(NSRect)frame;
+{
+    _frame = frame;
+}
+@end
+
 /* This is a workaround to directly access NSOpenGLContext's CGL context */
 /* We need to do this in order to check for errors */
 @interface NSOpenGLContext (CGLContextAccess)
--- a/src/video/quartz/SDL_QuartzVideo.m	Mon Aug 12 14:54:38 2002 +0000
+++ b/src/video/quartz/SDL_QuartzVideo.m	Mon Aug 12 22:43:58 2002 +0000
@@ -345,7 +345,8 @@
 
         SDL_QuartzGammaTable gamma_table;
         int gamma_error;
-
+        NSRect screen_rect;
+        
         gamma_error = QZ_FadeGammaOut (this, &gamma_table);
 
         /* Release the OpenGL context */
@@ -361,6 +362,13 @@
         CGDisplayRelease (display_id);
         ShowMenuBar ();
 
+        /* 
+           reset the main screen's rectangle, see comment
+           in QZ_SetVideoFullscreen
+        */
+        screen_rect = NSMakeRect(0,0,device_width,device_height);
+        [ [ NSScreen mainScreen ] setFrame:screen_rect ];
+        
         if (! gamma_error)
             QZ_FadeGammaIn (this, &gamma_table);
     }
@@ -401,7 +409,8 @@
     int exact_match;
     int gamma_error;
     SDL_QuartzGammaTable gamma_table;
-
+    NSRect screen_rect;
+    
     /* See if requested mode exists */
     mode = CGDisplayBestModeForParameters (display_id, bpp, width,
                                            height, &exact_match);
@@ -484,6 +493,16 @@
     if (! gamma_error )
         QZ_FadeGammaIn (this, &gamma_table);
 
+    /* 
+       There is a bug in Cocoa where NSScreen doesn't synchronize
+       with CGDirectDisplay, so the main screen's frame is wrong.
+       As a result, coordinate translation produces wrong results.
+       We can hack around this bug by setting the screen rect
+       ourselves. This hack should be removed if/when the bug is fixed.
+    */
+    screen_rect = NSMakeRect(0,0,width,height);
+    [ [ NSScreen mainScreen ] setFrame:screen_rect ]; 
+
     /* Save the flags to ensure correct tear-down */
     mode_flags = current->flags;