From ff10d68a80623b922a81bfd72ab88ee16f03ef9f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 21 Feb 2011 10:50:53 -0800 Subject: [PATCH] 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 | 76 +++++--------- src/events/SDL_mouse_c.h | 39 ++++++++ src/video/cocoa/SDL_cocoamouse.m | 57 +---------- src/video/cocoa/SDL_cocoawindow.h | 6 +- src/video/cocoa/SDL_cocoawindow.m | 160 ++++++++++++++++++------------ 5 files changed, 161 insertions(+), 177 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 6205c400d..36772d518 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -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 @@ SDL_MouseInit(void) return (0); } +SDL_Mouse * +SDL_GetMouse(void) +{ + return &SDL_mouse; +} + void SDL_ResetMouse(void) { @@ -85,7 +55,7 @@ SDL_ResetMouse(void) SDL_Window * SDL_GetMouseFocus(void) { - SDL_Mouse *mouse = &SDL_mouse; + SDL_Mouse *mouse = SDL_GetMouse(); return mouse->focus; } @@ -93,7 +63,7 @@ SDL_GetMouseFocus(void) 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 @@ SDL_SetMouseFocus(SDL_Window * window) 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 @@ SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y) 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 @@ SDL_SendMouseButton(SDL_Window * window, Uint8 state, Uint8 button) 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 @@ SDL_MouseQuit(void) 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 @@ SDL_GetMouseState(int *x, int *y) 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 @@ SDL_GetRelativeMouseState(int *x, int *y) 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 @@ SDL_WarpMouseInWindow(SDL_Window * window, int x, int y) 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_SetRelativeMouseMode(SDL_bool enabled) SDL_bool SDL_GetRelativeMouseMode() { - SDL_Mouse *mouse = &SDL_mouse; + SDL_Mouse *mouse = SDL_GetMouse(); return mouse->relative_mode; } @@ -358,7 +328,7 @@ SDL_Cursor * 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 @@ SDL_CreateCursor(const Uint8 * data, const Uint8 * mask, 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_SetCursor(SDL_Cursor * cursor) SDL_Cursor * SDL_GetCursor(void) { - SDL_Mouse *mouse = &SDL_mouse; + SDL_Mouse *mouse = SDL_GetMouse(); if (!mouse) { return NULL; @@ -469,7 +439,7 @@ SDL_GetCursor(void) 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 @@ SDL_FreeCursor(SDL_Cursor * cursor) int SDL_ShowCursor(int toggle) { - SDL_Mouse *mouse = &SDL_mouse; + SDL_Mouse *mouse = SDL_GetMouse(); SDL_bool shown; if (!mouse) { diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h index 8a0d5cfef..dcfdc7d76 100644 --- a/src/events/SDL_mouse_c.h +++ b/src/events/SDL_mouse_c.h @@ -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); diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m index 78667de63..b96656406 100644 --- a/src/video/cocoa/SDL_cocoamouse.m +++ b/src/video/cocoa/SDL_cocoamouse.m @@ -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 diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index 45b7e3673..3b91ea23b 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -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 { -#else @interface Cocoa_WindowListener : NSResponder { -#endif SDL_WindowData *_data; } @@ -59,6 +55,8 @@ typedef struct SDL_WindowData SDL_WindowData; -(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; diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index de90b7e03..c0755b3ed 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -41,54 +41,53 @@ @implementation Cocoa_WindowListener - (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 @@ - (void)windowDidBecomeKey:(NSNotification *)aNotification 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 @@ - (void)otherMouseUp:(NSEvent *)theEvent [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 + + if (SDL_GetMouseFocus() == window) { + NSPoint point; + + point = [theEvent locationInWindow]; + point.y = window->h - point.y; - 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 { SDL_SendMouseMotion(window, 0, (int)point.x, (int)point.y); } } @@ -386,28 +413,14 @@ - (BOOL)canBecomeMainWindow } @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 @@ - (void)rightMouseDown:(NSEvent *)theEvent /* 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 @@ - (void)rightMouseDown:(NSEvent *)theEvent 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 @@ - (void)rightMouseDown:(NSEvent *)theEvent [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