From 26d36170deacd6f8a9b3ebfe56432ee6e72a1907 Mon Sep 17 00:00:00 2001 From: Robin Broda Date: Thu, 4 Apr 2019 01:50:22 +0200 Subject: [PATCH] glava-config: Use X11 as rendering backend --- glava-config/glava-config.c | 233 ++++++++-- glava-config/nuklear_glfw_gl3.h | 492 --------------------- glava-config/nuklear_xlib_gl3.h | 743 ++++++++++++++++++++++++++++++++ meson.build | 5 +- 4 files changed, 934 insertions(+), 539 deletions(-) delete mode 100644 glava-config/nuklear_glfw_gl3.h create mode 100644 glava-config/nuklear_xlib_gl3.h diff --git a/glava-config/glava-config.c b/glava-config/glava-config.c index b11dba2..c623319 100644 --- a/glava-config/glava-config.c +++ b/glava-config/glava-config.c @@ -2,9 +2,6 @@ #include #include -#include -#include - #define NK_INCLUDE_FIXED_TYPES #define NK_INCLUDE_STANDARD_IO #define NK_INCLUDE_STANDARD_VARARGS @@ -13,10 +10,10 @@ #define NK_INCLUDE_FONT_BAKING #define NK_INCLUDE_DEFAULT_FONT #define NK_IMPLEMENTATION -#define NK_GLFW_GL3_IMPLEMENTATION -#define NK_KEYSTATE_BASED_INPUT +#define NK_XLIB_GL3_IMPLEMENTATION +#define NK_XLIB_LOAD_OPENGL_EXTENSIONS #include "nuklear.h" -#include "nuklear_glfw_gl3.h" +#include "nuklear_xlib_gl3.h" #define WINDOW_WIDTH 400 #define WINDOW_HEIGHT 400 @@ -24,54 +21,196 @@ #define MAX_VERTEX_BUFFER 512 * 1024 #define MAX_ELEMENT_BUFFER 128 * 1024 -static void error_callback(int e, const char *d) {printf("[GLFW] Error %d: %s\n", e, d);} +struct XWindow { + Display *dpy; + Window win; + XVisualInfo *vis; + Colormap cmap; + XSetWindowAttributes swa; + XWindowAttributes attr; + GLXFBConfig fbc; + Atom wm_delete_window; + int width, height; +}; +static int gl_err = nk_false; +static int gl_error_handler(Display *dpy, XErrorEvent *ev) { + NK_UNUSED(dpy); + NK_UNUSED(ev); + gl_err = nk_true; + return 0; +} + +static void die(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + exit(EXIT_FAILURE); +} + +static int has_extension(const char *string, const char *ext) { + const char *start, *where, *term; + where = strchr(ext, ' '); + if (where || *ext == '\0') + return nk_false; + + for (start = string;;) { + where = strstr((const char*)start, ext); + if (!where) break; + term = where + strlen(ext); + if (where == start || *(where - 1) == ' ') { + if (*term == ' ' || *term == '\0') + return nk_true; + } + start = term; + } + return nk_false; +} int main(int argc, char** argv) { /* Platform */ - static GLFWwindow *win; - int width = 0, height = 0; + int running = 1; + struct XWindow win; + GLXContext glContext; struct nk_context *ctx; struct nk_colorf bg; - /* GLFW */ - glfwSetErrorCallback(error_callback); - if (!glfwInit()) { - fprintf(stdout, "[GFLW] Failed to init!\n"); - exit(EXIT_FAILURE); - } - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -#ifdef __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); -#endif - win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "glava-config", NULL, NULL); - glfwMakeContextCurrent(win); - glfwGetWindowSize(win, &width, &height); + memset(&win, 0, sizeof(win)); + win.dpy = XOpenDisplay(NULL); + if (!win.dpy) die("Failed to open X display\n"); - /* OpenGL */ - glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - glewExperimental = 1; - if (glewInit() != GLEW_OK) { - fprintf(stderr, "Failed to setup GLEW\n"); - exit(1); + { + /* check glx version */ + int glx_major, glx_minor; + if (!glXQueryVersion(win.dpy, &glx_major, &glx_minor)) + die("[X11]: Error: Failed to query OpenGL version\n"); + if ((glx_major == 1 && glx_minor < 3) || (glx_major < 1)) + die("[X11]: Error: Invalid GLX version!\n"); + } + { + /* find and pick matching framebuffer visual */ + int fb_count; + static GLint attr[] = { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + GLXFBConfig *fbc; + fbc = glXChooseFBConfig(win.dpy, DefaultScreen(win.dpy), attr, &fb_count); + if (!fbc) die("[X11]: Error: failed to retrieve framebuffer configuration\n"); + { + /* pick framebuffer with most samples per pixel */ + int i; + int fb_best = -1, best_num_samples = -1; + for (i = 0; i < fb_count; ++i) { + XVisualInfo *vi = glXGetVisualFromFBConfig(win.dpy, fbc[i]); + if (vi) { + int sample_buffer, samples; + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLE_BUFFERS, &sample_buffer); + glXGetFBConfigAttrib(win.dpy, fbc[i], GLX_SAMPLES, &samples); + if ((fb_best < 0) || (sample_buffer && samples > best_num_samples)) + fb_best = i, best_num_samples = samples; + } + } + win.fbc = fbc[fb_best]; + XFree(fbc); + win.vis = glXGetVisualFromFBConfig(win.dpy, win.fbc); + } + } + { + /* create window */ + win.cmap = XCreateColormap(win.dpy, RootWindow(win.dpy, win.vis->screen), win.vis->visual, AllocNone); + win.swa.colormap = win.cmap; + win.swa.background_pixmap = None; + win.swa.border_pixel = 0; + win.swa.event_mask = + ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPress | ButtonReleaseMask| ButtonMotionMask | + Button1MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask| + PointerMotionMask| StructureNotifyMask; + win.win = XCreateWindow(win.dpy, RootWindow(win.dpy, win.vis->screen), 0, 0, + WINDOW_WIDTH, WINDOW_HEIGHT, 0, win.vis->depth, InputOutput, + win.vis->visual, CWBorderPixel|CWColormap|CWEventMask, &win.swa); + if (!win.win) die("[X11]: Failed to create window\n"); + XFree(win.vis); + XStoreName(win.dpy, win.win, "glava-config"); + XMapWindow(win.dpy, win.win); + win.wm_delete_window = XInternAtom(win.dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(win.dpy, win.win, &win.wm_delete_window, 1); + } + { + /* create opengl context */ + typedef GLXContext(*glxCreateContext)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + int(*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(gl_error_handler); + const char *extensions_str = glXQueryExtensionsString(win.dpy, DefaultScreen(win.dpy)); + glxCreateContext create_context = (glxCreateContext) + glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); + + gl_err = nk_false; + if (!has_extension(extensions_str, "GLX_ARB_create_context") || !create_context) { + fprintf(stdout, "[X11]: glXCreateContextAttribARB() not found...\n"); + fprintf(stdout, "[X11]: ... using old-style GLX context\n"); + glContext = glXCreateNewContext(win.dpy, win.fbc, GLX_RGBA_TYPE, 0, True); + } else { + GLint attr[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + XSync(win.dpy, False); + if (gl_err || !glContext) { + /* Could not create GL 3.0 context. Fallback to old 2.x context. + * If a version below 3.0 is requested, implementations will + * return the newest context version compatible with OpenGL + * version less than version 3.0.*/ + attr[1] = 1; attr[3] = 0; + gl_err = nk_false; + fprintf(stdout, "[X11] Failed to create OpenGL 3.0 context\n"); + fprintf(stdout, "[X11] ... using old-style GLX context!\n"); + glContext = create_context(win.dpy, win.fbc, 0, True, attr); + } + } + XSync(win.dpy, False); + XSetErrorHandler(old_handler); + if (gl_err || !glContext) + die("[X11]: Failed to create an OpenGL context\n"); + glXMakeCurrent(win.dpy, win.win, glContext); } - ctx = nk_glfw3_init(win, NK_GLFW3_INSTALL_CALLBACKS); + ctx = nk_x11_init(win.dpy, win.win); { struct nk_font_atlas *atlas; - nk_glfw3_font_stash_begin(&atlas); - nk_glfw3_font_stash_end(); + nk_x11_font_stash_begin(&atlas); + nk_x11_font_stash_end(); } bg.r = 0.10f, bg.g = 0.18f, bg.b = 0.24f, bg.a = 1.0f; - while (!glfwWindowShouldClose(win)) { + while (running) + { /* Input */ - glfwPollEvents(); - nk_glfw3_new_frame(); + XEvent evt; + nk_input_begin(ctx); + while (XPending(win.dpy)) { + XNextEvent(win.dpy, &evt); + if (evt.type == ClientMessage) goto cleanup; + if (XFilterEvent(&evt, win.win)) continue; + nk_x11_handle_event(&evt); + } + nk_input_end(ctx); /* GUI */ - if (nk_begin(ctx, "glava-config", nk_rect(0, 0, width, height), 0)) { + if (nk_begin(ctx, "glava-config", nk_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT), 0)) { nk_layout_row_static(ctx, 30, 180, 1); if (nk_button_label(ctx, "Reload glava")) { fprintf(stdout, "sending SIGUSR1 to glava\n"); @@ -89,15 +228,21 @@ int main(int argc, char** argv) { nk_end(ctx); /* Draw */ - glfwGetWindowSize(win, &width, &height); - glViewport(0, 0, width, height); + XGetWindowAttributes(win.dpy, win.win, &win.attr); + glViewport(0, 0, win.width, win.height); glClear(GL_COLOR_BUFFER_BIT); glClearColor(bg.r, bg.g, bg.b, bg.a); - nk_glfw3_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); - glfwSwapBuffers(win); + nk_x11_render(NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); + glXSwapBuffers(win.dpy, win.win); } - nk_glfw3_shutdown(); - glfwTerminate(); + +cleanup: + nk_x11_shutdown(); + glXMakeCurrent(win.dpy, 0, 0); + glXDestroyContext(win.dpy, glContext); + XUnmapWindow(win.dpy, win.win); + XFreeColormap(win.dpy, win.cmap); + XDestroyWindow(win.dpy, win.win); + XCloseDisplay(win.dpy); return EXIT_SUCCESS; } - diff --git a/glava-config/nuklear_glfw_gl3.h b/glava-config/nuklear_glfw_gl3.h deleted file mode 100644 index c8c6916..0000000 --- a/glava-config/nuklear_glfw_gl3.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Nuklear - 1.32.0 - public domain - * no warrenty implied; use at your own risk. - * authored from 2015-2016 by Micha Mettke - */ -/* - * ============================================================== - * - * API - * - * =============================================================== - */ -#ifndef NK_GLFW_GL3_H_ -#define NK_GLFW_GL3_H_ - -#include - -enum nk_glfw_init_state{ - NK_GLFW3_DEFAULT=0, - NK_GLFW3_INSTALL_CALLBACKS -}; - -NK_API struct nk_context* nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state); -NK_API void nk_glfw3_shutdown(void); -NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas); -NK_API void nk_glfw3_font_stash_end(void); -NK_API void nk_glfw3_new_frame(void); -NK_API void nk_glfw3_render(enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer); - -NK_API void nk_glfw3_device_destroy(void); -NK_API void nk_glfw3_device_create(void); - -NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); -NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff); -NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, int action, int mods); - -#endif -/* - * ============================================================== - * - * IMPLEMENTATION - * - * =============================================================== - */ -#ifdef NK_GLFW_GL3_IMPLEMENTATION - -#ifndef NK_GLFW_TEXT_MAX -#define NK_GLFW_TEXT_MAX 256 -#endif -#ifndef NK_GLFW_DOUBLE_CLICK_LO -#define NK_GLFW_DOUBLE_CLICK_LO 0.02 -#endif -#ifndef NK_GLFW_DOUBLE_CLICK_HI -#define NK_GLFW_DOUBLE_CLICK_HI 0.2 -#endif - -struct nk_glfw_device { - struct nk_buffer cmds; - struct nk_draw_null_texture null; - GLuint vbo, vao, ebo; - GLuint prog; - GLuint vert_shdr; - GLuint frag_shdr; - GLint attrib_pos; - GLint attrib_uv; - GLint attrib_col; - GLint uniform_tex; - GLint uniform_proj; - GLuint font_tex; -}; - -struct nk_glfw_vertex { - float position[2]; - float uv[2]; - nk_byte col[4]; -}; - -static struct nk_glfw { - GLFWwindow *win; - int width, height; - int display_width, display_height; - struct nk_glfw_device ogl; - struct nk_context ctx; - struct nk_font_atlas atlas; - struct nk_vec2 fb_scale; - unsigned int text[NK_GLFW_TEXT_MAX]; - int text_len; - struct nk_vec2 scroll; - double last_button_click; - int is_double_click_down; - struct nk_vec2 double_click_pos; -} glfw; - -#ifdef __APPLE__ - #define NK_SHADER_VERSION "#version 150\n" -#else - #define NK_SHADER_VERSION "#version 300 es\n" -#endif - -NK_API void -nk_glfw3_device_create(void) -{ - GLint status; - static const GLchar *vertex_shader = - NK_SHADER_VERSION - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 TexCoord;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "void main() {\n" - " Frag_UV = TexCoord;\n" - " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" - "}\n"; - static const GLchar *fragment_shader = - NK_SHADER_VERSION - "precision mediump float;\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main(){\n" - " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" - "}\n"; - - struct nk_glfw_device *dev = &glfw.ogl; - nk_buffer_init_default(&dev->cmds); - dev->prog = glCreateProgram(); - dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); - dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); - glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); - glCompileShader(dev->vert_shdr); - glCompileShader(dev->frag_shdr); - glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); - assert(status == GL_TRUE); - glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); - assert(status == GL_TRUE); - glAttachShader(dev->prog, dev->vert_shdr); - glAttachShader(dev->prog, dev->frag_shdr); - glLinkProgram(dev->prog); - glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); - assert(status == GL_TRUE); - - dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); - dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); - dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); - dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); - dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); - - { - /* buffer setup */ - GLsizei vs = sizeof(struct nk_glfw_vertex); - size_t vp = offsetof(struct nk_glfw_vertex, position); - size_t vt = offsetof(struct nk_glfw_vertex, uv); - size_t vc = offsetof(struct nk_glfw_vertex, col); - - glGenBuffers(1, &dev->vbo); - glGenBuffers(1, &dev->ebo); - glGenVertexArrays(1, &dev->vao); - - glBindVertexArray(dev->vao); - glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); - - glEnableVertexAttribArray((GLuint)dev->attrib_pos); - glEnableVertexAttribArray((GLuint)dev->attrib_uv); - glEnableVertexAttribArray((GLuint)dev->attrib_col); - - glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); - glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); - glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -NK_INTERN void -nk_glfw3_device_upload_atlas(const void *image, int width, int height) -{ - struct nk_glfw_device *dev = &glfw.ogl; - glGenTextures(1, &dev->font_tex); - glBindTexture(GL_TEXTURE_2D, dev->font_tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, image); -} - -NK_API void -nk_glfw3_device_destroy(void) -{ - struct nk_glfw_device *dev = &glfw.ogl; - glDetachShader(dev->prog, dev->vert_shdr); - glDetachShader(dev->prog, dev->frag_shdr); - glDeleteShader(dev->vert_shdr); - glDeleteShader(dev->frag_shdr); - glDeleteProgram(dev->prog); - glDeleteTextures(1, &dev->font_tex); - glDeleteBuffers(1, &dev->vbo); - glDeleteBuffers(1, &dev->ebo); - nk_buffer_free(&dev->cmds); -} - -NK_API void -nk_glfw3_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) -{ - struct nk_glfw_device *dev = &glfw.ogl; - struct nk_buffer vbuf, ebuf; - GLfloat ortho[4][4] = { - {2.0f, 0.0f, 0.0f, 0.0f}, - {0.0f,-2.0f, 0.0f, 0.0f}, - {0.0f, 0.0f,-1.0f, 0.0f}, - {-1.0f,1.0f, 0.0f, 1.0f}, - }; - ortho[0][0] /= (GLfloat)glfw.width; - ortho[1][1] /= (GLfloat)glfw.height; - - /* setup global state */ - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glActiveTexture(GL_TEXTURE0); - - /* setup program */ - glUseProgram(dev->prog); - glUniform1i(dev->uniform_tex, 0); - glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); - glViewport(0,0,(GLsizei)glfw.display_width,(GLsizei)glfw.display_height); - { - /* convert from command queue into draw list and draw to screen */ - const struct nk_draw_command *cmd; - void *vertices, *elements; - const nk_draw_index *offset = NULL; - - /* allocate vertex and element buffer */ - glBindVertexArray(dev->vao); - glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); - - glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW); - - /* load draw vertices & elements directly into vertex + element buffer */ - vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); - { - /* fill convert configuration */ - struct nk_convert_config config; - static const struct nk_draw_vertex_layout_element vertex_layout[] = { - {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, - {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, - {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, - {NK_VERTEX_LAYOUT_END} - }; - NK_MEMSET(&config, 0, sizeof(config)); - config.vertex_layout = vertex_layout; - config.vertex_size = sizeof(struct nk_glfw_vertex); - config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); - config.null = dev->null; - config.circle_segment_count = 22; - config.curve_segment_count = 22; - config.arc_segment_count = 22; - config.global_alpha = 1.0f; - config.shape_AA = AA; - config.line_AA = AA; - - /* setup buffers to load vertices and elements */ - nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer); - nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer); - nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config); - } - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); - - /* iterate over and execute each draw command */ - nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) - { - if (!cmd->elem_count) continue; - glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); - glScissor( - (GLint)(cmd->clip_rect.x * glfw.fb_scale.x), - (GLint)((glfw.height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw.fb_scale.y), - (GLint)(cmd->clip_rect.w * glfw.fb_scale.x), - (GLint)(cmd->clip_rect.h * glfw.fb_scale.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); - offset += cmd->elem_count; - } - nk_clear(&glfw.ctx); - } - - /* default OpenGL state */ - glUseProgram(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); - glDisable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); -} - -NK_API void -nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) -{ - (void)win; - if (glfw.text_len < NK_GLFW_TEXT_MAX) - glfw.text[glfw.text_len++] = codepoint; -} - -NK_API void -nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) -{ - (void)win; (void)xoff; - glfw.scroll.x += (float)xoff; - glfw.scroll.y += (float)yoff; -} - -NK_API void -nk_glfw3_mouse_button_callback(GLFWwindow* window, int button, int action, int mods) -{ - double x, y; - if (button != GLFW_MOUSE_BUTTON_LEFT) return; - glfwGetCursorPos(window, &x, &y); - if (action == GLFW_PRESS) { - double dt = glfwGetTime() - glfw.last_button_click; - if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) { - glfw.is_double_click_down = nk_true; - glfw.double_click_pos = nk_vec2((float)x, (float)y); - } - glfw.last_button_click = glfwGetTime(); - } else glfw.is_double_click_down = nk_false; -} - -NK_INTERN void -nk_glfw3_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) -{ - const char *text = glfwGetClipboardString(glfw.win); - if (text) nk_textedit_paste(edit, text, nk_strlen(text)); - (void)usr; -} - -NK_INTERN void -nk_glfw3_clipboard_copy(nk_handle usr, const char *text, int len) -{ - char *str = 0; - (void)usr; - if (!len) return; - str = (char*)malloc((size_t)len+1); - if (!str) return; - memcpy(str, text, (size_t)len); - str[len] = '\0'; - glfwSetClipboardString(glfw.win, str); - free(str); -} - -NK_API struct nk_context* -nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state) -{ - glfw.win = win; - if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { - glfwSetScrollCallback(win, nk_gflw3_scroll_callback); - glfwSetCharCallback(win, nk_glfw3_char_callback); - glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback); - } - nk_init_default(&glfw.ctx, 0); - glfw.ctx.clip.copy = nk_glfw3_clipboard_copy; - glfw.ctx.clip.paste = nk_glfw3_clipboard_paste; - glfw.ctx.clip.userdata = nk_handle_ptr(0); - glfw.last_button_click = 0; - nk_glfw3_device_create(); - - glfw.is_double_click_down = nk_false; - glfw.double_click_pos = nk_vec2(0, 0); - - return &glfw.ctx; -} - -NK_API void -nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) -{ - nk_font_atlas_init_default(&glfw.atlas); - nk_font_atlas_begin(&glfw.atlas); - *atlas = &glfw.atlas; -} - -NK_API void -nk_glfw3_font_stash_end(void) -{ - const void *image; int w, h; - image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); - nk_glfw3_device_upload_atlas(image, w, h); - nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null); - if (glfw.atlas.default_font) - nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle); -} - -NK_API void -nk_glfw3_new_frame(void) -{ - int i; - double x, y; - struct nk_context *ctx = &glfw.ctx; - struct GLFWwindow *win = glfw.win; - - glfwGetWindowSize(win, &glfw.width, &glfw.height); - glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height); - glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width; - glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height; - - nk_input_begin(ctx); - for (i = 0; i < glfw.text_len; ++i) - nk_input_unicode(ctx, glfw.text[i]); - -#ifdef NK_GLFW_GL3_MOUSE_GRABBING - /* optional grabbing behavior */ - if (ctx->input.mouse.grab) - glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - else if (ctx->input.mouse.ungrab) - glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif - - nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS|| - glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS); - - if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || - glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { - nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); - } else { - nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); - nk_input_key(ctx, NK_KEY_COPY, 0); - nk_input_key(ctx, NK_KEY_PASTE, 0); - nk_input_key(ctx, NK_KEY_CUT, 0); - nk_input_key(ctx, NK_KEY_SHIFT, 0); - } - - glfwGetCursorPos(win, &x, &y); - nk_input_motion(ctx, (int)x, (int)y); -#ifdef NK_GLFW_GL3_MOUSE_GRABBING - if (ctx->input.mouse.grabbed) { - glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y); - ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; - ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; - } -#endif - nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); - nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); - nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); - nk_input_button(ctx, NK_BUTTON_DOUBLE, (int)glfw.double_click_pos.x, (int)glfw.double_click_pos.y, glfw.is_double_click_down); - nk_input_scroll(ctx, glfw.scroll); - nk_input_end(&glfw.ctx); - glfw.text_len = 0; - glfw.scroll = nk_vec2(0,0); -} - -NK_API -void nk_glfw3_shutdown(void) -{ - nk_font_atlas_clear(&glfw.atlas); - nk_free(&glfw.ctx); - nk_glfw3_device_destroy(); - memset(&glfw, 0, sizeof(glfw)); -} - -#endif diff --git a/glava-config/nuklear_xlib_gl3.h b/glava-config/nuklear_xlib_gl3.h new file mode 100644 index 0000000..487dbc7 --- /dev/null +++ b/glava-config/nuklear_xlib_gl3.h @@ -0,0 +1,743 @@ +/* + * Nuklear - v1.17 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2016 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_XLIB_GL3_H_ +#define NK_XLIB_GL3_H_ + +#include +NK_API struct nk_context* nk_x11_init(Display *dpy, Window win); +NK_API void nk_x11_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_x11_font_stash_end(void); +NK_API int nk_x11_handle_event(XEvent *evt); +NK_API void nk_x11_render(enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer); +NK_API void nk_x11_shutdown(void); +NK_API int nk_x11_device_create(void); +NK_API void nk_x11_device_destroy(void); + +#endif +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_XLIB_GL3_IMPLEMENTATION +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifndef NK_X11_DOUBLE_CLICK_LO +#define NK_X11_DOUBLE_CLICK_LO 20 +#endif +#ifndef NK_X11_DOUBLE_CLICK_HI +#define NK_X11_DOUBLE_CLICK_HI 200 +#endif + +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS +#include + +/* GL_ARB_vertex_buffer_object */ +typedef void(*nkglGenBuffers)(GLsizei, GLuint*); +typedef void(*nkglBindBuffer)(GLenum, GLuint); +typedef void(*nkglBufferData)(GLenum, GLsizeiptr, const GLvoid*, GLenum); +typedef void(*nkglBufferSubData)(GLenum, GLintptr, GLsizeiptr, const GLvoid*); +typedef void*(*nkglMapBuffer)(GLenum, GLenum); +typedef GLboolean(*nkglUnmapBuffer)(GLenum); +typedef void(*nkglDeleteBuffers)(GLsizei, GLuint*); +/* GL_ARB_vertex_array_object */ +typedef void (*nkglGenVertexArrays)(GLsizei, GLuint*); +typedef void (*nkglBindVertexArray)(GLuint); +typedef void (*nkglDeleteVertexArrays)(GLsizei, const GLuint*); +/* GL_ARB_vertex_program / GL_ARB_fragment_program */ +typedef void(*nkglVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const GLvoid*); +typedef void(*nkglEnableVertexAttribArray)(GLuint); +typedef void(*nkglDisableVertexAttribArray)(GLuint); +/* GL_ARB_framebuffer_object */ +typedef void(*nkglGenerateMipmap)(GLenum target); +/* GLSL/OpenGL 2.0 core */ +typedef GLuint(*nkglCreateShader)(GLenum); +typedef void(*nkglShaderSource)(GLuint, GLsizei, const GLchar**, const GLint*); +typedef void(*nkglCompileShader)(GLuint); +typedef void(*nkglGetShaderiv)(GLuint, GLenum, GLint*); +typedef void(*nkglGetShaderInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*); +typedef void(*nkglDeleteShader)(GLuint); +typedef GLuint(*nkglCreateProgram)(void); +typedef void(*nkglAttachShader)(GLuint, GLuint); +typedef void(*nkglDetachShader)(GLuint, GLuint); +typedef void(*nkglLinkProgram)(GLuint); +typedef void(*nkglUseProgram)(GLuint); +typedef void(*nkglGetProgramiv)(GLuint, GLenum, GLint*); +typedef void(*nkglGetProgramInfoLog)(GLuint, GLsizei, GLsizei*, GLchar*); +typedef void(*nkglDeleteProgram)(GLuint); +typedef GLint(*nkglGetUniformLocation)(GLuint, const GLchar*); +typedef GLint(*nkglGetAttribLocation)(GLuint, const GLchar*); +typedef void(*nkglUniform1i)(GLint, GLint); +typedef void(*nkglUniform1f)(GLint, GLfloat); +typedef void(*nkglUniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat*); +typedef void(*nkglUniformMatrix4fv)(GLint, GLsizei, GLboolean, const GLfloat*); + +static nkglGenBuffers glGenBuffers; +static nkglBindBuffer glBindBuffer; +static nkglBufferData glBufferData; +static nkglBufferSubData glBufferSubData; +static nkglMapBuffer glMapBuffer; +static nkglUnmapBuffer glUnmapBuffer; +static nkglDeleteBuffers glDeleteBuffers; +static nkglGenVertexArrays glGenVertexArrays; +static nkglBindVertexArray glBindVertexArray; +static nkglDeleteVertexArrays glDeleteVertexArrays; +static nkglVertexAttribPointer glVertexAttribPointer; +static nkglEnableVertexAttribArray glEnableVertexAttribArray; +static nkglDisableVertexAttribArray glDisableVertexAttribArray; +static nkglGenerateMipmap glGenerateMipmap; +static nkglCreateShader glCreateShader; +static nkglShaderSource glShaderSource; +static nkglCompileShader glCompileShader; +static nkglGetShaderiv glGetShaderiv; +static nkglGetShaderInfoLog glGetShaderInfoLog; +static nkglDeleteShader glDeleteShader; +static nkglCreateProgram glCreateProgram; +static nkglAttachShader glAttachShader; +static nkglDetachShader glDetachShader; +static nkglLinkProgram glLinkProgram; +static nkglUseProgram glUseProgram; +static nkglGetProgramiv glGetProgramiv; +static nkglGetProgramInfoLog glGetProgramInfoLog; +static nkglDeleteProgram glDeleteProgram; +static nkglGetUniformLocation glGetUniformLocation; +static nkglGetAttribLocation glGetAttribLocation; +static nkglUniform1i glUniform1i; +static nkglUniform1f glUniform1f; +static nkglUniformMatrix3fv glUniformMatrix3fv; +static nkglUniformMatrix4fv glUniformMatrix4fv; + +enum graphics_card_vendors { + VENDOR_UNKNOWN, + VENDOR_NVIDIA, + VENDOR_AMD, + VENDOR_INTEL +}; + +struct opengl_info { + /* info */ + const char *vendor_str; + const char *version_str; + const char *extensions_str; + const char *renderer_str; + const char *glsl_version_str; + enum graphics_card_vendors vendor; + /* version */ + float version; + int major_version; + int minor_version; + /* extensions */ + int glsl_available; + int vertex_buffer_obj_available; + int vertex_array_obj_available; + int map_buffer_range_available; + int fragment_program_available; + int frame_buffer_object_available; +}; +#endif + +struct nk_x11_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +struct nk_x11_device { +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS + struct opengl_info info; +#endif + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint vbo, vao, ebo; + GLuint prog; + GLuint vert_shdr; + GLuint frag_shdr; + GLint attrib_pos; + GLint attrib_uv; + GLint attrib_col; + GLint uniform_tex; + GLint uniform_proj; + GLuint font_tex; +}; + +static struct nk_x11 { + struct nk_x11_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + Cursor cursor; + Display *dpy; + Window win; + long last_button_click; +} x11; + +#ifdef __APPLE__ + #define NK_SHADER_VERSION "#version 150\n" +#else + #define NK_SHADER_VERSION "#version 300 es\n" +#endif + +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS +#include + +NK_INTERN long +nk_timestamp(void) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) return 0; + return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000); +} + +NK_INTERN int +nk_x11_stricmpn(const char *a, const char *b, int len) +{ + int i = 0; + for (i = 0; i < len && a[i] && b[i]; ++i) + if (a[i] != b[i]) return 1; + if (i != len) return 1; + return 0; +} + +NK_INTERN int +nk_x11_check_extension(struct opengl_info *GL, const char *ext) +{ + const char *start, *where, *term; + where = strchr(ext, ' '); + if (where || *ext == '\0') + return nk_false; + + for (start = GL->extensions_str;;) { + where = strstr((const char*)start, ext); + if (!where) break; + term = where + strlen(ext); + if (where == start || *(where - 1) == ' ') { + if (*term == ' ' || *term == '\0') + return nk_true; + } + start = term; + } + return nk_false; +} + +#define GL_EXT(name) (nk##name)nk_gl_ext(#name) +NK_INTERN __GLXextFuncPtr +nk_gl_ext(const char *name) +{ + __GLXextFuncPtr func; + func = glXGetProcAddress((const GLubyte*)name); + if (!func) { + fprintf(stdout, "[GL]: failed to load extension: %s", name); + return NULL; + } + return func; +} + +NK_INTERN int +nk_load_opengl(struct opengl_info *gl) +{ + int failed = nk_false; + gl->version_str = (const char*)glGetString(GL_VERSION); + glGetIntegerv(GL_MAJOR_VERSION, &gl->major_version); + glGetIntegerv(GL_MINOR_VERSION, &gl->minor_version); + if (gl->major_version < 2) { + fprintf(stderr, "[GL]: Graphics card does not fullfill minimum OpenGL 2.0 support\n"); + return 0; + } + gl->version = (float)gl->major_version + (float)gl->minor_version * 0.1f; + gl->renderer_str = (const char*)glGetString(GL_RENDERER); + gl->extensions_str = (const char*)glGetString(GL_EXTENSIONS); + gl->glsl_version_str = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); + gl->vendor_str = (const char*)glGetString(GL_VENDOR); + if (!nk_x11_stricmpn(gl->vendor_str, "ATI", 4) || + !nk_x11_stricmpn(gl->vendor_str, "AMD", 4)) + gl->vendor = VENDOR_AMD; + else if (!nk_x11_stricmpn(gl->vendor_str, "NVIDIA", 6)) + gl->vendor = VENDOR_NVIDIA; + else if (!nk_x11_stricmpn(gl->vendor_str, "Intel", 5)) + gl->vendor = VENDOR_INTEL; + else gl->vendor = VENDOR_UNKNOWN; + + /* Extensions */ + gl->glsl_available = (gl->version >= 2.0f); + if (gl->glsl_available) { + /* GLSL core in OpenGL > 2 */ + glCreateShader = GL_EXT(glCreateShader); + glShaderSource = GL_EXT(glShaderSource); + glCompileShader = GL_EXT(glCompileShader); + glGetShaderiv = GL_EXT(glGetShaderiv); + glGetShaderInfoLog = GL_EXT(glGetShaderInfoLog); + glDeleteShader = GL_EXT(glDeleteShader); + glCreateProgram = GL_EXT(glCreateProgram); + glAttachShader = GL_EXT(glAttachShader); + glDetachShader = GL_EXT(glDetachShader); + glLinkProgram = GL_EXT(glLinkProgram); + glUseProgram = GL_EXT(glUseProgram); + glGetProgramiv = GL_EXT(glGetProgramiv); + glGetProgramInfoLog = GL_EXT(glGetProgramInfoLog); + glDeleteProgram = GL_EXT(glDeleteProgram); + glGetUniformLocation = GL_EXT(glGetUniformLocation); + glGetAttribLocation = GL_EXT(glGetAttribLocation); + glUniform1i = GL_EXT(glUniform1i); + glUniform1f = GL_EXT(glUniform1f); + glUniformMatrix3fv = GL_EXT(glUniformMatrix3fv); + glUniformMatrix4fv = GL_EXT(glUniformMatrix4fv); + } + gl->vertex_buffer_obj_available = nk_x11_check_extension(gl, "GL_ARB_vertex_buffer_object"); + if (gl->vertex_buffer_obj_available) { + /* GL_ARB_vertex_buffer_object */ + glGenBuffers = GL_EXT(glGenBuffers); + glBindBuffer = GL_EXT(glBindBuffer); + glBufferData = GL_EXT(glBufferData); + glBufferSubData = GL_EXT(glBufferSubData); + glMapBuffer = GL_EXT(glMapBuffer); + glUnmapBuffer = GL_EXT(glUnmapBuffer); + glDeleteBuffers = GL_EXT(glDeleteBuffers); + } + gl->fragment_program_available = nk_x11_check_extension(gl, "GL_ARB_fragment_program"); + if (gl->fragment_program_available) { + /* GL_ARB_vertex_program / GL_ARB_fragment_program */ + glVertexAttribPointer = GL_EXT(glVertexAttribPointer); + glEnableVertexAttribArray = GL_EXT(glEnableVertexAttribArray); + glDisableVertexAttribArray = GL_EXT(glDisableVertexAttribArray); + } + gl->vertex_array_obj_available = nk_x11_check_extension(gl, "GL_ARB_vertex_array_object"); + if (gl->vertex_array_obj_available) { + /* GL_ARB_vertex_array_object */ + glGenVertexArrays = GL_EXT(glGenVertexArrays); + glBindVertexArray = GL_EXT(glBindVertexArray); + glDeleteVertexArrays = GL_EXT(glDeleteVertexArrays); + } + gl->frame_buffer_object_available = nk_x11_check_extension(gl, "GL_ARB_framebuffer_object"); + if (gl->frame_buffer_object_available) { + /* GL_ARB_framebuffer_object */ + glGenerateMipmap = GL_EXT(glGenerateMipmap); + } + if (!gl->vertex_buffer_obj_available) { + fprintf(stdout, "[GL] Error: GL_ARB_vertex_buffer_object is not available!\n"); + failed = nk_true; + } + if (!gl->fragment_program_available) { + fprintf(stdout, "[GL] Error: GL_ARB_fragment_program is not available!\n"); + failed = nk_true; + } + if (!gl->vertex_array_obj_available) { + fprintf(stdout, "[GL] Error: GL_ARB_vertex_array_object is not available!\n"); + failed = nk_true; + } + if (!gl->frame_buffer_object_available) { + fprintf(stdout, "[GL] Error: GL_ARB_framebuffer_object is not available!\n"); + failed = nk_true; + } + return !failed; +} +#endif + +NK_API int +nk_x11_device_create(void) +{ + GLint status; + static const GLchar *vertex_shader = + NK_SHADER_VERSION + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 TexCoord;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main() {\n" + " Frag_UV = TexCoord;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" + "}\n"; + static const GLchar *fragment_shader = + NK_SHADER_VERSION + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main(){\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + struct nk_x11_device *dev = &x11.ogl; +#ifdef NK_XLIB_LOAD_OPENGL_EXTENSIONS + if (!nk_load_opengl(&dev->info)) return 0; +#endif + nk_buffer_init_default(&dev->cmds); + + dev->prog = glCreateProgram(); + dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); + dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); + glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); + glCompileShader(dev->vert_shdr); + glCompileShader(dev->frag_shdr); + glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); + assert(status == GL_TRUE); + glAttachShader(dev->prog, dev->vert_shdr); + glAttachShader(dev->prog, dev->frag_shdr); + glLinkProgram(dev->prog); + glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); + assert(status == GL_TRUE); + + dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); + dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); + dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); + dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); + dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); + + { + /* buffer setup */ + GLsizei vs = sizeof(struct nk_x11_vertex); + size_t vp = offsetof(struct nk_x11_vertex, position); + size_t vt = offsetof(struct nk_x11_vertex, uv); + size_t vc = offsetof(struct nk_x11_vertex, col); + + glGenBuffers(1, &dev->vbo); + glGenBuffers(1, &dev->ebo); + glGenVertexArrays(1, &dev->vao); + + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glEnableVertexAttribArray((GLuint)dev->attrib_pos); + glEnableVertexAttribArray((GLuint)dev->attrib_uv); + glEnableVertexAttribArray((GLuint)dev->attrib_col); + + glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); + glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); + glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + return 1; +} + +NK_INTERN void +nk_x11_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_x11_device *dev = &x11.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_x11_device_destroy(void) +{ + struct nk_x11_device *dev = &x11.ogl; + glDetachShader(dev->prog, dev->vert_shdr); + glDetachShader(dev->prog, dev->frag_shdr); + glDeleteShader(dev->vert_shdr); + glDeleteShader(dev->frag_shdr); + glDeleteProgram(dev->prog); + glDeleteTextures(1, &dev->font_tex); + glDeleteBuffers(1, &dev->vbo); + glDeleteBuffers(1, &dev->ebo); + nk_buffer_free(&dev->cmds); +} + +NK_API void +nk_x11_render(enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) +{ + int width, height; + XWindowAttributes attr; + struct nk_x11_device *dev = &x11.ogl; + GLfloat ortho[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f,-2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f,-1.0f, 0.0f}, + {-1.0f,1.0f, 0.0f, 1.0f}, + }; + XGetWindowAttributes(x11.dpy, x11.win, &attr); + width = attr.width; + height = attr.height; + + ortho[0][0] /= (GLfloat)width; + ortho[1][1] /= (GLfloat)height; + + /* setup global state */ + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glActiveTexture(GL_TEXTURE0); + + /* setup program */ + glUseProgram(dev->prog); + glUniform1i(dev->uniform_tex, 0); + glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); + glViewport(0,0,(GLsizei)width,(GLsizei)height); + { + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + void *vertices, *elements; + const nk_draw_index *offset = NULL; + struct nk_buffer vbuf, ebuf; + + /* allocate vertex and element buffer */ + glBindVertexArray(dev->vao); + glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); + + glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW); + + /* load draw vertices & elements directly into vertex + element buffer */ + vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + { + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_x11_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_x11_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_x11_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_x11_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_x11_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* setup buffers to load vertices and elements */ + nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer); + nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer); + nk_convert(&x11.ctx, &dev->cmds, &vbuf, &ebuf, &config); + } + glUnmapBuffer(GL_ARRAY_BUFFER); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + /* iterate over and execute each draw command */ + nk_draw_foreach(cmd, &x11.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x), + (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h))), + (GLint)(cmd->clip_rect.w), + (GLint)(cmd->clip_rect.h)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&x11.ctx); + } + + /* default OpenGL state */ + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +} + +NK_API void +nk_x11_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&x11.atlas); + nk_font_atlas_begin(&x11.atlas); + *atlas = &x11.atlas; +} + +NK_API void +nk_x11_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&x11.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_x11_device_upload_atlas(image, w, h); + nk_font_atlas_end(&x11.atlas, nk_handle_id((int)x11.ogl.font_tex), &x11.ogl.null); + if (x11.atlas.default_font) + nk_style_set_font(&x11.ctx, &x11.atlas.default_font->handle); +} + +NK_API int +nk_x11_handle_event(XEvent *evt) +{ + struct nk_context *ctx = &x11.ctx; + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) { + XDefineCursor(x11.dpy, x11.win, x11.cursor); + ctx->input.mouse.grab = 0; + } else if (ctx->input.mouse.ungrab) { + XWarpPointer(x11.dpy, None, x11.win, 0, 0, 0, 0, + (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + XUndefineCursor(x11.dpy, x11.win); + ctx->input.mouse.ungrab = 0; + } + + if (evt->type == KeyPress || evt->type == KeyRelease) + { + /* Key handler */ + int ret, down = (evt->type == KeyPress); + KeySym *code = XGetKeyboardMapping(x11.dpy, (KeyCode)evt->xkey.keycode, 1, &ret); + if (*code == XK_Shift_L || *code == XK_Shift_R) nk_input_key(ctx, NK_KEY_SHIFT, down); + else if (*code == XK_Delete) nk_input_key(ctx, NK_KEY_DEL, down); + else if (*code == XK_Return) nk_input_key(ctx, NK_KEY_ENTER, down); + else if (*code == XK_Tab) nk_input_key(ctx, NK_KEY_TAB, down); + else if (*code == XK_Left) nk_input_key(ctx, NK_KEY_LEFT, down); + else if (*code == XK_Right) nk_input_key(ctx, NK_KEY_RIGHT, down); + else if (*code == XK_Up) nk_input_key(ctx, NK_KEY_UP, down); + else if (*code == XK_Down) nk_input_key(ctx, NK_KEY_DOWN, down); + else if (*code == XK_BackSpace) nk_input_key(ctx, NK_KEY_BACKSPACE, down); + else if (*code == XK_space && !down) nk_input_char(ctx, ' '); + else if (*code == XK_Page_Up) nk_input_key(ctx, NK_KEY_SCROLL_UP, down); + else if (*code == XK_Page_Down) nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); + else if (*code == XK_Home) { + nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); + } else if (*code == XK_End) { + nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); + } else { + if (*code == 'c' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_COPY, down); + else if (*code == 'v' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_PASTE, down); + else if (*code == 'x' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_CUT, down); + else if (*code == 'z' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_UNDO, down); + else if (*code == 'r' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_REDO, down); + else if (*code == XK_Left && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else if (*code == XK_Right && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else if (*code == 'b' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down); + else if (*code == 'e' && (evt->xkey.state & ControlMask)) + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down); + else { + if (*code == 'i') + nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, down); + else if (*code == 'r') + nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, down); + if (down) { + char buf[32]; + KeySym keysym = 0; + if (XLookupString((XKeyEvent*)evt, buf, 32, &keysym, NULL) != NoSymbol) + nk_input_glyph(ctx, buf); + } + } + } + XFree(code); + return 1; + } else if (evt->type == ButtonPress || evt->type == ButtonRelease) { + /* Button handler */ + int down = (evt->type == ButtonPress); + const int x = evt->xbutton.x, y = evt->xbutton.y; + if (evt->xbutton.button == Button1) { + if (down) { /* Double-Click Button handler */ + long dt = nk_timestamp() - x11.last_button_click; + if (dt > NK_X11_DOUBLE_CLICK_LO && dt < NK_X11_DOUBLE_CLICK_HI) + nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, nk_true); + x11.last_button_click = nk_timestamp(); + } else nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, nk_false); + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + } else if (evt->xbutton.button == Button2) + nk_input_button(ctx, NK_BUTTON_MIDDLE, x, y, down); + else if (evt->xbutton.button == Button3) + nk_input_button(ctx, NK_BUTTON_RIGHT, x, y, down); + else if (evt->xbutton.button == Button4) + nk_input_scroll(ctx, nk_vec2(0,1.0f)); + else if (evt->xbutton.button == Button5) + nk_input_scroll(ctx, nk_vec2(0,-1.0f)); + else return 0; + return 1; + } else if (evt->type == MotionNotify) { + /* Mouse motion handler */ + const int x = evt->xmotion.x, y = evt->xmotion.y; + nk_input_motion(ctx, x, y); + if (ctx->input.mouse.grabbed) { + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + XWarpPointer(x11.dpy, None, x11.win, 0, 0, 0, 0, (int)ctx->input.mouse.pos.x, (int)ctx->input.mouse.pos.y); + } + return 1; + } else if (evt->type == KeymapNotify) { + XRefreshKeyboardMapping(&evt->xmapping); + return 1; + } + return 0; +} + +NK_API struct nk_context* +nk_x11_init(Display *dpy, Window win) +{ + if (!setlocale(LC_ALL,"")) return 0; + if (!XSupportsLocale()) return 0; + if (!XSetLocaleModifiers("@im=none")) return 0; + if (!nk_x11_device_create()) return 0; + + x11.dpy = dpy; + x11.win = win; + + /* create invisible cursor */ + {static XColor dummy; char data[1] = {0}; + Pixmap blank = XCreateBitmapFromData(dpy, win, data, 1, 1); + if (blank == None) return 0; + x11.cursor = XCreatePixmapCursor(dpy, blank, blank, &dummy, &dummy, 0, 0); + XFreePixmap(dpy, blank);} + + nk_init_default(&x11.ctx, 0); + return &x11.ctx; +} + +NK_API void +nk_x11_shutdown(void) +{ + nk_font_atlas_clear(&x11.atlas); + nk_free(&x11.ctx); + nk_x11_device_destroy(); + XFreeCursor(x11.dpy, x11.cursor); + memset(&x11, 0, sizeof(x11)); +} + +#endif diff --git a/meson.build b/meson.build index b7e15f5..227cbf3 100644 --- a/meson.build +++ b/meson.build @@ -97,9 +97,8 @@ executable( 'glava-config', sources: run_command('find', 'glava-config', '-type', 'f', '-name', '*.c', '-print').stdout().strip().split('\n'), dependencies: [ - dependency('glew'), - cc.find_library('glfw'), - cc.find_library('GL') + cc.find_library('GL'), + cc.find_library('X11') ], c_args: '-Wunused-but-set-variable', install: true