From 3050f6f350f1611751fc2b5b81423b844768ce9e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 8 Nov 2012 02:26:40 -0800 Subject: [PATCH] Reset the mouse button state when losing mouse focus. Implemented mouse focus handling entirely using mouse motion events, with SetCapture() semantics, as long as the windowing system continues to provide mouse events. --- src/events/SDL_mouse.c | 113 ++++++++++++++++++++++++------ src/video/SDL_bmp.c | 8 +-- src/video/cocoa/SDL_cocoawindow.h | 2 - src/video/cocoa/SDL_cocoawindow.m | 61 +++++----------- 4 files changed, 115 insertions(+), 69 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index ff20a79ed..c35cf3063 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -22,11 +22,13 @@ /* General mouse handling code for SDL */ +#include "SDL_assert.h" #include "SDL_events.h" #include "SDL_events_c.h" #include "default_cursor.h" #include "../video/SDL_sysvideo.h" +/*#define DEBUG_MOUSE*/ /* The mouse state */ static SDL_Mouse SDL_mouse; @@ -68,6 +70,23 @@ SDL_GetMouseFocus(void) return mouse->focus; } +void +SDL_ResetMouse(void) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + Uint8 i; + +#ifdef DEBUG_MOUSE + printf("Resetting mouse\n"); +#endif + for (i = 1; i <= sizeof(mouse->buttonstate)*8; ++i) { + if (mouse->buttonstate & SDL_BUTTON(i)) { + SDL_SendMouseButton(mouse->focus, SDL_RELEASED, i); + } + } + SDL_assert(mouse->buttonstate == 0); +} + void SDL_SetMouseFocus(SDL_Window * window) { @@ -77,6 +96,11 @@ SDL_SetMouseFocus(SDL_Window * window) return; } + if (mouse->focus && !window) { + /* We won't get anymore mouse messages, so reset mouse state */ + SDL_ResetMouse(); + } + /* See if the current window has lost focus */ if (mouse->focus) { SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0); @@ -87,6 +111,45 @@ SDL_SetMouseFocus(SDL_Window * window) if (mouse->focus) { SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0); } + + /* Update cursor visibility */ + SDL_SetCursor(NULL); +} + +/* Check to see if we need to synthesize focus events */ +static SDL_bool +SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint8 buttonstate) +{ + SDL_Mouse *mouse = SDL_GetMouse(); + int w, h; + SDL_bool inWindow; + + SDL_GetWindowSize(window, &w, &h); + if (x < 0 || y < 0 || x >= w || y >= h) { + inWindow = SDL_FALSE; + } else { + inWindow = SDL_TRUE; + } + if (!inWindow && !buttonstate) { + if (window == mouse->focus) { +#ifdef DEBUG_MOUSE + printf("Mouse left window, synthesizing focus lost event\n"); +#endif + SDL_SetMouseFocus(NULL); + } + return SDL_FALSE; + } + + if (window != mouse->focus) { + mouse->last_x = x; + mouse->last_y = y; + +#ifdef DEBUG_MOUSE + printf("Mouse entered window, synthesizing focus gain event\n"); +#endif + SDL_SetMouseFocus(window); + } + return SDL_TRUE; } int @@ -98,11 +161,13 @@ SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y) int yrel; int x_max = 0, y_max = 0; - if (window) { - SDL_SetMouseFocus(window); + if (window && !relative) { + if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) { + return 0; + } } - /* the relative motion is calculated regarding the system cursor last position */ + /* relative motion is calculated regarding the system cursor last position */ if (relative) { xrel = x; yrel = y; @@ -115,7 +180,7 @@ SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y) /* Drop events that don't change state */ if (!xrel && !yrel) { -#if 0 +#ifdef DEBUG_MOUSE printf("Mouse event didn't change state - dropped!\n"); #endif return 0; @@ -135,7 +200,6 @@ SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y) --y_max; /* make sure that the pointers find themselves inside the windows */ - /* only check if mouse->xmax is set ! */ if (mouse->x > x_max) { mouse->x = x_max; } @@ -174,8 +238,9 @@ SDL_SendMouseMotion(SDL_Window * window, int relative, int x, int y) event.motion.yrel = yrel; posted = (SDL_PushEvent(&event) > 0); } - mouse->last_x = mouse->x; - mouse->last_y = mouse->y; + /* Use unclamped values if we're getting events outside the window */ + mouse->last_x = x; + mouse->last_y = y; return posted; } @@ -185,34 +250,34 @@ SDL_SendMouseButton(SDL_Window * window, Uint8 state, Uint8 button) SDL_Mouse *mouse = SDL_GetMouse(); int posted; Uint32 type; - - if (window) { - SDL_SetMouseFocus(window); - } + Uint8 buttonstate = mouse->buttonstate; /* Figure out which event to perform */ switch (state) { case SDL_PRESSED: - if (mouse->buttonstate & SDL_BUTTON(button)) { - /* Ignore this event, no state change */ - return 0; - } type = SDL_MOUSEBUTTONDOWN; - mouse->buttonstate |= SDL_BUTTON(button); + buttonstate |= SDL_BUTTON(button); break; case SDL_RELEASED: - if (!(mouse->buttonstate & SDL_BUTTON(button))) { - /* Ignore this event, no state change */ - return 0; - } type = SDL_MOUSEBUTTONUP; - mouse->buttonstate &= ~SDL_BUTTON(button); + buttonstate &= ~SDL_BUTTON(button); break; default: /* Invalid state -- bail */ return 0; } + /* We do this after calculating buttonstate so button presses gain focus */ + if (window && state == SDL_PRESSED) { + SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate); + } + + if (buttonstate == mouse->buttonstate) { + /* Ignore this event, no state change */ + return 0; + } + mouse->buttonstate = buttonstate; + /* Post the event, if desired */ posted = 0; if (SDL_GetEventState(type) == SDL_ENABLE) { @@ -225,6 +290,12 @@ SDL_SendMouseButton(SDL_Window * window, Uint8 state, Uint8 button) event.button.windowID = mouse->focus ? mouse->focus->id : 0; posted = (SDL_PushEvent(&event) > 0); } + + /* We do this after dispatching event so button releases can lose focus */ + if (window && state == SDL_RELEASED) { + SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate); + } + return posted; } diff --git a/src/video/SDL_bmp.c b/src/video/SDL_bmp.c index f4a7f6140..9103fa95f 100644 --- a/src/video/SDL_bmp.c +++ b/src/video/SDL_bmp.c @@ -51,7 +51,7 @@ SDL_Surface * SDL_LoadBMP_RW(SDL_RWops * src, int freesrc) { SDL_bool was_error; - long fp_offset = 0; + Sint64 fp_offset = 0; int bmpPitch; int i, pad; SDL_Surface *surface; @@ -371,7 +371,7 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc) int SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst) { - long fp_offset; + Sint64 fp_offset; int i, pad; SDL_Surface *surface; Uint8 *bits; @@ -515,7 +515,7 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst) } /* Write the bitmap offset */ - bfOffBits = SDL_RWtell(dst) - fp_offset; + bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset); if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) { SDL_Error(SDL_EFSEEK); } @@ -542,7 +542,7 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst) } /* Write the BMP file size */ - bfSize = SDL_RWtell(dst) - fp_offset; + bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset); if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) { SDL_Error(SDL_EFSEEK); } diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index 427356149..d17604d71 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -56,8 +56,6 @@ 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 2d9b5a957..dc1a85096 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -200,10 +200,8 @@ - (void)windowDidBecomeKey:(NSNotification *)aNotification y = (int)(window->h - point.y); if (x >= 0 && x < window->w && y >= 0 && y < window->h) { - if (SDL_GetMouseFocus() != window) { - [self mouseEntered:nil]; - } SDL_SendMouseMotion(window, 0, x, y); + SDL_SetCursor(NULL); } } @@ -309,38 +307,6 @@ - (void)otherMouseUp:(NSEvent *)theEvent [self mouseUp:theEvent]; } -- (void)mouseEntered:(NSEvent *)theEvent -{ - SDL_SetMouseFocus(_data->window); - - SDL_SetCursor(NULL); -} - -- (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); - SDL_SetCursor(NULL); - } - } -} - - (void)mouseMoved:(NSEvent *)theEvent { SDL_Mouse *mouse = SDL_GetMouse(); @@ -357,15 +323,26 @@ - (void)mouseMoved:(NSEvent *)theEvent y = (int)(window->h - point.y); if (x < 0 || x >= window->w || y < 0 || y >= window->h) { - if (SDL_GetMouseFocus() == window) { - [self mouseExited:theEvent]; - } - } else { - if (SDL_GetMouseFocus() != window) { - [self mouseEntered:theEvent]; + if (window->flags & SDL_WINDOW_INPUT_GRABBED) { + CGPoint cgpoint; + + if (x < 0) { + x = 0; + } else if (x >= window->w) { + x = window->w - 1; + } + if (y < 0) { + y = 0; + } else if (y >= window->h) { + y = window->h - 1; + } + + cgpoint.x = window->x + x; + cgpoint.y = window->y + y; + CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); } - SDL_SendMouseMotion(window, 0, x, y); } + SDL_SendMouseMotion(window, 0, x, y); } - (void)mouseDragged:(NSEvent *)theEvent