123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- /*
- * ui_glfw.h
- * https://gitlab.com/bztsrc/smgui
- *
- * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
- * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
- * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * @brief GLFW backend for SMGUI
- */
- #ifndef UI_GLFW_H
- #define UI_GLFW_H 1
- #ifndef UI_BACKEND
- #define UI_BACKEND 1
- #else
- #error "An UI backend has already been included"
- #endif
- #include <stdint.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #ifdef __WIN32__
- #include <GL/glew.h>
- #else
- #define GL_GLEXT_PROTOTYPES
- #include <GL/gl.h>
- #include <GL/glext.h>
- #endif
- #include <GLFW/glfw3.h>
- #ifndef UI_H
- #include <ui.h>
- #undef UI_H
- #endif
- #ifdef __cplusplus
- extern "C" {
- #endif
- /**
- * Gamepad structure
- */
- typedef struct {
- int id, btn, lx, ly, rx, ry;
- } ui_gamepad_t;
- /**
- * The UI backend structure, it's backend specific
- */
- typedef struct ui_backend_s {
- void *ctx;
- GLFWwindow *window;
- GLuint screen;
- GLuint program, VAO, VBO, EBO;
- GLint mloc, ploc, sloc;
- ui_gamepad_t controller[4];
- int btn, fullscr, w, h;
- } ui_backend_t;
- /**
- * Error callback
- */
- void ui_glfw_error(int error, const char *msg)
- {
- fprintf(stderr, "glfw error %d: %s\n", error, msg);
- exit(1);
- }
- /**
- * Mouse button event callback
- */
- void ui_glfw_mouse(GLFWwindow *window, int button, int action, int mods)
- {
- int m = 0;
- ui_event_t *evt;
- ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
- (void)mods;
- if(bck && (evt = _ui_evtslot(bck->ctx))) {
- switch(button) {
- case GLFW_MOUSE_BUTTON_LEFT: m = UI_BTN_L; break;
- case GLFW_MOUSE_BUTTON_MIDDLE: m = UI_BTN_M; break;
- case GLFW_MOUSE_BUTTON_RIGHT: m = UI_BTN_R; break;
- }
- if(action == GLFW_PRESS)
- bck->btn |= m;
- else
- bck->btn &= ~m;
- ui_getmouse(bck->ctx, &evt->x, &evt->y);
- evt->btn = bck->btn | (action != GLFW_PRESS ? UI_BTN_RELEASE : 0);
- evt->type = UI_EVT_MOUSE;
- }
- }
- /**
- * Scrolling event callback
- */
- void ui_glfw_scroll(GLFWwindow *window, double xdelta, double ydelta)
- {
- ui_event_t *evt;
- ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
- if(bck && (evt = _ui_evtslot(bck->ctx))) {
- ui_getmouse(bck->ctx, &evt->x, &evt->y);
- 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);
- evt->type = UI_EVT_MOUSE;
- }
- }
- /**
- * Drop file event callback
- */
- void ui_glfw_dropfile(GLFWwindow *window, int count, const char **fn)
- {
- ui_event_t *evt;
- ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
- int i;
- for(i = 0; fn && i < count; i++)
- if(fn[i] && bck && (evt = _ui_evtslot(bck->ctx))) {
- ui_getmouse(bck->ctx, &evt->x, &evt->y);
- evt->fn = !memcmp(fn[i], "file://", 7) ? fn[i] + 7 : fn[i];
- evt->btn = (bck->btn & 0xf00000);
- evt->type = UI_EVT_DROP;
- }
- }
- /**
- * Process a keyboard event callback
- */
- void ui_glfw_key(GLFWwindow *window, int key, int scancode, int action, int mods)
- {
- ui_event_t *evt;
- ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
- char keystr[8] = { 0 };
- (void)mods;
- if(!bck) return;
- if(key == GLFW_KEY_UNKNOWN && scancode >= 119 && scancode <= 151) key = scancode;
- if(action == GLFW_PRESS)
- switch(key) {
- case GLFW_KEY_LEFT_SHIFT: bck->btn |= UI_BTN_SHIFT; memcpy(keystr, "LShift", 6); break;
- case GLFW_KEY_RIGHT_SHIFT: bck->btn |= UI_BTN_SHIFT; memcpy(keystr, "RShift", 6); break;
- case GLFW_KEY_LEFT_CONTROL: bck->btn |= UI_BTN_CONTROL; memcpy(keystr, "LCtrl", 5); break;
- case GLFW_KEY_RIGHT_CONTROL: bck->btn |= UI_BTN_CONTROL; memcpy(keystr, "RCtrl", 5); break;
- case GLFW_KEY_LEFT_ALT: bck->btn |= UI_BTN_ALT; memcpy(keystr, "LAlt", 4); break;
- case GLFW_KEY_RIGHT_ALT: bck->btn |= UI_BTN_ALT; memcpy(keystr, "RAlt", 4); break;
- case GLFW_KEY_LEFT_SUPER: bck->btn |= UI_BTN_GUI; memcpy(keystr, "LGui", 4); break;
- case GLFW_KEY_RIGHT_SUPER: bck->btn |= UI_BTN_GUI; memcpy(keystr, "RGui", 4); break;
- case GLFW_KEY_ESCAPE: keystr[0] = 0x1b; break;
- case GLFW_KEY_BACKSPACE: keystr[0] = 8; break;
- case GLFW_KEY_TAB: keystr[0] = '\t'; break;
- case GLFW_KEY_ENTER: keystr[0] = '\n'; break;
- case GLFW_KEY_F1: memcpy(keystr, "F1", 2); break;
- case GLFW_KEY_F2: memcpy(keystr, "F2", 2); break;
- case GLFW_KEY_F3: memcpy(keystr, "F3", 2); break;
- case GLFW_KEY_F4: memcpy(keystr, "F4", 2); break;
- case GLFW_KEY_F5: memcpy(keystr, "F5", 2); break;
- case GLFW_KEY_F6: memcpy(keystr, "F6", 2); break;
- case GLFW_KEY_F7: memcpy(keystr, "F7", 2); break;
- case GLFW_KEY_F8: memcpy(keystr, "F8", 2); break;
- case GLFW_KEY_F9: memcpy(keystr, "F9", 2); break;
- case GLFW_KEY_F10: memcpy(keystr, "F10", 3); break;
- case GLFW_KEY_F11: memcpy(keystr, "F11", 3); break;
- case GLFW_KEY_F12: memcpy(keystr, "F12", 3); break;
- case GLFW_KEY_PRINT_SCREEN: memcpy(keystr, "PrScr", 5); break;
- case GLFW_KEY_CAPS_LOCK: memcpy(keystr, "CLck", 4); break;
- case GLFW_KEY_SCROLL_LOCK: memcpy(keystr, "SLck", 4); break;
- case GLFW_KEY_NUM_LOCK: memcpy(keystr, "NLck", 4); break;
- case GLFW_KEY_UP: memcpy(keystr, "Up", 2); break;
- case GLFW_KEY_DOWN: memcpy(keystr, "Down", 4); break;
- case GLFW_KEY_LEFT: memcpy(keystr, "Left", 4); break;
- case GLFW_KEY_RIGHT: memcpy(keystr, "Right", 5); break;
- case GLFW_KEY_HOME: memcpy(keystr, "Home", 4); break;
- case GLFW_KEY_END: memcpy(keystr, "End", 3); break;
- case GLFW_KEY_PAGE_UP: memcpy(keystr, "PgUp", 4); break;
- case GLFW_KEY_PAGE_DOWN: memcpy(keystr, "PgDown", 6); break;
- case GLFW_KEY_INSERT: memcpy(keystr, "Ins", 3); break;
- case GLFW_KEY_DELETE: memcpy(keystr, "Del", 3); break;
- case GLFW_KEY_WORLD_1: memcpy(keystr, "Int1", 4); break;
- case GLFW_KEY_WORLD_2: memcpy(keystr, "Int2", 4); break;
- case GLFW_KEY_MENU: memcpy(keystr, "Menu", 4); break;
- 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;
- 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;
- 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;
- 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;
- 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;
- case 119: memcpy(keystr, "Select", 6); break;
- case 120: memcpy(keystr, "Stop", 4); break;
- case 121: memcpy(keystr, "Redo", 4); break;
- case 122: memcpy(keystr, "Undo", 4); break;
- case 123: memcpy(keystr, "Cut", 3); break;
- case 124: memcpy(keystr, "Copy", 4); break;
- case 125: memcpy(keystr, "Paste", 5); break;
- case 126: memcpy(keystr, "Find", 4); break;
- case 127: memcpy(keystr, "Mute", 4); break;
- case 128: memcpy(keystr, "Vol+", 4); break;
- case 129: memcpy(keystr, "Vol-", 4); break;
- case 135: memcpy(keystr, "Int1", 4); break;
- case 136: memcpy(keystr, "Int2", 4); break;
- case 137: memcpy(keystr, "Int3", 4); break;
- case 138: memcpy(keystr, "Int4", 4); break;
- case 139: memcpy(keystr, "Int5", 4); break;
- case 140: memcpy(keystr, "Int6", 4); break;
- case 141: memcpy(keystr, "Int7", 4); break;
- case 142: memcpy(keystr, "Int8", 4); break;
- case 144: memcpy(keystr, "Lang1", 5); break;
- case 145: memcpy(keystr, "Lang2", 5); break;
- case 146: memcpy(keystr, "Lang3", 5); break;
- case 147: memcpy(keystr, "Lang4", 5); break;
- case 148: memcpy(keystr, "Lang5", 5); break;
- case 149: memcpy(keystr, "Lang6", 5); break;
- case 150: memcpy(keystr, "Lang7", 5); break;
- case 151: memcpy(keystr, "Lang8", 5); break;
- default: if((bck->btn & ~UI_BTN_SHIFT) && key >= GLFW_KEY_A && key <= GLFW_KEY_Z) keystr[0] = key; break;
- }
- if(action == GLFW_RELEASE)
- switch(key) {
- case GLFW_KEY_LEFT_SHIFT: bck->btn &= ~UI_BTN_SHIFT; memcpy(keystr, "LShift", 6); break;
- case GLFW_KEY_RIGHT_SHIFT: bck->btn &= ~UI_BTN_SHIFT; memcpy(keystr, "RShift", 6); break;
- case GLFW_KEY_LEFT_CONTROL: bck->btn &= ~UI_BTN_CONTROL; memcpy(keystr, "LCtrl", 5); break;
- case GLFW_KEY_RIGHT_CONTROL: bck->btn &= ~UI_BTN_CONTROL; memcpy(keystr, "RCtrl", 5); break;
- case GLFW_KEY_LEFT_ALT: bck->btn &= ~UI_BTN_ALT; memcpy(keystr, "LAlt", 4); break;
- case GLFW_KEY_RIGHT_ALT: bck->btn &= ~UI_BTN_ALT; memcpy(keystr, "RAlt", 4); break;
- case GLFW_KEY_LEFT_SUPER: bck->btn &= ~UI_BTN_GUI; memcpy(keystr, "LGui", 4); break;
- case GLFW_KEY_RIGHT_SUPER: bck->btn &= ~UI_BTN_GUI; memcpy(keystr, "RGui", 4); break;
- }
- if(keystr[0] && (evt = _ui_evtslot(bck->ctx))) {
- memcpy(evt->key, keystr, 8);
- evt->btn = (bck->btn & 0xf00000) | (action != GLFW_PRESS ? UI_BTN_RELEASE : 0);
- evt->type = UI_EVT_KEY;
- }
- }
- /**
- * Text input event callback
- */
- void ui_glfw_input(GLFWwindow *window, unsigned int unicode)
- {
- ui_event_t *evt;
- ui_backend_t *bck = (ui_backend_t*)glfwGetWindowUserPointer(window);
- if(unicode >= 32 && bck && (evt = _ui_evtslot(bck->ctx))) {
- if(unicode<0x80) { evt->key[0]=unicode; } else
- if(unicode<0x800) { evt->key[0]=((unicode>>6)&0x1F)|0xC0; evt->key[1]=(unicode&0x3F)|0x80; } else
- if(unicode<0x10000) { evt->key[0]=((unicode>>12)&0x0F)|0xE0; evt->key[1]=((unicode>>6)&0x3F)|0x80; evt->key[2]=(unicode&0x3F)|0x80; }
- 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; }
- evt->btn = (bck->btn & 0xf00000);
- evt->type = UI_EVT_KEY;
- }
- }
- /**
- * Toggle fullscreen
- */
- int ui_backend_fullscreen(ui_backend_t *bck)
- {
- GLFWmonitor *monitor;
- const GLFWvidmode *vidmode;
- if(!bck || !bck->window) return UI_ERR_BADINP;
- if(!(monitor = glfwGetPrimaryMonitor()) || !(vidmode = glfwGetVideoMode(monitor))) return UI_ERR_BACKEND;
- if((bck->fullscr ^= 1)) {
- bck->w = ((ui_t*)bck->ctx)->screen.w;
- bck->h = ((ui_t*)bck->ctx)->screen.h;
- glfwSetWindowSize(bck->window, vidmode->width, vidmode->height);
- glfwSetWindowMonitor(bck->window, monitor, 0, 0, vidmode->width, vidmode->height, vidmode->refreshRate);
- _ui_resize(bck->ctx, vidmode->width, vidmode->height);
- } else {
- glfwSetWindowMonitor(bck->window, NULL, (vidmode->width - bck->w) / 2, (vidmode->height - bck->h) / 2, bck->w, bck->h, 0);
- _ui_resize(bck->ctx, bck->w, bck->h);
- }
- return UI_OK;
- }
- /**
- * Set window focus
- */
- int ui_backend_focus(ui_backend_t *bck)
- {
- if(!bck || !bck->window) return UI_ERR_BADINP;
- glfwFocusWindow(bck->window);
- return UI_OK;
- }
- /**
- * Set window title
- */
- int ui_backend_settitle(ui_backend_t *bck, char *title)
- {
- if(!bck || !title || !*title || !bck->window) return UI_ERR_BADINP;
- glfwSetWindowTitle(bck->window, title);
- return UI_OK;
- }
- /**
- * Get the clipboard's text
- */
- char *ui_backend_getclipboard(ui_backend_t *bck)
- {
- const char *str = glfwGetClipboardString(NULL);
- char *ret = NULL;
- (void)bck;
- if(str && *str && (ret = (char*)malloc(strlen(str) + 1)))
- strcpy(ret, str);
- return ret;
- }
- /**
- * Set the clipboard's text
- */
- int ui_backend_setclipboard(ui_backend_t *bck, char *str)
- {
- (void)bck;
- glfwSetClipboardString(NULL, str);
- return UI_OK;
- }
- /**
- * Hide hardware mouse cursor
- */
- int ui_backend_hidecursor(ui_backend_t *bck)
- {
- if(!bck || !bck->window) return UI_ERR_BADINP;
- glfwSetInputMode(bck->window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
- return UI_OK;
- }
- /**
- * Show hardware mouse cursor
- */
- int ui_backend_showcursor(ui_backend_t *bck)
- {
- if(!bck || !bck->window) return UI_ERR_BADINP;
- glfwSetInputMode(bck->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
- return UI_OK;
- }
- /**
- * Hide OS-native on-screen keyboard
- */
- int ui_backend_hideosk(ui_backend_t *bck)
- {
- (void)bck;
- return UI_OK;
- }
- /**
- * Show OS-native on-screen keyboard
- */
- int ui_backend_showosk(ui_backend_t *bck)
- {
- (void)bck;
- return UI_OK;
- }
- /**
- * Initialize the backend
- */
- int ui_backend_init(ui_t *ctx, char *title, int w, int h, ui_image_t *icon)
- {
- ui_backend_t *bck;
- GLFWimage gicon;
- #ifndef UI_GLFW_NOSHADER
- #ifndef UI_GLFW_GLES2
- 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);}";
- const GLchar *fstr = "#version 330 core\nin vec2 vtex;\nuniform sampler2D stex;\nvoid main(){gl_FragColor=texture2D(stex,vtex);}";
- #else
- 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);}";
- const GLchar *fstr = "precision mediump float;\nvarying vec2 vtex;\nuniform sampler2D stex;\nvoid main(){gl_FragColor=texture2D(stex,vtex);}";
- #endif
- GLuint vshdr = 0, fshdr = 0;
- GLint ret;
- #endif
- int i, j;
- if(!ctx || !title || !*title || w < 1 || h < 1) return UI_ERR_BADINP;
- if(!(ctx->bck = bck = (ui_backend_t*)realloc(ctx->bck, sizeof(ui_backend_t)))) return UI_ERR_NOMEM;
- memset(bck, 0, sizeof(ui_backend_t));
- bck->ctx = ctx;
- #ifndef UI_BACKEND_INITIALIZED
- if(!glfwInit()) return UI_ERR_BACKEND;
- #endif
- glfwSetErrorCallback(ui_glfw_error);
- #if !defined(UI_GLFW_NOSHADER) && defined(UI_GLFW_GLES2)
- /* we must set OpenGL ES 2.0 in GLFW too, otherwise shaders won't work */
- glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
- glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
- #endif
- /* common GLFW hints */
- glfwWindowHint(GLFW_SAMPLES, 0);
- glfwWindowHint(GLFW_DEPTH_BITS, 16);
- glfwWindowHint(GLFW_RED_BITS, 8);
- glfwWindowHint(GLFW_GREEN_BITS, 8);
- glfwWindowHint(GLFW_BLUE_BITS, 8);
- glfwWindowHint(GLFW_ALPHA_BITS, 8);
- /* create window */
- if(!(bck->window = glfwCreateWindow(w, h, title, NULL, NULL))) {
- memset(bck, 0, sizeof(ui_backend_t));
- #ifndef UI_BACKEND_INITIALIZED
- glfwTerminate();
- #endif
- return UI_ERR_BACKEND;
- }
- if(icon && icon->w > 0 && icon->h > 0 && icon->buf && (gicon.pixels = (uint8_t*)malloc(icon->w * icon->h * 4))) {
- gicon.width = icon->w; gicon.height = icon->h;
- for(j = icon->w * 4, i = 0; i < icon->h; i++)
- memcpy(gicon.pixels + i * j, icon->buf + i * icon->p, j);
- glfwSetWindowIcon(bck->window, 1, &gicon);
- free(gicon.pixels);
- }
- glfwMakeContextCurrent(bck->window);
- glfwSetWindowUserPointer(bck->window, bck);
- /* load GL extensions. For some reason this most be called *after* the window is created */
- #ifdef GLAD_GL_H_
- if(!glCreateShader && !gladLoadGL(glfwGetProcAddress)) return UI_ERR_BACKEND;
- #elif defined(__GLEW_H__)
- if(!glCreateShader && glewInit() != GLEW_OK) return UI_ERR_BACKEND;
- #endif
- #ifndef UI_GLFW_NOSHADER
- /* compile and link shaders */
- if(!(vshdr = glCreateShader(GL_VERTEX_SHADER))) {
- serr: if(vshdr) glDeleteShader(vshdr);
- if(fshdr) glDeleteShader(fshdr);
- #ifndef UI_BACKEND_INITIALIZED
- glfwTerminate();
- #endif
- return UI_ERR_BACKEND;
- }
- glShaderSource(vshdr, 1, &vstr, NULL);
- glCompileShader(vshdr);
- glGetShaderiv(vshdr, GL_COMPILE_STATUS, &ret);
- if(!ret) goto serr;
- if(!(fshdr = glCreateShader(GL_FRAGMENT_SHADER))) goto serr;
- glShaderSource(fshdr, 1, &fstr, NULL);
- glCompileShader(fshdr);
- glGetShaderiv(fshdr, GL_COMPILE_STATUS, &ret);
- if(!ret) goto serr;
- if(!(bck->program = glCreateProgram())) goto serr;
- glAttachShader(bck->program, vshdr);
- glAttachShader(bck->program, fshdr);
- glLinkProgram(bck->program);
- glGetProgramiv(bck->program, GL_LINK_STATUS, &ret);
- if(!ret) { glDeleteProgram(bck->program); goto serr; }
- glDeleteShader(vshdr);
- glDeleteShader(fshdr);
- /* the texture sampler and the projection matrix is common */
- bck->sloc = glGetUniformLocation(bck->program, "stex");
- bck->mloc = glGetUniformLocation(bck->program, "mvp");
- #ifndef UI_GLFW_GLES2
- /* OpenGL 3.3 has VertexArrays, VertexBuffers and ElementBuffers */
- glGenVertexArrays(1, &bck->VAO);
- glBindVertexArray(bck->VAO);
- glGenBuffers(1, &bck->VBO);
- glBindBuffer(GL_ARRAY_BUFFER, bck->VBO);
- glGenBuffers(1, &bck->EBO);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bck->EBO);
- glEnableVertexAttribArray(0);
- #else
- /* OpenGL ES has only one attribute in the shader */
- bck->ploc = glGetAttribLocation(bck->program, "apos");
- glEnableVertexAttribArray(bck->ploc);
- #endif
- #endif
- /* set up texture */
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glGenTextures(1, &bck->screen);
- glBindTexture(GL_TEXTURE_2D, bck->screen);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- for(i = 0, j = GLFW_JOYSTICK_1; i < 4 && j < GLFW_JOYSTICK_LAST; j++)
- bck->controller[i++].id = glfwJoystickPresent(j) ? j : -1;
- #ifndef UI_GLFW_CUSTOMHOOKS
- glfwSetKeyCallback(bck->window, ui_glfw_key);
- glfwSetCharCallback(bck->window, ui_glfw_input);
- glfwSetMouseButtonCallback(bck->window, ui_glfw_mouse);
- glfwSetScrollCallback(bck->window, ui_glfw_scroll);
- glfwSetDropCallback(bck->window, ui_glfw_dropfile);
- #endif
- ui_backend_focus(bck);
- return UI_OK;
- }
- /**
- * Return a backend specific window handle
- */
- void *ui_backend_getwindow(ui_backend_t *bck)
- {
- return bck ? bck->window : NULL;
- }
- /**
- * Main event handler
- */
- int ui_backend_event(ui_backend_t *bck)
- {
- ui_event_t *evt;
- double mx, my;
- GLFWgamepadstate state;
- int i, w, h, m, lx, ly, rx, ry;
- if(!bck || !bck->window) return UI_ERR_BADINP;
- if(glfwWindowShouldClose(bck->window)) return 1;
- /* check mouse position */
- glfwGetCursorPos(bck->window, &mx, &my);
- _ui_setmouse(bck->ctx, (int)mx, (int)my);
- glfwGetFramebufferSize(bck->window, &w, &h);
- if(((ui_t*)bck->ctx)->screen.w != w || ((ui_t*)bck->ctx)->screen.h != h)
- _ui_resize(bck->ctx, w, h);
- glfwPollEvents();
- /* no events for gamepads, we must poll... */
- for(i = 0; i < 4; i++)
- if(bck->controller[i].id != -1 && glfwGetGamepadState(bck->controller[i].id, &state)) {
- m = 0;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP]) m |= UI_BTN_U;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT]) m |= UI_BTN_R;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN]) m |= UI_BTN_D;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT]) m |= UI_BTN_L;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_A]) m |= UI_BTN_A;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_B]) m |= UI_BTN_B;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_X]) m |= UI_BTN_X;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_Y]) m |= UI_BTN_Y;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER]) m |= UI_BTN_LS;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER]) m |= UI_BTN_RS;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_BACK]) m |= UI_BTN_BA;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_START]) m |= UI_BTN_ST;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE]) m |= UI_BTN_GU;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB]) m |= UI_BTN_LT;
- if(state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB]) m |= UI_BTN_RT;
- lx = state.axes[GLFW_GAMEPAD_AXIS_LEFT_X] * 32768.0; rx = state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X] * 32768.0;
- ly = state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y] * 32768.0; ry = state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y] * 32768.0;
- if(bck->controller[i].lx != lx || bck->controller[i].ly != ly ||
- bck->controller[i].rx != rx || bck->controller[i].ry != ry || bck->controller[i].btn != m) {
- if((evt = _ui_evtslot(bck->ctx))) {
- evt->key[0] = i;
- evt->btn = m | (bck->btn & 0xf00000) | (bck->controller[i].btn > m ? UI_BTN_RELEASE : 0);
- evt->x = lx;
- evt->y = ly;
- evt->rx = rx;
- evt->ry = ry;
- evt->type = UI_EVT_GAMEPAD;
- }
- bck->controller[i].btn = m;
- bck->controller[i].lx = lx;
- bck->controller[i].ly = ly;
- bck->controller[i].rx = rx;
- bck->controller[i].ry = ry;
- }
- }
- return UI_OK;
- }
- /**
- * Set up orthographic projection in a matrix
- */
- void _ui_glfw_ortho(GLfloat *m, float l, float r, float b, float t, float n, float f)
- {
- memset(m, 0, 16 * sizeof(GLfloat));
- m[ 0] = 2.0/(r-l);
- m[ 5] = 2.0/(t-b);
- m[10] = -2.0/(f-n);
- m[ 3] = -(r+l)/(r-l);
- m[ 7] = -(t+b)/(t-b);
- m[11] = -(f+n)/(f-n);
- m[15] = 1.0;
- }
- /**
- * Main redraw handler
- */
- int ui_backend_redraw(ui_backend_t *bck)
- {
- #ifndef UI_GLFW_NOSHADER
- static GLushort idx[] = { 0, 1, 2, 0, 2, 3 };
- GLfloat mvp[16] = { 0 }, vert[16] = { 0 };
- #endif
- int w, h, ww, hh;
- if(!bck || !bck->window) return UI_ERR_BADINP;
- glfwGetFramebufferSize(bck->window, &w, &h);
- glViewport(0, 0, w, h);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, bck->screen);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ((ui_t*)bck->ctx)->screen.w, ((ui_t*)bck->ctx)->screen.h, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, ((ui_t*)bck->ctx)->screen.buf);
- ww = w >> 1; hh = h >> 1;
- #ifndef UI_GLFW_NOSHADER
- /* set up vertex buffer */
- vert[ 0] = -ww; vert[ 1] = -hh; vert[ 2] = 0; vert[ 3] = 0;
- vert[ 4] = -ww; vert[ 5] = hh; vert[ 6] = 0; vert[ 7] = 1.0;
- vert[ 8] = ww; vert[ 9] = hh; vert[10] = 1.0; vert[11] = 1.0;
- vert[12] = ww; vert[13] = -hh; vert[14] = 1.0; vert[15] = 0;
- glUseProgram(bck->program);
- glUniform1i(bck->sloc, 0);
- _ui_glfw_ortho(mvp, -ww, ww, hh, -hh, 0.0, 1.0);
- glUniformMatrix4fv(bck->mloc, 1, GL_FALSE, mvp);
- #ifndef UI_GLFW_GLES2
- /* OpenGL 3.3 bind buffers and draw (note: last argument is 0) */
- glBindVertexArray(bck->VAO);
- glBindBuffer(GL_ARRAY_BUFFER, bck->VBO);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bck->EBO);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW);
- glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0);
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
- #else
- /* OpenGL ES set attribute and draw (note: last argument is idx) */
- glVertexAttribPointer(bck->ploc, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vert);
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, idx);
- #endif
- #else
- /* Old-school, no shaders, no extensions */
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(-ww, ww, -hh, hh, 0.0, 1.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glEnable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glVertex3f(-ww, -hh, 0); glTexCoord2f(0, 0);
- glVertex3f(-ww, hh, 0); glTexCoord2f(1, 0);
- glVertex3f( ww, hh, 0); glTexCoord2f(1, 1);
- glVertex3f( ww, -hh, 0); glTexCoord2f(0, 1);
- glEnd();
- glFlush();
- glDisable(GL_TEXTURE_2D);
- #endif
- /* swap the back buffer with the front buffer */
- #ifndef UI_BACKEND_NOFLUSH
- glfwSwapBuffers(bck->window);
- glClear(GL_COLOR_BUFFER_BIT);
- #endif
- return UI_OK;
- }
- /**
- * Free backend resources
- */
- int ui_backend_free(ui_backend_t *bck)
- {
- if(!bck) return UI_ERR_BADINP;
- if(bck->fullscr) ui_backend_fullscreen(bck);
- if(bck->screen) glDeleteTextures(1, &bck->screen);
- if(bck->program) glDeleteProgram(bck->program);
- if(bck->window) glfwDestroyWindow(bck->window);
- free(bck);
- #ifndef UI_BACKEND_INITIALIZED
- glfwTerminate();
- #endif
- return UI_OK;
- }
- #ifdef __cplusplus
- }
- #endif
- #endif /* UI_GLFW_H */
|