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