Fixed the responder chain for event handling, the listener fully handles mouse events - even in fullscreen mode.
authorSam Lantinga <slouken@libsdl.org>
Mon, 21 Feb 2011 10:50:53 -0800
changeset 5371 fc3d3d580777
parent 5370 cb219a294ebf
child 5372 a244ea780baa
Fixed the responder chain for event handling, the listener fully handles mouse events - even in fullscreen mode. The only reason we need a custom view is to handle right mouse down. Implemented mouse grabbing, although it's kind of clunky right now. I'll be adding a relative mode that will be smoother soon.
src/events/SDL_mouse.c
src/events/SDL_mouse_c.h
src/video/cocoa/SDL_cocoamouse.m
src/video/cocoa/SDL_cocoawindow.h
src/video/cocoa/SDL_cocoawindow.m
--- a/src/events/SDL_mouse.c	Sun Feb 20 23:51:59 2011 -0800
+++ b/src/events/SDL_mouse.c	Mon Feb 21 10:50:53 2011 -0800
@@ -29,43 +29,7 @@
 #include "../video/SDL_sysvideo.h"
 
 
-/* Global mouse information */
-
-typedef struct SDL_Mouse SDL_Mouse;
-
-struct SDL_Mouse
-{
-    /* Create a cursor from a surface */
-    SDL_Cursor *(*CreateCursor) (SDL_Surface * surface, int hot_x, int hot_y);
-
-    /* Show the specified cursor, or hide if cursor is NULL */
-    int (*ShowCursor) (SDL_Cursor * cursor);
-
-    /* This is called when a mouse motion event occurs */
-    void (*MoveCursor) (SDL_Cursor * cursor);
-
-    /* Free a window manager cursor */
-    void (*FreeCursor) (SDL_Cursor * cursor);
-
-    /* Warp the mouse to (x,y) */
-    void (*WarpMouse) (SDL_Mouse * mouse, SDL_Window * window, int x, int y);
-
-    /* Data common to all mice */
-    SDL_Window *focus;
-    int x;
-    int y;
-    int xdelta;
-    int ydelta;
-    int last_x, last_y;         /* the last reported x and y coordinates */
-    Uint8 buttonstate;
-    SDL_bool relative_mode;
-
-    SDL_Cursor *cursors;
-    SDL_Cursor *def_cursor;
-    SDL_Cursor *cur_cursor;
-    SDL_bool cursor_shown;
-};
-
+/* The mouse state */
 static SDL_Mouse SDL_mouse;
 
 
@@ -76,6 +40,12 @@
     return (0);
 }
 
+SDL_Mouse *
+SDL_GetMouse(void)
+{
+    return &SDL_mouse;
+}
+
 void
 SDL_ResetMouse(void)
 {
@@ -85,7 +55,7 @@
 SDL_Window *
 SDL_GetMouseFocus(void)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     return mouse->focus;
 }
@@ -93,7 +63,7 @@
 void
 SDL_SetMouseFocus(SDL_Window * window)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     if (mouse->focus == window) {
         return;
@@ -114,7 +84,7 @@
 int
 SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
     int posted;
     int xrel;
     int yrel;
@@ -204,7 +174,7 @@
 int
 SDL_SendMouseButton(SDL_Window * window, Uint8 state, Uint8 button)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
     int posted;
     Uint32 type;
 
@@ -253,7 +223,7 @@
 int
 SDL_SendMouseWheel(SDL_Window * window, int x, int y)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
     int posted;
 
     if (window) {
@@ -285,7 +255,7 @@
 Uint8
 SDL_GetMouseState(int *x, int *y)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     if (x) {
         *x = mouse->x;
@@ -299,7 +269,7 @@
 Uint8
 SDL_GetRelativeMouseState(int *x, int *y)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     if (x) {
         *x = mouse->xdelta;
@@ -315,10 +285,10 @@
 void
 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     if (mouse->WarpMouse) {
-        mouse->WarpMouse(mouse, window, x, y);
+        mouse->WarpMouse(window, x, y);
     } else {
         SDL_SendMouseMotion(window, 0, x, y);
     }
@@ -327,7 +297,7 @@
 int
 SDL_SetRelativeMouseMode(SDL_bool enabled)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     /* Flush pending mouse motion */
     SDL_FlushEvent(SDL_MOUSEMOTION);
@@ -349,7 +319,7 @@
 SDL_bool
 SDL_GetRelativeMouseMode()
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     return mouse->relative_mode;
 }
@@ -358,7 +328,7 @@
 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
                  int w, int h, int hot_x, int hot_y)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
     SDL_Surface *surface;
     SDL_Cursor *cursor;
     int x, y;
@@ -424,7 +394,7 @@
 void
 SDL_SetCursor(SDL_Cursor * cursor)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     /* Set the new cursor */
     if (cursor) {
@@ -458,7 +428,7 @@
 SDL_Cursor *
 SDL_GetCursor(void)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
     if (!mouse) {
         return NULL;
@@ -469,7 +439,7 @@
 void
 SDL_FreeCursor(SDL_Cursor * cursor)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
     SDL_Cursor *curr, *prev;
 
     if (!cursor) {
@@ -503,7 +473,7 @@
 int
 SDL_ShowCursor(int toggle)
 {
-    SDL_Mouse *mouse = &SDL_mouse;
+    SDL_Mouse *mouse = SDL_GetMouse();
     SDL_bool shown;
 
     if (!mouse) {
--- a/src/events/SDL_mouse_c.h	Sun Feb 20 23:51:59 2011 -0800
+++ b/src/events/SDL_mouse_c.h	Mon Feb 21 10:50:53 2011 -0800
@@ -24,15 +24,54 @@
 #ifndef _SDL_mouse_c_h
 #define _SDL_mouse_c_h
 
+#include "SDL_mouse.h"
+
 struct SDL_Cursor
 {
     struct SDL_Cursor *next;
     void *driverdata;
 };
 
+typedef struct
+{
+    /* Create a cursor from a surface */
+    SDL_Cursor *(*CreateCursor) (SDL_Surface * surface, int hot_x, int hot_y);
+
+    /* Show the specified cursor, or hide if cursor is NULL */
+    int (*ShowCursor) (SDL_Cursor * cursor);
+
+    /* This is called when a mouse motion event occurs */
+    void (*MoveCursor) (SDL_Cursor * cursor);
+
+    /* Free a window manager cursor */
+    void (*FreeCursor) (SDL_Cursor * cursor);
+
+    /* Warp the mouse to (x,y) */
+    void (*WarpMouse) (SDL_Window * window, int x, int y);
+
+    /* Data common to all mice */
+    SDL_Window *focus;
+    int x;
+    int y;
+    int xdelta;
+    int ydelta;
+    int last_x, last_y;         /* the last reported x and y coordinates */
+    Uint8 buttonstate;
+    SDL_bool relative_mode;
+
+    SDL_Cursor *cursors;
+    SDL_Cursor *def_cursor;
+    SDL_Cursor *cur_cursor;
+    SDL_bool cursor_shown;
+} SDL_Mouse;
+
+
 /* Initialize the mouse subsystem */
 extern int SDL_MouseInit(void);
 
+/* Get the mouse state structure */
+SDL_Mouse *SDL_GetMouse(void);
+
 /* Clear the mouse state */
 extern void SDL_ResetMouse(void);
 
--- a/src/video/cocoa/SDL_cocoamouse.m	Sun Feb 20 23:51:59 2011 -0800
+++ b/src/video/cocoa/SDL_cocoamouse.m	Mon Feb 21 10:50:53 2011 -0800
@@ -49,62 +49,7 @@
 void
 Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
 {
-    int i;
-    NSPoint point = { 0, 0 };
-    SDL_Window *window;
-    SDL_Window *focus = SDL_GetMouseFocus();
-
-    /* See if there are any fullscreen windows that might handle this event */
-    window = NULL;
-    for (i = 0; i < _this->num_displays; ++i) {
-        SDL_VideoDisplay *display = &_this->displays[i];
-        SDL_Window *candidate = display->fullscreen_window;
-
-        if (candidate) {
-            SDL_Rect bounds;
-
-            Cocoa_GetDisplayBounds(_this, display, &bounds);
-            point = [NSEvent mouseLocation];
-            point.x = point.x - bounds.x;
-            point.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - point.y - bounds.y;
-            if ((point.x >= 0 && point.x < candidate->w) &&
-                (point.y >= 0 && point.y < candidate->h)) {
-                /* This is it! */
-                window = candidate;
-                break;
-            } else if (candidate == focus) {
-                SDL_SetMouseFocus(NULL);
-            }
-        }
-    }
-
-    if (!window) {
-        return;
-    }
-
-    switch ([event type]) {
-    case NSLeftMouseDown:
-    case NSOtherMouseDown:
-    case NSRightMouseDown:
-        SDL_SendMouseButton(window, SDL_PRESSED, ConvertMouseButtonToSDL([event buttonNumber]));
-        break;
-    case NSLeftMouseUp:
-    case NSOtherMouseUp:
-    case NSRightMouseUp:
-        SDL_SendMouseButton(window, SDL_RELEASED, ConvertMouseButtonToSDL([event buttonNumber]));
-        break;
-    case NSScrollWheel:
-        Cocoa_HandleMouseWheel(window, event);
-        break;
-    case NSLeftMouseDragged:
-    case NSRightMouseDragged:
-    case NSOtherMouseDragged: /* usually middle mouse dragged */
-    case NSMouseMoved:
-        SDL_SendMouseMotion(window, 0, (int)point.x, (int)point.y);
-        break;
-    default: /* just to avoid compiler warnings */
-        break;
-    }
+    /* We're correctly using views even in fullscreen mode now */
 }
 
 void
--- a/src/video/cocoa/SDL_cocoawindow.h	Sun Feb 20 23:51:59 2011 -0800
+++ b/src/video/cocoa/SDL_cocoawindow.h	Mon Feb 21 10:50:53 2011 -0800
@@ -29,11 +29,7 @@
 typedef struct SDL_WindowData SDL_WindowData;
 
 /* *INDENT-OFF* */
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
-@interface Cocoa_WindowListener : NSResponder <NSWindowDelegate> {
-#else
 @interface Cocoa_WindowListener : NSResponder {
-#endif		
     SDL_WindowData *_data;
 }
 
@@ -59,6 +55,8 @@
 -(void) mouseUp:(NSEvent *) theEvent;
 -(void) rightMouseUp:(NSEvent *) theEvent;
 -(void) otherMouseUp:(NSEvent *) theEvent;
+-(void) mouseEntered:(NSEvent *)theEvent;
+-(void) mouseExited:(NSEvent *)theEvent;
 -(void) mouseMoved:(NSEvent *) theEvent;
 -(void) mouseDragged:(NSEvent *) theEvent;
 -(void) rightMouseDragged:(NSEvent *) theEvent;
--- a/src/video/cocoa/SDL_cocoawindow.m	Sun Feb 20 23:51:59 2011 -0800
+++ b/src/video/cocoa/SDL_cocoawindow.m	Mon Feb 21 10:50:53 2011 -0800
@@ -41,54 +41,53 @@
 - (void)listen:(SDL_WindowData *)data
 {
     NSNotificationCenter *center;
+    NSWindow *window = data->nswindow;
+    NSView *view = [window contentView];
 
     _data = data;
 
     center = [NSNotificationCenter defaultCenter];
 
-    [_data->nswindow setNextResponder:self];
-    if ([_data->nswindow delegate] != nil) {
-        [center addObserver:self selector:@selector(windowDisExpose:) name:NSWindowDidExposeNotification object:_data->nswindow];
-        [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:_data->nswindow];
-        [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:_data->nswindow];
-        [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:_data->nswindow];
-        [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:_data->nswindow];
-        [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:_data->nswindow];
-        [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:_data->nswindow];
-    } else {
-        [_data->nswindow setDelegate:self];
-    }
-// FIXME: Why doesn't this work?
-//    [center addObserver:self selector:@selector(rightMouseDown:) name:[NSString stringWithCString:"rightMouseDown" encoding:NSUTF8StringEncoding] object:[_data->nswindow contentView]];
+    [center addObserver:self selector:@selector(windowDisExpose:) name:NSWindowDidExposeNotification object:window];
+    [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
+    [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
+    [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
+    [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
+    [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
+    [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
     [center addObserver:self selector:@selector(windowDidHide:) name:NSApplicationDidHideNotification object:NSApp];
     [center addObserver:self selector:@selector(windowDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp];
 
-    [_data->nswindow setAcceptsMouseMovedEvents:YES];
+    [window setNextResponder:self];
+    [window setAcceptsMouseMovedEvents:YES];
+
+    [view setNextResponder:self];
+    [view addTrackingRect:[view visibleRect] owner:self userData:nil assumeInside:NO];
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
-    [[_data->nswindow contentView] setAcceptsTouchEvents:YES];
+    [view setAcceptsTouchEvents:YES];
 #endif
 }
 
 - (void)close
 {
     NSNotificationCenter *center;
+    NSWindow *window = _data->nswindow;
+    NSView *view = [window contentView];
 
     center = [NSNotificationCenter defaultCenter];
 
-    [_data->nswindow setNextResponder:nil];
-    if ([_data->nswindow delegate] != self) {
-        [center removeObserver:self name:NSWindowDidExposeNotification object:_data->nswindow];
-        [center removeObserver:self name:NSWindowDidMoveNotification object:_data->nswindow];
-        [center removeObserver:self name:NSWindowDidResizeNotification object:_data->nswindow];
-        [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:_data->nswindow];
-        [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:_data->nswindow];
-        [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:_data->nswindow];
-        [center removeObserver:self name:NSWindowDidResignKeyNotification object:_data->nswindow];
-    } else {
-        [_data->nswindow setDelegate:nil];
-    }
+    [center removeObserver:self name:NSWindowDidExposeNotification object:window];
+    [center removeObserver:self name:NSWindowDidMoveNotification object:window];
+    [center removeObserver:self name:NSWindowDidResizeNotification object:window];
+    [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
+    [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
+    [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
+    [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
     [center removeObserver:self name:NSApplicationDidHideNotification object:NSApp];
     [center removeObserver:self name:NSApplicationDidUnhideNotification object:NSApp];
+
+    [window setNextResponder:nil];
+    [view setNextResponder:nil];
 }
 
 - (BOOL)windowShouldClose:(id)sender
@@ -141,11 +140,10 @@
     SDL_SetKeyboardFocus(window);
 
     /* If we just gained focus we need the updated mouse position */
-    if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
+    if (SDL_GetMouseFocus() == window) {
         NSPoint point;
         point = [NSEvent mouseLocation];
         point = [_data->nswindow convertScreenToBase:point];
-        point = [[_data->nswindow contentView] convertPoint:point fromView:nil];
         point.y = window->h - point.y;
         SDL_SendMouseMotion(window, 0, (int)point.x, (int)point.y);
     }
@@ -239,22 +237,51 @@
     [self mouseUp:theEvent];
 }
 
+- (void)mouseEntered:(NSEvent *)theEvent
+{
+    SDL_SetMouseFocus(_data->window);
+}
+
+- (void)mouseExited:(NSEvent *)theEvent
+{
+    SDL_Window *window = _data->window;
+
+    if (SDL_GetMouseFocus() == window) {
+        if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
+            int x, y;
+            NSPoint point;
+            CGPoint cgpoint;
+
+            point = [theEvent locationInWindow];
+            point.y = window->h - point.y;
+
+            SDL_SendMouseMotion(window, 0, (int)point.x, (int)point.y);
+            SDL_GetMouseState(&x, &y);
+            cgpoint.x = window->x + x;
+            cgpoint.y = window->y + y;
+            CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
+        } else {
+            SDL_SetMouseFocus(NULL);
+        }
+    }
+}
+
 - (void)mouseMoved:(NSEvent *)theEvent
 {
     SDL_Window *window = _data->window;
-    NSPoint point;
 
-    if (window->flags & SDL_WINDOW_FULLSCREEN)
+#ifdef RELATIVE_MOTION
+    if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
         return;
+    }
+#endif
 
-    point = [theEvent locationInWindow];
-    point.y = window->h - point.y;
-    if ( point.x < 0 || point.x >= window->w ||
-         point.y < 0 || point.y >= window->h ) {
-        if (SDL_GetMouseFocus() == window) {
-            SDL_SetMouseFocus(NULL);
-        }
-    } else {
+    if (SDL_GetMouseFocus() == window) {
+        NSPoint point;
+
+        point = [theEvent locationInWindow];
+        point.y = window->h - point.y;
+
         SDL_SendMouseMotion(window, 0, (int)point.x, (int)point.y);
     }
 }
@@ -386,28 +413,14 @@
 }
 @end
 
-@interface SDLView : NSView {
-    Cocoa_WindowListener *listener;
-}
+@interface SDLView : NSView { }
 @end
 
 @implementation SDLView
-
-- (id) initWithFrame: (NSRect) rect
-            listener: (Cocoa_WindowListener *) theListener
-{
-    if (self = [super initWithFrame:rect]) {
-        listener = theListener;
-    }
-
-    return self;
-}
-
 - (void)rightMouseDown:(NSEvent *)theEvent
 {
-    [listener mouseDown:theEvent];
+    [[self nextResponder] rightMouseDown:theEvent];
 }
-
 @end
 
 static unsigned int
@@ -452,16 +465,11 @@
 
     /* Create an event listener for the window */
     data->listener = [[Cocoa_WindowListener alloc] init];
-    [data->listener listen:data];
 
     /* Fill in the SDL window with the window data */
     {
         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
-        NSView *contentView = [[SDLView alloc] initWithFrame: rect
-                                                    listener: data->listener];
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
-        [contentView setAcceptsTouchEvents:YES];
-#endif
+        NSView *contentView = [[SDLView alloc] initWithFrame:rect];
         [nswindow setContentView: contentView];
         [contentView release];
 
@@ -471,6 +479,10 @@
         window->w = (int)rect.size.width;
         window->h = (int)rect.size.height;
     }
+
+    /* Set up the listener after we create the view */
+    [data->listener listen:data];
+
     if ([nswindow isVisible]) {
         window->flags |= SDL_WINDOW_SHOWN;
     } else {
@@ -764,15 +776,35 @@
     [pool release];
 }
 
+NSPoint origin;
 void
 Cocoa_SetWindowGrab(_THIS, SDL_Window * window)
 {
+#ifdef RELATIVE_MOTION
+    /* FIXME: work in progress
+        You set relative mode by using the following code in conjunction with
+        CGDisplayHideCursor(kCGDirectMainDisplay) and
+        CGDisplayShowCursor(kCGDirectMainDisplay)
+    */
     if ((window->flags & SDL_WINDOW_INPUT_GRABBED) &&
         (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
-        /* FIXME: Grab mouse */
+        CGAssociateMouseAndMouseCursorPosition(NO);
     } else {
-        /* FIXME: Release mouse */
+        CGAssociateMouseAndMouseCursorPosition(YES);
     }
+#else
+    /* Move the cursor to the nearest point in the window */
+    if ((window->flags & SDL_WINDOW_INPUT_GRABBED) &&
+        (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
+        int x, y;
+        CGPoint cgpoint;
+
+        SDL_GetMouseState(&x, &y);
+        cgpoint.x = window->x + x;
+        cgpoint.y = window->y + y;
+        CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
+    }
+#endif
 }
 
 void