SDL_mouse.c 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 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_c.h"
  21. #include "../video/SDL_sysvideo.h"
  22. #include "SDL_events_c.h"
  23. #include "SDL_mouse_c.h"
  24. #if defined(SDL_PLATFORM_WINDOWS)
  25. #include "../core/windows/SDL_windows.h" // For GetDoubleClickTime()
  26. #endif
  27. // #define DEBUG_MOUSE
  28. #define WARP_EMULATION_THRESHOLD_NS SDL_MS_TO_NS(30)
  29. typedef struct SDL_MouseInstance
  30. {
  31. SDL_MouseID instance_id;
  32. char *name;
  33. } SDL_MouseInstance;
  34. // The mouse state
  35. static SDL_Mouse SDL_mouse;
  36. static int SDL_mouse_count;
  37. static SDL_MouseInstance *SDL_mice;
  38. // for mapping mouse events to touch
  39. static bool track_mouse_down = false;
  40. static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y);
  41. static void SDLCALL 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. #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
  48. mouse->double_click_time = GetDoubleClickTime();
  49. #else
  50. mouse->double_click_time = 500;
  51. #endif
  52. }
  53. }
  54. static void SDLCALL SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  55. {
  56. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  57. if (hint && *hint) {
  58. mouse->double_click_radius = SDL_atoi(hint);
  59. } else {
  60. mouse->double_click_radius = 32; // 32 pixels seems about right for touch interfaces
  61. }
  62. }
  63. static void SDLCALL SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  64. {
  65. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  66. if (hint && *hint) {
  67. mouse->enable_normal_speed_scale = true;
  68. mouse->normal_speed_scale = (float)SDL_atof(hint);
  69. } else {
  70. mouse->enable_normal_speed_scale = false;
  71. mouse->normal_speed_scale = 1.0f;
  72. }
  73. }
  74. static void SDLCALL SDL_MouseRelativeSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  75. {
  76. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  77. if (hint && *hint) {
  78. mouse->enable_relative_speed_scale = true;
  79. mouse->relative_speed_scale = (float)SDL_atof(hint);
  80. } else {
  81. mouse->enable_relative_speed_scale = false;
  82. mouse->relative_speed_scale = 1.0f;
  83. }
  84. }
  85. static void SDLCALL SDL_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  86. {
  87. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  88. mouse->relative_mode_center = SDL_GetStringBoolean(hint, true);
  89. }
  90. static void SDLCALL SDL_MouseRelativeSystemScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  91. {
  92. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  93. mouse->enable_relative_system_scale = SDL_GetStringBoolean(hint, false);
  94. }
  95. static void SDLCALL SDL_MouseWarpEmulationChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  96. {
  97. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  98. mouse->warp_emulation_hint = SDL_GetStringBoolean(hint, true);
  99. if (!mouse->warp_emulation_hint && mouse->warp_emulation_active) {
  100. SDL_SetRelativeMouseMode(false);
  101. mouse->warp_emulation_active = false;
  102. }
  103. }
  104. static void SDLCALL SDL_TouchMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  105. {
  106. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  107. mouse->touch_mouse_events = SDL_GetStringBoolean(hint, true);
  108. }
  109. #ifdef SDL_PLATFORM_VITA
  110. static void SDLCALL SDL_VitaTouchMouseDeviceChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  111. {
  112. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  113. if (hint) {
  114. switch (*hint) {
  115. default:
  116. case '0':
  117. mouse->vita_touch_mouse_device = 1;
  118. break;
  119. case '1':
  120. mouse->vita_touch_mouse_device = 2;
  121. break;
  122. case '2':
  123. mouse->vita_touch_mouse_device = 3;
  124. break;
  125. }
  126. }
  127. }
  128. #endif
  129. static void SDLCALL SDL_MouseTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  130. {
  131. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  132. bool default_value;
  133. #if defined(SDL_PLATFORM_ANDROID) || (defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_TVOS))
  134. default_value = true;
  135. #else
  136. default_value = false;
  137. #endif
  138. mouse->mouse_touch_events = SDL_GetStringBoolean(hint, default_value);
  139. if (mouse->mouse_touch_events) {
  140. if (!mouse->added_mouse_touch_device) {
  141. SDL_AddTouch(SDL_MOUSE_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "mouse_input");
  142. mouse->added_mouse_touch_device = true;
  143. }
  144. } else {
  145. if (mouse->added_mouse_touch_device) {
  146. SDL_DelTouch(SDL_MOUSE_TOUCHID);
  147. mouse->added_mouse_touch_device = false;
  148. }
  149. }
  150. }
  151. static void SDLCALL SDL_PenMouseEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  152. {
  153. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  154. mouse->pen_mouse_events = SDL_GetStringBoolean(hint, true);
  155. }
  156. static void SDLCALL SDL_PenTouchEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  157. {
  158. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  159. mouse->pen_touch_events = SDL_GetStringBoolean(hint, true);
  160. if (mouse->pen_touch_events) {
  161. if (!mouse->added_pen_touch_device) {
  162. SDL_AddTouch(SDL_PEN_TOUCHID, SDL_TOUCH_DEVICE_DIRECT, "pen_input");
  163. mouse->added_pen_touch_device = true;
  164. }
  165. } else {
  166. if (mouse->added_pen_touch_device) {
  167. SDL_DelTouch(SDL_PEN_TOUCHID);
  168. mouse->added_pen_touch_device = false;
  169. }
  170. }
  171. }
  172. static void SDLCALL SDL_MouseAutoCaptureChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  173. {
  174. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  175. bool auto_capture = SDL_GetStringBoolean(hint, true);
  176. if (auto_capture != mouse->auto_capture) {
  177. mouse->auto_capture = auto_capture;
  178. SDL_UpdateMouseCapture(false);
  179. }
  180. }
  181. static void SDLCALL SDL_MouseRelativeWarpMotionChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  182. {
  183. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  184. mouse->relative_mode_warp_motion = SDL_GetStringBoolean(hint, false);
  185. }
  186. static void SDLCALL SDL_MouseRelativeCursorVisibleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  187. {
  188. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  189. mouse->relative_mode_hide_cursor = !(SDL_GetStringBoolean(hint, false));
  190. SDL_RedrawCursor(); // Update cursor visibility
  191. }
  192. static void SDLCALL SDL_MouseIntegerModeChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  193. {
  194. SDL_Mouse *mouse = (SDL_Mouse *)userdata;
  195. if (hint && *hint) {
  196. mouse->integer_mode_flags = (Uint8)SDL_atoi(hint);
  197. } else {
  198. mouse->integer_mode_flags = 0;
  199. }
  200. }
  201. // Public functions
  202. bool SDL_PreInitMouse(void)
  203. {
  204. SDL_Mouse *mouse = SDL_GetMouse();
  205. SDL_zerop(mouse);
  206. SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME,
  207. SDL_MouseDoubleClickTimeChanged, mouse);
  208. SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS,
  209. SDL_MouseDoubleClickRadiusChanged, mouse);
  210. SDL_AddHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
  211. SDL_MouseNormalSpeedScaleChanged, mouse);
  212. SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
  213. SDL_MouseRelativeSpeedScaleChanged, mouse);
  214. SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
  215. SDL_MouseRelativeSystemScaleChanged, mouse);
  216. SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER,
  217. SDL_MouseRelativeModeCenterChanged, mouse);
  218. SDL_AddHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,
  219. SDL_MouseWarpEmulationChanged, mouse);
  220. SDL_AddHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
  221. SDL_TouchMouseEventsChanged, mouse);
  222. #ifdef SDL_PLATFORM_VITA
  223. SDL_AddHintCallback(SDL_HINT_VITA_TOUCH_MOUSE_DEVICE,
  224. SDL_VitaTouchMouseDeviceChanged, mouse);
  225. #endif
  226. SDL_AddHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
  227. SDL_MouseTouchEventsChanged, mouse);
  228. SDL_AddHintCallback(SDL_HINT_PEN_MOUSE_EVENTS,
  229. SDL_PenMouseEventsChanged, mouse);
  230. SDL_AddHintCallback(SDL_HINT_PEN_TOUCH_EVENTS,
  231. SDL_PenTouchEventsChanged, mouse);
  232. SDL_AddHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE,
  233. SDL_MouseAutoCaptureChanged, mouse);
  234. SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION,
  235. SDL_MouseRelativeWarpMotionChanged, mouse);
  236. SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE,
  237. SDL_MouseRelativeCursorVisibleChanged, mouse);
  238. SDL_AddHintCallback("SDL_MOUSE_INTEGER_MODE",
  239. SDL_MouseIntegerModeChanged, mouse);
  240. mouse->was_touch_mouse_events = false; // no touch to mouse movement event pending
  241. mouse->cursor_visible = true;
  242. return true;
  243. }
  244. void SDL_PostInitMouse(void)
  245. {
  246. SDL_Mouse *mouse = SDL_GetMouse();
  247. /* Create a dummy mouse cursor for video backends that don't support true cursors,
  248. * so that mouse grab and focus functionality will work.
  249. */
  250. if (!mouse->def_cursor) {
  251. SDL_Surface *surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ARGB8888);
  252. if (surface) {
  253. SDL_memset(surface->pixels, 0, (size_t)surface->h * surface->pitch);
  254. SDL_SetDefaultCursor(SDL_CreateColorCursor(surface, 0, 0));
  255. SDL_DestroySurface(surface);
  256. }
  257. }
  258. }
  259. bool SDL_IsMouse(Uint16 vendor, Uint16 product)
  260. {
  261. // Eventually we'll have a blacklist of devices that enumerate as mice but aren't really
  262. return true;
  263. }
  264. static int SDL_GetMouseIndex(SDL_MouseID mouseID)
  265. {
  266. for (int i = 0; i < SDL_mouse_count; ++i) {
  267. if (mouseID == SDL_mice[i].instance_id) {
  268. return i;
  269. }
  270. }
  271. return -1;
  272. }
  273. void SDL_AddMouse(SDL_MouseID mouseID, const char *name, bool send_event)
  274. {
  275. int mouse_index = SDL_GetMouseIndex(mouseID);
  276. if (mouse_index >= 0) {
  277. // We already know about this mouse
  278. return;
  279. }
  280. SDL_assert(mouseID != 0);
  281. SDL_MouseInstance *mice = (SDL_MouseInstance *)SDL_realloc(SDL_mice, (SDL_mouse_count + 1) * sizeof(*mice));
  282. if (!mice) {
  283. return;
  284. }
  285. SDL_MouseInstance *instance = &mice[SDL_mouse_count];
  286. instance->instance_id = mouseID;
  287. instance->name = SDL_strdup(name ? name : "");
  288. SDL_mice = mice;
  289. ++SDL_mouse_count;
  290. if (send_event) {
  291. SDL_Event event;
  292. SDL_zero(event);
  293. event.type = SDL_EVENT_MOUSE_ADDED;
  294. event.mdevice.which = mouseID;
  295. SDL_PushEvent(&event);
  296. }
  297. }
  298. void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event)
  299. {
  300. int mouse_index = SDL_GetMouseIndex(mouseID);
  301. if (mouse_index < 0) {
  302. // We don't know about this mouse
  303. return;
  304. }
  305. SDL_free(SDL_mice[mouse_index].name);
  306. if (mouse_index != SDL_mouse_count - 1) {
  307. SDL_memmove(&SDL_mice[mouse_index], &SDL_mice[mouse_index + 1], (SDL_mouse_count - mouse_index - 1) * sizeof(SDL_mice[mouse_index]));
  308. }
  309. --SDL_mouse_count;
  310. // Remove any mouse input sources for this mouseID
  311. SDL_Mouse *mouse = SDL_GetMouse();
  312. for (int i = 0; i < mouse->num_sources; ++i) {
  313. SDL_MouseInputSource *source = &mouse->sources[i];
  314. if (source->mouseID == mouseID) {
  315. SDL_free(source->clickstate);
  316. if (i != mouse->num_sources - 1) {
  317. SDL_memmove(&mouse->sources[i], &mouse->sources[i + 1], (mouse->num_sources - i - 1) * sizeof(mouse->sources[i]));
  318. }
  319. --mouse->num_sources;
  320. break;
  321. }
  322. }
  323. if (send_event) {
  324. SDL_Event event;
  325. SDL_zero(event);
  326. event.type = SDL_EVENT_MOUSE_REMOVED;
  327. event.mdevice.which = mouseID;
  328. SDL_PushEvent(&event);
  329. }
  330. }
  331. void SDL_SetMouseName(SDL_MouseID mouseID, const char *name)
  332. {
  333. SDL_assert(mouseID != 0);
  334. const int mouse_index = SDL_GetMouseIndex(mouseID);
  335. if (mouse_index >= 0) {
  336. SDL_MouseInstance *instance = &SDL_mice[mouse_index];
  337. SDL_free(instance->name);
  338. instance->name = SDL_strdup(name ? name : "");
  339. }
  340. }
  341. bool SDL_HasMouse(void)
  342. {
  343. return (SDL_mouse_count > 0);
  344. }
  345. SDL_MouseID *SDL_GetMice(int *count)
  346. {
  347. int i;
  348. SDL_MouseID *mice;
  349. mice = (SDL_JoystickID *)SDL_malloc((SDL_mouse_count + 1) * sizeof(*mice));
  350. if (mice) {
  351. if (count) {
  352. *count = SDL_mouse_count;
  353. }
  354. for (i = 0; i < SDL_mouse_count; ++i) {
  355. mice[i] = SDL_mice[i].instance_id;
  356. }
  357. mice[i] = 0;
  358. } else {
  359. if (count) {
  360. *count = 0;
  361. }
  362. }
  363. return mice;
  364. }
  365. const char *SDL_GetMouseNameForID(SDL_MouseID instance_id)
  366. {
  367. int mouse_index = SDL_GetMouseIndex(instance_id);
  368. if (mouse_index < 0) {
  369. SDL_SetError("Mouse %" SDL_PRIu32 " not found", instance_id);
  370. return NULL;
  371. }
  372. return SDL_GetPersistentString(SDL_mice[mouse_index].name);
  373. }
  374. void SDL_SetDefaultCursor(SDL_Cursor *cursor)
  375. {
  376. SDL_Mouse *mouse = SDL_GetMouse();
  377. if (cursor == mouse->def_cursor) {
  378. return;
  379. }
  380. if (mouse->def_cursor) {
  381. SDL_Cursor *default_cursor = mouse->def_cursor;
  382. SDL_Cursor *prev, *curr;
  383. if (mouse->cur_cursor == mouse->def_cursor) {
  384. mouse->cur_cursor = NULL;
  385. }
  386. mouse->def_cursor = NULL;
  387. for (prev = NULL, curr = mouse->cursors; curr;
  388. prev = curr, curr = curr->next) {
  389. if (curr == default_cursor) {
  390. if (prev) {
  391. prev->next = curr->next;
  392. } else {
  393. mouse->cursors = curr->next;
  394. }
  395. break;
  396. }
  397. }
  398. if (mouse->FreeCursor && default_cursor->internal) {
  399. mouse->FreeCursor(default_cursor);
  400. } else {
  401. SDL_free(default_cursor);
  402. }
  403. }
  404. mouse->def_cursor = cursor;
  405. if (!mouse->cur_cursor) {
  406. SDL_SetCursor(cursor);
  407. }
  408. }
  409. SDL_SystemCursor SDL_GetDefaultSystemCursor(void)
  410. {
  411. SDL_SystemCursor id = SDL_SYSTEM_CURSOR_DEFAULT;
  412. const char *value = SDL_GetHint(SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR);
  413. if (value) {
  414. int index = SDL_atoi(value);
  415. if (0 <= index && index < SDL_SYSTEM_CURSOR_COUNT) {
  416. id = (SDL_SystemCursor)index;
  417. }
  418. }
  419. return id;
  420. }
  421. SDL_Mouse *SDL_GetMouse(void)
  422. {
  423. return &SDL_mouse;
  424. }
  425. static SDL_MouseButtonFlags SDL_GetMouseButtonState(SDL_Mouse *mouse, SDL_MouseID mouseID, bool include_touch)
  426. {
  427. int i;
  428. SDL_MouseButtonFlags buttonstate = 0;
  429. for (i = 0; i < mouse->num_sources; ++i) {
  430. if (mouseID == SDL_GLOBAL_MOUSE_ID || mouseID == SDL_TOUCH_MOUSEID) {
  431. if (include_touch || mouse->sources[i].mouseID != SDL_TOUCH_MOUSEID) {
  432. buttonstate |= mouse->sources[i].buttonstate;
  433. }
  434. } else {
  435. if (mouseID == mouse->sources[i].mouseID) {
  436. buttonstate |= mouse->sources[i].buttonstate;
  437. break;
  438. }
  439. }
  440. }
  441. return buttonstate;
  442. }
  443. SDL_Window *SDL_GetMouseFocus(void)
  444. {
  445. SDL_Mouse *mouse = SDL_GetMouse();
  446. return mouse->focus;
  447. }
  448. /* TODO RECONNECT: Hello from the Wayland video driver!
  449. * This was once removed from SDL, but it's been added back in comment form
  450. * because we will need it when Wayland adds compositor reconnect support.
  451. * If you need this before we do, great! Otherwise, leave this alone, we'll
  452. * uncomment it at the right time.
  453. * -flibit
  454. */
  455. #if 0
  456. void SDL_ResetMouse(void)
  457. {
  458. SDL_Mouse *mouse = SDL_GetMouse();
  459. Uint32 buttonState = SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false);
  460. int i;
  461. for (i = 1; i <= sizeof(buttonState)*8; ++i) {
  462. if (buttonState & SDL_BUTTON_MASK(i)) {
  463. SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, i, false);
  464. }
  465. }
  466. SDL_assert(SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false) == 0);
  467. }
  468. #endif // 0
  469. void SDL_SetMouseFocus(SDL_Window *window)
  470. {
  471. SDL_Mouse *mouse = SDL_GetMouse();
  472. if (mouse->focus == window) {
  473. return;
  474. }
  475. /* Actually, this ends up being a bad idea, because most operating
  476. systems have an implicit grab when you press the mouse button down
  477. so you can drag things out of the window and then get the mouse up
  478. when it happens. So, #if 0...
  479. */
  480. #if 0
  481. if (mouse->focus && !window) {
  482. // We won't get anymore mouse messages, so reset mouse state
  483. SDL_ResetMouse();
  484. }
  485. #endif
  486. // See if the current window has lost focus
  487. if (mouse->focus) {
  488. SDL_SendWindowEvent(mouse->focus, SDL_EVENT_WINDOW_MOUSE_LEAVE, 0, 0);
  489. }
  490. mouse->focus = window;
  491. mouse->has_position = false;
  492. if (mouse->focus) {
  493. SDL_SendWindowEvent(mouse->focus, SDL_EVENT_WINDOW_MOUSE_ENTER, 0, 0);
  494. }
  495. // Update cursor visibility
  496. SDL_RedrawCursor();
  497. }
  498. bool SDL_MousePositionInWindow(SDL_Window *window, float x, float y)
  499. {
  500. if (!window) {
  501. return false;
  502. }
  503. if (window && !(window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
  504. if (x < 0.0f || y < 0.0f || x >= (float)window->w || y >= (float)window->h) {
  505. return false;
  506. }
  507. }
  508. return true;
  509. }
  510. // Check to see if we need to synthesize focus events
  511. static bool SDL_UpdateMouseFocus(SDL_Window *window, float x, float y, Uint32 buttonstate, bool send_mouse_motion)
  512. {
  513. SDL_Mouse *mouse = SDL_GetMouse();
  514. bool inWindow = SDL_MousePositionInWindow(window, x, y);
  515. if (!inWindow) {
  516. if (window == mouse->focus) {
  517. #ifdef DEBUG_MOUSE
  518. SDL_Log("Mouse left window, synthesizing move & focus lost event");
  519. #endif
  520. if (send_mouse_motion) {
  521. SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y);
  522. }
  523. SDL_SetMouseFocus(NULL);
  524. }
  525. return false;
  526. }
  527. if (window != mouse->focus) {
  528. #ifdef DEBUG_MOUSE
  529. SDL_Log("Mouse entered window, synthesizing focus gain & move event");
  530. #endif
  531. SDL_SetMouseFocus(window);
  532. if (send_mouse_motion) {
  533. SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y);
  534. }
  535. }
  536. return true;
  537. }
  538. void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y)
  539. {
  540. if (window && !relative) {
  541. SDL_Mouse *mouse = SDL_GetMouse();
  542. if (!SDL_UpdateMouseFocus(window, x, y, SDL_GetMouseButtonState(mouse, mouseID, true), (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID))) {
  543. return;
  544. }
  545. }
  546. SDL_PrivateSendMouseMotion(timestamp, window, mouseID, relative, x, y);
  547. }
  548. static void ConstrainMousePosition(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y)
  549. {
  550. /* make sure that the pointers find themselves inside the windows,
  551. unless we have the mouse captured. */
  552. if (window && !(window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
  553. int x_min = 0, x_max = window->w - 1;
  554. int y_min = 0, y_max = window->h - 1;
  555. const SDL_Rect *confine = SDL_GetWindowMouseRect(window);
  556. if (confine) {
  557. SDL_Rect window_rect;
  558. SDL_Rect mouse_rect;
  559. window_rect.x = 0;
  560. window_rect.y = 0;
  561. window_rect.w = x_max + 1;
  562. window_rect.h = y_max + 1;
  563. if (SDL_GetRectIntersection(confine, &window_rect, &mouse_rect)) {
  564. x_min = mouse_rect.x;
  565. y_min = mouse_rect.y;
  566. x_max = x_min + mouse_rect.w - 1;
  567. y_max = y_min + mouse_rect.h - 1;
  568. }
  569. }
  570. if (*x >= (float)(x_max + 1)) {
  571. *x = SDL_max((float)x_max, mouse->last_x);
  572. }
  573. if (*x < (float)x_min) {
  574. *x = (float)x_min;
  575. }
  576. if (*y >= (float)(y_max + 1)) {
  577. *y = SDL_max((float)y_max, mouse->last_y);
  578. }
  579. if (*y < (float)y_min) {
  580. *y = (float)y_min;
  581. }
  582. }
  583. }
  584. static void SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y)
  585. {
  586. SDL_Mouse *mouse = SDL_GetMouse();
  587. float xrel = 0.0f;
  588. float yrel = 0.0f;
  589. bool window_is_relative = mouse->focus && (mouse->focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE);
  590. // SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events
  591. if (mouse->mouse_touch_events) {
  592. if (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID && !relative && track_mouse_down) {
  593. if (window) {
  594. float normalized_x = x / (float)window->w;
  595. float normalized_y = y / (float)window->h;
  596. SDL_SendTouchMotion(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, normalized_x, normalized_y, 1.0f);
  597. }
  598. }
  599. }
  600. // SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer
  601. if (!mouse->touch_mouse_events && mouseID == SDL_TOUCH_MOUSEID) {
  602. return;
  603. }
  604. if (relative) {
  605. if (mouse->relative_mode) {
  606. if (mouse->InputTransform) {
  607. void *data = mouse->input_transform_data;
  608. mouse->InputTransform(data, timestamp, window, mouseID, &x, &y);
  609. } else {
  610. if (mouse->enable_relative_system_scale) {
  611. if (mouse->ApplySystemScale) {
  612. mouse->ApplySystemScale(mouse->system_scale_data, timestamp, window, mouseID, &x, &y);
  613. }
  614. }
  615. if (mouse->enable_relative_speed_scale) {
  616. x *= mouse->relative_speed_scale;
  617. y *= mouse->relative_speed_scale;
  618. }
  619. }
  620. } else {
  621. if (mouse->enable_normal_speed_scale) {
  622. x *= mouse->normal_speed_scale;
  623. y *= mouse->normal_speed_scale;
  624. }
  625. }
  626. if (mouse->integer_mode_flags & 1) {
  627. // Accumulate the fractional relative motion and only process the integer portion
  628. mouse->integer_mode_residual_motion_x = SDL_modff(mouse->integer_mode_residual_motion_x + x, &x);
  629. mouse->integer_mode_residual_motion_y = SDL_modff(mouse->integer_mode_residual_motion_y + y, &y);
  630. }
  631. xrel = x;
  632. yrel = y;
  633. x = (mouse->last_x + xrel);
  634. y = (mouse->last_y + yrel);
  635. ConstrainMousePosition(mouse, window, &x, &y);
  636. } else {
  637. if (mouse->integer_mode_flags & 1) {
  638. // Discard the fractional component from absolute coordinates
  639. x = SDL_truncf(x);
  640. y = SDL_truncf(y);
  641. }
  642. ConstrainMousePosition(mouse, window, &x, &y);
  643. if (mouse->has_position) {
  644. xrel = x - mouse->last_x;
  645. yrel = y - mouse->last_y;
  646. }
  647. }
  648. if (mouse->has_position && xrel == 0.0f && yrel == 0.0f) { // Drop events that don't change state
  649. #ifdef DEBUG_MOUSE
  650. SDL_Log("Mouse event didn't change state - dropped!");
  651. #endif
  652. return;
  653. }
  654. // Ignore relative motion positioning the first touch
  655. if (mouseID == SDL_TOUCH_MOUSEID && !SDL_GetMouseButtonState(mouse, mouseID, true)) {
  656. xrel = 0.0f;
  657. yrel = 0.0f;
  658. }
  659. // modify internal state
  660. {
  661. mouse->x_accu += xrel;
  662. mouse->y_accu += yrel;
  663. if (relative && mouse->has_position) {
  664. mouse->x += xrel;
  665. mouse->y += yrel;
  666. ConstrainMousePosition(mouse, window, &mouse->x, &mouse->y);
  667. } else {
  668. mouse->x = x;
  669. mouse->y = y;
  670. }
  671. mouse->has_position = true;
  672. // Use unclamped values if we're getting events outside the window
  673. mouse->last_x = relative ? mouse->x : x;
  674. mouse->last_y = relative ? mouse->y : y;
  675. mouse->click_motion_x += xrel;
  676. mouse->click_motion_y += yrel;
  677. }
  678. // Move the mouse cursor, if needed
  679. if (mouse->cursor_visible && !mouse->relative_mode &&
  680. mouse->MoveCursor && mouse->cur_cursor) {
  681. mouse->MoveCursor(mouse->cur_cursor);
  682. }
  683. // Post the event, if desired
  684. if (SDL_EventEnabled(SDL_EVENT_MOUSE_MOTION)) {
  685. if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) {
  686. // We're not in relative mode, so all mouse events are global mouse events
  687. mouseID = SDL_GLOBAL_MOUSE_ID;
  688. }
  689. if (!relative && window_is_relative) {
  690. if (!mouse->relative_mode_warp_motion) {
  691. return;
  692. }
  693. xrel = 0.0f;
  694. yrel = 0.0f;
  695. }
  696. SDL_Event event;
  697. event.type = SDL_EVENT_MOUSE_MOTION;
  698. event.common.timestamp = timestamp;
  699. event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
  700. event.motion.which = mouseID;
  701. // Set us pending (or clear during a normal mouse movement event) as having triggered
  702. mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID);
  703. event.motion.state = SDL_GetMouseButtonState(mouse, mouseID, true);
  704. event.motion.x = mouse->x;
  705. event.motion.y = mouse->y;
  706. event.motion.xrel = xrel;
  707. event.motion.yrel = yrel;
  708. SDL_PushEvent(&event);
  709. }
  710. }
  711. static SDL_MouseInputSource *GetMouseInputSource(SDL_Mouse *mouse, SDL_MouseID mouseID, bool down, Uint8 button)
  712. {
  713. SDL_MouseInputSource *source, *match = NULL, *sources;
  714. int i;
  715. for (i = 0; i < mouse->num_sources; ++i) {
  716. source = &mouse->sources[i];
  717. if (source->mouseID == mouseID) {
  718. match = source;
  719. break;
  720. }
  721. }
  722. if (!down && (!match || !(match->buttonstate & SDL_BUTTON_MASK(button)))) {
  723. /* This might be a button release from a transition between mouse messages and raw input.
  724. * See if there's another mouse source that already has that button down and use that.
  725. */
  726. for (i = 0; i < mouse->num_sources; ++i) {
  727. source = &mouse->sources[i];
  728. if ((source->buttonstate & SDL_BUTTON_MASK(button))) {
  729. match = source;
  730. break;
  731. }
  732. }
  733. }
  734. if (match) {
  735. return match;
  736. }
  737. sources = (SDL_MouseInputSource *)SDL_realloc(mouse->sources, (mouse->num_sources + 1) * sizeof(*mouse->sources));
  738. if (sources) {
  739. mouse->sources = sources;
  740. ++mouse->num_sources;
  741. source = &sources[mouse->num_sources - 1];
  742. SDL_zerop(source);
  743. source->mouseID = mouseID;
  744. return source;
  745. }
  746. return NULL;
  747. }
  748. static SDL_MouseClickState *GetMouseClickState(SDL_MouseInputSource *source, Uint8 button)
  749. {
  750. if (button >= source->num_clickstates) {
  751. int i, count = button + 1;
  752. SDL_MouseClickState *clickstate = (SDL_MouseClickState *)SDL_realloc(source->clickstate, count * sizeof(*source->clickstate));
  753. if (!clickstate) {
  754. return NULL;
  755. }
  756. source->clickstate = clickstate;
  757. for (i = source->num_clickstates; i < count; ++i) {
  758. SDL_zero(source->clickstate[i]);
  759. }
  760. source->num_clickstates = count;
  761. }
  762. return &source->clickstate[button];
  763. }
  764. static void SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks)
  765. {
  766. SDL_Mouse *mouse = SDL_GetMouse();
  767. SDL_EventType type;
  768. Uint32 buttonstate;
  769. SDL_MouseInputSource *source;
  770. source = GetMouseInputSource(mouse, mouseID, down, button);
  771. if (!source) {
  772. return;
  773. }
  774. buttonstate = source->buttonstate;
  775. // SDL_HINT_MOUSE_TOUCH_EVENTS: controlling whether mouse events should generate synthetic touch events
  776. if (mouse->mouse_touch_events) {
  777. if (mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID && button == SDL_BUTTON_LEFT) {
  778. if (down) {
  779. track_mouse_down = true;
  780. } else {
  781. track_mouse_down = false;
  782. }
  783. if (window) {
  784. type = track_mouse_down ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP;
  785. float normalized_x = mouse->x / (float)window->w;
  786. float normalized_y = mouse->y / (float)window->h;
  787. SDL_SendTouch(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, type, normalized_x, normalized_y, 1.0f);
  788. }
  789. }
  790. }
  791. // SDL_HINT_TOUCH_MOUSE_EVENTS: if not set, discard synthetic mouse events coming from platform layer
  792. if (mouse->touch_mouse_events == 0) {
  793. if (mouseID == SDL_TOUCH_MOUSEID) {
  794. return;
  795. }
  796. }
  797. // Figure out which event to perform
  798. if (down) {
  799. type = SDL_EVENT_MOUSE_BUTTON_DOWN;
  800. buttonstate |= SDL_BUTTON_MASK(button);
  801. } else {
  802. type = SDL_EVENT_MOUSE_BUTTON_UP;
  803. buttonstate &= ~SDL_BUTTON_MASK(button);
  804. }
  805. // We do this after calculating buttonstate so button presses gain focus
  806. if (window && down) {
  807. SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, true);
  808. }
  809. if (buttonstate == source->buttonstate) {
  810. // Ignore this event, no state change
  811. return;
  812. }
  813. source->buttonstate = buttonstate;
  814. if (clicks < 0) {
  815. SDL_MouseClickState *clickstate = GetMouseClickState(source, button);
  816. if (clickstate) {
  817. if (down) {
  818. Uint64 now = SDL_GetTicks();
  819. if (now >= (clickstate->last_timestamp + mouse->double_click_time) ||
  820. SDL_fabs(mouse->click_motion_x - clickstate->click_motion_x) > mouse->double_click_radius ||
  821. SDL_fabs(mouse->click_motion_y - clickstate->click_motion_y) > mouse->double_click_radius) {
  822. clickstate->click_count = 0;
  823. }
  824. clickstate->last_timestamp = now;
  825. clickstate->click_motion_x = mouse->click_motion_x;
  826. clickstate->click_motion_y = mouse->click_motion_y;
  827. if (clickstate->click_count < 255) {
  828. ++clickstate->click_count;
  829. }
  830. }
  831. clicks = clickstate->click_count;
  832. } else {
  833. clicks = 1;
  834. }
  835. }
  836. // Post the event, if desired
  837. if (SDL_EventEnabled(type)) {
  838. if ((!mouse->relative_mode || mouse->warp_emulation_active) && mouseID != SDL_TOUCH_MOUSEID && mouseID != SDL_PEN_MOUSEID) {
  839. // We're not in relative mode, so all mouse events are global mouse events
  840. mouseID = SDL_GLOBAL_MOUSE_ID;
  841. } else {
  842. mouseID = source->mouseID;
  843. }
  844. SDL_Event event;
  845. event.type = type;
  846. event.common.timestamp = timestamp;
  847. event.button.windowID = mouse->focus ? mouse->focus->id : 0;
  848. event.button.which = mouseID;
  849. event.button.down = down;
  850. event.button.button = button;
  851. event.button.clicks = (Uint8)SDL_min(clicks, 255);
  852. event.button.x = mouse->x;
  853. event.button.y = mouse->y;
  854. SDL_PushEvent(&event);
  855. }
  856. // We do this after dispatching event so button releases can lose focus
  857. if (window && !down) {
  858. SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate, true);
  859. }
  860. // Automatically capture the mouse while buttons are pressed
  861. if (mouse->auto_capture) {
  862. SDL_UpdateMouseCapture(false);
  863. }
  864. }
  865. void SDL_SendMouseButtonClicks(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks)
  866. {
  867. clicks = SDL_max(clicks, 0);
  868. SDL_PrivateSendMouseButton(timestamp, window, mouseID, button, down, clicks);
  869. }
  870. void SDL_SendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down)
  871. {
  872. SDL_PrivateSendMouseButton(timestamp, window, mouseID, button, down, -1);
  873. }
  874. void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction)
  875. {
  876. SDL_Mouse *mouse = SDL_GetMouse();
  877. if (window) {
  878. SDL_SetMouseFocus(window);
  879. }
  880. if (x == 0.0f && y == 0.0f) {
  881. return;
  882. }
  883. // Post the event, if desired
  884. if (SDL_EventEnabled(SDL_EVENT_MOUSE_WHEEL)) {
  885. float integer_x, integer_y;
  886. if (!mouse->relative_mode || mouse->warp_emulation_active) {
  887. // We're not in relative mode, so all mouse events are global mouse events
  888. mouseID = SDL_GLOBAL_MOUSE_ID;
  889. }
  890. SDL_Event event;
  891. event.type = SDL_EVENT_MOUSE_WHEEL;
  892. event.common.timestamp = timestamp;
  893. event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
  894. event.wheel.which = mouseID;
  895. event.wheel.direction = direction;
  896. event.wheel.mouse_x = mouse->x;
  897. event.wheel.mouse_y = mouse->y;
  898. mouse->residual_scroll_x = SDL_modff(mouse->residual_scroll_x + x, &integer_x);
  899. event.wheel.integer_x = (Sint32)integer_x;
  900. mouse->residual_scroll_y = SDL_modff(mouse->residual_scroll_y + y, &integer_y);
  901. event.wheel.integer_y = (Sint32)integer_y;
  902. // Return the accumulated values in x/y when integer wheel mode is enabled.
  903. // This is necessary for compatibility with sdl2-compat 2.32.54.
  904. if (mouse->integer_mode_flags & 2) {
  905. event.wheel.x = integer_x;
  906. event.wheel.y = integer_y;
  907. } else {
  908. event.wheel.x = x;
  909. event.wheel.y = y;
  910. }
  911. SDL_PushEvent(&event);
  912. }
  913. }
  914. void SDL_QuitMouse(void)
  915. {
  916. SDL_Cursor *cursor, *next;
  917. SDL_Mouse *mouse = SDL_GetMouse();
  918. if (mouse->added_mouse_touch_device) {
  919. SDL_DelTouch(SDL_MOUSE_TOUCHID);
  920. mouse->added_mouse_touch_device = false;
  921. }
  922. if (mouse->added_pen_touch_device) {
  923. SDL_DelTouch(SDL_PEN_TOUCHID);
  924. mouse->added_pen_touch_device = false;
  925. }
  926. if (mouse->CaptureMouse) {
  927. SDL_CaptureMouse(false);
  928. SDL_UpdateMouseCapture(true);
  929. }
  930. SDL_SetRelativeMouseMode(false);
  931. SDL_ShowCursor();
  932. if (mouse->def_cursor) {
  933. SDL_SetDefaultCursor(NULL);
  934. }
  935. cursor = mouse->cursors;
  936. while (cursor) {
  937. next = cursor->next;
  938. SDL_DestroyCursor(cursor);
  939. cursor = next;
  940. }
  941. mouse->cursors = NULL;
  942. mouse->cur_cursor = NULL;
  943. if (mouse->sources) {
  944. for (int i = 0; i < mouse->num_sources; ++i) {
  945. SDL_MouseInputSource *source = &mouse->sources[i];
  946. SDL_free(source->clickstate);
  947. }
  948. SDL_free(mouse->sources);
  949. mouse->sources = NULL;
  950. }
  951. mouse->num_sources = 0;
  952. SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME,
  953. SDL_MouseDoubleClickTimeChanged, mouse);
  954. SDL_RemoveHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS,
  955. SDL_MouseDoubleClickRadiusChanged, mouse);
  956. SDL_RemoveHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
  957. SDL_MouseNormalSpeedScaleChanged, mouse);
  958. SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE,
  959. SDL_MouseRelativeSpeedScaleChanged, mouse);
  960. SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
  961. SDL_MouseRelativeSystemScaleChanged, mouse);
  962. SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER,
  963. SDL_MouseRelativeModeCenterChanged, mouse);
  964. SDL_RemoveHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,
  965. SDL_MouseWarpEmulationChanged, mouse);
  966. SDL_RemoveHintCallback(SDL_HINT_TOUCH_MOUSE_EVENTS,
  967. SDL_TouchMouseEventsChanged, mouse);
  968. SDL_RemoveHintCallback(SDL_HINT_MOUSE_TOUCH_EVENTS,
  969. SDL_MouseTouchEventsChanged, mouse);
  970. SDL_RemoveHintCallback(SDL_HINT_PEN_MOUSE_EVENTS,
  971. SDL_PenMouseEventsChanged, mouse);
  972. SDL_RemoveHintCallback(SDL_HINT_PEN_TOUCH_EVENTS,
  973. SDL_PenTouchEventsChanged, mouse);
  974. SDL_RemoveHintCallback(SDL_HINT_MOUSE_AUTO_CAPTURE,
  975. SDL_MouseAutoCaptureChanged, mouse);
  976. SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION,
  977. SDL_MouseRelativeWarpMotionChanged, mouse);
  978. SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE,
  979. SDL_MouseRelativeCursorVisibleChanged, mouse);
  980. SDL_RemoveHintCallback("SDL_MOUSE_INTEGER_MODE",
  981. SDL_MouseIntegerModeChanged, mouse);
  982. for (int i = SDL_mouse_count; i--; ) {
  983. SDL_RemoveMouse(SDL_mice[i].instance_id, false);
  984. }
  985. SDL_free(SDL_mice);
  986. SDL_mice = NULL;
  987. }
  988. bool SDL_SetRelativeMouseTransform(SDL_MouseMotionTransformCallback transform, void *userdata)
  989. {
  990. SDL_Mouse *mouse = SDL_GetMouse();
  991. if (mouse->relative_mode) {
  992. return SDL_SetError("Can't set mouse transform while relative mode is active");
  993. }
  994. mouse->InputTransform = transform;
  995. mouse->input_transform_data = userdata;
  996. return true;
  997. }
  998. SDL_MouseButtonFlags SDL_GetMouseState(float *x, float *y)
  999. {
  1000. SDL_Mouse *mouse = SDL_GetMouse();
  1001. if (x) {
  1002. *x = mouse->x;
  1003. }
  1004. if (y) {
  1005. *y = mouse->y;
  1006. }
  1007. return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true);
  1008. }
  1009. SDL_MouseButtonFlags SDL_GetRelativeMouseState(float *x, float *y)
  1010. {
  1011. SDL_Mouse *mouse = SDL_GetMouse();
  1012. if (x) {
  1013. *x = mouse->x_accu;
  1014. }
  1015. if (y) {
  1016. *y = mouse->y_accu;
  1017. }
  1018. mouse->x_accu = 0.0f;
  1019. mouse->y_accu = 0.0f;
  1020. return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, true);
  1021. }
  1022. SDL_MouseButtonFlags SDL_GetGlobalMouseState(float *x, float *y)
  1023. {
  1024. SDL_Mouse *mouse = SDL_GetMouse();
  1025. if (mouse->GetGlobalMouseState) {
  1026. float tmpx, tmpy;
  1027. // make sure these are never NULL for the backend implementations...
  1028. if (!x) {
  1029. x = &tmpx;
  1030. }
  1031. if (!y) {
  1032. y = &tmpy;
  1033. }
  1034. *x = *y = 0.0f;
  1035. return mouse->GetGlobalMouseState(x, y);
  1036. } else {
  1037. return SDL_GetMouseState(x, y);
  1038. }
  1039. }
  1040. void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, bool ignore_relative_mode)
  1041. {
  1042. SDL_Mouse *mouse = SDL_GetMouse();
  1043. if (!window) {
  1044. window = mouse->focus;
  1045. }
  1046. if (!window) {
  1047. return;
  1048. }
  1049. if ((window->flags & SDL_WINDOW_MINIMIZED) == SDL_WINDOW_MINIMIZED) {
  1050. return;
  1051. }
  1052. // Ignore the previous position when we warp
  1053. mouse->last_x = x;
  1054. mouse->last_y = y;
  1055. mouse->has_position = false;
  1056. if (mouse->relative_mode && !ignore_relative_mode) {
  1057. /* 2.0.22 made warping in relative mode actually functional, which
  1058. * surprised many applications that weren't expecting the additional
  1059. * mouse motion.
  1060. *
  1061. * So for now, warping in relative mode adjusts the absolution position
  1062. * but doesn't generate motion events, unless SDL_HINT_MOUSE_RELATIVE_WARP_MOTION is set.
  1063. */
  1064. if (!mouse->relative_mode_warp_motion) {
  1065. mouse->x = x;
  1066. mouse->y = y;
  1067. mouse->has_position = true;
  1068. return;
  1069. }
  1070. }
  1071. if (mouse->WarpMouse && !mouse->relative_mode) {
  1072. mouse->WarpMouse(window, x, y);
  1073. } else {
  1074. SDL_PrivateSendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, x, y);
  1075. }
  1076. }
  1077. void SDL_DisableMouseWarpEmulation(void)
  1078. {
  1079. SDL_Mouse *mouse = SDL_GetMouse();
  1080. if (mouse->warp_emulation_active) {
  1081. SDL_SetRelativeMouseMode(false);
  1082. }
  1083. mouse->warp_emulation_prohibited = true;
  1084. }
  1085. static void SDL_MaybeEnableWarpEmulation(SDL_Window *window, float x, float y)
  1086. {
  1087. SDL_Mouse *mouse = SDL_GetMouse();
  1088. if (!mouse->warp_emulation_prohibited && mouse->warp_emulation_hint && !mouse->cursor_visible && !mouse->warp_emulation_active) {
  1089. if (!window) {
  1090. window = mouse->focus;
  1091. }
  1092. if (window) {
  1093. const float cx = window->w / 2.f;
  1094. const float cy = window->h / 2.f;
  1095. if (x >= SDL_floorf(cx) && x <= SDL_ceilf(cx) &&
  1096. y >= SDL_floorf(cy) && y <= SDL_ceilf(cy)) {
  1097. // Require two consecutive warps to the center within a certain timespan to enter warp emulation mode.
  1098. const Uint64 now = SDL_GetTicksNS();
  1099. if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) {
  1100. mouse->warp_emulation_active = true;
  1101. if (!SDL_SetRelativeMouseMode(true)) {
  1102. mouse->warp_emulation_active = false;
  1103. }
  1104. }
  1105. mouse->last_center_warp_time_ns = now;
  1106. return;
  1107. }
  1108. }
  1109. mouse->last_center_warp_time_ns = 0;
  1110. }
  1111. }
  1112. void SDL_WarpMouseInWindow(SDL_Window *window, float x, float y)
  1113. {
  1114. SDL_Mouse *mouse = SDL_GetMouse();
  1115. SDL_MaybeEnableWarpEmulation(window, x, y);
  1116. SDL_PerformWarpMouseInWindow(window, x, y, mouse->warp_emulation_active);
  1117. }
  1118. bool SDL_WarpMouseGlobal(float x, float y)
  1119. {
  1120. SDL_Mouse *mouse = SDL_GetMouse();
  1121. if (mouse->WarpMouseGlobal) {
  1122. return mouse->WarpMouseGlobal(x, y);
  1123. }
  1124. return SDL_Unsupported();
  1125. }
  1126. bool SDL_SetRelativeMouseMode(bool enabled)
  1127. {
  1128. SDL_Mouse *mouse = SDL_GetMouse();
  1129. SDL_Window *focusWindow = SDL_GetKeyboardFocus();
  1130. if (!enabled) {
  1131. // If warps were being emulated, reset the flag.
  1132. mouse->warp_emulation_active = false;
  1133. }
  1134. if (enabled == mouse->relative_mode) {
  1135. return true;
  1136. }
  1137. // Set the relative mode
  1138. if (!mouse->SetRelativeMouseMode || !mouse->SetRelativeMouseMode(enabled)) {
  1139. if (enabled) {
  1140. return SDL_SetError("No relative mode implementation available");
  1141. }
  1142. }
  1143. mouse->relative_mode = enabled;
  1144. if (enabled) {
  1145. // Update cursor visibility before we potentially warp the mouse
  1146. SDL_RedrawCursor();
  1147. }
  1148. if (enabled && focusWindow) {
  1149. SDL_SetMouseFocus(focusWindow);
  1150. }
  1151. if (focusWindow) {
  1152. SDL_UpdateWindowGrab(focusWindow);
  1153. // Put the cursor back to where the application expects it
  1154. if (!enabled) {
  1155. SDL_PerformWarpMouseInWindow(focusWindow, mouse->x, mouse->y, true);
  1156. }
  1157. SDL_UpdateMouseCapture(false);
  1158. }
  1159. if (!enabled) {
  1160. // Update cursor visibility after we restore the mouse position
  1161. SDL_RedrawCursor();
  1162. }
  1163. // Flush pending mouse motion - ideally we would pump events, but that's not always safe
  1164. SDL_FlushEvent(SDL_EVENT_MOUSE_MOTION);
  1165. return true;
  1166. }
  1167. bool SDL_GetRelativeMouseMode(void)
  1168. {
  1169. SDL_Mouse *mouse = SDL_GetMouse();
  1170. return mouse->relative_mode;
  1171. }
  1172. void SDL_UpdateRelativeMouseMode(void)
  1173. {
  1174. SDL_Mouse *mouse = SDL_GetMouse();
  1175. SDL_Window *focus = SDL_GetKeyboardFocus();
  1176. bool relative_mode = (focus && (focus->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE));
  1177. if (relative_mode != mouse->relative_mode) {
  1178. SDL_SetRelativeMouseMode(relative_mode);
  1179. }
  1180. }
  1181. bool SDL_UpdateMouseCapture(bool force_release)
  1182. {
  1183. SDL_Mouse *mouse = SDL_GetMouse();
  1184. SDL_Window *capture_window = NULL;
  1185. if (!mouse->CaptureMouse) {
  1186. return true;
  1187. }
  1188. if (!force_release) {
  1189. if (SDL_GetMessageBoxCount() == 0 &&
  1190. (mouse->capture_desired || (mouse->auto_capture && SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, false) != 0))) {
  1191. if (!mouse->relative_mode) {
  1192. capture_window = mouse->focus;
  1193. }
  1194. }
  1195. }
  1196. if (capture_window != mouse->capture_window) {
  1197. /* We can get here recursively on Windows, so make sure we complete
  1198. * all of the window state operations before we change the capture state
  1199. * (e.g. https://github.com/libsdl-org/SDL/pull/5608)
  1200. */
  1201. SDL_Window *previous_capture = mouse->capture_window;
  1202. if (previous_capture) {
  1203. previous_capture->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
  1204. }
  1205. if (capture_window) {
  1206. capture_window->flags |= SDL_WINDOW_MOUSE_CAPTURE;
  1207. }
  1208. mouse->capture_window = capture_window;
  1209. if (!mouse->CaptureMouse(capture_window)) {
  1210. // CaptureMouse() will have set an error, just restore the state
  1211. if (previous_capture) {
  1212. previous_capture->flags |= SDL_WINDOW_MOUSE_CAPTURE;
  1213. }
  1214. if (capture_window) {
  1215. capture_window->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
  1216. }
  1217. mouse->capture_window = previous_capture;
  1218. return false;
  1219. }
  1220. }
  1221. return true;
  1222. }
  1223. bool SDL_CaptureMouse(bool enabled)
  1224. {
  1225. SDL_Mouse *mouse = SDL_GetMouse();
  1226. if (!mouse->CaptureMouse) {
  1227. return SDL_Unsupported();
  1228. }
  1229. #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
  1230. /* Windows mouse capture is tied to the current thread, and must be called
  1231. * from the thread that created the window being captured. Since we update
  1232. * the mouse capture state from the event processing, any application state
  1233. * changes must be processed on that thread as well.
  1234. */
  1235. if (!SDL_OnVideoThread()) {
  1236. return SDL_SetError("SDL_CaptureMouse() must be called on the main thread");
  1237. }
  1238. #endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
  1239. if (enabled && SDL_GetKeyboardFocus() == NULL) {
  1240. return SDL_SetError("No window has focus");
  1241. }
  1242. mouse->capture_desired = enabled;
  1243. return SDL_UpdateMouseCapture(false);
  1244. }
  1245. SDL_Cursor *SDL_CreateCursor(const Uint8 *data, const Uint8 *mask, int w, int h, int hot_x, int hot_y)
  1246. {
  1247. SDL_Surface *surface;
  1248. SDL_Cursor *cursor;
  1249. int x, y;
  1250. Uint32 *pixels;
  1251. Uint8 datab = 0, maskb = 0;
  1252. const Uint32 black = 0xFF000000;
  1253. const Uint32 white = 0xFFFFFFFF;
  1254. const Uint32 transparent = 0x00000000;
  1255. #if defined(SDL_PLATFORM_WIN32)
  1256. // Only Windows backend supports inverted pixels in mono cursors.
  1257. const Uint32 inverted = 0x00FFFFFF;
  1258. #else
  1259. const Uint32 inverted = 0xFF000000;
  1260. #endif // defined(SDL_PLATFORM_WIN32)
  1261. // Make sure the width is a multiple of 8
  1262. w = ((w + 7) & ~7);
  1263. // Create the surface from a bitmap
  1264. surface = SDL_CreateSurface(w, h, SDL_PIXELFORMAT_ARGB8888);
  1265. if (!surface) {
  1266. return NULL;
  1267. }
  1268. for (y = 0; y < h; ++y) {
  1269. pixels = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
  1270. for (x = 0; x < w; ++x) {
  1271. if ((x % 8) == 0) {
  1272. datab = *data++;
  1273. maskb = *mask++;
  1274. }
  1275. if (maskb & 0x80) {
  1276. *pixels++ = (datab & 0x80) ? black : white;
  1277. } else {
  1278. *pixels++ = (datab & 0x80) ? inverted : transparent;
  1279. }
  1280. datab <<= 1;
  1281. maskb <<= 1;
  1282. }
  1283. }
  1284. cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
  1285. SDL_DestroySurface(surface);
  1286. return cursor;
  1287. }
  1288. SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
  1289. {
  1290. SDL_Mouse *mouse = SDL_GetMouse();
  1291. SDL_Surface *temp = NULL;
  1292. SDL_Cursor *cursor;
  1293. if (!surface) {
  1294. SDL_InvalidParamError("surface");
  1295. return NULL;
  1296. }
  1297. // Allow specifying the hot spot via properties on the surface
  1298. SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
  1299. hot_x = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, hot_x);
  1300. hot_y = (int)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER, hot_y);
  1301. // Sanity check the hot spot
  1302. if ((hot_x < 0) || (hot_y < 0) ||
  1303. (hot_x >= surface->w) || (hot_y >= surface->h)) {
  1304. SDL_SetError("Cursor hot spot doesn't lie within cursor");
  1305. return NULL;
  1306. }
  1307. if (surface->format != SDL_PIXELFORMAT_ARGB8888) {
  1308. temp = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
  1309. if (!temp) {
  1310. return NULL;
  1311. }
  1312. surface = temp;
  1313. }
  1314. if (mouse->CreateCursor) {
  1315. cursor = mouse->CreateCursor(surface, hot_x, hot_y);
  1316. } else {
  1317. cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
  1318. }
  1319. if (cursor) {
  1320. cursor->next = mouse->cursors;
  1321. mouse->cursors = cursor;
  1322. }
  1323. SDL_DestroySurface(temp);
  1324. return cursor;
  1325. }
  1326. SDL_Cursor *SDL_CreateSystemCursor(SDL_SystemCursor id)
  1327. {
  1328. SDL_Mouse *mouse = SDL_GetMouse();
  1329. SDL_Cursor *cursor;
  1330. if (!mouse->CreateSystemCursor) {
  1331. SDL_SetError("CreateSystemCursor is not currently supported");
  1332. return NULL;
  1333. }
  1334. cursor = mouse->CreateSystemCursor(id);
  1335. if (cursor) {
  1336. cursor->next = mouse->cursors;
  1337. mouse->cursors = cursor;
  1338. }
  1339. return cursor;
  1340. }
  1341. void SDL_RedrawCursor(void)
  1342. {
  1343. SDL_Mouse *mouse = SDL_GetMouse();
  1344. SDL_Cursor *cursor;
  1345. if (mouse->focus) {
  1346. cursor = mouse->cur_cursor;
  1347. } else {
  1348. cursor = mouse->def_cursor;
  1349. }
  1350. if (mouse->focus && (!mouse->cursor_visible || (mouse->relative_mode && mouse->relative_mode_hide_cursor))) {
  1351. cursor = NULL;
  1352. }
  1353. if (mouse->ShowCursor) {
  1354. mouse->ShowCursor(cursor);
  1355. }
  1356. }
  1357. /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
  1358. if this is desired for any reason. This is used when setting
  1359. the video mode and when the SDL window gains the mouse focus.
  1360. */
  1361. bool SDL_SetCursor(SDL_Cursor *cursor)
  1362. {
  1363. SDL_Mouse *mouse = SDL_GetMouse();
  1364. // already on this cursor, no further action required
  1365. if (cursor == mouse->cur_cursor) {
  1366. return true;
  1367. }
  1368. // Set the new cursor
  1369. if (cursor) {
  1370. // Make sure the cursor is still valid for this mouse
  1371. if (cursor != mouse->def_cursor) {
  1372. SDL_Cursor *found;
  1373. for (found = mouse->cursors; found; found = found->next) {
  1374. if (found == cursor) {
  1375. break;
  1376. }
  1377. }
  1378. if (!found) {
  1379. return SDL_SetError("Cursor not associated with the current mouse");
  1380. }
  1381. }
  1382. mouse->cur_cursor = cursor;
  1383. }
  1384. SDL_RedrawCursor();
  1385. return true;
  1386. }
  1387. SDL_Cursor *SDL_GetCursor(void)
  1388. {
  1389. SDL_Mouse *mouse = SDL_GetMouse();
  1390. if (!mouse) {
  1391. return NULL;
  1392. }
  1393. return mouse->cur_cursor;
  1394. }
  1395. SDL_Cursor *SDL_GetDefaultCursor(void)
  1396. {
  1397. SDL_Mouse *mouse = SDL_GetMouse();
  1398. if (!mouse) {
  1399. return NULL;
  1400. }
  1401. return mouse->def_cursor;
  1402. }
  1403. void SDL_DestroyCursor(SDL_Cursor *cursor)
  1404. {
  1405. SDL_Mouse *mouse = SDL_GetMouse();
  1406. SDL_Cursor *curr, *prev;
  1407. if (!cursor) {
  1408. return;
  1409. }
  1410. if (cursor == mouse->def_cursor) {
  1411. return;
  1412. }
  1413. if (cursor == mouse->cur_cursor) {
  1414. SDL_SetCursor(mouse->def_cursor);
  1415. }
  1416. for (prev = NULL, curr = mouse->cursors; curr;
  1417. prev = curr, curr = curr->next) {
  1418. if (curr == cursor) {
  1419. if (prev) {
  1420. prev->next = curr->next;
  1421. } else {
  1422. mouse->cursors = curr->next;
  1423. }
  1424. if (mouse->FreeCursor && curr->internal) {
  1425. mouse->FreeCursor(curr);
  1426. } else {
  1427. SDL_free(curr);
  1428. }
  1429. return;
  1430. }
  1431. }
  1432. }
  1433. bool SDL_ShowCursor(void)
  1434. {
  1435. SDL_Mouse *mouse = SDL_GetMouse();
  1436. if (mouse->warp_emulation_active) {
  1437. SDL_SetRelativeMouseMode(false);
  1438. mouse->warp_emulation_active = false;
  1439. }
  1440. if (!mouse->cursor_visible) {
  1441. mouse->cursor_visible = true;
  1442. SDL_RedrawCursor();
  1443. }
  1444. return true;
  1445. }
  1446. bool SDL_HideCursor(void)
  1447. {
  1448. SDL_Mouse *mouse = SDL_GetMouse();
  1449. if (mouse->cursor_visible) {
  1450. mouse->cursor_visible = false;
  1451. SDL_RedrawCursor();
  1452. }
  1453. return true;
  1454. }
  1455. bool SDL_CursorVisible(void)
  1456. {
  1457. SDL_Mouse *mouse = SDL_GetMouse();
  1458. return mouse->cursor_visible;
  1459. }