SDL_mouse.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2013 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_config.h"
  19. /* General mouse handling code for SDL */
  20. #include "SDL_assert.h"
  21. #include "SDL_events.h"
  22. #include "SDL_events_c.h"
  23. #include "default_cursor.h"
  24. #include "../video/SDL_sysvideo.h"
  25. /* #define DEBUG_MOUSE */
  26. /* The mouse state */
  27. static SDL_Mouse SDL_mouse;
  28. static int
  29. SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
  30. /* Public functions */
  31. int
  32. SDL_MouseInit(void)
  33. {
  34. SDL_Mouse *mouse = SDL_GetMouse();
  35. mouse->cursor_shown = SDL_TRUE;
  36. return (0);
  37. }
  38. void
  39. SDL_SetDefaultCursor(SDL_Cursor * cursor)
  40. {
  41. SDL_Mouse *mouse = SDL_GetMouse();
  42. mouse->def_cursor = cursor;
  43. if (!mouse->cur_cursor) {
  44. SDL_SetCursor(cursor);
  45. }
  46. }
  47. SDL_Mouse *
  48. SDL_GetMouse(void)
  49. {
  50. return &SDL_mouse;
  51. }
  52. SDL_Window *
  53. SDL_GetMouseFocus(void)
  54. {
  55. SDL_Mouse *mouse = SDL_GetMouse();
  56. return mouse->focus;
  57. }
  58. void
  59. SDL_ResetMouse(void)
  60. {
  61. SDL_Mouse *mouse = SDL_GetMouse();
  62. Uint8 i;
  63. #ifdef DEBUG_MOUSE
  64. printf("Resetting mouse\n");
  65. #endif
  66. for (i = 1; i <= sizeof(mouse->buttonstate)*8; ++i) {
  67. if (mouse->buttonstate & SDL_BUTTON(i)) {
  68. SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, i);
  69. }
  70. }
  71. SDL_assert(mouse->buttonstate == 0);
  72. }
  73. void
  74. SDL_SetMouseFocus(SDL_Window * window)
  75. {
  76. SDL_Mouse *mouse = SDL_GetMouse();
  77. if (mouse->focus == window) {
  78. return;
  79. }
  80. /* Actually, this ends up being a bad idea, because most operating
  81. systems have an implicit grab when you press the mouse button down
  82. so you can drag things out of the window and then get the mouse up
  83. when it happens. So, #if 0...
  84. */
  85. #if 0
  86. if (mouse->focus && !window) {
  87. /* We won't get anymore mouse messages, so reset mouse state */
  88. SDL_ResetMouse();
  89. }
  90. #endif
  91. /* See if the current window has lost focus */
  92. if (mouse->focus) {
  93. SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_LEAVE, 0, 0);
  94. }
  95. mouse->focus = window;
  96. if (mouse->focus) {
  97. SDL_SendWindowEvent(mouse->focus, SDL_WINDOWEVENT_ENTER, 0, 0);
  98. }
  99. /* Update cursor visibility */
  100. SDL_SetCursor(NULL);
  101. }
  102. /* Check to see if we need to synthesize focus events */
  103. static SDL_bool
  104. SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
  105. {
  106. SDL_Mouse *mouse = SDL_GetMouse();
  107. int w, h;
  108. SDL_bool inWindow;
  109. SDL_GetWindowSize(window, &w, &h);
  110. if (x < 0 || y < 0 || x >= w || y >= h) {
  111. inWindow = SDL_FALSE;
  112. } else {
  113. inWindow = SDL_TRUE;
  114. }
  115. /* Linux doesn't give you mouse events outside your window unless you grab
  116. the pointer.
  117. Windows doesn't give you mouse events outside your window unless you call
  118. SetCapture().
  119. Both of these are slightly scary changes, so for now we'll punt and if the
  120. mouse leaves the window you'll lose mouse focus and reset button state.
  121. */
  122. #ifdef SUPPORT_DRAG_OUTSIDE_WINDOW
  123. if (!inWindow && !buttonstate) {
  124. #else
  125. if (!inWindow) {
  126. #endif
  127. if (window == mouse->focus) {
  128. #ifdef DEBUG_MOUSE
  129. printf("Mouse left window, synthesizing move & focus lost event\n");
  130. #endif
  131. SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
  132. SDL_SetMouseFocus(NULL);
  133. }
  134. return SDL_FALSE;
  135. }
  136. if (window != mouse->focus) {
  137. #ifdef DEBUG_MOUSE
  138. printf("Mouse entered window, synthesizing focus gain & move event\n");
  139. #endif
  140. SDL_SetMouseFocus(window);
  141. SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y);
  142. }
  143. return SDL_TRUE;
  144. }
  145. int
  146. SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
  147. {
  148. if (window && !relative) {
  149. SDL_Mouse *mouse = SDL_GetMouse();
  150. if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) {
  151. return 0;
  152. }
  153. }
  154. return SDL_PrivateSendMouseMotion(window, mouseID, relative, x, y);
  155. }
  156. static int
  157. SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y)
  158. {
  159. SDL_Mouse *mouse = SDL_GetMouse();
  160. int posted;
  161. int xrel;
  162. int yrel;
  163. int x_max = 0, y_max = 0;
  164. /* relative motion is calculated regarding the system cursor last position */
  165. if (relative) {
  166. xrel = x;
  167. yrel = y;
  168. x = (mouse->last_x + x);
  169. y = (mouse->last_y + y);
  170. } else {
  171. xrel = x - mouse->last_x;
  172. yrel = y - mouse->last_y;
  173. }
  174. /* Drop events that don't change state */
  175. if (!xrel && !yrel) {
  176. #ifdef DEBUG_MOUSE
  177. printf("Mouse event didn't change state - dropped!\n");
  178. #endif
  179. return 0;
  180. }
  181. /* Update internal mouse coordinates */
  182. if (mouse->relative_mode == SDL_FALSE) {
  183. mouse->x = x;
  184. mouse->y = y;
  185. } else {
  186. mouse->x += xrel;
  187. mouse->y += yrel;
  188. }
  189. SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
  190. --x_max;
  191. --y_max;
  192. /* make sure that the pointers find themselves inside the windows */
  193. if (mouse->x > x_max) {
  194. mouse->x = x_max;
  195. }
  196. if (mouse->x < 0) {
  197. mouse->x = 0;
  198. }
  199. if (mouse->y > y_max) {
  200. mouse->y = y_max;
  201. }
  202. if (mouse->y < 0) {
  203. mouse->y = 0;
  204. }
  205. mouse->xdelta += xrel;
  206. mouse->ydelta += yrel;
  207. /* Move the mouse cursor, if needed */
  208. if (mouse->cursor_shown && !mouse->relative_mode &&
  209. mouse->MoveCursor && mouse->cur_cursor) {
  210. mouse->MoveCursor(mouse->cur_cursor);
  211. }
  212. /* Post the event, if desired */
  213. posted = 0;
  214. if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) {
  215. SDL_Event event;
  216. event.motion.type = SDL_MOUSEMOTION;
  217. event.motion.windowID = mouse->focus ? mouse->focus->id : 0;
  218. event.motion.which = mouseID;
  219. event.motion.state = mouse->buttonstate;
  220. event.motion.x = mouse->x;
  221. event.motion.y = mouse->y;
  222. event.motion.xrel = xrel;
  223. event.motion.yrel = yrel;
  224. posted = (SDL_PushEvent(&event) > 0);
  225. }
  226. /* Use unclamped values if we're getting events outside the window */
  227. mouse->last_x = x;
  228. mouse->last_y = y;
  229. return posted;
  230. }
  231. int
  232. SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
  233. {
  234. SDL_Mouse *mouse = SDL_GetMouse();
  235. int posted;
  236. Uint32 type;
  237. Uint32 buttonstate = mouse->buttonstate;
  238. /* Figure out which event to perform */
  239. switch (state) {
  240. case SDL_PRESSED:
  241. type = SDL_MOUSEBUTTONDOWN;
  242. buttonstate |= SDL_BUTTON(button);
  243. break;
  244. case SDL_RELEASED:
  245. type = SDL_MOUSEBUTTONUP;
  246. buttonstate &= ~SDL_BUTTON(button);
  247. break;
  248. default:
  249. /* Invalid state -- bail */
  250. return 0;
  251. }
  252. /* We do this after calculating buttonstate so button presses gain focus */
  253. if (window && state == SDL_PRESSED) {
  254. SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
  255. }
  256. if (buttonstate == mouse->buttonstate) {
  257. /* Ignore this event, no state change */
  258. return 0;
  259. }
  260. mouse->buttonstate = buttonstate;
  261. /* Post the event, if desired */
  262. posted = 0;
  263. if (SDL_GetEventState(type) == SDL_ENABLE) {
  264. SDL_Event event;
  265. event.type = type;
  266. event.button.windowID = mouse->focus ? mouse->focus->id : 0;
  267. event.button.which = mouseID;
  268. event.button.state = state;
  269. event.button.button = button;
  270. event.button.x = mouse->x;
  271. event.button.y = mouse->y;
  272. posted = (SDL_PushEvent(&event) > 0);
  273. }
  274. /* We do this after dispatching event so button releases can lose focus */
  275. if (window && state == SDL_RELEASED) {
  276. SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
  277. }
  278. return posted;
  279. }
  280. int
  281. SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y)
  282. {
  283. SDL_Mouse *mouse = SDL_GetMouse();
  284. int posted;
  285. if (window) {
  286. SDL_SetMouseFocus(window);
  287. }
  288. if (!x && !y) {
  289. return 0;
  290. }
  291. /* Post the event, if desired */
  292. posted = 0;
  293. if (SDL_GetEventState(SDL_MOUSEWHEEL) == SDL_ENABLE) {
  294. SDL_Event event;
  295. event.type = SDL_MOUSEWHEEL;
  296. event.wheel.windowID = mouse->focus ? mouse->focus->id : 0;
  297. event.wheel.which = mouseID;
  298. event.wheel.x = x;
  299. event.wheel.y = y;
  300. posted = (SDL_PushEvent(&event) > 0);
  301. }
  302. return posted;
  303. }
  304. void
  305. SDL_MouseQuit(void)
  306. {
  307. SDL_Cursor *cursor, *next;
  308. SDL_Mouse *mouse = SDL_GetMouse();
  309. SDL_ShowCursor(1);
  310. cursor = mouse->cursors;
  311. while (cursor) {
  312. next = cursor->next;
  313. SDL_FreeCursor(cursor);
  314. cursor = next;
  315. }
  316. if (mouse->def_cursor && mouse->FreeCursor) {
  317. mouse->FreeCursor(mouse->def_cursor);
  318. }
  319. SDL_zerop(mouse);
  320. }
  321. Uint32
  322. SDL_GetMouseState(int *x, int *y)
  323. {
  324. SDL_Mouse *mouse = SDL_GetMouse();
  325. if (x) {
  326. *x = mouse->x;
  327. }
  328. if (y) {
  329. *y = mouse->y;
  330. }
  331. return mouse->buttonstate;
  332. }
  333. Uint32
  334. SDL_GetRelativeMouseState(int *x, int *y)
  335. {
  336. SDL_Mouse *mouse = SDL_GetMouse();
  337. if (x) {
  338. *x = mouse->xdelta;
  339. }
  340. if (y) {
  341. *y = mouse->ydelta;
  342. }
  343. mouse->xdelta = 0;
  344. mouse->ydelta = 0;
  345. return mouse->buttonstate;
  346. }
  347. void
  348. SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
  349. {
  350. SDL_Mouse *mouse = SDL_GetMouse();
  351. if ( window == NULL )
  352. window = mouse->focus;
  353. if ( window == NULL )
  354. return;
  355. if (mouse->WarpMouse) {
  356. mouse->WarpMouse(window, x, y);
  357. } else {
  358. SDL_SendMouseMotion(window, mouse->mouseID, 0, x, y);
  359. }
  360. }
  361. int
  362. SDL_SetRelativeMouseMode(SDL_bool enabled)
  363. {
  364. SDL_Mouse *mouse = SDL_GetMouse();
  365. SDL_Window *focusWindow = SDL_GetKeyboardFocus();
  366. int original_x = mouse->x, original_y = mouse->y;
  367. if (enabled == mouse->relative_mode) {
  368. return 0;
  369. }
  370. if (!mouse->SetRelativeMouseMode) {
  371. return SDL_Unsupported();
  372. }
  373. if (enabled && focusWindow) {
  374. /* Center it in the focused window to prevent clicks from going through
  375. * to background windows.
  376. */
  377. SDL_SetMouseFocus(focusWindow);
  378. SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2);
  379. }
  380. if (mouse->SetRelativeMouseMode(enabled) < 0) {
  381. return -1;
  382. }
  383. /* Set the relative mode */
  384. mouse->relative_mode = enabled;
  385. if (enabled) {
  386. /* Save the expected mouse position */
  387. mouse->original_x = original_x;
  388. mouse->original_y = original_y;
  389. } else if (mouse->focus) {
  390. /* Restore the expected mouse position */
  391. SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y);
  392. }
  393. /* Flush pending mouse motion */
  394. SDL_FlushEvent(SDL_MOUSEMOTION);
  395. /* Update cursor visibility */
  396. SDL_SetCursor(NULL);
  397. return 0;
  398. }
  399. SDL_bool
  400. SDL_GetRelativeMouseMode()
  401. {
  402. SDL_Mouse *mouse = SDL_GetMouse();
  403. return mouse->relative_mode;
  404. }
  405. SDL_Cursor *
  406. SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
  407. int w, int h, int hot_x, int hot_y)
  408. {
  409. SDL_Surface *surface;
  410. SDL_Cursor *cursor;
  411. int x, y;
  412. Uint32 *pixel;
  413. Uint8 datab = 0, maskb = 0;
  414. const Uint32 black = 0xFF000000;
  415. const Uint32 white = 0xFFFFFFFF;
  416. const Uint32 transparent = 0x00000000;
  417. /* Make sure the width is a multiple of 8 */
  418. w = ((w + 7) & ~7);
  419. /* Create the surface from a bitmap */
  420. surface = SDL_CreateRGBSurface(0, w, h, 32,
  421. 0x00FF0000,
  422. 0x0000FF00,
  423. 0x000000FF,
  424. 0xFF000000);
  425. if (!surface) {
  426. return NULL;
  427. }
  428. for (y = 0; y < h; ++y) {
  429. pixel = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch);
  430. for (x = 0; x < w; ++x) {
  431. if ((x % 8) == 0) {
  432. datab = *data++;
  433. maskb = *mask++;
  434. }
  435. if (maskb & 0x80) {
  436. *pixel++ = (datab & 0x80) ? black : white;
  437. } else {
  438. *pixel++ = (datab & 0x80) ? black : transparent;
  439. }
  440. datab <<= 1;
  441. maskb <<= 1;
  442. }
  443. }
  444. cursor = SDL_CreateColorCursor(surface, hot_x, hot_y);
  445. SDL_FreeSurface(surface);
  446. return cursor;
  447. }
  448. SDL_Cursor *
  449. SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)
  450. {
  451. SDL_Mouse *mouse = SDL_GetMouse();
  452. SDL_Surface *temp = NULL;
  453. SDL_Cursor *cursor;
  454. if (!surface) {
  455. SDL_SetError("Passed NULL cursor surface");
  456. return NULL;
  457. }
  458. if (!mouse->CreateCursor) {
  459. SDL_SetError("Cursors are not currently supported");
  460. return NULL;
  461. }
  462. /* Sanity check the hot spot */
  463. if ((hot_x < 0) || (hot_y < 0) ||
  464. (hot_x >= surface->w) || (hot_y >= surface->h)) {
  465. SDL_SetError("Cursor hot spot doesn't lie within cursor");
  466. return NULL;
  467. }
  468. if (surface->format->format != SDL_PIXELFORMAT_ARGB8888) {
  469. temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
  470. if (!temp) {
  471. return NULL;
  472. }
  473. surface = temp;
  474. }
  475. cursor = mouse->CreateCursor(surface, hot_x, hot_y);
  476. if (cursor) {
  477. cursor->next = mouse->cursors;
  478. mouse->cursors = cursor;
  479. }
  480. SDL_FreeSurface(temp);
  481. return cursor;
  482. }
  483. SDL_Cursor *
  484. SDL_CreateSystemCursor(SDL_SystemCursor id)
  485. {
  486. SDL_Mouse *mouse = SDL_GetMouse();
  487. SDL_Cursor *cursor;
  488. if (!mouse->CreateSystemCursor) {
  489. SDL_SetError("CreateSystemCursor is not currently supported");
  490. return NULL;
  491. }
  492. cursor = mouse->CreateSystemCursor(id);
  493. if (cursor) {
  494. cursor->next = mouse->cursors;
  495. mouse->cursors = cursor;
  496. }
  497. return cursor;
  498. }
  499. /* SDL_SetCursor(NULL) can be used to force the cursor redraw,
  500. if this is desired for any reason. This is used when setting
  501. the video mode and when the SDL window gains the mouse focus.
  502. */
  503. void
  504. SDL_SetCursor(SDL_Cursor * cursor)
  505. {
  506. SDL_Mouse *mouse = SDL_GetMouse();
  507. /* Set the new cursor */
  508. if (cursor) {
  509. /* Make sure the cursor is still valid for this mouse */
  510. if (cursor != mouse->def_cursor) {
  511. SDL_Cursor *found;
  512. for (found = mouse->cursors; found; found = found->next) {
  513. if (found == cursor) {
  514. break;
  515. }
  516. }
  517. if (!found) {
  518. SDL_SetError("Cursor not associated with the current mouse");
  519. return;
  520. }
  521. }
  522. mouse->cur_cursor = cursor;
  523. } else {
  524. if (mouse->focus) {
  525. cursor = mouse->cur_cursor;
  526. } else {
  527. cursor = mouse->def_cursor;
  528. }
  529. }
  530. if (cursor && mouse->cursor_shown && !mouse->relative_mode) {
  531. if (mouse->ShowCursor) {
  532. mouse->ShowCursor(cursor);
  533. }
  534. } else {
  535. if (mouse->ShowCursor) {
  536. mouse->ShowCursor(NULL);
  537. }
  538. }
  539. }
  540. SDL_Cursor *
  541. SDL_GetCursor(void)
  542. {
  543. SDL_Mouse *mouse = SDL_GetMouse();
  544. if (!mouse) {
  545. return NULL;
  546. }
  547. return mouse->cur_cursor;
  548. }
  549. SDL_Cursor *
  550. SDL_GetDefaultCursor(void)
  551. {
  552. SDL_Mouse *mouse = SDL_GetMouse();
  553. if (!mouse) {
  554. return NULL;
  555. }
  556. return mouse->def_cursor;
  557. }
  558. void
  559. SDL_FreeCursor(SDL_Cursor * cursor)
  560. {
  561. SDL_Mouse *mouse = SDL_GetMouse();
  562. SDL_Cursor *curr, *prev;
  563. if (!cursor) {
  564. return;
  565. }
  566. if (cursor == mouse->def_cursor) {
  567. return;
  568. }
  569. if (cursor == mouse->cur_cursor) {
  570. SDL_SetCursor(mouse->def_cursor);
  571. }
  572. for (prev = NULL, curr = mouse->cursors; curr;
  573. prev = curr, curr = curr->next) {
  574. if (curr == cursor) {
  575. if (prev) {
  576. prev->next = curr->next;
  577. } else {
  578. mouse->cursors = curr->next;
  579. }
  580. if (mouse->FreeCursor) {
  581. mouse->FreeCursor(curr);
  582. }
  583. return;
  584. }
  585. }
  586. }
  587. int
  588. SDL_ShowCursor(int toggle)
  589. {
  590. SDL_Mouse *mouse = SDL_GetMouse();
  591. SDL_bool shown;
  592. if (!mouse) {
  593. return 0;
  594. }
  595. shown = mouse->cursor_shown;
  596. if (toggle >= 0) {
  597. if (toggle) {
  598. mouse->cursor_shown = SDL_TRUE;
  599. } else {
  600. mouse->cursor_shown = SDL_FALSE;
  601. }
  602. if (mouse->cursor_shown != shown) {
  603. SDL_SetCursor(NULL);
  604. }
  605. }
  606. return shown;
  607. }
  608. /* vi: set ts=4 sw=4 expandtab: */