diff options
Diffstat (limited to 'nk_pugl')
-rw-r--r-- | nk_pugl/COPYING | 201 | ||||
-rw-r--r-- | nk_pugl/nk_pugl.h | 1299 |
2 files changed, 1500 insertions, 0 deletions
diff --git a/nk_pugl/COPYING b/nk_pugl/COPYING new file mode 100644 index 0000000..ddb9a46 --- /dev/null +++ b/nk_pugl/COPYING @@ -0,0 +1,201 @@ + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/nk_pugl/nk_pugl.h b/nk_pugl/nk_pugl.h new file mode 100644 index 0000000..d20752c --- /dev/null +++ b/nk_pugl/nk_pugl.h @@ -0,0 +1,1299 @@ +/* + * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef _NK_PUGL_H +#define _NK_PUGL_H + +#include <stdatomic.h> +#include <ctype.h> // isalpha + +#ifdef __cplusplus +extern C { +#endif + +#include "pugl/pugl.h" +#include "pugl/gl.h" + +#if defined(_WIN32) +# include <windows.h> // Broken Windows GL headers require this +# include "GL/wglext.h" +# include "GL/glext.h" +#endif + +#if !defined(__APPLE__) && !defined(_WIN32) +# include "GL/glx.h" +# include "GL/glext.h" +#endif + +#define NK_ZERO_COMMAND_MEMORY +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_SIN sinf +#define NK_COS cosf +#define NK_SQRT sqrtf + +#include "nuklear/nuklear.h" +#include "nuklear/example/stb_image.h" + +#ifndef NK_PUGL_API +# define NK_PUGL_API static +#endif + +typedef struct _nk_pugl_config_t nk_pugl_config_t; +typedef struct _nk_pugl_window_t nk_pugl_window_t; +typedef void (*nkglGenerateMipmap)(GLenum target); +typedef void (*nk_pugl_expose_t)(struct nk_context *ctx, + struct nk_rect wbounds, void *data); + +struct _nk_pugl_config_t { + unsigned width; + unsigned height; + unsigned min_width; + unsigned min_height; + + bool resizable; + bool fixed_aspect; + bool ignore; + const char *class; + const char *title; + + struct { + char *face; + int size; + } font; + + intptr_t parent; + + LV2UI_Resize *host_resize; + + void *data; + nk_pugl_expose_t expose; +}; + +struct _nk_pugl_window_t { + nk_pugl_config_t cfg; + float scale; + + PuglView *view; + int quit; + + struct nk_buffer cmds; + struct nk_buffer vbuf; + struct nk_buffer ebuf; + struct nk_draw_null_texture null; + struct nk_context ctx; + struct nk_font_atlas atlas; + struct nk_convert_config conv; + struct { + void *buffer; + size_t size; + } last; + bool has_left; + bool has_entered; + + GLuint font_tex; + nkglGenerateMipmap glGenerateMipmap; + + intptr_t widget; + PuglMod state; +#if !defined(__APPLE__) && !defined(_WIN32) + atomic_flag async; + Display *disp; +#endif +}; + +NK_PUGL_API intptr_t +nk_pugl_init(nk_pugl_window_t *win); + +NK_PUGL_API void +nk_pugl_show(nk_pugl_window_t *win); + +NK_PUGL_API void +nk_pugl_hide(nk_pugl_window_t *win); + +NK_PUGL_API void +nk_pugl_shutdown(nk_pugl_window_t *win); + +NK_PUGL_API void +nk_pugl_wait_for_event(nk_pugl_window_t *win); + +NK_PUGL_API int +nk_pugl_process_events(nk_pugl_window_t *win); + +NK_PUGL_API int +nk_pugl_resize(nk_pugl_window_t *win, int width, int height); + +NK_PUGL_API void +nk_pugl_post_redisplay(nk_pugl_window_t *win); + +NK_PUGL_API void +nk_pugl_async_redisplay(nk_pugl_window_t *win); + +NK_PUGL_API void +nk_pugl_quit(nk_pugl_window_t *win); + +NK_PUGL_API struct nk_image +nk_pugl_icon_load(nk_pugl_window_t *win, const char *filename); + +NK_PUGL_API void +nk_pugl_icon_unload(nk_pugl_window_t *win, struct nk_image img); + +NK_PUGL_API bool +nk_pugl_is_shortcut_pressed(struct nk_input *in, char letter, bool clear); + +NK_PUGL_API void +nk_pugl_copy_to_clipboard(nk_pugl_window_t *win, const char *selection, size_t len); + +NK_PUGL_API const char * +nk_pugl_paste_from_clipboard(nk_pugl_window_t *win, size_t *len); + +NK_PUGL_API float +nk_pugl_get_scale(nk_pugl_window_t *win); + +#ifdef __cplusplus +} +#endif + +#endif // _NK_PUGL_H + +#ifdef NK_PUGL_IMPLEMENTATION + +#ifdef __cplusplus +extern C { +#endif + +#define NK_ZERO_COMMAND_MEMORY +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_STANDARD_IO +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_SIN sinf +#define NK_COS cosf +#define NK_SQRT sqrtf + +# define NK_IMPLEMENTATION +# include "nuklear/nuklear.h" + +# define STB_IMAGE_IMPLEMENTATION +# include "nuklear/example/stb_image.h" + +typedef struct _nk_pugl_vertex_t nk_pugl_vertex_t; + +struct _nk_pugl_vertex_t { + float position [2]; + float uv [2]; + nk_byte col [4]; +}; + +static const struct nk_draw_vertex_layout_element vertex_layout [] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_pugl_vertex_t, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_pugl_vertex_t, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(nk_pugl_vertex_t, col)}, + {NK_VERTEX_LAYOUT_END} +}; + +#if defined(__APPLE__) +# define GL_EXT(name) name + +#elif defined(_WIN32) +static void * +_nk_pugl_gl_ext(const char *name) +{ + void *p = wglGetProcAddress(name); + + if( (p == 0) || (p == (void *)-1) + || (p == (void *)0x1) || (p == (void *)0x2) || (p == (void *)0x3) ) + { + HMODULE module = LoadLibraryA("opengl32.dll"); + p = (void *)GetProcAddress(module, name); + } + + if(!p) + fprintf(stderr, "[GL]: failed to load extension: %s", name); + + return p; +} +# define GL_EXT(name) (nk##name)_nk_pugl_gl_ext(#name) + +#else +static void * +_nk_pugl_gl_ext(const char *name) +{ + void *p = glXGetProcAddress((const GLubyte*)name); + + if(!p) + fprintf(stderr, "[GL]: failed to load extension: %s", name); + + return p; +} +# define GL_EXT(name) (nk##name)_nk_pugl_gl_ext(#name) +#endif + +static inline void +_nk_pugl_device_upload_atlas(nk_pugl_window_t *win, const void *image, + int width, int height) +{ + glGenTextures(1, &win->font_tex); + glBindTexture(GL_TEXTURE_2D, win->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, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +static inline void +_nk_pugl_render_gl2_push(unsigned width, unsigned height) +{ + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.f, width, height, 0.f, -1.f, 1.f); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); +} + +static inline void +_nk_pugl_render_gl2_pop(void) +{ + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glPopAttrib(); +} + +static inline void +_nk_pugl_render_gl2(nk_pugl_window_t *win) +{ + nk_pugl_config_t *cfg = &win->cfg; + bool has_changes = win->has_left || win->has_entered; + + // compare current command buffer with last one to defer any changes + if(!has_changes) + { + const size_t size = win->ctx.memory.allocated; + const void *commands = nk_buffer_memory_const(&win->ctx.memory); + + if( (size != win->last.size) || memcmp(commands, win->last.buffer, size) ) + { + // swap last buffer with current one for next comparison + win->last.buffer = realloc(win->last.buffer, size); + if(win->last.buffer) + { + win->last.size = size; + memcpy(win->last.buffer, commands, size); + } + else + { + win->last.size = 0; + } + has_changes = true; + } + } + + if(has_changes) + { + // clear command/vertex buffers of last stable view + nk_buffer_clear(&win->cmds); + nk_buffer_clear(&win->vbuf); + nk_buffer_clear(&win->ebuf); + nk_draw_list_clear(&win->ctx.draw_list); + + // convert shapes into vertexes if there were changes + nk_convert(&win->ctx, &win->cmds, &win->vbuf, &win->ebuf, &win->conv); + } + + _nk_pugl_render_gl2_push(cfg->width, cfg->height); + + // setup vertex buffer pointers + const GLsizei vs = sizeof(nk_pugl_vertex_t); + const size_t vp = offsetof(nk_pugl_vertex_t, position); + const size_t vt = offsetof(nk_pugl_vertex_t, uv); + const size_t vc = offsetof(nk_pugl_vertex_t, col); + const nk_byte *vertices = nk_buffer_memory_const(&win->vbuf); + glVertexPointer(2, GL_FLOAT, vs, &vertices[vp]); + glTexCoordPointer(2, GL_FLOAT, vs, &vertices[vt]); + glColorPointer(4, GL_UNSIGNED_BYTE, vs, &vertices[vc]); + + // iterate over and execute each draw command + const nk_draw_index *offset = nk_buffer_memory_const(&win->ebuf); + const struct nk_draw_command *cmd; + nk_draw_foreach(cmd, &win->ctx, &win->cmds) + { + if(!cmd->elem_count) + continue; + + glBindTexture(GL_TEXTURE_2D, cmd->texture.id); + glScissor( + cmd->clip_rect.x, + cfg->height - (cmd->clip_rect.y + cmd->clip_rect.h), + cmd->clip_rect.w, + cmd->clip_rect.h); + glDrawElements(GL_TRIANGLES, cmd->elem_count, GL_UNSIGNED_SHORT, offset); + + offset += cmd->elem_count; + } + + _nk_pugl_render_gl2_pop(); + + win->has_entered = false; + + nk_clear(&win->ctx); +} + +static void +_nk_pugl_font_init(nk_pugl_window_t *win) +{ + nk_pugl_config_t *cfg = &win->cfg; + + const int font_size = cfg->font.size * win->scale; + + // init nuklear font + struct nk_font *ttf = NULL; + struct nk_font_config fcfg = nk_font_config(font_size); + static const nk_rune range [] = { + 0x0020, 0x007F, // Basic Latin + 0x00A0, 0x00FF, // Latin-1 Supplement + 0x0100, 0x017F, // Latin Extended-A + 0x0180, 0x024F, // Latin Extended-B + 0x0300, 0x036F, // Combining Diacritical Marks + 0x0370, 0x03FF, // Greek and Coptic + 0x0400, 0x04FF, // Cyrillic + 0x0500, 0x052F, // Cyrillic Supplementary + 0 + }; + fcfg.range = range; + fcfg.oversample_h = 8; + fcfg.oversample_v = 8; + + struct nk_font_atlas *atlas = &win->atlas; + nk_font_atlas_init_default(atlas); + nk_font_atlas_begin(atlas); + + if(cfg->font.face && font_size) + ttf = nk_font_atlas_add_from_file(&win->atlas, cfg->font.face, font_size, &fcfg); + + int w = 0; + int h = 0; + struct nk_draw_null_texture null; + const void *image = nk_font_atlas_bake(atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + _nk_pugl_device_upload_atlas(win, image, w, h); + nk_font_atlas_end(atlas, nk_handle_id(win->font_tex), &null); + + if(atlas->default_font) + nk_style_set_font(&win->ctx, &atlas->default_font->handle); + + if(ttf) + nk_style_set_font(&win->ctx, &ttf->handle); +} + +static void +_nk_pugl_font_deinit(nk_pugl_window_t *win) +{ + nk_font_atlas_clear(&win->atlas); + + if(win->font_tex) + glDeleteTextures(1, (const GLuint *)&win->font_tex); +} + +static void +_nk_pugl_host_resize(nk_pugl_window_t *win) +{ + nk_pugl_config_t *cfg = &win->cfg; + + if(cfg->host_resize) + { + cfg->host_resize->ui_resize(cfg->host_resize->handle, + cfg->width, cfg->height); + } +} + +static void +_nk_pugl_reconfigure(nk_pugl_window_t *win) +{ + puglEnterContext(win->view); + { + _nk_pugl_font_deinit(win); + _nk_pugl_font_init(win); + } + puglLeaveContext(win->view, false); + +#if 0 + if(win->cfg.resizable) + _nk_pugl_host_resize(win); +#endif +} + +static void +_nk_pugl_zoom_in(nk_pugl_window_t *win) +{ + win->scale += 0.1; + + _nk_pugl_reconfigure(win); +} + +static void +_nk_pugl_zoom_out(nk_pugl_window_t *win) +{ + if(win->scale >= 0.6) + { + win->scale -= 0.1; + + _nk_pugl_reconfigure(win); + } +} + +static void +_nk_pugl_key_press(struct nk_context *ctx, enum nk_keys key) +{ + nk_input_key(ctx, key, nk_true); + nk_input_key(ctx, key, nk_false); +} + +static bool +_nk_pugl_special_key_down(nk_pugl_window_t *win, const PuglEventKey *ev) +{ + struct nk_context *ctx = &win->ctx; + const bool control = ev->state & PUGL_MOD_CTRL; + + switch(ev->special) + { + case PUGL_KEY_F1: + case PUGL_KEY_F2: + case PUGL_KEY_F3: + case PUGL_KEY_F4: + case PUGL_KEY_F5: + case PUGL_KEY_F6: + case PUGL_KEY_F7: + case PUGL_KEY_F8: + case PUGL_KEY_F9: + case PUGL_KEY_F10: + case PUGL_KEY_F11: + case PUGL_KEY_F12: + { + //TODO + } break; + case PUGL_KEY_LEFT: + { + if(control) + _nk_pugl_key_press(ctx, NK_KEY_TEXT_WORD_LEFT); + else + _nk_pugl_key_press(ctx, NK_KEY_LEFT); + } break; + case PUGL_KEY_RIGHT: + { + if(control) + _nk_pugl_key_press(ctx, NK_KEY_TEXT_WORD_RIGHT); + else + _nk_pugl_key_press(ctx, NK_KEY_RIGHT); + } break; + case PUGL_KEY_UP: + { + _nk_pugl_key_press(ctx, NK_KEY_UP); + } break; + case PUGL_KEY_DOWN: + { + _nk_pugl_key_press(ctx, NK_KEY_DOWN); + } break; + case PUGL_KEY_PAGE_UP: + { + _nk_pugl_key_press(ctx, NK_KEY_SCROLL_UP); + } break; + case PUGL_KEY_PAGE_DOWN: + { + _nk_pugl_key_press(ctx, NK_KEY_SCROLL_DOWN); + } break; + case PUGL_KEY_HOME: + { + if(control) + { + _nk_pugl_key_press(ctx, NK_KEY_TEXT_START); + _nk_pugl_key_press(ctx, NK_KEY_SCROLL_START); + } + else + { + _nk_pugl_key_press(ctx, NK_KEY_TEXT_LINE_START); + } + } break; + case PUGL_KEY_END: + { + if(control) + { + _nk_pugl_key_press(ctx, NK_KEY_TEXT_END); + _nk_pugl_key_press(ctx, NK_KEY_SCROLL_END); + } + else + { + _nk_pugl_key_press(ctx, NK_KEY_TEXT_LINE_END); + } + } break; + case PUGL_KEY_INSERT: + { + _nk_pugl_key_press(ctx, NK_KEY_TEXT_INSERT_MODE); + } break; + case PUGL_KEY_SHIFT: + { + win->state |= PUGL_MOD_SHIFT; + nk_input_key(ctx, NK_KEY_SHIFT, nk_true); + } return true; + case PUGL_KEY_CTRL: + { + win->state |= PUGL_MOD_CTRL; + nk_input_key(ctx, NK_KEY_CTRL, nk_true); + } return true; + case PUGL_KEY_ALT: + { + // TODO + } break; + case PUGL_KEY_SUPER: + { + // TODO + } break; + } + + return false; +} + +static bool +_nk_pugl_special_key_up(nk_pugl_window_t *win, const PuglEventKey *ev) +{ + struct nk_context *ctx = &win->ctx; + + switch(ev->special) + { + case PUGL_KEY_F1: + case PUGL_KEY_F2: + case PUGL_KEY_F3: + case PUGL_KEY_F4: + case PUGL_KEY_F5: + case PUGL_KEY_F6: + case PUGL_KEY_F7: + case PUGL_KEY_F8: + case PUGL_KEY_F9: + case PUGL_KEY_F10: + case PUGL_KEY_F11: + case PUGL_KEY_F12: + case PUGL_KEY_LEFT: + case PUGL_KEY_RIGHT: + case PUGL_KEY_UP: + case PUGL_KEY_DOWN: + case PUGL_KEY_PAGE_UP: + case PUGL_KEY_PAGE_DOWN: + case PUGL_KEY_HOME: + case PUGL_KEY_END: + case PUGL_KEY_INSERT: + { + // ignore + } break; + case PUGL_KEY_SHIFT: + { + nk_input_key(ctx, NK_KEY_SHIFT, nk_false); + win->state &= ~PUGL_MOD_SHIFT; + } return true; + case PUGL_KEY_CTRL: + { + nk_input_key(ctx, NK_KEY_CTRL, nk_false); + win->state &= ~PUGL_MOD_CTRL; + } return true; + case PUGL_KEY_ALT: + { + // TODO + } break; + case PUGL_KEY_SUPER: + { + // TODO + } break; + } + + return false; +} + +static bool +_nk_pugl_other_key_down(nk_pugl_window_t *win, const PuglEventKey *ev) +{ + struct nk_context *ctx = &win->ctx; +#if defined(__APPLE__) + const bool modifier = ev->state & PUGL_MOD_SUPER; +#else + const bool modifier = ev->state & PUGL_MOD_CTRL; +#endif + const bool shift = ev->state & PUGL_MOD_SHIFT; + + // automatically enter insert mode upon non-special key press + _nk_pugl_key_press(ctx, NK_KEY_TEXT_INSERT_MODE); + + switch(ev->character) + { + case '\n': + case '\r': + { + _nk_pugl_key_press(ctx, NK_KEY_ENTER); + } break; + case '\t': + { + _nk_pugl_key_press(ctx, NK_KEY_TAB); + } break; + case PUGL_CHAR_DELETE: + { +#if defined(__APPLE__) // quirk around Apple's Delete key strangeness + _nk_pugl_key_press(ctx, NK_KEY_BACKSPACE); +#else + _nk_pugl_key_press(ctx, NK_KEY_DEL); +#endif + } break; + case PUGL_CHAR_BACKSPACE: + { +#if defined(__APPLE__) // quirk around Apple's Delete key strangeness + _nk_pugl_key_press(ctx, NK_KEY_DEL); +#else + _nk_pugl_key_press(ctx, NK_KEY_BACKSPACE); +#endif + } break; + case PUGL_CHAR_ESCAPE: + { + _nk_pugl_key_press(ctx, NK_KEY_TEXT_RESET_MODE); + } break; + + default: + { + if(modifier) + { + if(ev->character == '+') + { + _nk_pugl_zoom_in(win); + break; + } + else if(ev->character == '-') + { + _nk_pugl_zoom_out(win); + break; + } + + // unescape ASCII control chars + Control + const uint32_t character = ev->character | 0x60; + + switch(character) + { + case 'c': + { + _nk_pugl_key_press(ctx, NK_KEY_COPY); + } break; + case 'v': + { + _nk_pugl_key_press(ctx, NK_KEY_PASTE); + } break; + case 'x': + { + _nk_pugl_key_press(ctx, NK_KEY_CUT); + } break; + case 'z': + { + if(shift) + _nk_pugl_key_press(ctx, NK_KEY_TEXT_REDO); + else + _nk_pugl_key_press(ctx, NK_KEY_TEXT_UNDO); + } break; + + default: + { + nk_input_glyph(ctx, (const char *)ev->utf8); + } break; + } + } + else + { + nk_input_glyph(ctx, (const char *)ev->utf8); + } + } break; + } + + return false; +} + +static void +_nk_pugl_modifiers(nk_pugl_window_t *win, PuglMod state) +{ + struct nk_context *ctx = &win->ctx; + + if(win->state != state) // modifiers changed + { + if( (win->state & PUGL_MOD_SHIFT) != (state & PUGL_MOD_SHIFT)) + nk_input_key(ctx, NK_KEY_SHIFT, (state & PUGL_MOD_SHIFT) == PUGL_MOD_SHIFT); + + if( (win->state & PUGL_MOD_CTRL) != (state & PUGL_MOD_CTRL)) + nk_input_key(ctx, NK_KEY_CTRL, (state & PUGL_MOD_CTRL) == PUGL_MOD_CTRL); + + if( (win->state & PUGL_MOD_ALT) != (state & PUGL_MOD_ALT)) + { + // not yet supported in nuklear + } + + if( (win->state & PUGL_MOD_SUPER) != (state & PUGL_MOD_SUPER)) + { + // not yet supported in nuklear + } + + win->state = state; // switch old and new modifier states + } +} + +static bool +_nk_pugl_key_down(nk_pugl_window_t *win, const PuglEventKey *ev) +{ + if(ev->special) + return _nk_pugl_special_key_down(win, ev); + else if(ev->character && !ev->filter) + return _nk_pugl_other_key_down(win, ev); + + return false; +} + +static bool +_nk_pugl_key_up(nk_pugl_window_t *win, const PuglEventKey *ev) +{ + if(ev->special) + return _nk_pugl_special_key_up(win, ev); + + return false; +} + +static inline void +_nk_pugl_expose(PuglView *view) +{ + nk_pugl_window_t *win = puglGetHandle(view); + nk_pugl_config_t *cfg = &win->cfg; + struct nk_context *ctx = &win->ctx; + + const struct nk_rect wbounds = nk_rect(0, 0, cfg->width, cfg->height); + + if(nk_begin(ctx, "__bg__", wbounds, 0)) + { + const struct nk_rect obounds = nk_window_get_bounds(ctx); + + if( (obounds.x != wbounds.x) || (obounds.y != wbounds.y) + || (obounds.w != wbounds.w) || (obounds.h != wbounds.h) ) + { + // size has changed + nk_window_set_bounds(ctx, wbounds); + puglPostRedisplay(view); + } + + // clears window with widget background color + } + nk_end(ctx); + + if(cfg->expose) + cfg->expose(ctx, wbounds, cfg->data); + + _nk_pugl_render_gl2(win); +} + +static void +_nk_pugl_dummy_func(PuglView *view, const PuglEvent *e) +{ + // do nothing +} + +static void +_nk_pugl_event_func(PuglView *view, const PuglEvent *e) +{ + nk_pugl_window_t *win = puglGetHandle(view); + nk_pugl_config_t *cfg = &win->cfg; + struct nk_context *ctx = &win->ctx; + + switch(e->type) + { + case PUGL_NOTHING: + { + break; + } + case PUGL_BUTTON_PRESS: + { + const PuglEventButton *ev = (const PuglEventButton *)e; + + _nk_pugl_modifiers(win, ev->state); + nk_input_button(ctx, ev->button - 1, ev->x, ev->y, 1); + + puglPostRedisplay(win->view); + break; + } + case PUGL_BUTTON_RELEASE: + { + const PuglEventButton *ev = (const PuglEventButton *)e; + + _nk_pugl_modifiers(win, ev->state); + nk_input_button(ctx, ev->button - 1, ev->x, ev->y, 0); + + puglPostRedisplay(win->view); + break; + } + case PUGL_CONFIGURE: + { + const PuglEventConfigure *ev = (const PuglEventConfigure *)e; + + cfg->width = ev->width; + cfg->height = ev->height; + + puglPostRedisplay(win->view); + break; + } + case PUGL_EXPOSE: + { + nk_input_end(ctx); + _nk_pugl_expose(win->view); + nk_input_begin(ctx); + + break; + } + case PUGL_CLOSE: + { + nk_pugl_quit(win); + + break; + } + case PUGL_KEY_PRESS: + { + const PuglEventKey *ev = (const PuglEventKey *)e; + + if(!_nk_pugl_key_down(win, ev)) // no modifier change + _nk_pugl_modifiers(win, ev->state); + + puglPostRedisplay(win->view); + break; + } + case PUGL_KEY_RELEASE: + { + const PuglEventKey *ev = (const PuglEventKey *)e; + + if(!_nk_pugl_key_up(win, ev)) // no modifier change + _nk_pugl_modifiers(win, ev->state); + + puglPostRedisplay(win->view); + break; + } + case PUGL_MOTION_NOTIFY: + { + const PuglEventMotion *ev = (const PuglEventMotion *)e; + + _nk_pugl_modifiers(win, ev->state); + nk_input_motion(ctx, ev->x, ev->y); + + puglPostRedisplay(win->view); + break; + } + case PUGL_SCROLL: + { + const PuglEventScroll *ev = (const PuglEventScroll *)e; + + _nk_pugl_modifiers(win, ev->state); + if(win->state & PUGL_MOD_CTRL) + { + if(ev->dy > 0) + _nk_pugl_zoom_in(win); + else + _nk_pugl_zoom_out(win); + } + else + nk_input_scroll(ctx, ev->dy); + + puglPostRedisplay(win->view); + break; + } + case PUGL_LEAVE_NOTIFY: + { + const PuglEventCrossing *ev = (const PuglEventCrossing *)e; + + _nk_pugl_modifiers(win, ev->state); + win->has_left = true; + puglPostRedisplay(win->view); + break; + } + case PUGL_ENTER_NOTIFY: + { + const PuglEventCrossing *ev = (const PuglEventCrossing *)e; + + _nk_pugl_modifiers(win, ev->state); + win->has_left = false; + win->has_entered = true; + puglPostRedisplay(win->view); + break; + } + + case PUGL_FOCUS_OUT: + case PUGL_FOCUS_IN: + { + break; + } + } +} + +NK_PUGL_API intptr_t +nk_pugl_init(nk_pugl_window_t *win) +{ + nk_pugl_config_t *cfg = &win->cfg; + struct nk_convert_config *conv = &win->conv; + + const char *NK_SCALE = getenv("NK_SCALE"); + const float scale = NK_SCALE ? atof(NK_SCALE) : 1.f; + const float dpi0 = 96.f; // reference DPI we're designing for + +#if defined(__APPLE__) + const float dpi1 = dpi0; //TODO implement this +#elif defined(_WIN32) + // GetDpiForSystem/Monitor/Window is Win10 only + HDC screen = GetDC(NULL); + const float dpi1 = GetDeviceCaps(screen, LOGPIXELSX); + ReleaseDC(NULL, screen); +#else + win->async = (atomic_flag)ATOMIC_FLAG_INIT; + win->disp = XOpenDisplay(0); + const float dpi1 = XDisplayWidth(win->disp, 0) * 25.4f / XDisplayWidthMM(win->disp, 0); +#endif + + win->scale = scale * dpi1 / dpi0; + if(win->scale < 0.5) + win->scale = 0.5; + win->has_left = true; + + cfg->width *= win->scale; + cfg->height *= win->scale; + cfg->min_width *= win->scale; + cfg->min_height *= win->scale; + + // init pugl + win->view = puglInit(NULL, NULL); + puglInitWindowClass(win->view, cfg->class ? cfg->class : "nuklear"); + puglInitWindowSize(win->view, cfg->width, cfg->height); + puglInitWindowMinSize(win->view, cfg->min_width, cfg->min_height); + puglInitResizable(win->view, cfg->resizable); + if(cfg->fixed_aspect) + puglInitWindowAspectRatio(win->view, cfg->width, cfg->height, cfg->width, cfg->height); + puglInitWindowParent(win->view, cfg->parent); + puglInitTransientFor(win->view, cfg->parent); + puglSetHandle(win->view, win); + puglSetEventFunc(win->view, _nk_pugl_dummy_func); + puglIgnoreKeyRepeat(win->view, cfg->ignore); + puglInitContextType(win->view, PUGL_GL); + const int stat = puglCreateWindow(win->view, cfg->title ? cfg->title : "Nuklear"); + assert(stat == 0); + + // init nuklear + nk_buffer_init_default(&win->cmds); + nk_buffer_init_default(&win->vbuf); + nk_buffer_init_default(&win->ebuf); + nk_init_default(&win->ctx, 0); + + puglEnterContext(win->view); + { + // init font system + _nk_pugl_font_init(win); + + win->glGenerateMipmap = GL_EXT(glGenerateMipmap); + } + puglLeaveContext(win->view, false); + + // fill convert configuration + conv->vertex_layout = vertex_layout; + conv->vertex_size = sizeof(nk_pugl_vertex_t); + conv->vertex_alignment = NK_ALIGNOF(nk_pugl_vertex_t); + conv->null = win->null; + conv->circle_segment_count = 22; + conv->curve_segment_count = 22; + conv->arc_segment_count = 22; + conv->global_alpha = 1.0f; + conv->shape_AA = NK_ANTI_ALIASING_ON; + conv->line_AA = NK_ANTI_ALIASING_ON; + + puglSetEventFunc(win->view, _nk_pugl_event_func); + nk_input_begin(&win->ctx); + + win->widget = puglGetNativeWindow(win->view); + return win->widget; +} + +NK_PUGL_API void +nk_pugl_show(nk_pugl_window_t *win) +{ + if(!win->view) + return; + + puglShowWindow(win->view); + _nk_pugl_host_resize(win); +} + +NK_PUGL_API void +nk_pugl_hide(nk_pugl_window_t *win) +{ + if(!win->view) + return; + + puglHideWindow(win->view); +} + +NK_PUGL_API void +nk_pugl_shutdown(nk_pugl_window_t *win) +{ + if(!win->view) + return; + + nk_input_end(&win->ctx); + + if(win->last.buffer) + free(win->last.buffer); + + puglEnterContext(win->view); + { + // deinit font system + _nk_pugl_font_deinit(win); + } + puglLeaveContext(win->view, false); + + // shutdown nuklear + nk_buffer_free(&win->cmds); + nk_buffer_free(&win->vbuf); + nk_buffer_free(&win->ebuf); + nk_free(&win->ctx); + + // shutdown pugl + puglDestroy(win->view); + +#if !defined(__APPLE__) && !defined(_WIN32) + if(win->disp) + XCloseDisplay(win->disp); +#endif +} + +NK_PUGL_API void +nk_pugl_wait_for_event(nk_pugl_window_t *win) +{ + if(!win->view) + return; + + puglWaitForEvent(win->view); +} + +NK_PUGL_API int +nk_pugl_process_events(nk_pugl_window_t *win) +{ + if(!win->view) + return 1; // quit + + struct nk_context *ctx = &win->ctx; + + PuglStatus stat = puglProcessEvents(win->view); + (void)stat; + + return win->quit; +} + +NK_PUGL_API int +nk_pugl_resize(nk_pugl_window_t *win, int width, int height) +{ + if(!win->view) + return 1; // quit + + win->cfg.width = width; + win->cfg.height = height; + + puglPostRedisplay(win->view); + + return 0; +} + +NK_PUGL_API void +nk_pugl_post_redisplay(nk_pugl_window_t *win) +{ + if(!win->view) + return; + + puglPostRedisplay(win->view); +} + +NK_PUGL_API void +nk_pugl_async_redisplay(nk_pugl_window_t *win) +{ + if(!win->view) + return; + +#if defined(__APPLE__) +// TODO +#elif defined(_WIN32) + const HWND widget = (HWND)win->widget; + const int status = SendNotifyMessage(widget, WM_PAINT, 0, 0); + (void)status; +#else + const Window widget = (Window)win->widget; + XExposeEvent xevent = { + .type = Expose, + .display = win->disp, + .window = widget + }; + + while(atomic_flag_test_and_set_explicit(&win->async, memory_order_acquire)) + { + // spin + } + + const Status status = XSendEvent(win->disp, widget, false, ExposureMask, + (XEvent *)&xevent); + (void)status; + XFlush(win->disp); + + atomic_flag_clear_explicit(&win->async, memory_order_release); +#endif +} + +NK_PUGL_API void +nk_pugl_quit(nk_pugl_window_t *win) +{ + win->quit = 1; +} + +NK_PUGL_API struct nk_image +nk_pugl_icon_load(nk_pugl_window_t *win, const char *filename) +{ + GLuint tex = 0; + + if(!win->view) + return nk_image_id(tex); + + int w, h, n; + uint8_t *data = stbi_load(filename, &w, &h, &n, 4); + if(data) + { + puglEnterContext(win->view); + { + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if(!win->glGenerateMipmap) // for GL >= 1.4 && < 3.1 + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + if(win->glGenerateMipmap) // for GL >= 3.1 + win->glGenerateMipmap(GL_TEXTURE_2D); + } + puglLeaveContext(win->view, false); + + stbi_image_free(data); + } + + return nk_image_id(tex); +} + +NK_PUGL_API void +nk_pugl_icon_unload(nk_pugl_window_t *win, struct nk_image img) +{ + if(!win->view) + return; + + if(img.handle.id) + { + puglEnterContext(win->view); + { + glDeleteTextures(1, (const GLuint *)&img.handle.id); + } + puglLeaveContext(win->view, false); + } +} + +NK_PUGL_API bool +nk_pugl_is_shortcut_pressed(struct nk_input *in, char letter, bool clear) +{ + const bool control = nk_input_is_key_down(in, NK_KEY_CTRL); + + if(control && (in->keyboard.text_len == 1) ) + { + // unescape ASCII control chars + Control + const char ch = in->keyboard.text[0] | 0x60; + + if(isalpha(ch) && (ch == letter) ) // pass non-alpha characters through + { + if(clear) + in->keyboard.text_len = 0; + + return true; // matching shortcut + } + } + + return false; +} + +NK_PUGL_API void +nk_pugl_copy_to_clipboard(nk_pugl_window_t *win, const char *selection, size_t len) +{ + puglCopyToClipboard(win->view, selection, len); +} + +NK_PUGL_API const char * +nk_pugl_paste_from_clipboard(nk_pugl_window_t *win, size_t *len) +{ + return puglPasteFromClipboard(win->view, len); +} + +NK_PUGL_API float +nk_pugl_get_scale(nk_pugl_window_t *win) +{ + return win->scale; +} + +#ifdef __cplusplus +} +#endif + +#endif // NK_PUGL_IMPLEMENTATION |