SDL_mouse.c 30 KB

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