From 3e33cd6ca44809355c190e5d41271d63fd1aac40 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 12 Aug 2002 22:43:58 +0000 Subject: [PATCH] Darrell's fix for Quartz mouse motion --- src/video/quartz/SDL_QuartzEvents.m | 82 ++++++++++++++++------------- src/video/quartz/SDL_QuartzVideo.h | 18 +++++++ src/video/quartz/SDL_QuartzVideo.m | 23 +++++++- 3 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/video/quartz/SDL_QuartzEvents.m b/src/video/quartz/SDL_QuartzEvents.m index 87ac064b6..cbcc0b6c1 100644 --- a/src/video/quartz/SDL_QuartzEvents.m +++ b/src/video/quartz/SDL_QuartzEvents.m @@ -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_DoDeactivate (_THIS) { static void QZ_PumpEvents (_THIS) { - static NSPoint lastMouse; - NSPoint mouse, saveMouse; - Point qdMouse; + int firstMouseEvent; CGMouseDelta dx, dy; NSDate *distantPast; @@ -320,33 +320,13 @@ static void QZ_PumpEvents (_THIS) 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 @@ static void QZ_PumpEvents (_THIS) 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 @@ static void QZ_PumpEvents (_THIS) } 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 @@ static void QZ_PumpEvents (_THIS) 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 @@ static void QZ_PumpEvents (_THIS) } } 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 ]; } diff --git a/src/video/quartz/SDL_QuartzVideo.h b/src/video/quartz/SDL_QuartzVideo.h index 391bbf086..d89548be0 100644 --- a/src/video/quartz/SDL_QuartzVideo.h +++ b/src/video/quartz/SDL_QuartzVideo.h @@ -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) diff --git a/src/video/quartz/SDL_QuartzVideo.m b/src/video/quartz/SDL_QuartzVideo.m index f802d6893..55da900be 100644 --- a/src/video/quartz/SDL_QuartzVideo.m +++ b/src/video/quartz/SDL_QuartzVideo.m @@ -345,7 +345,8 @@ static void QZ_UnsetVideoMode (_THIS) { 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 @@ static void QZ_UnsetVideoMode (_THIS) { 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 @@ static void QZ_UnsetVideoMode (_THIS) { 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 @@ static void QZ_UnsetVideoMode (_THIS) { 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;