Reset the mouse button state when losing mouse focus.
authorSam Lantinga <slouken@libsdl.org>
Thu, 08 Nov 2012 02:26:40 -0800
changeset 6666 018f8019ce36
parent 6665 c0ea8c57034c
child 6667 243bfb5c31d6
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
src/video/SDL_bmp.c
src/video/cocoa/SDL_cocoawindow.h
src/video/cocoa/SDL_cocoawindow.m
--- a/src/events/SDL_mouse.c	Thu Nov 08 01:07:29 2012 -0800
+++ b/src/events/SDL_mouse.c	Thu Nov 08 02:26:40 2012 -0800
@@ -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;
@@ -69,6 +71,23 @@
 }
 
 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)
 {
     SDL_Mouse *mouse = SDL_GetMouse();
@@ -77,6 +96,11 @@
         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 @@
     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 @@
     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 @@
 
     /* 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 @@
     --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 @@
         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_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 @@
         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;
 }
 
--- a/src/video/SDL_bmp.c	Thu Nov 08 01:07:29 2012 -0800
+++ b/src/video/SDL_bmp.c	Thu Nov 08 02:26:40 2012 -0800
@@ -51,7 +51,7 @@
 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 @@
 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 @@
         }
 
         /* 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 @@
         }
 
         /* 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);
         }
--- a/src/video/cocoa/SDL_cocoawindow.h	Thu Nov 08 01:07:29 2012 -0800
+++ b/src/video/cocoa/SDL_cocoawindow.h	Thu Nov 08 02:26:40 2012 -0800
@@ -56,8 +56,6 @@
 -(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	Thu Nov 08 01:07:29 2012 -0800
+++ b/src/video/cocoa/SDL_cocoawindow.m	Thu Nov 08 02:26:40 2012 -0800
@@ -200,10 +200,8 @@
         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 @@
     [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 @@
     y = (int)(window->h - point.y);
 
     if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
-        if (SDL_GetMouseFocus() == window) {
-            [self mouseExited: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);
         }
-    } else {
-        if (SDL_GetMouseFocus() != window) {
-            [self mouseEntered:theEvent];
-        }
-        SDL_SendMouseMotion(window, 0, x, y);
     }
+    SDL_SendMouseMotion(window, 0, x, y);
 }
 
 - (void)mouseDragged:(NSEvent *)theEvent