diff --git a/src/video/quartz/SDL_QuartzEvents.m b/src/video/quartz/SDL_QuartzEvents.m index 20861ffaa..5b59a33b9 100644 --- a/src/video/quartz/SDL_QuartzEvents.m +++ b/src/video/quartz/SDL_QuartzEvents.m @@ -300,16 +300,18 @@ static void QZ_DoModifiers (_THIS, unsigned int newMods) { static void QZ_DoActivate (_THIS) { in_foreground = YES; - - /* Regrab the mouse, only if it was previously grabbed */ - if ( current_grab_mode == SDL_GRAB_ON ) { - QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); - CGAssociateMouseAndMouseCursorPosition (0); + + /* Hide the mouse cursor if was hidden */ + if (!cursor_visible) { + HideCursor (); } - /* Hide the mouse cursor if inside the app window */ - if (!QZ_cursor_visible) { - HideCursor (); + /* Regrab input, only if it was previously grabbed */ + if ( current_grab_mode == SDL_GRAB_ON ) { + + /* Restore cursor location if input was grabbed */ + QZ_PrivateWarpCursor (this, cursor_loc.x, cursor_loc.y); + QZ_ChangeGrabState (this, QZ_ENABLE_GRAB); } SDL_PrivateAppActive (1, SDL_APPINPUTFOCUS); @@ -319,15 +321,17 @@ static void QZ_DoDeactivate (_THIS) { in_foreground = NO; - /* Ungrab mouse if it is grabbed */ - if ( current_grab_mode == SDL_GRAB_ON ) { - CGAssociateMouseAndMouseCursorPosition (1); - } + /* Get the current cursor location, for restore on activate */ + cursor_loc = [ NSEvent mouseLocation ]; /* global coordinates */ + if (qz_window) + QZ_PrivateGlobalToLocal (this, &cursor_loc); + QZ_PrivateCocoaToSDL (this, &cursor_loc); + + /* Reassociate mouse and cursor */ + CGAssociateMouseAndMouseCursorPosition (1); - /* Show the mouse cursor */ - if (!QZ_cursor_visible) { - ShowCursor (); - } + /* Show the cursor */ + ShowCursor (); SDL_PrivateAppActive (0, SDL_APPINPUTFOCUS); } @@ -342,10 +346,10 @@ void QZ_SleepNotificationHandler (void * refcon, switch(messageType) { case kIOMessageSystemWillSleep: - IOAllowPowerChange(powerConnection, (long) messageArgument); + IOAllowPowerChange(power_connection, (long) messageArgument); break; case kIOMessageCanSystemSleep: - IOAllowPowerChange(powerConnection, (long) messageArgument); + IOAllowPowerChange(power_connection, (long) messageArgument); break; case kIOMessageSystemHasPoweredOn: /* awake */ @@ -360,9 +364,9 @@ static void QZ_RegisterForSleepNotifications (_THIS) IONotificationPortRef thePortRef; io_object_t notifier; - powerConnection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, ¬ifier); + power_connection = IORegisterForSystemPower (this, &thePortRef, QZ_SleepNotificationHandler, ¬ifier); - if (powerConnection == 0) + if (power_connection == 0) NSLog(@"SDL: QZ_SleepNotificationHandler() IORegisterForSystemPower failed."); rls = IONotificationPortGetRunLoopSource (thePortRef); @@ -401,17 +405,18 @@ static void QZ_PumpEvents (_THIS) event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast inMode: NSDefaultRunLoopMode dequeue:YES ]; - if (event != nil) { unsigned int type; BOOL isForGameWin; - - #define DO_MOUSE_DOWN(button, sendToWindow) do { \ + BOOL isInGameWin; + + #define DO_MOUSE_DOWN(button) do { \ if ( in_foreground ) { \ - if ( (SDL_VideoSurface->flags & SDL_FULLSCREEN) || \ - NSPointInRect([event locationInWindow], winRect) ) \ - SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ + if ( isInGameWin ) { \ + SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); \ + expect_mouse_up |= 1<flags & SDL_FULLSCREEN) || \ - !NSPointInRect([event locationInWindow], titleBarRect) ) \ - SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); \ - [ NSApp sendEvent:event ]; \ + #define DO_MOUSE_UP(button) do { \ + if ( expect_mouse_up & (1<= winRect.size.width ) + p.x = winRect.size.width-1; + + if ( p.y >= winRect.size.height ) + p.y = winRect.size.height-1; + + QZ_PrivateWarpCursor (this, p.x, p.y); + } break; case NSScrollWheel: - if (NSPointInRect([ event locationInWindow ], winRect)) { + if ( isInGameWin ) { float dy; Uint8 button; dy = [ event deltaY ]; @@ -545,7 +550,7 @@ static void QZ_PumpEvents (_THIS) button = SDL_BUTTON_WHEELUP; else /* Scroll down */ button = SDL_BUTTON_WHEELDOWN; - /* For now, wheel is sent as a quick down+up */ + /* For now, wheel is sent as a quick down+up */ SDL_PrivateMouseButton (SDL_PRESSED, button, 0, 0); SDL_PrivateMouseButton (SDL_RELEASED, button, 0, 0); } diff --git a/src/video/quartz/SDL_QuartzVideo.h b/src/video/quartz/SDL_QuartzVideo.h index 3e098fd6c..e54b7daad 100644 --- a/src/video/quartz/SDL_QuartzVideo.h +++ b/src/video/quartz/SDL_QuartzVideo.h @@ -132,8 +132,12 @@ typedef struct SDL_PrivateVideoData { SDLKey keymap[256]; /* Mac OS X to SDL key mapping */ Uint32 current_mods; /* current keyboard modifiers, to track modifier state */ Uint32 last_virtual_button;/* last virtual mouse button pressed */ - io_connect_t powerConnection; /* used with IOKit to detect wake from sleep */ - + io_connect_t power_connection; /* used with IOKit to detect wake from sleep */ + Uint8 expect_mouse_up; /* used to determine when to send mouse up events */ + Uint8 grab_state; /* used to manage grab behavior */ + NSPoint cursor_loc; /* saved cursor coords, for activate/deactivate when grabbed */ + BOOL cursor_visible; /* tells if cursor was hidden or not */ + ImageDescriptionHandle yuv_idh; MatrixRecordPtr yuv_matrix; DecompressorComponent yuv_codec; @@ -167,7 +171,11 @@ typedef struct SDL_PrivateVideoData { #define keymap (this->hidden->keymap) #define current_mods (this->hidden->current_mods) #define last_virtual_button (this->hidden->last_virtual_button) -#define powerConnection (this->hidden->powerConnection) +#define power_connection (this->hidden->power_connection) +#define expect_mouse_up (this->hidden->expect_mouse_up) +#define grab_state (this->hidden->grab_state) +#define cursor_loc (this->hidden->cursor_loc) +#define cursor_visible (this->hidden->cursor_visible) #define yuv_idh (this->hidden->yuv_idh) #define yuv_matrix (this->hidden->yuv_matrix) @@ -179,6 +187,22 @@ typedef struct SDL_PrivateVideoData { #define yuv_height (this->hidden->yuv_height) #define yuv_port (this->hidden->yuv_port) + +/* grab states - the input is in one of these states */ +enum { + QZ_UNGRABBED = 0, + QZ_VISIBLE_GRAB, + QZ_INVISIBLE_GRAB +}; + +/* grab actions - these can change the grabbed state */ +enum { + QZ_ENABLE_GRAB = 0, + QZ_DISABLE_GRAB, + QZ_HIDECURSOR, + QZ_SHOWCURSOR +}; + /* Obscuring code: maximum number of windows above ours (inclusive) diff --git a/src/video/quartz/SDL_QuartzVideo.m b/src/video/quartz/SDL_QuartzVideo.m index 8480bab35..e18a2a135 100644 --- a/src/video/quartz/SDL_QuartzVideo.m +++ b/src/video/quartz/SDL_QuartzVideo.m @@ -130,6 +130,7 @@ static int QZ_VideoInit (_THIS, SDL_PixelFormat *video_format) { /* Set misc globals */ current_grab_mode = SDL_GRAB_OFF; in_foreground = YES; + cursor_visible = YES; /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */ QZ_RegisterForSleepNotifications (this); @@ -1491,7 +1492,6 @@ static SDL_Overlay* QZ_CreateYUVOverlay (_THIS, int width, int height, QDFlushPortBuffer (port, nil); } */ - } else { port = [ window_view qdPort ]; diff --git a/src/video/quartz/SDL_QuartzWM.m b/src/video/quartz/SDL_QuartzWM.m index b02d1f299..4f098d381 100644 --- a/src/video/quartz/SDL_QuartzWM.m +++ b/src/video/quartz/SDL_QuartzWM.m @@ -20,6 +20,8 @@ slouken@libsdl.org */ +static void QZ_ChangeGrabState (_THIS, int action); + struct WMcursor { Cursor curs; }; @@ -66,21 +68,21 @@ static WMcursor* QZ_CreateWMCursor (_THIS, Uint8 *data, Uint8 *mask, return(cursor); } -static int QZ_cursor_visible = 1; - static int QZ_ShowWMCursor (_THIS, WMcursor *cursor) { if ( cursor == NULL) { - if ( QZ_cursor_visible ) { + if ( cursor_visible ) { HideCursor (); - QZ_cursor_visible = 0; + cursor_visible = NO; + QZ_ChangeGrabState (this, QZ_HIDECURSOR); } } else { SetCursor(&cursor->curs); - if ( ! QZ_cursor_visible ) { + if ( ! cursor_visible ) { ShowCursor (); - QZ_cursor_visible = 1; + cursor_visible = YES; + QZ_ChangeGrabState (this, QZ_SHOWCURSOR); } } @@ -126,7 +128,7 @@ static void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) { } } - p->y = height - p->y; + p->y = height - p->y - 1; } /* Convert Cocoa coordinate to SDL coordinate */ @@ -179,12 +181,12 @@ static void QZ_PrivateWarpCursor (_THIS, int x, int y) { CGPoint cgp; p = NSMakePoint (x, y); - cgp = QZ_PrivateSDLToCG (this, &p); - CGDisplayMoveCursorToPoint (display_id, cgp); - warp_ticks = SDL_GetTicks(); - warp_flag = 1; - - SDL_PrivateMouseMotion(0, 0, x, y); + cgp = QZ_PrivateSDLToCG (this, &p); + QZ_PrivateCGToSDL (this, &p); + + /* this is the magic call that fixes cursor "freezing" after warp */ + CGSetLocalEventsSuppressionInterval (0.0); + CGWarpMouseCursorPosition (cgp); } static void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) { @@ -195,6 +197,9 @@ static void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) { /* Do the actual warp */ QZ_PrivateWarpCursor (this, x, y); + + /* Generate the mouse moved event */ + SDL_PrivateMouseMotion (0, 0, x, y); } static void QZ_MoveWMCursor (_THIS, int x, int y) { } @@ -289,24 +294,77 @@ static int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) { return 0; }*/ +static void QZ_ChangeGrabState (_THIS, int action) { + + /* + Figure out what the next state should be based on the action. + Ignore actions that can't change the current state. + */ + if ( grab_state == QZ_UNGRABBED ) { + if ( action == QZ_ENABLE_GRAB ) { + if ( cursor_visible ) + grab_state = QZ_VISIBLE_GRAB; + else + grab_state = QZ_INVISIBLE_GRAB; + } + } + else if ( grab_state == QZ_VISIBLE_GRAB ) { + if ( action == QZ_DISABLE_GRAB ) + grab_state = QZ_UNGRABBED; + else if ( action == QZ_HIDECURSOR ) + grab_state = QZ_INVISIBLE_GRAB; + } + else { + assert( grab_state == QZ_INVISIBLE_GRAB ); + + if ( action == QZ_DISABLE_GRAB ) + grab_state = QZ_UNGRABBED; + else if ( action == QZ_SHOWCURSOR ) + grab_state = QZ_VISIBLE_GRAB; + } + + /* now apply the new state */ + if (grab_state == QZ_UNGRABBED) { + + CGAssociateMouseAndMouseCursorPosition (1); + } + else if (grab_state == QZ_VISIBLE_GRAB) { + + CGAssociateMouseAndMouseCursorPosition (1); + } + else { + assert( grab_state == QZ_INVISIBLE_GRAB ); + + QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); + CGAssociateMouseAndMouseCursorPosition (0); + } +} + static SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode) { - switch (grab_mode) { - case SDL_GRAB_QUERY: - break; - case SDL_GRAB_OFF: - CGAssociateMouseAndMouseCursorPosition (1); - current_grab_mode = SDL_GRAB_OFF; - break; - case SDL_GRAB_ON: - QZ_WarpWMCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); - CGAssociateMouseAndMouseCursorPosition (0); - current_grab_mode = SDL_GRAB_ON; - break; - case SDL_GRAB_FULLSCREEN: - break; + int doGrab = grab_mode & SDL_GRAB_ON; + /*int fullscreen = grab_mode & SDL_GRAB_FULLSCREEN;*/ + + if ( this->screen == NULL ) { + SDL_SetError ("QZ_GrabInput: screen is NULL"); + return SDL_GRAB_OFF; } + if ( ! video_set ) { + /*SDL_SetError ("QZ_GrabInput: video is not set, grab will take effect on mode switch"); */ + current_grab_mode = grab_mode; + return grab_mode; /* Will be set later on mode switch */ + } + + if ( grab_mode != SDL_GRAB_QUERY ) { + if ( doGrab ) + QZ_ChangeGrabState (this, QZ_ENABLE_GRAB); + else + QZ_ChangeGrabState (this, QZ_DISABLE_GRAB); + + current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF; + } + return current_grab_mode; } @@ -386,4 +444,4 @@ static void QZ_DrawResizeIcon (_THIS, RgnHandle dirtyRegion) { SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect); } } -} \ No newline at end of file +}