1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../SDL_internal.h"
22 
23 /* General mouse handling code for SDL */
24 
25 #include "SDL_assert.h"
26 #include "SDL_hints.h"
27 #include "SDL_timer.h"
28 #include "SDL_events.h"
29 #include "SDL_events_c.h"
30 #include "../SDL_hints_c.h"
31 #include "../video/SDL_sysvideo.h"
32 #ifdef __WIN32__
33 #include "../core/windows/SDL_windows.h"    // For GetDoubleClickTime()
34 #endif
35 
36 /* #define DEBUG_MOUSE */
37 
38 /* The mouse state */
39 static SDL_Mouse SDL_mouse;
40 
41 /* for mapping mouse events to touch */
42 static SDL_bool track_mouse_down = SDL_FALSE;
43 
44 static int
45 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
46 
47 static void SDLCALL
SDL_MouseDoubleClickTimeChanged(void * userdata,const char * name,const char * oldValue,const char * hint)48 SDL_MouseDoubleClickTimeChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
49 {
50     SDL_Mouse *mouse = (SDL_Mouse *)userdata;
51 
52     if (hint && *hint) {
53         mouse->double_click_time = SDL_atoi(hint);
54     } else {
55 #ifdef __WIN32__
56         mouse->double_click_time = GetDoubleClickTime();
57 #else
58         mouse->double_click_time = 500;
59 #endif
60     }
61 }
62 
63 static void SDLCALL
SDL_MouseDoubleClickRadiusChanged(void * userdata,const char * name,const char * oldValue,const char * hint)64 SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
65 {
66     SDL_Mouse *mouse = (SDL_Mouse *)userdata;
67 
68     if (hint && *hint) {
69         mouse->double_click_radius = SDL_atoi(hint);
70     } else {
71         mouse->double_click_radius = 32;    /* 32 pixels seems about right for touch interfaces */
72     }
73 }
74 
75 static void SDLCALL
SDL_MouseNormalSpeedScaleChanged(void * userdata,const char * name,const char * oldValue,const char * hint)76 SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
77 {
78     SDL_Mouse *mouse = (SDL_Mouse *)userdata;
79 
80     if (hint && *hint) {
81         mouse->normal_speed_scale = (float)SDL_atof(hint);
82     } else {
83         mouse->normal_speed_scale = 1.0f;
84     }
85 }
86 
87 static void SDLCALL
SDL_MouseRelativeSpeedScaleChanged(void * userdata,const char * name,const char * oldValue,const char * hint)88 SDL_MouseRelativeSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
89 {
90     SDL_Mouse *mouse = (SDL_Mouse *)userdata;
91 
92     if (hint && *hint) {
93         mouse->relative_speed_scale = (float)SDL_atof(hint);
94     } else {
95         mouse->relative_speed_scale = 1.0f;
96     }
97 }
98 
99 static void SDLCALL
SDL_TouchMouseEventsChanged(void * userdata,const char * name,const char * oldValue,const char * hint)100 SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
101 {
102     SDL_Mouse *mouse = (SDL_Mouse *)userdata;
103 
104     mouse->touch_mouse_events = SDL_GetStringBoolean(hint, SDL_TRUE);
105 }
106 
107 static void SDLCALL
SDL_MouseTouchEventsChanged(void * userdata,const char * name,const char * oldValue,const char * hint)108 SDL_MouseTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
109 {
110     SDL_Mouse *mouse = (SDL_Mouse *)userdata;
111     SDL_bool default_value;
112 
113 #if defined(__ANDROID__) || (defined(__IPHONEOS__) && !defined(__TVOS__))
114     default_value = SDL_TRUE;
115 #else
116     default_value = SDL_FALSE;
117 #endif
118     mouse->mouse_touch_events = SDL_GetStringBoolean(hint, default_value);
119 
120     if (mouse->mouse_touch_events) {
121         SDL_AddTouch(SDL_MOUSE_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "mouse_input");
122     }
123 }
124 
125 /* Public functions */
126 int
SDL_MouseInit(void)127 SDL_MouseInit(void)
128 {
129     SDL_Mouse *mouse = SDL_GetMouse();
130 
131     SDL_zerop(mouse);
132 
133     SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME,
134                         SDL_MouseDoubleClickTimeChanged, mouse);
135 
136     SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS,
137                         SDL_MouseDoubleClickRadiusChanged, mouse);
138 
139     SDL_AddHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
140                         SDL_MouseNormalSpeedScaleChanged, mouse);
141 
142     SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
143                         SDL_MouseRelativeSpeedScaleChanged, mouse);
144 
145     SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
146                         SDL_TouchMouseEventsChanged, mouse);
147 
148     SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
149                         SDL_MouseTouchEventsChanged, mouse);
150 
151     mouse->was_touch_mouse_events = SDL_FALSE; /* no touch to mouse movement event pending */
152 
153     mouse->cursor_shown = SDL_TRUE;
154 
155     return (0);
156 }
157 
158 void
SDL_SetDefaultCursor(SDL_Cursor * cursor)159 SDL_SetDefaultCursor(SDL_Cursor * cursor)
160 {
161     SDL_Mouse *mouse = SDL_GetMouse();
162 
163     mouse->def_cursor = cursor;
164     if (!mouse->cur_cursor) {
165         SDL_SetCursor(cursor);
166     }
167 }
168 
169 SDL_Mouse *
SDL_GetMouse(void)170 SDL_GetMouse(void)
171 {
172     return &SDL_mouse;
173 }
174 
175 SDL_Window *
SDL_GetMouseFocus(void)176 SDL_GetMouseFocus(void)
177 {
178     SDL_Mouse *mouse = SDL_GetMouse();
179 
180     return mouse->focus;
181 }
182 
183 #if 0
184 void
185 SDL_ResetMouse(void)
186 {
187     SDL_Mouse *mouse = SDL_GetMouse();
188     Uint8 i;
189 
190 #ifdef DEBUG_MOUSE
191     printf("Resetting mouse\n");
192 #endif
193     for (i = 1; i <= sizeof(mouse->buttonstate)*8; ++i) {
194         if (mouse->buttonstate & SDL_BUTTON(i)) {
195             SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, i);
196         }
197     }
198     SDL_assert(mouse->buttonstate == 0);
199 }
200 #endif
201 
202 void
SDL_SetMouseFocus(SDL_Window * window)203 SDL_SetMouseFocus(SDL_Window * window)
204 {
205     SDL_Mouse *mouse = SDL_GetMouse();
206 
207     if (mouse->focus == window) {
208         return;
209     }
210 
211     /* Actually, this ends up being a bad idea, because most operating
212        systems have an implicit grab when you press the mouse button down
213        so you can drag things out of the window and then get the mouse up
214        when it happens.  So, #if 0...
215     */
216 #if 0
217     if (mouse->focus && !window) {
218         /* We won't get anymore mouse messages, so reset mouse state */
219         SDL_ResetMouse();
220     }
221 #endif
222 
223     /* See if the current window has lost focus */
224     if (mouse->focus) {
225         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0);
226     }
227 
228     mouse->focus = window;
229     mouse->has_position = SDL_FALSE;
230 
231     if (mouse->focus) {
232         SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0);
233     }
234 
235     /* Update cursor visibility */
236     SDL_SetCursor(NULL);
237 }
238 
239 /* Check to see if we need to synthesize focus events */
240 static SDL_bool
SDL_UpdateMouseFocus(SDL_Window * window,int x,int y,Uint32 buttonstate,SDL_bool send_mouse_motion)241 SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate, SDL_bool send_mouse_motion)
242 {
243     SDL_Mouse *mouse = SDL_GetMouse();
244     SDL_bool inWindow = SDL_TRUE;
245 
246     if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) {
247         int w, h;
248         SDL_GetWindowSize(window, &w, &h);
249         if (x < 0 || y < 0 || x >= w || y >= h) {
250             inWindow = SDL_FALSE;
251         }
252     }
253 
254 /* Linux doesn't give you mouse events outside your window unless you grab
255    the pointer.
256 
257    Windows doesn't give you mouse events outside your window unless you call
258    SetCapture().
259 
260    Both of these are slightly scary changes, so for now we'll punt and if the
261    mouse leaves the window you'll lose mouse focus and reset button state.
262 */
263 #ifdef SUPPORT_DRAG_OUTSIDE_WINDOW
264     if (!inWindow && !buttonstate) {
265 #else
266     if (!inWindow) {
267 #endif
268         if (window == mouse->focus) {
269 #ifdef DEBUG_MOUSE
270             printf("Mouse left window, synthesizing move & focus lost event\n");
271 #endif
272             if (send_mouse_motion) {
273                 SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
274             }
275             SDL_SetMouseFocus(NULL);
276         }
277         return SDL_FALSE;
278     }
279 
280     if (window != mouse->focus) {
281 #ifdef DEBUG_MOUSE
282         printf("Mouse entered window, synthesizing focus gain & move event\n");
283 #endif
284         SDL_SetMouseFocus(window);
285         if (send_mouse_motion) {
286             SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
287         }
288     }
289     return SDL_TRUE;
290 }
291 
292 int
293 SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
294 {
295     if (window && !relative) {
296         SDL_Mouse *mouse = SDL_GetMouse();
297         if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate, (mouseID == SDL_TOUCH_MOUSEID) ? SDL_FALSE : SDL_TRUE)) {
298             return 0;
299         }
300     }
301 
302     return SDL_PrivateSendMouseMotion(window, mouseID, relative, x, y);
303 }
304 
305 static int
306 GetScaledMouseDelta(float scale, int value, float *accum)
307 {
308     if (scale != 1.0f) {
309         *accum += scale * value;
310         if (*accum >= 0.0f) {
311             value = (int)SDL_floor(*accum);
312         } else {
313             value = (int)SDL_ceil(*accum);
314         }
315         *accum -= value;
316     }
317     return value;
318 }
319 
320 static int
321 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
322 {
323     SDL_Mouse *mouse = SDL_GetMouse();
324     int posted;
325     int xrel;
326     int yrel;
327 
328     /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */
329     if (mouse->mouse_touch_events) {
330         if (mouseID != SDL_TOUCH_MOUSEID && !relative && track_mouse_down) {
331             if (window) {
332                 float fx = (float)x / (float)window->w;
333                 float fy = (float)y / (float)window->h;
334                 SDL_SendTouchMotion(SDL_MOUSE_TOUCHID, 0, window, fx, fy, 1.0f);
335             }
336         }
337     }
338 
339     /* SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer */
340     if (mouse->touch_mouse_events == 0) {
341         if (mouseID == SDL_TOUCH_MOUSEID) {
342             return 0;
343         }
344     }
345 
346     if (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) {
347         int center_x = 0, center_y = 0;
348         SDL_GetWindowSize(window, &center_x, &center_y);
349         center_x /= 2;
350         center_y /= 2;
351         if (x == center_x && y == center_y) {
352             mouse->last_x = center_x;
353             mouse->last_y = center_y;
354             return 0;
355         }
356         SDL_WarpMouseInWindow(window, center_x, center_y);
357     }
358 
359     if (relative) {
360         if (mouse->relative_mode) {
361             x = GetScaledMouseDelta(mouse->relative_speed_scale, x, &mouse->scale_accum_x);
362             y = GetScaledMouseDelta(mouse->relative_speed_scale, y, &mouse->scale_accum_y);
363         } else {
364             x = GetScaledMouseDelta(mouse->normal_speed_scale, x, &mouse->scale_accum_x);
365             y = GetScaledMouseDelta(mouse->normal_speed_scale, y, &mouse->scale_accum_y);
366         }
367         xrel = x;
368         yrel = y;
369         x = (mouse->last_x + xrel);
370         y = (mouse->last_y + yrel);
371     } else {
372         xrel = x - mouse->last_x;
373         yrel = y - mouse->last_y;
374     }
375 
376     /* Ignore relative motion when first positioning the mouse */
377     if (!mouse->has_position) {
378         xrel = 0;
379         yrel = 0;
380         mouse->has_position = SDL_TRUE;
381     } else if (!xrel && !yrel) {  /* Drop events that don't change state */
382 #ifdef DEBUG_MOUSE
383         printf("Mouse event didn't change state - dropped!\n");
384 #endif
385         return 0;
386     }
387 
388     /* Ignore relative motion positioning the first touch */
389     if (mouseID == SDL_TOUCH_MOUSEID && !mouse->buttonstate) {
390         xrel = 0;
391         yrel = 0;
392     }
393 
394     /* Update internal mouse coordinates */
395     if (!mouse->relative_mode) {
396         mouse->x = x;
397         mouse->y = y;
398     } else {
399         mouse->x += xrel;
400         mouse->y += yrel;
401     }
402 
403     /* make sure that the pointers find themselves inside the windows,
404        unless we have the mouse captured. */
405     if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) {
406         int x_max = 0, y_max = 0;
407 
408         /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */
409         SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
410         --x_max;
411         --y_max;
412 
413         if (mouse->x > x_max) {
414             mouse->x = x_max;
415         }
416         if (mouse->x < 0) {
417             mouse->x = 0;
418         }
419 
420         if (mouse->y > y_max) {
421             mouse->y = y_max;
422         }
423         if (mouse->y < 0) {
424             mouse->y = 0;
425         }
426     }
427 
428     mouse->xdelta += xrel;
429     mouse->ydelta += yrel;
430 
431     /* Move the mouse cursor, if needed */
432     if (mouse->cursor_shown && !mouse->relative_mode &&
433         mouse->MoveCursor && mouse->cur_cursor) {
434         mouse->MoveCursor(mouse->cur_cursor);
435     }
436 
437     /* Post the event, if desired */
438     posted = 0;
439     if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
440         SDL_Event event;
441         event.motion.type = SDL_MOUSEMOTION;
442         event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
443         event.motion.which = mouseID;
444         /* Set us pending (or clear during a normal mouse movement event) as having triggered */
445         mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID)? SDL_TRUE : SDL_FALSE;
446         event.motion.state = mouse->buttonstate;
447         event.motion.x = mouse->x;
448         event.motion.y = mouse->y;
449         event.motion.xrel = xrel;
450         event.motion.yrel = yrel;
451         posted = (SDL_PushEvent(&event) > 0);
452     }
453     if (relative) {
454         mouse->last_x = mouse->x;
455         mouse->last_y = mouse->y;
456     } else {
457         /* Use unclamped values if we're getting events outside the window */
458         mouse->last_x = x;
459         mouse->last_y = y;
460     }
461     return posted;
462 }
463 
464 static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button)
465 {
466     if (button >= mouse->num_clickstates) {
467         int i, count = button + 1;
468         SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(mouse->clickstate, count * sizeof(*mouse->clickstate));
469         if (!clickstate) {
470             return NULL;
471         }
472         mouse->clickstate = clickstate;
473 
474         for (i = mouse->num_clickstates; i < count; ++i) {
475             SDL_zero(mouse->clickstate[i]);
476         }
477         mouse->num_clickstates = count;
478     }
479     return &mouse->clickstate[button];
480 }
481 
482 static int
483 SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
484 {
485     SDL_Mouse *mouse = SDL_GetMouse();
486     int posted;
487     Uint32 type;
488     Uint32 buttonstate = mouse->buttonstate;
489 
490     /* SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events */
491     if (mouse->mouse_touch_events) {
492         if (mouseID != SDL_TOUCH_MOUSEID && button == SDL_BUTTON_LEFT) {
493             if (state == SDL_PRESSED) {
494                 track_mouse_down = SDL_TRUE;
495             } else {
496                 track_mouse_down = SDL_FALSE;
497             }
498             if (window) {
499                 float fx = (float)mouse->x / (float)window->w;
500                 float fy = (float)mouse->y / (float)window->h;
501                 SDL_SendTouch(SDL_MOUSE_TOUCHID, 0, window, track_mouse_down, fx, fy, 1.0f);
502             }
503         }
504     }
505 
506     /* SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer */
507     if (mouse->touch_mouse_events == 0) {
508         if (mouseID == SDL_TOUCH_MOUSEID) {
509             return 0;
510         }
511     }
512 
513     /* Figure out which event to perform */
514     switch (state) {
515     case SDL_PRESSED:
516         type = SDL_MOUSEBUTTONDOWN;
517         buttonstate |= SDL_BUTTON(button);
518         break;
519     case SDL_RELEASED:
520         type = SDL_MOUSEBUTTONUP;
521         buttonstate &= ~SDL_BUTTON(button);
522         break;
523     default:
524         /* Invalid state -- bail */
525         return 0;
526     }
527 
528     /* We do this after calculating buttonstate so button presses gain focus */
529     if (window && state == SDL_PRESSED) {
530         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE);
531     }
532 
533     if (buttonstate == mouse->buttonstate) {
534         /* Ignore this event, no state change */
535         return 0;
536     }
537     mouse->buttonstate = buttonstate;
538 
539     if (clicks < 0) {
540         SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button);
541         if (clickstate) {
542             if (state == SDL_PRESSED) {
543                 Uint32 now = SDL_GetTicks();
544 
545                 if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + mouse->double_click_time) ||
546                     SDL_abs(mouse->x - clickstate->last_x) > mouse->double_click_radius ||
547                     SDL_abs(mouse->y - clickstate->last_y) > mouse->double_click_radius) {
548                     clickstate->click_count = 0;
549                 }
550                 clickstate->last_timestamp = now;
551                 clickstate->last_x = mouse->x;
552                 clickstate->last_y = mouse->y;
553                 if (clickstate->click_count < 255) {
554                     ++clickstate->click_count;
555                 }
556             }
557             clicks = clickstate->click_count;
558         } else {
559             clicks = 1;
560         }
561     }
562 
563     /* Post the event, if desired */
564     posted = 0;
565     if (SDL_GetEventState(type) == SDL_ENABLE) {
566         SDL_Event event;
567         event.type = type;
568         event.button.windowID = mouse->focus ? mouse->focus->id : 0;
569         event.button.which = mouseID;
570         event.button.state = state;
571         event.button.button = button;
572         event.button.clicks = (Uint8) SDL_min(clicks, 255);
573         event.button.x = mouse->x;
574         event.button.y = mouse->y;
575         posted = (SDL_PushEvent(&event) > 0);
576     }
577 
578     /* We do this after dispatching event so button releases can lose focus */
579     if (window && state == SDL_RELEASED) {
580         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, SDL_TRUE);
581     }
582 
583     return posted;
584 }
585 
586 int
587 SDL_SendMouseButtonClicks(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
588 {
589     clicks = SDL_max(clicks, 0);
590     return SDL_PrivateSendMouseButton(window, mouseID, state, button, clicks);
591 }
592 
593 int
594 SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
595 {
596     return SDL_PrivateSendMouseButton(window, mouseID, state, button, -1);
597 }
598 
599 int
600 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction)
601 {
602     SDL_Mouse *mouse = SDL_GetMouse();
603     int posted;
604     int integral_x, integral_y;
605 
606     if (window) {
607         SDL_SetMouseFocus(window);
608     }
609 
610     if (x == 0.0f && y == 0.0f) {
611         return 0;
612     }
613 
614     mouse->accumulated_wheel_x += x;
615     if (mouse->accumulated_wheel_x > 0) {
616         integral_x = (int)SDL_floor(mouse->accumulated_wheel_x);
617     } else if (mouse->accumulated_wheel_x < 0) {
618         integral_x = (int)SDL_ceil(mouse->accumulated_wheel_x);
619     } else {
620         integral_x = 0;
621     }
622     mouse->accumulated_wheel_x -= integral_x;
623 
624     mouse->accumulated_wheel_y += y;
625     if (mouse->accumulated_wheel_y > 0) {
626         integral_y = (int)SDL_floor(mouse->accumulated_wheel_y);
627     } else if (mouse->accumulated_wheel_y < 0) {
628         integral_y = (int)SDL_ceil(mouse->accumulated_wheel_y);
629     } else {
630         integral_y = 0;
631     }
632     mouse->accumulated_wheel_y -= integral_y;
633 
634     /* Post the event, if desired */
635     posted = 0;
636     if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
637         SDL_Event event;
638         event.type = SDL_MOUSEWHEEL;
639         event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
640         event.wheel.which = mouseID;
641 #if 0 /* Uncomment this when it goes in for SDL 2.1 */
642         event.wheel.preciseX = x;
643         event.wheel.preciseY = y;
644 #endif
645         event.wheel.x = integral_x;
646         event.wheel.y = integral_y;
647         event.wheel.direction = (Uint32)direction;
648         posted = (SDL_PushEvent(&event) > 0);
649     }
650     return posted;
651 }
652 
653 void
654 SDL_MouseQuit(void)
655 {
656     SDL_Cursor *cursor, *next;
657     SDL_Mouse *mouse = SDL_GetMouse();
658 
659     if (mouse->CaptureMouse) {
660         SDL_CaptureMouse(SDL_FALSE);
661     }
662     SDL_SetRelativeMouseMode(SDL_FALSE);
663     SDL_ShowCursor(1);
664 
665     cursor = mouse->cursors;
666     while (cursor) {
667         next = cursor->next;
668         SDL_FreeCursor(cursor);
669         cursor = next;
670     }
671     mouse->cursors = NULL;
672     mouse->cur_cursor = NULL;
673 
674     if (mouse->def_cursor && mouse->FreeCursor) {
675         mouse->FreeCursor(mouse->def_cursor);
676         mouse->def_cursor = NULL;
677     }
678 
679     if (mouse->clickstate) {
680         SDL_free(mouse->clickstate);
681         mouse->clickstate = NULL;
682     }
683 
684     SDL_DelHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
685                         SDL_MouseNormalSpeedScaleChanged, mouse);
686 
687     SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
688                         SDL_MouseRelativeSpeedScaleChanged, mouse);
689 }
690 
691 Uint32
692 SDL_GetMouseState(int *x, int *y)
693 {
694     SDL_Mouse *mouse = SDL_GetMouse();
695 
696     if (x) {
697         *x = mouse->x;
698     }
699     if (y) {
700         *y = mouse->y;
701     }
702     return mouse->buttonstate;
703 }
704 
705 Uint32
706 SDL_GetRelativeMouseState(int *x, int *y)
707 {
708     SDL_Mouse *mouse = SDL_GetMouse();
709 
710     if (x) {
711         *x = mouse->xdelta;
712     }
713     if (y) {
714         *y = mouse->ydelta;
715     }
716     mouse->xdelta = 0;
717     mouse->ydelta = 0;
718     return mouse->buttonstate;
719 }
720 
721 Uint32
722 SDL_GetGlobalMouseState(int *x, int *y)
723 {
724     SDL_Mouse *mouse = SDL_GetMouse();
725 
726     if (mouse->GetGlobalMouseState) {
727         int tmpx, tmpy;
728 
729         /* make sure these are never NULL for the backend implementations... */
730         if (!x) {
731             x = &tmpx;
732         }
733         if (!y) {
734             y = &tmpy;
735         }
736 
737         *x = *y = 0;
738 
739         return mouse->GetGlobalMouseState(x, y);
740     } else {
741         return SDL_GetMouseState(x, y);
742     }
743 }
744 
745 void
746 SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
747 {
748     SDL_Mouse *mouse = SDL_GetMouse();
749 
750     if (window == NULL) {
751         window = mouse->focus;
752     }
753 
754     if (window == NULL) {
755         return;
756     }
757 
758     if (mouse->WarpMouse) {
759         mouse->WarpMouse(window, x, y);
760     } else {
761         SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
762     }
763 }
764 
765 int
766 SDL_WarpMouseGlobal(int x, int y)
767 {
768     SDL_Mouse *mouse = SDL_GetMouse();
769 
770     if (mouse->WarpMouseGlobal) {
771         return mouse->WarpMouseGlobal(x, y);
772     }
773 
774     return SDL_Unsupported();
775 }
776 
777 static SDL_bool
778 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
779 {
780     if (!mouse->WarpMouse) {
781         /* Need this functionality for relative mode warp implementation */
782         return SDL_FALSE;
783     }
784 
785     return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE);
786 }
787 
788 int
789 SDL_SetRelativeMouseMode(SDL_bool enabled)
790 {
791     SDL_Mouse *mouse = SDL_GetMouse();
792     SDL_Window *focusWindow = SDL_GetKeyboardFocus();
793 
794     if (enabled == mouse->relative_mode) {
795         return 0;
796     }
797 
798     /* Set the relative mode */
799     if (!enabled && mouse->relative_mode_warp) {
800         mouse->relative_mode_warp = SDL_FALSE;
801     } else if (enabled && ShouldUseRelativeModeWarp(mouse)) {
802         mouse->relative_mode_warp = SDL_TRUE;
803     } else if (!mouse->SetRelativeMouseMode || mouse->SetRelativeMouseMode(enabled) < 0) {
804         if (enabled) {
805             /* Fall back to warp mode if native relative mode failed */
806             if (!mouse->WarpMouse) {
807                 return SDL_SetError("No relative mode implementation available");
808             }
809             mouse->relative_mode_warp = SDL_TRUE;
810         }
811     }
812     mouse->relative_mode = enabled;
813     mouse->scale_accum_x = 0.0f;
814     mouse->scale_accum_y = 0.0f;
815 
816     if (enabled && focusWindow) {
817         /* Center it in the focused window to prevent clicks from going through
818          * to background windows.
819          */
820         SDL_SetMouseFocus(focusWindow);
821         SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
822     }
823 
824     if (mouse->focus) {
825         SDL_UpdateWindowGrab(mouse->focus);
826 
827         /* Put the cursor back to where the application expects it */
828         if (!enabled) {
829             SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y);
830         }
831     }
832 
833     /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */
834     SDL_FlushEvent(SDL_MOUSEMOTION);
835 
836     /* Update cursor visibility */
837     SDL_SetCursor(NULL);
838 
839     return 0;
840 }
841 
842 SDL_bool
843 SDL_GetRelativeMouseMode()
844 {
845     SDL_Mouse *mouse = SDL_GetMouse();
846 
847     return mouse->relative_mode;
848 }
849 
850 int
851 SDL_CaptureMouse(SDL_bool enabled)
852 {
853     SDL_Mouse *mouse = SDL_GetMouse();
854     SDL_Window *focusWindow;
855     SDL_bool isCaptured;
856 
857     if (!mouse->CaptureMouse) {
858         return SDL_Unsupported();
859     }
860 
861     focusWindow = SDL_GetKeyboardFocus();
862 
863     isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
864     if (isCaptured == enabled) {
865         return 0;  /* already done! */
866     }
867 
868     if (enabled) {
869         if (!focusWindow) {
870             return SDL_SetError("No window has focus");
871         } else if (mouse->CaptureMouse(focusWindow) == -1) {
872             return -1;  /* CaptureMouse() should call SetError */
873         }
874         focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
875     } else {
876         if (mouse->CaptureMouse(NULL) == -1) {
877             return -1;  /* CaptureMouse() should call SetError */
878         }
879         focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
880     }
881 
882     return 0;
883 }
884 
885 SDL_Cursor *
886 SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
887                  int w, int h, int hot_x, int hot_y)
888 {
889     SDL_Surface *surface;
890     SDL_Cursor *cursor;
891     int x, y;
892     Uint32 *pixel;
893     Uint8 datab = 0, maskb = 0;
894     const Uint32 black = 0xFF000000;
895     const Uint32 white = 0xFFFFFFFF;
896     const Uint32 transparent = 0x00000000;
897 
898     /* Make sure the width is a multiple of 8 */
899     w = ((w + 7) & ~7);
900 
901     /* Create the surface from a bitmap */
902     surface = SDL_CreateRGBSurface(0, w, h, 32,
903                                    0x00FF0000,
904                                    0x0000FF00,
905                                    0x000000FF,
906                                    0xFF000000);
907     if (!surface) {
908         return NULL;
909     }
910     for (y = 0; y < h; ++y) {
911         pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
912         for (x = 0; x < w; ++x) {
913             if ((x % 8) == 0) {
914                 datab = *data++;
915                 maskb = *mask++;
916             }
917             if (maskb & 0x80) {
918                 *pixel++ = (datab & 0x80) ? black : white;
919             } else {
920                 *pixel++ = (datab & 0x80) ? black : transparent;
921             }
922             datab <<= 1;
923             maskb <<= 1;
924         }
925     }
926 
927     cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
928 
929     SDL_FreeSurface(surface);
930 
931     return cursor;
932 }
933 
934 SDL_Cursor *
935 SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
936 {
937     SDL_Mouse *mouse = SDL_GetMouse();
938     SDL_Surface *temp = NULL;
939     SDL_Cursor *cursor;
940 
941     if (!surface) {
942         SDL_SetError("Passed NULL cursor surface");
943         return NULL;
944     }
945 
946     if (!mouse->CreateCursor) {
947         SDL_SetError("Cursors are not currently supported");
948         return NULL;
949     }
950 
951     /* Sanity check the hot spot */
952     if ((hot_x < 0) || (hot_y < 0) ||
953         (hot_x >= surface->w) || (hot_y >= surface->h)) {
954         SDL_SetError("Cursor hot spot doesn't lie within cursor");
955         return NULL;
956     }
957 
958     if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
959         temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
960         if (!temp) {
961             return NULL;
962         }
963         surface = temp;
964     }
965 
966     cursor = mouse->CreateCursor(surface, hot_x, hot_y);
967     if (cursor) {
968         cursor->next = mouse->cursors;
969         mouse->cursors = cursor;
970     }
971 
972     SDL_FreeSurface(temp);
973 
974     return cursor;
975 }
976 
977 SDL_Cursor *
978 SDL_CreateSystemCursor(SDL_SystemCursor id)
979 {
980     SDL_Mouse *mouse = SDL_GetMouse();
981     SDL_Cursor *cursor;
982 
983     if (!mouse->CreateSystemCursor) {
984         SDL_SetError("CreateSystemCursor is not currently supported");
985         return NULL;
986     }
987 
988     cursor = mouse->CreateSystemCursor(id);
989     if (cursor) {
990         cursor->next = mouse->cursors;
991         mouse->cursors = cursor;
992     }
993 
994     return cursor;
995 }
996 
997 /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
998    if this is desired for any reason.  This is used when setting
999    the video mode and when the SDL window gains the mouse focus.
1000  */
1001 void
1002 SDL_SetCursor(SDL_Cursor * cursor)
1003 {
1004     SDL_Mouse *mouse = SDL_GetMouse();
1005 
1006     /* Set the new cursor */
1007     if (cursor) {
1008         /* Make sure the cursor is still valid for this mouse */
1009         if (cursor != mouse->def_cursor) {
1010             SDL_Cursor *found;
1011             for (found = mouse->cursors; found; found = found->next) {
1012                 if (found == cursor) {
1013                     break;
1014                 }
1015             }
1016             if (!found) {
1017                 SDL_SetError("Cursor not associated with the current mouse");
1018                 return;
1019             }
1020         }
1021         mouse->cur_cursor = cursor;
1022     } else {
1023         if (mouse->focus) {
1024             cursor = mouse->cur_cursor;
1025         } else {
1026             cursor = mouse->def_cursor;
1027         }
1028     }
1029 
1030     if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
1031         if (mouse->ShowCursor) {
1032             mouse->ShowCursor(cursor);
1033         }
1034     } else {
1035         if (mouse->ShowCursor) {
1036             mouse->ShowCursor(NULL);
1037         }
1038     }
1039 }
1040 
1041 SDL_Cursor *
1042 SDL_GetCursor(void)
1043 {
1044     SDL_Mouse *mouse = SDL_GetMouse();
1045 
1046     if (!mouse) {
1047         return NULL;
1048     }
1049     return mouse->cur_cursor;
1050 }
1051 
1052 SDL_Cursor *
1053 SDL_GetDefaultCursor(void)
1054 {
1055     SDL_Mouse *mouse = SDL_GetMouse();
1056 
1057     if (!mouse) {
1058         return NULL;
1059     }
1060     return mouse->def_cursor;
1061 }
1062 
1063 void
1064 SDL_FreeCursor(SDL_Cursor * cursor)
1065 {
1066     SDL_Mouse *mouse = SDL_GetMouse();
1067     SDL_Cursor *curr, *prev;
1068 
1069     if (!cursor) {
1070         return;
1071     }
1072 
1073     if (cursor == mouse->def_cursor) {
1074         return;
1075     }
1076     if (cursor == mouse->cur_cursor) {
1077         SDL_SetCursor(mouse->def_cursor);
1078     }
1079 
1080     for (prev = NULL, curr = mouse->cursors; curr;
1081          prev = curr, curr = curr->next) {
1082         if (curr == cursor) {
1083             if (prev) {
1084                 prev->next = curr->next;
1085             } else {
1086                 mouse->cursors = curr->next;
1087             }
1088 
1089             if (mouse->FreeCursor) {
1090                 mouse->FreeCursor(curr);
1091             }
1092             return;
1093         }
1094     }
1095 }
1096 
1097 int
1098 SDL_ShowCursor(int toggle)
1099 {
1100     SDL_Mouse *mouse = SDL_GetMouse();
1101     SDL_bool shown;
1102 
1103     if (!mouse) {
1104         return 0;
1105     }
1106 
1107     shown = mouse->cursor_shown;
1108     if (toggle >= 0) {
1109         if (toggle) {
1110             mouse->cursor_shown = SDL_TRUE;
1111         } else {
1112             mouse->cursor_shown = SDL_FALSE;
1113         }
1114         if (mouse->cursor_shown != shown) {
1115             SDL_SetCursor(NULL);
1116         }
1117     }
1118     return shown;
1119 }
1120 
1121 /* vi: set ts=4 sw=4 expandtab: */
1122