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