ui_sdl.h 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. /*
  2. * ui_sdl.h
  3. * https://gitlab.com/bztsrc/smgui
  4. *
  5. * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
  20. * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  22. * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. *
  24. * @brief SDL2/3 backend for SMGUI
  25. */
  26. #ifndef UI_SDL_H
  27. #define UI_SDL_H 1
  28. #ifndef UI_BACKEND
  29. #define UI_BACKEND 1
  30. #else
  31. #error "An UI backend has already been included"
  32. #endif
  33. #include <stdint.h>
  34. #include <string.h>
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #define SDL_ENABLE_OLD_NAMES
  38. #include <SDL.h>
  39. #if SDL_VERSION_ATLEAST(3,0,0)
  40. #define SDL_ENABLE 1
  41. #define SDL_DISABLE 0
  42. #define cdevice jdevice
  43. #define caxis jaxis
  44. #define cbutton button
  45. #endif
  46. #ifndef UI_H
  47. #include <ui.h>
  48. #undef UI_H
  49. #endif
  50. #ifdef __cplusplus
  51. extern "C" {
  52. #endif
  53. /**
  54. * Gamepad structure
  55. */
  56. typedef struct {
  57. int id, btn, lx, ly, rx, ry;
  58. #if SDL_VERSION_ATLEAST(3,0,0)
  59. SDL_Gamepad *pad;
  60. #else
  61. SDL_GameController *pad;
  62. #endif
  63. } ui_gamepad_t;
  64. /**
  65. * The UI backend structure, it's backend specific
  66. */
  67. typedef struct ui_backend_s {
  68. void *ctx;
  69. SDL_Window *window;
  70. SDL_Renderer *renderer;
  71. ui_gamepad_t controller[4];
  72. int btn, nodraw, fullscr, x, y, w, h;
  73. char path[4096];
  74. } ui_backend_t;
  75. /**
  76. * Toggle fullscreen
  77. */
  78. int ui_backend_fullscreen(ui_backend_t *bck)
  79. {
  80. SDL_Event event;
  81. #if SDL_VERSION_ATLEAST(3,0,0)
  82. const SDL_DisplayMode *dm;
  83. #else
  84. SDL_DisplayMode dm;
  85. #endif
  86. if(!bck || !bck->window) return UI_ERR_BADINP;
  87. if((bck->fullscr ^= 1)) {
  88. bck->w = ((ui_t*)bck->ctx)->screen.w;
  89. bck->h = ((ui_t*)bck->ctx)->screen.h;
  90. SDL_GetWindowPosition(bck->window, &bck->x, &bck->y);
  91. SDL_RenderClear(bck->renderer);
  92. SDL_RenderPresent(bck->renderer);
  93. #if SDL_VERSION_ATLEAST(3,0,0)
  94. dm = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
  95. SDL_SetWindowSize(bck->window, dm->w, dm->h);
  96. _ui_resize(bck->ctx, dm->w, dm->h);
  97. #else
  98. SDL_GetDesktopDisplayMode(0, &dm);
  99. SDL_SetWindowSize(bck->window, dm.w, dm.h);
  100. _ui_resize(bck->ctx, dm.w, dm.h);
  101. #endif
  102. SDL_SetWindowFullscreen(bck->window, 1);
  103. while(SDL_PollEvent(&event));
  104. SDL_WarpMouseInWindow(bck->window, ((ui_t*)bck->ctx)->mousex + bck->x, ((ui_t*)bck->ctx)->mousey + bck->y);
  105. } else {
  106. SDL_SetWindowFullscreen(bck->window, 0);
  107. SDL_SetWindowSize(bck->window, bck->w, bck->h);
  108. _ui_resize(bck->ctx, bck->w, bck->h);
  109. while(SDL_PollEvent(&event));
  110. SDL_WarpMouseInWindow(bck->window, ((ui_t*)bck->ctx)->mousex - bck->x, ((ui_t*)bck->ctx)->mousey - bck->y);
  111. }
  112. return UI_OK;
  113. }
  114. /**
  115. * Set window focus
  116. */
  117. int ui_backend_focus(ui_backend_t *bck)
  118. {
  119. if(!bck || !bck->window) return UI_ERR_BADINP;
  120. SDL_ShowWindow(bck->window);
  121. SDL_RaiseWindow(bck->window);
  122. return UI_OK;
  123. }
  124. /**
  125. * Set window title
  126. */
  127. int ui_backend_settitle(ui_backend_t *bck, char *title)
  128. {
  129. if(!bck || !title || !*title || !bck->window) return UI_ERR_BADINP;
  130. SDL_SetWindowTitle(bck->window, title);
  131. return UI_OK;
  132. }
  133. /**
  134. * Get the clipboard's text
  135. */
  136. char *ui_backend_getclipboard(ui_backend_t *bck)
  137. {
  138. const char *str = SDL_GetClipboardText();
  139. char *ret = NULL;
  140. (void)bck;
  141. if(str && *str && (ret = (char*)malloc(strlen(str) + 1)))
  142. strcpy(ret, str);
  143. return ret;
  144. }
  145. /**
  146. * Set the clipboard's text
  147. */
  148. int ui_backend_setclipboard(ui_backend_t *bck, char *str)
  149. {
  150. (void)bck;
  151. SDL_SetClipboardText(str);
  152. return UI_OK;
  153. }
  154. /**
  155. * Hide hardware mouse cursor
  156. */
  157. int ui_backend_hidecursor(ui_backend_t *bck)
  158. {
  159. if(!bck || !bck->window) return UI_ERR_BADINP;
  160. #if SDL_VERSION_ATLEAST(3,0,0)
  161. SDL_HideCursor();
  162. #else
  163. SDL_ShowCursor(SDL_DISABLE);
  164. #endif
  165. return UI_OK;
  166. }
  167. /**
  168. * Show hardware mouse cursor
  169. */
  170. int ui_backend_showcursor(ui_backend_t *bck)
  171. {
  172. if(!bck || !bck->window) return UI_ERR_BADINP;
  173. #if SDL_VERSION_ATLEAST(3,0,0)
  174. SDL_ShowCursor();
  175. #else
  176. SDL_ShowCursor(SDL_ENABLE);
  177. #endif
  178. return UI_OK;
  179. }
  180. /**
  181. * Hide OS-native on-screen keyboard
  182. */
  183. int ui_backend_hideosk(ui_backend_t *bck)
  184. {
  185. (void)bck;
  186. #if (!defined(__ANDROID__) && !defined(__IOS__)) || !defined(UI_TEXTOSK)
  187. SDL_StopTextInput();
  188. #endif
  189. return UI_OK;
  190. }
  191. /**
  192. * Show OS-native on-screen keyboard
  193. */
  194. int ui_backend_showosk(ui_backend_t *bck)
  195. {
  196. (void)bck;
  197. #if (!defined(__ANDROID__) && !defined(__IOS__)) || !defined(UI_TEXTOSK)
  198. SDL_StartTextInput();
  199. #endif
  200. return UI_OK;
  201. }
  202. /**
  203. * Workaround a stupid iOS and Android bug
  204. */
  205. int _ui_backend_stupidios(void *data, SDL_Event *event)
  206. {
  207. ui_backend_t *bck = (ui_backend_t*)data;
  208. if(bck)
  209. switch(event->type) {
  210. case SDL_APP_WILLENTERBACKGROUND: bck->nodraw = 1; break;
  211. case SDL_APP_WILLENTERFOREGROUND: bck->nodraw = 0; break;
  212. }
  213. return 1;
  214. }
  215. /**
  216. * Initialize the backend
  217. */
  218. int ui_backend_init(ui_t *ctx, char *title, int w, int h, ui_image_t *icon)
  219. {
  220. ui_backend_t *bck;
  221. int i;
  222. SDL_Surface *srf;
  223. if(!ctx || !title || !*title || w < 1 || h < 1) return UI_ERR_BADINP;
  224. if(!(ctx->bck = bck = (ui_backend_t*)realloc(ctx->bck, sizeof(ui_backend_t)))) return UI_ERR_NOMEM;
  225. memset(bck, 0, sizeof(ui_backend_t));
  226. bck->ctx = ctx;
  227. for(i = 0; i < 4; i++)
  228. bck->controller[i].id = -1;
  229. #ifndef UI_BACKEND_INITIALIZED
  230. if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS|SDL_INIT_GAMECONTROLLER)) return UI_ERR_BACKEND;
  231. #endif
  232. #if SDL_VERSION_ATLEAST(3,0,0)
  233. SDL_SetGamepadEventsEnabled(SDL_ENABLE);
  234. bck->window = SDL_CreateWindow(title, w, h, 0);
  235. #else
  236. SDL_GameControllerEventState(SDL_ENABLE);
  237. bck->window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_RESIZABLE);
  238. #endif
  239. if(!bck->window) {
  240. memset(bck, 0, sizeof(ui_backend_t));
  241. #ifndef UI_BACKEND_INITIALIZED
  242. SDL_Quit();
  243. #endif
  244. return UI_ERR_BACKEND;
  245. }
  246. SDL_AddEventWatch(_ui_backend_stupidios, bck);
  247. #if SDL_VERSION_ATLEAST(3,0,0)
  248. bck->renderer = SDL_CreateRenderer(bck->window, NULL, SDL_RENDERER_ACCELERATED);
  249. if(!bck->renderer) bck->renderer = SDL_CreateRenderer(bck->window, NULL, SDL_RENDERER_SOFTWARE);
  250. #else
  251. bck->renderer = SDL_CreateRenderer(bck->window, -1, SDL_RENDERER_ACCELERATED);
  252. if(!bck->renderer) bck->renderer = SDL_CreateRenderer(bck->window, -1, SDL_RENDERER_SOFTWARE);
  253. #endif
  254. if(!bck->renderer) {
  255. SDL_DestroyWindow(bck->window);
  256. memset(bck, 0, sizeof(ui_backend_t));
  257. #ifndef UI_BACKEND_INITIALIZED
  258. SDL_Quit();
  259. #endif
  260. return UI_ERR_BACKEND;
  261. }
  262. if(icon && icon->buf) {
  263. #if SDL_VERSION_ATLEAST(3,0,0)
  264. srf = SDL_CreateSurfaceFrom((Uint32 *)icon->buf, icon->w, icon->h, icon->p, SDL_PIXELFORMAT_RGBA8888);
  265. #else
  266. srf = SDL_CreateRGBSurfaceFrom((Uint32 *)icon->buf, icon->w, icon->h, 32, icon->p, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
  267. #endif
  268. if(srf) {
  269. SDL_SetWindowIcon(bck->window, srf);
  270. #if SDL_VERSION_ATLEAST(3,0,0)
  271. SDL_DestroySurface(srf);
  272. #else
  273. SDL_FreeSurface(srf);
  274. #endif
  275. }
  276. }
  277. ui_backend_focus(bck);
  278. return UI_OK;
  279. }
  280. /**
  281. * Return a backend specific window handle
  282. */
  283. void *ui_backend_getwindow(ui_backend_t *bck)
  284. {
  285. return bck ? bck->window : NULL;
  286. }
  287. /**
  288. * Main event handler
  289. */
  290. int ui_backend_event(ui_backend_t *bck)
  291. {
  292. ui_event_t *evt;
  293. char keystr[8] = { 0 };
  294. int i, m, key;
  295. SDL_Event event = { 0 };
  296. #if SDL_VERSION_ATLEAST(3,0,0)
  297. float mx, my;
  298. #else
  299. int mx, my;
  300. #endif
  301. if(!bck || !bck->window) return UI_ERR_BADINP;
  302. /* check mouse position */
  303. SDL_GetMouseState(&mx, &my);
  304. _ui_setmouse(bck->ctx, (int)mx, (int)my);
  305. while(SDL_PollEvent(&event)) {
  306. switch(event.type) {
  307. #if SDL_VERSION_ATLEAST(3,0,0)
  308. case SDL_EVENT_QUIT: return 1;
  309. case SDL_EVENT_WINDOW_RESIZED: {
  310. #else
  311. case SDL_QUIT: return 1;
  312. case SDL_WINDOWEVENT:
  313. switch(event.window.event) {
  314. case SDL_WINDOWEVENT_CLOSE: return 1;
  315. case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_SIZE_CHANGED:
  316. #endif
  317. _ui_resize(bck->ctx, event.window.data1, event.window.data2);
  318. break;
  319. }
  320. break;
  321. case SDL_MOUSEBUTTONDOWN:
  322. m = 0;
  323. switch(event.button.button) {
  324. case SDL_BUTTON_LEFT: m = UI_BTN_L; break;
  325. case SDL_BUTTON_MIDDLE: m = UI_BTN_M; break;
  326. case SDL_BUTTON_RIGHT: m = UI_BTN_R; break;
  327. }
  328. bck->btn |= m;
  329. if((evt = _ui_evtslot(bck->ctx))) {
  330. evt->x = (int)mx;
  331. evt->y = (int)my;
  332. evt->btn = bck->btn;
  333. evt->type = UI_EVT_MOUSE;
  334. }
  335. break;
  336. case SDL_MOUSEBUTTONUP:
  337. m = 0;
  338. switch(event.button.button) {
  339. case SDL_BUTTON_LEFT: m = UI_BTN_L; break;
  340. case SDL_BUTTON_MIDDLE: m = UI_BTN_M; break;
  341. case SDL_BUTTON_RIGHT: m = UI_BTN_R; break;
  342. }
  343. bck->btn &= ~m;
  344. if((evt = _ui_evtslot(bck->ctx))) {
  345. evt->x = (int)mx;
  346. evt->y = (int)my;
  347. evt->btn = bck->btn | UI_BTN_RELEASE;
  348. evt->type = UI_EVT_MOUSE;
  349. }
  350. break;
  351. case SDL_MOUSEWHEEL:
  352. if((evt = _ui_evtslot(bck->ctx))) {
  353. evt->x = (int)mx;
  354. evt->y = (int)my;
  355. evt->btn = bck->btn | (event.wheel.y > 0 ? UI_BTN_U : 0) | (event.wheel.y < 0 ? UI_BTN_D : 0) |
  356. (event.wheel.x > 0 ? UI_BTN_A : 0) | (event.wheel.x < 0 ? UI_BTN_B : 0);
  357. evt->type = UI_EVT_MOUSE;
  358. }
  359. break;
  360. case SDL_KEYDOWN:
  361. switch((key = event.key.keysym.sym)) {
  362. case SDLK_LSHIFT: bck->btn |= UI_BTN_SHIFT; memcpy(keystr, "LShift", 6); break;
  363. case SDLK_RSHIFT: bck->btn |= UI_BTN_SHIFT; memcpy(keystr, "RShift", 6); break;
  364. case SDLK_LCTRL: bck->btn |= UI_BTN_CONTROL; memcpy(keystr, "LCtrl", 5); break;
  365. case SDLK_RCTRL: bck->btn |= UI_BTN_CONTROL; memcpy(keystr, "RCtrl", 5); break;
  366. case SDLK_LALT: bck->btn |= UI_BTN_ALT; memcpy(keystr, "LAlt", 4); break;
  367. case SDLK_RALT: bck->btn |= UI_BTN_ALT; memcpy(keystr, "RAlt", 4); break;
  368. case SDLK_LGUI: bck->btn |= UI_BTN_GUI; memcpy(keystr, "LGui", 4); break;
  369. case SDLK_RGUI: bck->btn |= UI_BTN_GUI; memcpy(keystr, "RGui", 4); break;
  370. case SDLK_ESCAPE: keystr[0] = 0x1b; break;
  371. case SDLK_BACKSPACE: keystr[0] = '\b'; break;
  372. case SDLK_TAB: keystr[0] = '\t'; break;
  373. case SDLK_RETURN: keystr[0] = '\n'; break;
  374. case SDLK_F1: memcpy(keystr, "F1", 2); break;
  375. case SDLK_F2: memcpy(keystr, "F2", 2); break;
  376. case SDLK_F3: memcpy(keystr, "F3", 2); break;
  377. case SDLK_F4: memcpy(keystr, "F4", 2); break;
  378. case SDLK_F5: memcpy(keystr, "F5", 2); break;
  379. case SDLK_F6: memcpy(keystr, "F6", 2); break;
  380. case SDLK_F7: memcpy(keystr, "F7", 2); break;
  381. case SDLK_F8: memcpy(keystr, "F8", 2); break;
  382. case SDLK_F9: memcpy(keystr, "F9", 2); break;
  383. case SDLK_F10: memcpy(keystr, "F10", 3); break;
  384. case SDLK_F11: memcpy(keystr, "F11", 3); break;
  385. case SDLK_F12: memcpy(keystr, "F12", 3); break;
  386. case SDLK_PRINTSCREEN: memcpy(keystr, "PrScr", 5); break;
  387. case SDLK_CAPSLOCK: memcpy(keystr, "CLck", 4); break;
  388. case SDLK_SCROLLLOCK: memcpy(keystr, "SLck", 4); break;
  389. case SDLK_NUMLOCKCLEAR: memcpy(keystr, "NLck", 4); break;
  390. case SDLK_UP: memcpy(keystr, "Up", 2); break;
  391. case SDLK_DOWN: memcpy(keystr, "Down", 4); break;
  392. case SDLK_LEFT: memcpy(keystr, "Left", 4); break;
  393. case SDLK_RIGHT: memcpy(keystr, "Right", 5); break;
  394. case SDLK_HOME: memcpy(keystr, "Home", 4); break;
  395. case SDLK_END: memcpy(keystr, "End", 3); break;
  396. case SDLK_PAGEUP: memcpy(keystr, "PgUp", 4); break;
  397. case SDLK_PAGEDOWN: memcpy(keystr, "PgDown", 6); break;
  398. case SDLK_INSERT: memcpy(keystr, "Ins", 3); break;
  399. case SDLK_DELETE: memcpy(keystr, "Del", 3); break;
  400. case SDLK_APP1: memcpy(keystr, "Int1", 4); break;
  401. case SDLK_APP2: memcpy(keystr, "Int2", 4); break;
  402. case SDLK_MENU: memcpy(keystr, "Menu", 4); break;
  403. case SDLK_z: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Undo", 4); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  404. case SDLK_y: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Redo", 4); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  405. case SDLK_x: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Cut", 3); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  406. case SDLK_c: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Copy", 4); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  407. case SDLK_v: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Paste", 5); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  408. case SDLK_SELECT: memcpy(keystr, "Select", 6); break;
  409. case SDLK_STOP: memcpy(keystr, "Stop", 4); break;
  410. case SDLK_AGAIN: memcpy(keystr, "Redo", 4); break;
  411. case SDLK_UNDO: memcpy(keystr, "Undo", 4); break;
  412. case SDLK_CUT: memcpy(keystr, "Cut", 3); break;
  413. case SDLK_COPY: memcpy(keystr, "Copy", 4); break;
  414. case SDLK_PASTE: memcpy(keystr, "Paste", 5); break;
  415. case SDLK_FIND: memcpy(keystr, "Find", 4); break;
  416. case SDLK_MUTE: memcpy(keystr, "Mute", 4); break;
  417. case SDLK_VOLUMEUP: memcpy(keystr, "Vol+", 4); break;
  418. case SDLK_VOLUMEDOWN: memcpy(keystr, "Vol-", 4); break;
  419. default: if((bck->btn & ~UI_BTN_SHIFT) && key >= SDLK_a && key <= SDLK_z) keystr[0] = key; break;
  420. }
  421. if(keystr[0] && (evt = _ui_evtslot(bck->ctx))) {
  422. memcpy(evt->key, keystr, 8);
  423. evt->btn = (bck->btn & 0xf00000);
  424. evt->type = UI_EVT_KEY;
  425. }
  426. break;
  427. case SDL_KEYUP:
  428. switch(event.key.keysym.sym) {
  429. case SDLK_LSHIFT: bck->btn &= ~UI_BTN_SHIFT; memcpy(keystr, "LShift", 6); break;
  430. case SDLK_RSHIFT: bck->btn &= ~UI_BTN_SHIFT; memcpy(keystr, "RShift", 6); break;
  431. case SDLK_LCTRL: bck->btn &= ~UI_BTN_CONTROL; memcpy(keystr, "LCtrl", 5); break;
  432. case SDLK_RCTRL: bck->btn &= ~UI_BTN_CONTROL; memcpy(keystr, "RCtrl", 5); break;
  433. case SDLK_LALT: bck->btn &= ~UI_BTN_ALT; memcpy(keystr, "LAlt", 4); break;
  434. case SDLK_RALT: bck->btn &= ~UI_BTN_ALT; memcpy(keystr, "RAlt", 4); break;
  435. case SDLK_LGUI: bck->btn &= ~UI_BTN_GUI; memcpy(keystr, "LGui", 4); break;
  436. case SDLK_RGUI: bck->btn &= ~UI_BTN_GUI; memcpy(keystr, "RGui", 4); break;
  437. }
  438. if(keystr[0] && (evt = _ui_evtslot(bck->ctx))) {
  439. memcpy(evt->key, keystr, 8);
  440. evt->btn = (bck->btn & 0xf00000) | UI_BTN_RELEASE;
  441. evt->type = UI_EVT_KEY;
  442. }
  443. break;
  444. case SDL_TEXTINPUT:
  445. if(!(bck->btn & ~UI_BTN_SHIFT) && (uint8_t)event.text.text[0] >= 32 && (evt = _ui_evtslot(bck->ctx))) {
  446. memcpy(evt->key, &event.text.text, 8);
  447. evt->btn = (bck->btn & 0xf00000);
  448. evt->type = UI_EVT_KEY;
  449. }
  450. break;
  451. case SDL_CONTROLLERDEVICEADDED:
  452. for(i = 0; i < 4 && bck->controller[i].id != (int)event.cdevice.which; i++);
  453. if(i >= 4) for(i = 0; i < 4 && bck->controller[i].id != -1; i++);
  454. if(i < 4) {
  455. if(bck->controller[i].pad) SDL_GameControllerClose(bck->controller[i].pad);
  456. bck->controller[i].pad = SDL_GameControllerOpen(event.cdevice.which);
  457. bck->controller[i].id = event.cdevice.which;
  458. }
  459. break;
  460. case SDL_CONTROLLERDEVICEREMOVED:
  461. for(i = 0; i < 4 && bck->controller[i].id != (int)event.cdevice.which; i++);
  462. if(i < 4) {
  463. if(bck->controller[i].pad) SDL_GameControllerClose(bck->controller[i].pad);
  464. bck->controller[i].pad = NULL;
  465. bck->controller[i].id = -1;
  466. }
  467. break;
  468. case SDL_CONTROLLERBUTTONDOWN:
  469. for(i = 0; i < 4 && bck->controller[i].id != (int)event.cbutton.which; i++);
  470. if(i < 4) {
  471. m = 0;
  472. switch(event.cbutton.button) {
  473. case SDL_CONTROLLER_BUTTON_DPAD_LEFT: m |= UI_BTN_L; break;
  474. case SDL_CONTROLLER_BUTTON_DPAD_UP: m |= UI_BTN_U; break;
  475. case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: m |= UI_BTN_R; break;
  476. case SDL_CONTROLLER_BUTTON_DPAD_DOWN: m |= UI_BTN_D; break;
  477. case SDL_CONTROLLER_BUTTON_A: m |= UI_BTN_A; break;
  478. case SDL_CONTROLLER_BUTTON_B: m |= UI_BTN_B; break;
  479. case SDL_CONTROLLER_BUTTON_X: m |= UI_BTN_X; break;
  480. case SDL_CONTROLLER_BUTTON_Y: m |= UI_BTN_Y; break;
  481. case SDL_CONTROLLER_BUTTON_BACK: m |= UI_BTN_BA; break;
  482. case SDL_CONTROLLER_BUTTON_GUIDE: m |= UI_BTN_GU; break;
  483. case SDL_CONTROLLER_BUTTON_START: m |= UI_BTN_ST; break;
  484. case SDL_CONTROLLER_BUTTON_LEFTSTICK: m |= UI_BTN_LT; break;
  485. case SDL_CONTROLLER_BUTTON_RIGHTSTICK: m |= UI_BTN_RT; break;
  486. case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: m |= UI_BTN_LS; break;
  487. case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: m |= UI_BTN_RS; break;
  488. }
  489. bck->controller[i].btn |= m;
  490. if((evt = _ui_evtslot(bck->ctx))) {
  491. evt->key[0] = i;
  492. evt->btn = (bck->btn & 0xf00000) | bck->controller[i].btn;
  493. evt->x = bck->controller[i].lx;
  494. evt->y = bck->controller[i].ly;
  495. evt->rx = bck->controller[i].rx;
  496. evt->ry = bck->controller[i].ry;
  497. evt->type = UI_EVT_GAMEPAD;
  498. }
  499. }
  500. break;
  501. case SDL_CONTROLLERBUTTONUP:
  502. for(i = 0; i < 4 && bck->controller[i].id != (int)event.cbutton.which; i++);
  503. if(i < 4) {
  504. m = 0;
  505. switch(event.cbutton.button) {
  506. case SDL_CONTROLLER_BUTTON_DPAD_LEFT: m |= UI_BTN_L; break;
  507. case SDL_CONTROLLER_BUTTON_DPAD_UP: m |= UI_BTN_U; break;
  508. case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: m |= UI_BTN_R; break;
  509. case SDL_CONTROLLER_BUTTON_DPAD_DOWN: m |= UI_BTN_D; break;
  510. case SDL_CONTROLLER_BUTTON_A: m |= UI_BTN_A; break;
  511. case SDL_CONTROLLER_BUTTON_B: m |= UI_BTN_B; break;
  512. case SDL_CONTROLLER_BUTTON_X: m |= UI_BTN_X; break;
  513. case SDL_CONTROLLER_BUTTON_Y: m |= UI_BTN_Y; break;
  514. case SDL_CONTROLLER_BUTTON_BACK: m |= UI_BTN_BA; break;
  515. case SDL_CONTROLLER_BUTTON_GUIDE: m |= UI_BTN_GU; break;
  516. case SDL_CONTROLLER_BUTTON_START: m |= UI_BTN_ST; break;
  517. case SDL_CONTROLLER_BUTTON_LEFTSTICK: m |= UI_BTN_LT; break;
  518. case SDL_CONTROLLER_BUTTON_RIGHTSTICK: m |= UI_BTN_RT; break;
  519. case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: m |= UI_BTN_LS; break;
  520. case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: m |= UI_BTN_RS; break;
  521. }
  522. bck->controller[i].btn &= ~m;
  523. if((evt = _ui_evtslot(bck->ctx))) {
  524. evt->key[0] = i;
  525. evt->btn = (bck->btn & 0xf00000) | bck->controller[i].btn | UI_BTN_RELEASE;
  526. evt->x = bck->controller[i].lx;
  527. evt->y = bck->controller[i].ly;
  528. evt->rx = bck->controller[i].rx;
  529. evt->ry = bck->controller[i].ry;
  530. evt->type = UI_EVT_GAMEPAD;
  531. }
  532. }
  533. break;
  534. case SDL_CONTROLLERAXISMOTION:
  535. for(i = 0; i < 4 && bck->controller[i].id != (int)event.cbutton.which; i++);
  536. if(i < 4) {
  537. switch(event.caxis.axis) {
  538. case SDL_CONTROLLER_AXIS_LEFTX: bck->controller[i].lx = event.caxis.value; break;
  539. case SDL_CONTROLLER_AXIS_LEFTY: bck->controller[i].ly = event.caxis.value; break;
  540. case SDL_CONTROLLER_AXIS_RIGHTX: bck->controller[i].rx = event.caxis.value; break;
  541. case SDL_CONTROLLER_AXIS_RIGHTY: bck->controller[i].ry = event.caxis.value; break;
  542. }
  543. if((evt = _ui_evtslot(bck->ctx))) {
  544. evt->key[0] = i;
  545. evt->btn = (bck->btn & 0xf00000) | bck->controller[i].btn;
  546. evt->x = bck->controller[i].lx;
  547. evt->y = bck->controller[i].ly;
  548. evt->rx = bck->controller[i].rx;
  549. evt->ry = bck->controller[i].ry;
  550. evt->type = UI_EVT_GAMEPAD;
  551. }
  552. }
  553. break;
  554. case SDL_DROPFILE:
  555. if(event.drop.file) {
  556. if((evt = _ui_evtslot(bck->ctx))) {
  557. evt->x = (int)mx;
  558. evt->y = (int)my;
  559. strncpy(bck->path, !memcmp(event.drop.file, "file://", 7) ? event.drop.file + 7 : event.drop.file, sizeof(bck->path) - 1);
  560. evt->fn = bck->path;
  561. evt->btn = (bck->btn & 0xf00000);
  562. evt->type = UI_EVT_DROP;
  563. }
  564. SDL_free(event.drop.file);
  565. }
  566. break;
  567. }
  568. }
  569. return UI_OK;
  570. }
  571. /**
  572. * Main redraw handler
  573. */
  574. int ui_backend_redraw(ui_backend_t *bck)
  575. {
  576. ui_t *ctx;
  577. int i, p;
  578. uint8_t *s, *d;
  579. SDL_Texture *screen;
  580. #if SDL_VERSION_ATLEAST(3,0,0)
  581. SDL_FRect rect;
  582. #else
  583. SDL_Rect rect;
  584. #endif
  585. if(!bck || !bck->window) return UI_ERR_BADINP;
  586. if(bck->nodraw) return UI_OK;
  587. ctx = (ui_t*)bck->ctx;
  588. if(!ctx || !ctx->screen.buf) return UI_ERR_BADINP;
  589. if(!(screen = SDL_CreateTexture(bck->renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, ctx->screen.w, ctx->screen.h)))
  590. return UI_ERR_BACKEND;
  591. #if SDL_VERSION_ATLEAST(2,0,12)
  592. SDL_SetTextureScaleMode(screen,
  593. #if SDL_VERSION_ATLEAST(3,0,0)
  594. SDL_SCALEMODE_NEAREST
  595. #else
  596. SDL_ScaleModeNearest
  597. #endif
  598. );
  599. #endif
  600. SDL_SetTextureBlendMode(screen, SDL_BLENDMODE_BLEND);
  601. SDL_LockTexture(screen, NULL, (void**)&d, &p);
  602. for(i = 0, s = ctx->screen.buf; i < ctx->screen.h; i++, s += ctx->screen.p, d += p)
  603. memcpy(d, s, ctx->screen.p);
  604. SDL_UnlockTexture(screen);
  605. rect.x = rect.y = 0; rect.w = ctx->screen.w; rect.h = ctx->screen.h;
  606. SDL_RenderCopy(bck->renderer, screen, &rect, &rect);
  607. SDL_DestroyTexture(screen);
  608. #ifndef UI_BACKEND_NOFLUSH
  609. SDL_RenderPresent(bck->renderer);
  610. #endif
  611. return UI_OK;
  612. }
  613. /**
  614. * Free backend resources
  615. */
  616. int ui_backend_free(ui_backend_t *bck)
  617. {
  618. if(!bck) return UI_ERR_BADINP;
  619. if(bck->fullscr) ui_backend_fullscreen(bck);
  620. /* this crashes sometimes... but only sometimes... We are about to exit so should be freed anyway */
  621. /* if(bck->renderer) SDL_DestroyRenderer(bck->renderer);*/
  622. if(bck->window) SDL_DestroyWindow(bck->window);
  623. free(bck);
  624. #ifndef UI_BACKEND_INITIALIZED
  625. SDL_Quit();
  626. #endif
  627. return UI_OK;
  628. }
  629. #ifdef __cplusplus
  630. }
  631. #endif
  632. #endif /* UI_SDL_H */