Commit 746928350f9f011c85331020f133d703b80305d9

Sam Lantinga 2013-12-23T12:17:52

Added support for double-clicks, through a new "clicks" field in the mouse button event.

diff --git a/include/SDL_events.h b/include/SDL_events.h
index c089030..81ff4dd 100644
--- a/include/SDL_events.h
+++ b/include/SDL_events.h
@@ -239,8 +239,8 @@ typedef struct SDL_MouseButtonEvent
     Uint32 which;       /**< The mouse instance id, or SDL_TOUCH_MOUSEID */
     Uint8 button;       /**< The mouse button index */
     Uint8 state;        /**< ::SDL_PRESSED or ::SDL_RELEASED */
+    Uint8 clicks;       /**< 1 for single-click, 2 for double-click, etc. */
     Uint8 padding1;
-    Uint8 padding2;
     Sint32 x;           /**< X coordinate, relative to window */
     Sint32 y;           /**< Y coordinate, relative to window */
 } SDL_MouseButtonEvent;
diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 0bb0b18..742802a 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -23,6 +23,7 @@
 /* General mouse handling code for SDL */
 
 #include "SDL_assert.h"
+#include "SDL_timer.h"
 #include "SDL_events.h"
 #include "SDL_events_c.h"
 #include "default_cursor.h"
@@ -32,6 +33,8 @@
 
 /* The mouse state */
 static SDL_Mouse SDL_mouse;
+static Uint32 SDL_double_click_time = 500;
+static int SDL_double_click_radius = 1;
 
 static int
 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
@@ -64,6 +67,12 @@ SDL_GetMouse(void)
     return &SDL_mouse;
 }
 
+void
+SDL_SetDoubleClickTime(Uint32 interval)
+{
+    SDL_double_click_time = interval;
+}
+
 SDL_Window *
 SDL_GetMouseFocus(void)
 {
@@ -272,6 +281,23 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
     return posted;
 }
 
+static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button)
+{
+    if (button >= mouse->num_clickstates) {
+        int i, count = button + 1;
+        mouse->clickstate = (SDL_MouseClickState *)SDL_realloc(mouse->clickstate, count * sizeof(*mouse->clickstate));
+        if (!mouse->clickstate) {
+            return NULL;
+        }
+
+        for (i = mouse->num_clickstates; i < count; ++i) {
+            SDL_zero(mouse->clickstate[i]);
+        }
+        mouse->num_clickstates = count;
+    }
+    return &mouse->clickstate[button];
+}
+
 int
 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
 {
@@ -279,6 +305,8 @@ SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8
     int posted;
     Uint32 type;
     Uint32 buttonstate = mouse->buttonstate;
+    SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button);
+    Uint8 click_count;
 
     /* Figure out which event to perform */
     switch (state) {
@@ -306,6 +334,27 @@ SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8
     }
     mouse->buttonstate = buttonstate;
 
+    if (clickstate) {
+        if (state == SDL_PRESSED) {
+            Uint32 now = SDL_GetTicks();
+
+            if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + SDL_double_click_time) ||
+                SDL_abs(mouse->x - clickstate->last_x) > SDL_double_click_radius ||
+                SDL_abs(mouse->y - clickstate->last_y) > SDL_double_click_radius) {
+                clickstate->click_count = 0;
+            }
+            clickstate->last_timestamp = now;
+            clickstate->last_x = mouse->x;
+            clickstate->last_y = mouse->y;
+            if (clickstate->click_count < 255) {
+                ++clickstate->click_count;
+            }
+        }
+        click_count = clickstate->click_count;
+    } else {
+        click_count = 1;
+    }
+
     /* Post the event, if desired */
     posted = 0;
     if (SDL_GetEventState(type) == SDL_ENABLE) {
@@ -315,6 +364,7 @@ SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8
         event.button.which = mouseID;
         event.button.state = state;
         event.button.button = button;
+        event.button.clicks = click_count;
         event.button.x = mouse->x;
         event.button.y = mouse->y;
         posted = (SDL_PushEvent(&event) > 0);
@@ -376,6 +426,10 @@ SDL_MouseQuit(void)
         mouse->FreeCursor(mouse->def_cursor);
     }
 
+    if (mouse->clickstate) {
+        SDL_free(mouse->clickstate);
+    }
+
     SDL_zerop(mouse);
 }
 
diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h
index a8faa2e..fd34c66 100644
--- a/src/events/SDL_mouse_c.h
+++ b/src/events/SDL_mouse_c.h
@@ -35,6 +35,13 @@ struct SDL_Cursor
 
 typedef struct
 {
+    int last_x, last_y;
+    Uint32 last_timestamp;
+    Uint8 click_count;
+} SDL_MouseClickState;
+
+typedef struct
+{
     /* Create a cursor from a surface */
     SDL_Cursor *(*CreateCursor) (SDL_Surface * surface, int hot_x, int hot_y);
 
@@ -69,6 +76,10 @@ typedef struct
     /* the x and y coordinates when relative mode was activated */
     int original_x, original_y;
 
+    /* Data for double-click tracking */
+    int num_clickstates;
+    SDL_MouseClickState *clickstate;
+
     SDL_Cursor *cursors;
     SDL_Cursor *def_cursor;
     SDL_Cursor *cur_cursor;
@@ -85,6 +96,9 @@ extern int SDL_MouseInit(void);
 /* Get the mouse state structure */
 SDL_Mouse *SDL_GetMouse(void);
 
+/* Set the default double-click interval */
+extern void SDL_SetDoubleClickTime(Uint32 interval);
+
 /* Set the default mouse cursor */
 extern void SDL_SetDefaultCursor(SDL_Cursor * cursor);
 
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 3b4d332..ee65b3f 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -1042,13 +1042,13 @@ SDLTest_PrintEvent(SDL_Event * event)
                 event->motion.windowID);
         break;
     case SDL_MOUSEBUTTONDOWN:
-        fprintf(stderr, "Mouse: button %d pressed at %d,%d in window %d",
-                event->button.button, event->button.x, event->button.y,
+        fprintf(stderr, "Mouse: button %d pressed at %d,%d with click count %d in window %d",
+                event->button.button, event->button.x, event->button.y, event->button.clicks,
                 event->button.windowID);
         break;
     case SDL_MOUSEBUTTONUP:
-        fprintf(stderr, "Mouse: button %d released at %d,%d in window %d",
-                event->button.button, event->button.x, event->button.y,
+        fprintf(stderr, "Mouse: button %d released at %d,%d with click count %d in window %d",
+                event->button.button, event->button.x, event->button.y, event->button.clicks,
                 event->button.windowID);
         break;
     case SDL_MOUSEWHEEL:
diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c
index f75b8da..2298dc8 100644
--- a/src/video/windows/SDL_windowsmouse.c
+++ b/src/video/windows/SDL_windowsmouse.c
@@ -250,6 +250,8 @@ WIN_InitMouse(_THIS)
     mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
 
     SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
+
+    SDL_SetDoubleClickTime(GetDoubleClickTime());
 }
 
 void