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