ui_glfw.h 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. /*
  2. * ui_glfw.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 GLFW backend for SMGUI
  25. */
  26. #ifndef UI_GLFW_H
  27. #define UI_GLFW_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. #ifdef __WIN32__
  38. #include <GL/glew.h>
  39. #else
  40. #define GL_GLEXT_PROTOTYPES
  41. #include <GL/gl.h>
  42. #include <GL/glext.h>
  43. #endif
  44. #include <GLFW/glfw3.h>
  45. #ifndef UI_H
  46. #include <ui.h>
  47. #undef UI_H
  48. #endif
  49. #ifdef __cplusplus
  50. extern "C" {
  51. #endif
  52. /**
  53. * Gamepad structure
  54. */
  55. typedef struct {
  56. int id, btn, lx, ly, rx, ry;
  57. } ui_gamepad_t;
  58. /**
  59. * The UI backend structure, it's backend specific
  60. */
  61. typedef struct ui_backend_s {
  62. void *ctx;
  63. GLFWwindow *window;
  64. GLuint screen;
  65. GLuint program, VAO, VBO, EBO;
  66. GLint mloc, ploc, sloc;
  67. ui_gamepad_t controller[4];
  68. int btn, fullscr, w, h;
  69. } ui_backend_t;
  70. /**
  71. * Error callback
  72. */
  73. void ui_glfw_error(int error, const char *msg)
  74. {
  75. fprintf(stderr, "glfw error %d: %s\n", error, msg);
  76. exit(1);
  77. }
  78. /**
  79. * Mouse button event callback
  80. */
  81. void ui_glfw_mouse(GLFWwindow *window, int button, int action, int mods)
  82. {
  83. int m = 0;
  84. ui_event_t *evt;
  85. ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
  86. (void)mods;
  87. if(bck && (evt = _ui_evtslot(bck->ctx))) {
  88. switch(button) {
  89. case GLFW_MOUSE_BUTTON_LEFT: m = UI_BTN_L; break;
  90. case GLFW_MOUSE_BUTTON_MIDDLE: m = UI_BTN_M; break;
  91. case GLFW_MOUSE_BUTTON_RIGHT: m = UI_BTN_R; break;
  92. }
  93. if(action == GLFW_PRESS)
  94. bck->btn |= m;
  95. else
  96. bck->btn &= ~m;
  97. ui_getmouse(bck->ctx, &evt->x, &evt->y);
  98. evt->btn = bck->btn | (action != GLFW_PRESS ? UI_BTN_RELEASE : 0);
  99. evt->type = UI_EVT_MOUSE;
  100. }
  101. }
  102. /**
  103. * Scrolling event callback
  104. */
  105. void ui_glfw_scroll(GLFWwindow *window, double xdelta, double ydelta)
  106. {
  107. ui_event_t *evt;
  108. ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
  109. if(bck && (evt = _ui_evtslot(bck->ctx))) {
  110. ui_getmouse(bck->ctx, &evt->x, &evt->y);
  111. evt->btn = bck->btn | (ydelta > 0.0 ? UI_BTN_U : 0) | (ydelta < 0.0 ? UI_BTN_D : 0) | (xdelta > 0.0 ? UI_BTN_A : 0) | (xdelta < 0.0 ? UI_BTN_B : 0);
  112. evt->type = UI_EVT_MOUSE;
  113. }
  114. }
  115. /**
  116. * Drop file event callback
  117. */
  118. void ui_glfw_dropfile(GLFWwindow *window, int count, const char **fn)
  119. {
  120. ui_event_t *evt;
  121. ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
  122. int i;
  123. for(i = 0; fn && i < count; i++)
  124. if(fn[i] && bck && (evt = _ui_evtslot(bck->ctx))) {
  125. ui_getmouse(bck->ctx, &evt->x, &evt->y);
  126. evt->fn = !memcmp(fn[i], "file://", 7) ? fn[i] + 7 : fn[i];
  127. evt->btn = (bck->btn & 0xf00000);
  128. evt->type = UI_EVT_DROP;
  129. }
  130. }
  131. /**
  132. * Process a keyboard event callback
  133. */
  134. void ui_glfw_key(GLFWwindow *window, int key, int scancode, int action, int mods)
  135. {
  136. ui_event_t *evt;
  137. ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
  138. char keystr[8] = { 0 };
  139. (void)mods;
  140. if(!bck) return;
  141. if(key == GLFW_KEY_UNKNOWN && scancode >= 119 && scancode <= 151) key = scancode;
  142. if(action == GLFW_PRESS)
  143. switch(key) {
  144. case GLFW_KEY_LEFT_SHIFT: bck->btn |= UI_BTN_SHIFT; memcpy(keystr, "LShift", 6); break;
  145. case GLFW_KEY_RIGHT_SHIFT: bck->btn |= UI_BTN_SHIFT; memcpy(keystr, "RShift", 6); break;
  146. case GLFW_KEY_LEFT_CONTROL: bck->btn |= UI_BTN_CONTROL; memcpy(keystr, "LCtrl", 5); break;
  147. case GLFW_KEY_RIGHT_CONTROL: bck->btn |= UI_BTN_CONTROL; memcpy(keystr, "RCtrl", 5); break;
  148. case GLFW_KEY_LEFT_ALT: bck->btn |= UI_BTN_ALT; memcpy(keystr, "LAlt", 4); break;
  149. case GLFW_KEY_RIGHT_ALT: bck->btn |= UI_BTN_ALT; memcpy(keystr, "RAlt", 4); break;
  150. case GLFW_KEY_LEFT_SUPER: bck->btn |= UI_BTN_GUI; memcpy(keystr, "LGui", 4); break;
  151. case GLFW_KEY_RIGHT_SUPER: bck->btn |= UI_BTN_GUI; memcpy(keystr, "RGui", 4); break;
  152. case GLFW_KEY_ESCAPE: keystr[0] = 0x1b; break;
  153. case GLFW_KEY_BACKSPACE: keystr[0] = 8; break;
  154. case GLFW_KEY_TAB: keystr[0] = '\t'; break;
  155. case GLFW_KEY_ENTER: keystr[0] = '\n'; break;
  156. case GLFW_KEY_F1: memcpy(keystr, "F1", 2); break;
  157. case GLFW_KEY_F2: memcpy(keystr, "F2", 2); break;
  158. case GLFW_KEY_F3: memcpy(keystr, "F3", 2); break;
  159. case GLFW_KEY_F4: memcpy(keystr, "F4", 2); break;
  160. case GLFW_KEY_F5: memcpy(keystr, "F5", 2); break;
  161. case GLFW_KEY_F6: memcpy(keystr, "F6", 2); break;
  162. case GLFW_KEY_F7: memcpy(keystr, "F7", 2); break;
  163. case GLFW_KEY_F8: memcpy(keystr, "F8", 2); break;
  164. case GLFW_KEY_F9: memcpy(keystr, "F9", 2); break;
  165. case GLFW_KEY_F10: memcpy(keystr, "F10", 3); break;
  166. case GLFW_KEY_F11: memcpy(keystr, "F11", 3); break;
  167. case GLFW_KEY_F12: memcpy(keystr, "F12", 3); break;
  168. case GLFW_KEY_PRINT_SCREEN: memcpy(keystr, "PrScr", 5); break;
  169. case GLFW_KEY_CAPS_LOCK: memcpy(keystr, "CLck", 4); break;
  170. case GLFW_KEY_SCROLL_LOCK: memcpy(keystr, "SLck", 4); break;
  171. case GLFW_KEY_NUM_LOCK: memcpy(keystr, "NLck", 4); break;
  172. case GLFW_KEY_UP: memcpy(keystr, "Up", 2); break;
  173. case GLFW_KEY_DOWN: memcpy(keystr, "Down", 4); break;
  174. case GLFW_KEY_LEFT: memcpy(keystr, "Left", 4); break;
  175. case GLFW_KEY_RIGHT: memcpy(keystr, "Right", 5); break;
  176. case GLFW_KEY_HOME: memcpy(keystr, "Home", 4); break;
  177. case GLFW_KEY_END: memcpy(keystr, "End", 3); break;
  178. case GLFW_KEY_PAGE_UP: memcpy(keystr, "PgUp", 4); break;
  179. case GLFW_KEY_PAGE_DOWN: memcpy(keystr, "PgDown", 6); break;
  180. case GLFW_KEY_INSERT: memcpy(keystr, "Ins", 3); break;
  181. case GLFW_KEY_DELETE: memcpy(keystr, "Del", 3); break;
  182. case GLFW_KEY_WORLD_1: memcpy(keystr, "Int1", 4); break;
  183. case GLFW_KEY_WORLD_2: memcpy(keystr, "Int2", 4); break;
  184. case GLFW_KEY_MENU: memcpy(keystr, "Menu", 4); break;
  185. case GLFW_KEY_Z: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Undo", 4); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  186. case GLFW_KEY_Y: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Redo", 4); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  187. case GLFW_KEY_X: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Cut", 3); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  188. case GLFW_KEY_C: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Copy", 4); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  189. case GLFW_KEY_V: if(bck->btn == UI_BTN_CONTROL) memcpy(keystr, "Paste", 5); else if(bck->btn & ~UI_BTN_SHIFT) keystr[0] = key; break;
  190. case 119: memcpy(keystr, "Select", 6); break;
  191. case 120: memcpy(keystr, "Stop", 4); break;
  192. case 121: memcpy(keystr, "Redo", 4); break;
  193. case 122: memcpy(keystr, "Undo", 4); break;
  194. case 123: memcpy(keystr, "Cut", 3); break;
  195. case 124: memcpy(keystr, "Copy", 4); break;
  196. case 125: memcpy(keystr, "Paste", 5); break;
  197. case 126: memcpy(keystr, "Find", 4); break;
  198. case 127: memcpy(keystr, "Mute", 4); break;
  199. case 128: memcpy(keystr, "Vol+", 4); break;
  200. case 129: memcpy(keystr, "Vol-", 4); break;
  201. case 135: memcpy(keystr, "Int1", 4); break;
  202. case 136: memcpy(keystr, "Int2", 4); break;
  203. case 137: memcpy(keystr, "Int3", 4); break;
  204. case 138: memcpy(keystr, "Int4", 4); break;
  205. case 139: memcpy(keystr, "Int5", 4); break;
  206. case 140: memcpy(keystr, "Int6", 4); break;
  207. case 141: memcpy(keystr, "Int7", 4); break;
  208. case 142: memcpy(keystr, "Int8", 4); break;
  209. case 144: memcpy(keystr, "Lang1", 5); break;
  210. case 145: memcpy(keystr, "Lang2", 5); break;
  211. case 146: memcpy(keystr, "Lang3", 5); break;
  212. case 147: memcpy(keystr, "Lang4", 5); break;
  213. case 148: memcpy(keystr, "Lang5", 5); break;
  214. case 149: memcpy(keystr, "Lang6", 5); break;
  215. case 150: memcpy(keystr, "Lang7", 5); break;
  216. case 151: memcpy(keystr, "Lang8", 5); break;
  217. default: if((bck->btn & ~UI_BTN_SHIFT) && key >= GLFW_KEY_A && key <= GLFW_KEY_Z) keystr[0] = key; break;
  218. }
  219. if(action == GLFW_RELEASE)
  220. switch(key) {
  221. case GLFW_KEY_LEFT_SHIFT: bck->btn &= ~UI_BTN_SHIFT; memcpy(keystr, "LShift", 6); break;
  222. case GLFW_KEY_RIGHT_SHIFT: bck->btn &= ~UI_BTN_SHIFT; memcpy(keystr, "RShift", 6); break;
  223. case GLFW_KEY_LEFT_CONTROL: bck->btn &= ~UI_BTN_CONTROL; memcpy(keystr, "LCtrl", 5); break;
  224. case GLFW_KEY_RIGHT_CONTROL: bck->btn &= ~UI_BTN_CONTROL; memcpy(keystr, "RCtrl", 5); break;
  225. case GLFW_KEY_LEFT_ALT: bck->btn &= ~UI_BTN_ALT; memcpy(keystr, "LAlt", 4); break;
  226. case GLFW_KEY_RIGHT_ALT: bck->btn &= ~UI_BTN_ALT; memcpy(keystr, "RAlt", 4); break;
  227. case GLFW_KEY_LEFT_SUPER: bck->btn &= ~UI_BTN_GUI; memcpy(keystr, "LGui", 4); break;
  228. case GLFW_KEY_RIGHT_SUPER: bck->btn &= ~UI_BTN_GUI; memcpy(keystr, "RGui", 4); break;
  229. }
  230. if(keystr[0] && (evt = _ui_evtslot(bck->ctx))) {
  231. memcpy(evt->key, keystr, 8);
  232. evt->btn = (bck->btn & 0xf00000) | (action != GLFW_PRESS ? UI_BTN_RELEASE : 0);
  233. evt->type = UI_EVT_KEY;
  234. }
  235. }
  236. /**
  237. * Text input event callback
  238. */
  239. void ui_glfw_input(GLFWwindow *window, unsigned int unicode)
  240. {
  241. ui_event_t *evt;
  242. ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
  243. if(unicode >= 32 && bck && (evt = _ui_evtslot(bck->ctx))) {
  244. if(unicode<0x80) { evt->key[0]=unicode; } else
  245. if(unicode<0x800) { evt->key[0]=((unicode>>6)&0x1F)|0xC0; evt->key[1]=(unicode&0x3F)|0x80; } else
  246. if(unicode<0x10000) { evt->key[0]=((unicode>>12)&0x0F)|0xE0; evt->key[1]=((unicode>>6)&0x3F)|0x80; evt->key[2]=(unicode&0x3F)|0x80; }
  247. else { evt->key[0]=((unicode>>18)&0x07)|0xF0; evt->key[1]=((unicode>>12)&0x3F)|0x80; evt->key[2]=((unicode>>6)&0x3F)|0x80; evt->key[3]=(unicode&0x3F)|0x80; }
  248. evt->btn = (bck->btn & 0xf00000);
  249. evt->type = UI_EVT_KEY;
  250. }
  251. }
  252. /**
  253. * Toggle fullscreen
  254. */
  255. int ui_backend_fullscreen(ui_backend_t *bck)
  256. {
  257. GLFWmonitor *monitor;
  258. const GLFWvidmode *vidmode;
  259. if(!bck || !bck->window) return UI_ERR_BADINP;
  260. if(!(monitor = glfwGetPrimaryMonitor()) || !(vidmode = glfwGetVideoMode(monitor))) return UI_ERR_BACKEND;
  261. if((bck->fullscr ^= 1)) {
  262. bck->w = ((ui_t*)bck->ctx)->screen.w;
  263. bck->h = ((ui_t*)bck->ctx)->screen.h;
  264. glfwSetWindowSize(bck->window, vidmode->width, vidmode->height);
  265. glfwSetWindowMonitor(bck->window, monitor, 0, 0, vidmode->width, vidmode->height, vidmode->refreshRate);
  266. _ui_resize(bck->ctx, vidmode->width, vidmode->height);
  267. } else {
  268. glfwSetWindowMonitor(bck->window, NULL, (vidmode->width - bck->w) / 2, (vidmode->height - bck->h) / 2, bck->w, bck->h, 0);
  269. _ui_resize(bck->ctx, bck->w, bck->h);
  270. }
  271. return UI_OK;
  272. }
  273. /**
  274. * Set window focus
  275. */
  276. int ui_backend_focus(ui_backend_t *bck)
  277. {
  278. if(!bck || !bck->window) return UI_ERR_BADINP;
  279. glfwFocusWindow(bck->window);
  280. return UI_OK;
  281. }
  282. /**
  283. * Set window title
  284. */
  285. int ui_backend_settitle(ui_backend_t *bck, char *title)
  286. {
  287. if(!bck || !title || !*title || !bck->window) return UI_ERR_BADINP;
  288. glfwSetWindowTitle(bck->window, title);
  289. return UI_OK;
  290. }
  291. /**
  292. * Get the clipboard's text
  293. */
  294. char *ui_backend_getclipboard(ui_backend_t *bck)
  295. {
  296. const char *str = glfwGetClipboardString(NULL);
  297. char *ret = NULL;
  298. (void)bck;
  299. if(str && *str && (ret = (char*)malloc(strlen(str) + 1)))
  300. strcpy(ret, str);
  301. return ret;
  302. }
  303. /**
  304. * Set the clipboard's text
  305. */
  306. int ui_backend_setclipboard(ui_backend_t *bck, char *str)
  307. {
  308. (void)bck;
  309. glfwSetClipboardString(NULL, str);
  310. return UI_OK;
  311. }
  312. /**
  313. * Hide hardware mouse cursor
  314. */
  315. int ui_backend_hidecursor(ui_backend_t *bck)
  316. {
  317. if(!bck || !bck->window) return UI_ERR_BADINP;
  318. glfwSetInputMode(bck->window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  319. return UI_OK;
  320. }
  321. /**
  322. * Show hardware mouse cursor
  323. */
  324. int ui_backend_showcursor(ui_backend_t *bck)
  325. {
  326. if(!bck || !bck->window) return UI_ERR_BADINP;
  327. glfwSetInputMode(bck->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  328. return UI_OK;
  329. }
  330. /**
  331. * Hide OS-native on-screen keyboard
  332. */
  333. int ui_backend_hideosk(ui_backend_t *bck)
  334. {
  335. (void)bck;
  336. return UI_OK;
  337. }
  338. /**
  339. * Show OS-native on-screen keyboard
  340. */
  341. int ui_backend_showosk(ui_backend_t *bck)
  342. {
  343. (void)bck;
  344. return UI_OK;
  345. }
  346. /**
  347. * Initialize the backend
  348. */
  349. int ui_backend_init(ui_t *ctx, char *title, int w, int h, ui_image_t *icon)
  350. {
  351. ui_backend_t *bck;
  352. GLFWimage gicon;
  353. #ifndef UI_GLFW_NOSHADER
  354. #ifndef UI_GLFW_GLES2
  355. const GLchar *vstr = "#version 330 core\nlayout(location = 0) in vec4 apos;\nout vec2 vtex;\nuniform mat4 mvp;\nvoid main(){gl_Position=mvp*vec4(apos.x,apos.y,0.0,1.0);vtex=vec2(apos.z,apos.w);}";
  356. const GLchar *fstr = "#version 330 core\nin vec2 vtex;\nuniform sampler2D stex;\nvoid main(){gl_FragColor=texture2D(stex,vtex);}";
  357. #else
  358. const GLchar *vstr = "attribute vec4 apos;\nvarying vec2 vtex;\nuniform mat4 mvp;\nvoid main(){gl_Position=mvp*vec4(apos.x,apos.y,0.0,1.0);vtex=vec2(apos.z,apos.w);}";
  359. const GLchar *fstr = "precision mediump float;\nvarying vec2 vtex;\nuniform sampler2D stex;\nvoid main(){gl_FragColor=texture2D(stex,vtex);}";
  360. #endif
  361. GLuint vshdr = 0, fshdr = 0;
  362. GLint ret;
  363. #endif
  364. int i, j;
  365. if(!ctx || !title || !*title || w < 1 || h < 1) return UI_ERR_BADINP;
  366. if(!(ctx->bck = bck = (ui_backend_t*)realloc(ctx->bck, sizeof(ui_backend_t)))) return UI_ERR_NOMEM;
  367. memset(bck, 0, sizeof(ui_backend_t));
  368. bck->ctx = ctx;
  369. #ifndef UI_BACKEND_INITIALIZED
  370. if(!glfwInit()) return UI_ERR_BACKEND;
  371. #endif
  372. glfwSetErrorCallback(ui_glfw_error);
  373. #if !defined(UI_GLFW_NOSHADER) && defined(UI_GLFW_GLES2)
  374. /* we must set OpenGL ES 2.0 in GLFW too, otherwise shaders won't work */
  375. glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
  376. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  377. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  378. glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
  379. #endif
  380. /* common GLFW hints */
  381. glfwWindowHint(GLFW_SAMPLES, 0);
  382. glfwWindowHint(GLFW_DEPTH_BITS, 16);
  383. glfwWindowHint(GLFW_RED_BITS, 8);
  384. glfwWindowHint(GLFW_GREEN_BITS, 8);
  385. glfwWindowHint(GLFW_BLUE_BITS, 8);
  386. glfwWindowHint(GLFW_ALPHA_BITS, 8);
  387. /* create window */
  388. if(!(bck->window = glfwCreateWindow(w, h, title, NULL, NULL))) {
  389. memset(bck, 0, sizeof(ui_backend_t));
  390. #ifndef UI_BACKEND_INITIALIZED
  391. glfwTerminate();
  392. #endif
  393. return UI_ERR_BACKEND;
  394. }
  395. if(icon && icon->w > 0 && icon->h > 0 && icon->buf && (gicon.pixels = (uint8_t*)malloc(icon->w * icon->h * 4))) {
  396. gicon.width = icon->w; gicon.height = icon->h;
  397. for(j = icon->w * 4, i = 0; i < icon->h; i++)
  398. memcpy(gicon.pixels + i * j, icon->buf + i * icon->p, j);
  399. glfwSetWindowIcon(bck->window, 1, &gicon);
  400. free(gicon.pixels);
  401. }
  402. glfwMakeContextCurrent(bck->window);
  403. glfwSetWindowUserPointer(bck->window, bck);
  404. /* load GL extensions. For some reason this most be called *after* the window is created */
  405. #ifdef GLAD_GL_H_
  406. if(!glCreateShader && !gladLoadGL(glfwGetProcAddress)) return UI_ERR_BACKEND;
  407. #elif defined(__GLEW_H__)
  408. if(!glCreateShader && glewInit() != GLEW_OK) return UI_ERR_BACKEND;
  409. #endif
  410. #ifndef UI_GLFW_NOSHADER
  411. /* compile and link shaders */
  412. if(!(vshdr = glCreateShader(GL_VERTEX_SHADER))) {
  413. serr: if(vshdr) glDeleteShader(vshdr);
  414. if(fshdr) glDeleteShader(fshdr);
  415. #ifndef UI_BACKEND_INITIALIZED
  416. glfwTerminate();
  417. #endif
  418. return UI_ERR_BACKEND;
  419. }
  420. glShaderSource(vshdr, 1, &vstr, NULL);
  421. glCompileShader(vshdr);
  422. glGetShaderiv(vshdr, GL_COMPILE_STATUS, &ret);
  423. if(!ret) goto serr;
  424. if(!(fshdr = glCreateShader(GL_FRAGMENT_SHADER))) goto serr;
  425. glShaderSource(fshdr, 1, &fstr, NULL);
  426. glCompileShader(fshdr);
  427. glGetShaderiv(fshdr, GL_COMPILE_STATUS, &ret);
  428. if(!ret) goto serr;
  429. if(!(bck->program = glCreateProgram())) goto serr;
  430. glAttachShader(bck->program, vshdr);
  431. glAttachShader(bck->program, fshdr);
  432. glLinkProgram(bck->program);
  433. glGetProgramiv(bck->program, GL_LINK_STATUS, &ret);
  434. if(!ret) { glDeleteProgram(bck->program); goto serr; }
  435. glDeleteShader(vshdr);
  436. glDeleteShader(fshdr);
  437. /* the texture sampler and the projection matrix is common */
  438. bck->sloc = glGetUniformLocation(bck->program, "stex");
  439. bck->mloc = glGetUniformLocation(bck->program, "mvp");
  440. #ifndef UI_GLFW_GLES2
  441. /* OpenGL 3.3 has VertexArrays, VertexBuffers and ElementBuffers */
  442. glGenVertexArrays(1, &bck->VAO);
  443. glBindVertexArray(bck->VAO);
  444. glGenBuffers(1, &bck->VBO);
  445. glBindBuffer(GL_ARRAY_BUFFER, bck->VBO);
  446. glGenBuffers(1, &bck->EBO);
  447. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bck->EBO);
  448. glEnableVertexAttribArray(0);
  449. #else
  450. /* OpenGL ES has only one attribute in the shader */
  451. bck->ploc = glGetAttribLocation(bck->program, "apos");
  452. glEnableVertexAttribArray(bck->ploc);
  453. #endif
  454. #endif
  455. /* set up texture */
  456. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  457. glGenTextures(1, &bck->screen);
  458. glBindTexture(GL_TEXTURE_2D, bck->screen);
  459. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  460. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  461. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  462. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  463. for(i = 0, j = GLFW_JOYSTICK_1; i < 4 && j < GLFW_JOYSTICK_LAST; j++)
  464. bck->controller[i++].id = glfwJoystickPresent(j) ? j : -1;
  465. #ifndef UI_GLFW_CUSTOMHOOKS
  466. glfwSetKeyCallback(bck->window, ui_glfw_key);
  467. glfwSetCharCallback(bck->window, ui_glfw_input);
  468. glfwSetMouseButtonCallback(bck->window, ui_glfw_mouse);
  469. glfwSetScrollCallback(bck->window, ui_glfw_scroll);
  470. glfwSetDropCallback(bck->window, ui_glfw_dropfile);
  471. #endif
  472. ui_backend_focus(bck);
  473. return UI_OK;
  474. }
  475. /**
  476. * Return a backend specific window handle
  477. */
  478. void *ui_backend_getwindow(ui_backend_t *bck)
  479. {
  480. return bck ? bck->window : NULL;
  481. }
  482. /**
  483. * Main event handler
  484. */
  485. int ui_backend_event(ui_backend_t *bck)
  486. {
  487. ui_event_t *evt;
  488. double mx, my;
  489. GLFWgamepadstate state;
  490. int i, w, h, m, lx, ly, rx, ry;
  491. if(!bck || !bck->window) return UI_ERR_BADINP;
  492. if(glfwWindowShouldClose(bck->window)) return 1;
  493. /* check mouse position */
  494. glfwGetCursorPos(bck->window, &mx, &my);
  495. _ui_setmouse(bck->ctx, (int)mx, (int)my);
  496. glfwGetFramebufferSize(bck->window, &w, &h);
  497. if(((ui_t*)bck->ctx)->screen.w != w || ((ui_t*)bck->ctx)->screen.h != h)
  498. _ui_resize(bck->ctx, w, h);
  499. glfwPollEvents();
  500. /* no events for gamepads, we must poll... */
  501. for(i = 0; i < 4; i++)
  502. if(bck->controller[i].id != -1 && glfwGetGamepadState(bck->controller[i].id, &state)) {
  503. m = 0;
  504. if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP]) m |= UI_BTN_U;
  505. if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT]) m |= UI_BTN_R;
  506. if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN]) m |= UI_BTN_D;
  507. if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT]) m |= UI_BTN_L;
  508. if(state.buttons[GLFW_GAMEPAD_BUTTON_A]) m |= UI_BTN_A;
  509. if(state.buttons[GLFW_GAMEPAD_BUTTON_B]) m |= UI_BTN_B;
  510. if(state.buttons[GLFW_GAMEPAD_BUTTON_X]) m |= UI_BTN_X;
  511. if(state.buttons[GLFW_GAMEPAD_BUTTON_Y]) m |= UI_BTN_Y;
  512. if(state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER]) m |= UI_BTN_LS;
  513. if(state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER]) m |= UI_BTN_RS;
  514. if(state.buttons[GLFW_GAMEPAD_BUTTON_BACK]) m |= UI_BTN_BA;
  515. if(state.buttons[GLFW_GAMEPAD_BUTTON_START]) m |= UI_BTN_ST;
  516. if(state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE]) m |= UI_BTN_GU;
  517. if(state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB]) m |= UI_BTN_LT;
  518. if(state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB]) m |= UI_BTN_RT;
  519. lx = state.axes[GLFW_GAMEPAD_AXIS_LEFT_X] * 32768.0; rx = state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X] * 32768.0;
  520. ly = state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y] * 32768.0; ry = state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y] * 32768.0;
  521. if(bck->controller[i].lx != lx || bck->controller[i].ly != ly ||
  522. bck->controller[i].rx != rx || bck->controller[i].ry != ry || bck->controller[i].btn != m) {
  523. if((evt = _ui_evtslot(bck->ctx))) {
  524. evt->key[0] = i;
  525. evt->btn = m | (bck->btn & 0xf00000) | (bck->controller[i].btn > m ? UI_BTN_RELEASE : 0);
  526. evt->x = lx;
  527. evt->y = ly;
  528. evt->rx = rx;
  529. evt->ry = ry;
  530. evt->type = UI_EVT_GAMEPAD;
  531. }
  532. bck->controller[i].btn = m;
  533. bck->controller[i].lx = lx;
  534. bck->controller[i].ly = ly;
  535. bck->controller[i].rx = rx;
  536. bck->controller[i].ry = ry;
  537. }
  538. }
  539. return UI_OK;
  540. }
  541. /**
  542. * Set up orthographic projection in a matrix
  543. */
  544. void _ui_glfw_ortho(GLfloat *m, float l, float r, float b, float t, float n, float f)
  545. {
  546. memset(m, 0, 16 * sizeof(GLfloat));
  547. m[ 0] = 2.0/(r-l);
  548. m[ 5] = 2.0/(t-b);
  549. m[10] = -2.0/(f-n);
  550. m[ 3] = -(r+l)/(r-l);
  551. m[ 7] = -(t+b)/(t-b);
  552. m[11] = -(f+n)/(f-n);
  553. m[15] = 1.0;
  554. }
  555. /**
  556. * Main redraw handler
  557. */
  558. int ui_backend_redraw(ui_backend_t *bck)
  559. {
  560. #ifndef UI_GLFW_NOSHADER
  561. static GLushort idx[] = { 0, 1, 2, 0, 2, 3 };
  562. GLfloat mvp[16] = { 0 }, vert[16] = { 0 };
  563. #endif
  564. int w, h, ww, hh;
  565. if(!bck || !bck->window) return UI_ERR_BADINP;
  566. glfwGetFramebufferSize(bck->window, &w, &h);
  567. glViewport(0, 0, w, h);
  568. glEnable(GL_BLEND);
  569. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  570. glActiveTexture(GL_TEXTURE0);
  571. glBindTexture(GL_TEXTURE_2D, bck->screen);
  572. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ((ui_t*)bck->ctx)->screen.w, ((ui_t*)bck->ctx)->screen.h, 0,
  573. GL_RGBA, GL_UNSIGNED_BYTE, ((ui_t*)bck->ctx)->screen.buf);
  574. ww = w >> 1; hh = h >> 1;
  575. #ifndef UI_GLFW_NOSHADER
  576. /* set up vertex buffer */
  577. vert[ 0] = -ww; vert[ 1] = -hh; vert[ 2] = 0; vert[ 3] = 0;
  578. vert[ 4] = -ww; vert[ 5] = hh; vert[ 6] = 0; vert[ 7] = 1.0;
  579. vert[ 8] = ww; vert[ 9] = hh; vert[10] = 1.0; vert[11] = 1.0;
  580. vert[12] = ww; vert[13] = -hh; vert[14] = 1.0; vert[15] = 0;
  581. glUseProgram(bck->program);
  582. glUniform1i(bck->sloc, 0);
  583. _ui_glfw_ortho(mvp, -ww, ww, hh, -hh, 0.0, 1.0);
  584. glUniformMatrix4fv(bck->mloc, 1, GL_FALSE, mvp);
  585. #ifndef UI_GLFW_GLES2
  586. /* OpenGL 3.3 bind buffers and draw (note: last argument is 0) */
  587. glBindVertexArray(bck->VAO);
  588. glBindBuffer(GL_ARRAY_BUFFER, bck->VBO);
  589. glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);
  590. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bck->EBO);
  591. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);
  592. glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
  593. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
  594. #else
  595. /* OpenGL ES set attribute and draw (note: last argument is idx) */
  596. glVertexAttribPointer(bck->ploc, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vert);
  597. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, idx);
  598. #endif
  599. #else
  600. /* Old-school, no shaders, no extensions */
  601. glMatrixMode(GL_PROJECTION);
  602. glLoadIdentity();
  603. glOrtho(-ww, ww, -hh, hh, 0.0, 1.0);
  604. glMatrixMode(GL_MODELVIEW);
  605. glLoadIdentity();
  606. glEnable(GL_TEXTURE_2D);
  607. glBegin(GL_QUADS);
  608. glVertex3f(-ww, -hh, 0); glTexCoord2f(0, 0);
  609. glVertex3f(-ww, hh, 0); glTexCoord2f(1, 0);
  610. glVertex3f( ww, hh, 0); glTexCoord2f(1, 1);
  611. glVertex3f( ww, -hh, 0); glTexCoord2f(0, 1);
  612. glEnd();
  613. glFlush();
  614. glDisable(GL_TEXTURE_2D);
  615. #endif
  616. /* swap the back buffer with the front buffer */
  617. #ifndef UI_BACKEND_NOFLUSH
  618. glfwSwapBuffers(bck->window);
  619. glClear(GL_COLOR_BUFFER_BIT);
  620. #endif
  621. return UI_OK;
  622. }
  623. /**
  624. * Free backend resources
  625. */
  626. int ui_backend_free(ui_backend_t *bck)
  627. {
  628. if(!bck) return UI_ERR_BADINP;
  629. if(bck->fullscr) ui_backend_fullscreen(bck);
  630. if(bck->screen) glDeleteTextures(1, &bck->screen);
  631. if(bck->program) glDeleteProgram(bck->program);
  632. if(bck->window) glfwDestroyWindow(bck->window);
  633. free(bck);
  634. #ifndef UI_BACKEND_INITIALIZED
  635. glfwTerminate();
  636. #endif
  637. return UI_OK;
  638. }
  639. #ifdef __cplusplus
  640. }
  641. #endif
  642. #endif /* UI_GLFW_H */