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, ¢er_x, ¢er_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