1 /*
2 * Copyright (c) 2016-2020 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #ifndef _NK_PUGL_H
19 #define _NK_PUGL_H
20
21 #include <stdatomic.h>
22 #include <ctype.h> // isalpha
23 #include <math.h> // isalpha
24
25 #ifdef __cplusplus
26 extern C {
27 #endif
28
29 #if defined(__APPLE__)
30 # include <OpenGL/gl.h>
31 # include <OpenGL/glext.h>
32 #else
33 # include <GL/glew.h>
34 # ifdef _WIN32
35 # include <windows.h>
36 # else
37 # include <X11/Xresource.h>
38 # endif
39 #endif
40
41 #include "pugl/pugl.h"
42 #include "pugl/gl.h"
43
44 #include <lv2/ui/ui.h>
45
46 #define KEY_TAB '\t'
47 #define KEY_NEWLINE '\n'
48 #define KEY_RETURN '\r'
49 #define KEY_PLUS '+'
50 #define KEY_MINUS '-'
51 #define KEY_C 'c'
52 #define KEY_V 'v'
53 #define KEY_X 'x'
54 #define KEY_Z 'z'
55
56 #define NK_ZERO_COMMAND_MEMORY
57 #define NK_INCLUDE_FIXED_TYPES
58 #define NK_INCLUDE_DEFAULT_ALLOCATOR
59 #define NK_INCLUDE_STANDARD_IO
60 #define NK_INCLUDE_STANDARD_VARARGS
61 #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
62 #define NK_INCLUDE_FONT_BAKING
63 #define NK_INCLUDE_DEFAULT_FONT
64 #define NK_SIN sinf
65 #define NK_COS cosf
66 #define NK_SQRT sqrtf
67
68 #include "nuklear/nuklear.h"
69 #include "nuklear/example/stb_image.h"
70
71 #ifndef NK_PUGL_API
72 # define NK_PUGL_API static inline
73 #endif
74
75 typedef struct _nk_pugl_config_t nk_pugl_config_t;
76 typedef struct _nk_pugl_window_t nk_pugl_window_t;
77 typedef void (*nkglGenerateMipmap)(GLenum target);
78 typedef void (*nk_pugl_expose_t)(struct nk_context *ctx,
79 struct nk_rect wbounds, void *data);
80
81 struct _nk_pugl_config_t {
82 unsigned width;
83 unsigned height;
84 unsigned min_width;
85 unsigned min_height;
86
87 bool resizable;
88 bool fixed_aspect;
89 bool ignore;
90 const char *class;
91 const char *title;
92
93 struct {
94 char *face;
95 int size;
96 } font;
97
98 intptr_t parent;
99 bool threads;
100
101 LV2UI_Resize *host_resize;
102
103 void *data;
104 nk_pugl_expose_t expose;
105 };
106
107 struct _nk_pugl_window_t {
108 nk_pugl_config_t cfg;
109 char urn [46];
110
111 PuglWorld *world;
112 PuglView *view;
113 int quit;
114
115 struct nk_buffer cmds;
116 struct nk_buffer vbuf;
117 struct nk_buffer ebuf;
118 struct nk_draw_null_texture null;
119 struct nk_context ctx;
120 struct nk_font_atlas atlas;
121 struct nk_convert_config conv;
122 struct {
123 void *buffer;
124 size_t size;
125 } last;
126 bool has_left;
127 bool has_entered;
128
129 GLuint font_tex;
130 nkglGenerateMipmap glGenerateMipmap;
131
132 intptr_t widget;
133 PuglMod state;
134 #if !defined(__APPLE__) && !defined(_WIN32)
135 atomic_flag async;
136 #endif
137 };
138
139 NK_PUGL_API intptr_t
140 nk_pugl_init(nk_pugl_window_t *win);
141
142 NK_PUGL_API void
143 nk_pugl_show(nk_pugl_window_t *win);
144
145 NK_PUGL_API void
146 nk_pugl_hide(nk_pugl_window_t *win);
147
148 NK_PUGL_API void
149 nk_pugl_shutdown(nk_pugl_window_t *win);
150
151 NK_PUGL_API void
152 nk_pugl_wait_for_event(nk_pugl_window_t *win);
153
154 NK_PUGL_API int
155 nk_pugl_process_events(nk_pugl_window_t *win);
156
157 NK_PUGL_API int
158 nk_pugl_resize(nk_pugl_window_t *win, int width, int height);
159
160 NK_PUGL_API void
161 nk_pugl_post_redisplay(nk_pugl_window_t *win);
162
163 NK_PUGL_API void
164 nk_pugl_async_redisplay(nk_pugl_window_t *win);
165
166 NK_PUGL_API void
167 nk_pugl_quit(nk_pugl_window_t *win);
168
169 NK_PUGL_API struct nk_image
170 nk_pugl_icon_load(nk_pugl_window_t *win, const char *filename);
171
172 NK_PUGL_API void
173 nk_pugl_icon_unload(nk_pugl_window_t *win, struct nk_image img);
174
175 NK_PUGL_API bool
176 nk_pugl_is_shortcut_pressed(struct nk_input *in, char letter, bool clear);
177
178 NK_PUGL_API void
179 nk_pugl_copy_to_clipboard(nk_pugl_window_t *win, const char *selection, size_t len);
180
181 NK_PUGL_API const char *
182 nk_pugl_paste_from_clipboard(nk_pugl_window_t *win, size_t *len);
183
184 NK_PUGL_API float
185 nk_pugl_get_scale();
186
187 #ifdef __cplusplus
188 }
189 #endif
190
191 #endif // _NK_PUGL_H
192
193 #ifdef NK_PUGL_IMPLEMENTATION
194
195 #ifdef __cplusplus
196 extern C {
197 #endif
198
199 #define NK_ZERO_COMMAND_MEMORY
200 #define NK_INCLUDE_FIXED_TYPES
201 #define NK_INCLUDE_DEFAULT_ALLOCATOR
202 #define NK_INCLUDE_STANDARD_IO
203 #define NK_INCLUDE_STANDARD_VARARGS
204 #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
205 #define NK_INCLUDE_FONT_BAKING
206 #define NK_INCLUDE_DEFAULT_FONT
207 #define NK_SIN sinf
208 #define NK_COS cosf
209 #define NK_SQRT sqrtf
210
211 #define NK_IMPLEMENTATION
212 #pragma GCC diagnostic push
213 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
214 #include "nuklear/nuklear.h"
215 #pragma GCC diagnostic pop
216
217 #define STB_IMAGE_IMPLEMENTATION
218 #pragma GCC diagnostic push
219 #pragma GCC diagnostic ignored "-Wmisleading-indentation"
220 #pragma GCC diagnostic ignored "-Wshift-negative-value"
221 #include "nuklear/example/stb_image.h"
222 #pragma GCC diagnostic pop
223
224 typedef struct _nk_pugl_vertex_t nk_pugl_vertex_t;
225
226 struct _nk_pugl_vertex_t {
227 float position [2];
228 float uv [2];
229 nk_byte col [4];
230 };
231
232 static const struct nk_draw_vertex_layout_element vertex_layout [] = {
233 {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_pugl_vertex_t, position)},
234 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(nk_pugl_vertex_t, uv)},
235 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(nk_pugl_vertex_t, col)},
236 {NK_VERTEX_LAYOUT_END}
237 };
238
239 #if defined(__APPLE__)
240 # define GL_EXT(name) name
241
242 #elif defined(_WIN32)
243 static void *
_nk_pugl_gl_ext(const char * name)244 _nk_pugl_gl_ext(const char *name)
245 {
246 #pragma GCC diagnostic push
247 #pragma GCC diagnostic ignored "-Wpedantic"
248 void *p = wglGetProcAddress(name);
249 #pragma GCC diagnostic pop
250
251 if( (p == 0) || (p == (void *)-1)
252 || (p == (void *)0x1) || (p == (void *)0x2) || (p == (void *)0x3) )
253 {
254 HMODULE module = LoadLibraryA("opengl32.dll");
255 p = (void *)GetProcAddress(module, name);
256 }
257
258 if(!p)
259 {
260 fprintf(stderr, "[GL]: failed to load extension: %s", name);
261 }
262
263 return p;
264 }
265 # define GL_EXT(name) (nk##name)_nk_pugl_gl_ext(#name)
266
267 #else
268 static void *
_nk_pugl_gl_ext(const char * name)269 _nk_pugl_gl_ext(const char *name)
270 {
271 #pragma GCC diagnostic push
272 #pragma GCC diagnostic ignored "-Wpedantic"
273 void *p = puglGetProcAddress(name);
274 #pragma GCC diagnostic pop
275
276 if(!p)
277 {
278 fprintf(stderr, "[GL]: failed to load extension: %s", name);
279 }
280
281 return p;
282 }
283 # define GL_EXT(name) (nk##name)_nk_pugl_gl_ext(#name)
284 #endif
285
286 static inline void
_nk_pugl_device_upload_atlas(nk_pugl_window_t * win,const void * image,int width,int height)287 _nk_pugl_device_upload_atlas(nk_pugl_window_t *win, const void *image,
288 int width, int height)
289 {
290 glGenTextures(1, &win->font_tex);
291 glBindTexture(GL_TEXTURE_2D, win->font_tex);
292 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
293 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
294 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
295 GL_RGBA, GL_UNSIGNED_BYTE, image);
296 }
297
298 static inline void
_nk_pugl_render_gl2_push(unsigned width,unsigned height)299 _nk_pugl_render_gl2_push(unsigned width, unsigned height)
300 {
301 glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT);
302
303 glDisable(GL_CULL_FACE);
304 glDisable(GL_DEPTH_TEST);
305 glEnable(GL_SCISSOR_TEST);
306 glEnable(GL_BLEND);
307 glEnable(GL_TEXTURE_2D);
308
309 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
310 glViewport(0, 0, width, height);
311
312 glMatrixMode(GL_PROJECTION);
313 glPushMatrix();
314 glLoadIdentity();
315 glOrtho(0.f, width, height, 0.f, -1.f, 1.f);
316
317 glMatrixMode(GL_MODELVIEW);
318 glPushMatrix();
319 glLoadIdentity();
320
321 glEnableClientState(GL_VERTEX_ARRAY);
322 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
323 glEnableClientState(GL_COLOR_ARRAY);
324 }
325
326 static inline void
_nk_pugl_render_gl2_pop(void)327 _nk_pugl_render_gl2_pop(void)
328 {
329 glDisableClientState(GL_VERTEX_ARRAY);
330 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
331 glDisableClientState(GL_COLOR_ARRAY);
332
333 glDisable(GL_CULL_FACE);
334 glDisable(GL_DEPTH_TEST);
335 glDisable(GL_SCISSOR_TEST);
336 glDisable(GL_BLEND);
337 glDisable(GL_TEXTURE_2D);
338
339 glBindTexture(GL_TEXTURE_2D, 0);
340
341 glMatrixMode(GL_MODELVIEW);
342 glPopMatrix();
343
344 glMatrixMode(GL_PROJECTION);
345 glPopMatrix();
346
347 glPopAttrib();
348 }
349
350 static inline void
_nk_pugl_render_gl2(nk_pugl_window_t * win)351 _nk_pugl_render_gl2(nk_pugl_window_t *win)
352 {
353 nk_pugl_config_t *cfg = &win->cfg;
354 bool has_changes = win->has_left || win->has_entered;
355
356 // compare current command buffer with last one to defer any changes
357 if(!has_changes)
358 {
359 const size_t size = win->ctx.memory.allocated;
360 const void *commands = nk_buffer_memory_const(&win->ctx.memory);
361
362 if( (size != win->last.size) || memcmp(commands, win->last.buffer, size) )
363 {
364 // swap last buffer with current one for next comparison
365 win->last.buffer = realloc(win->last.buffer, size);
366 if(win->last.buffer)
367 {
368 win->last.size = size;
369 memcpy(win->last.buffer, commands, size);
370 }
371 else
372 {
373 win->last.size = 0;
374 }
375 has_changes = true;
376 }
377 }
378
379 if(has_changes)
380 {
381 // clear command/vertex buffers of last stable view
382 nk_buffer_clear(&win->cmds);
383 nk_buffer_clear(&win->vbuf);
384 nk_buffer_clear(&win->ebuf);
385 nk_draw_list_clear(&win->ctx.draw_list);
386
387 // convert shapes into vertexes if there were changes
388 nk_convert(&win->ctx, &win->cmds, &win->vbuf, &win->ebuf, &win->conv);
389 }
390
391 _nk_pugl_render_gl2_push(cfg->width, cfg->height);
392
393 // setup vertex buffer pointers
394 const GLsizei vs = sizeof(nk_pugl_vertex_t);
395 const size_t vp = offsetof(nk_pugl_vertex_t, position);
396 const size_t vt = offsetof(nk_pugl_vertex_t, uv);
397 const size_t vc = offsetof(nk_pugl_vertex_t, col);
398 const nk_byte *vertices = nk_buffer_memory_const(&win->vbuf);
399 glVertexPointer(2, GL_FLOAT, vs, &vertices[vp]);
400 glTexCoordPointer(2, GL_FLOAT, vs, &vertices[vt]);
401 glColorPointer(4, GL_UNSIGNED_BYTE, vs, &vertices[vc]);
402
403 // iterate over and execute each draw command
404 const nk_draw_index *offset = nk_buffer_memory_const(&win->ebuf);
405 const struct nk_draw_command *cmd;
406 nk_draw_foreach(cmd, &win->ctx, &win->cmds)
407 {
408 if(!cmd->elem_count)
409 {
410 continue;
411 }
412
413 glBindTexture(GL_TEXTURE_2D, cmd->texture.id);
414 glScissor(
415 cmd->clip_rect.x,
416 cfg->height - (cmd->clip_rect.y + cmd->clip_rect.h),
417 cmd->clip_rect.w,
418 cmd->clip_rect.h);
419 glDrawElements(GL_TRIANGLES, cmd->elem_count, GL_UNSIGNED_SHORT, offset);
420
421 offset += cmd->elem_count;
422 }
423
424 _nk_pugl_render_gl2_pop();
425
426 win->has_entered = false;
427
428 nk_clear(&win->ctx);
429 }
430
431 static void
_nk_pugl_glew_init()432 _nk_pugl_glew_init()
433 {
434 #if defined(__APPLE__)
435 //FIXME
436 #else
437 glewExperimental = GL_TRUE;
438 const GLenum err = glewInit();
439 if(err != GLEW_OK)
440 {
441 fprintf(stderr, "glewInit failed: %s\n", glewGetErrorString(err));
442 return;
443 }
444 #endif
445 }
446
447 static void
_nk_pugl_font_init(nk_pugl_window_t * win)448 _nk_pugl_font_init(nk_pugl_window_t *win)
449 {
450 nk_pugl_config_t *cfg = &win->cfg;
451
452 const int font_size = cfg->font.size;
453
454 // init nuklear font
455 struct nk_font *ttf = NULL;
456 struct nk_font_config fcfg = nk_font_config(font_size);
457 static const nk_rune range [] = {
458 0x0020, 0x007F, // Basic Latin
459 0x00A0, 0x00FF, // Latin-1 Supplement
460 0x0100, 0x017F, // Latin Extended-A
461 0x0180, 0x024F, // Latin Extended-B
462 0x0300, 0x036F, // Combining Diacritical Marks
463 0x0370, 0x03FF, // Greek and Coptic
464 0x0400, 0x04FF, // Cyrillic
465 0x0500, 0x052F, // Cyrillic Supplementary
466 0
467 };
468 fcfg.range = range;
469 fcfg.oversample_h = 8;
470 fcfg.oversample_v = 8;
471
472 struct nk_font_atlas *atlas = &win->atlas;
473 nk_font_atlas_init_default(atlas);
474 nk_font_atlas_begin(atlas);
475
476 if(cfg->font.face && font_size)
477 {
478 ttf = nk_font_atlas_add_from_file(&win->atlas, cfg->font.face, font_size, &fcfg);
479 }
480
481 int w = 0;
482 int h = 0;
483 struct nk_draw_null_texture null;
484 const void *image = nk_font_atlas_bake(atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
485 _nk_pugl_device_upload_atlas(win, image, w, h);
486 nk_font_atlas_end(atlas, nk_handle_id(win->font_tex), &null);
487
488 if(atlas->default_font)
489 {
490 nk_style_set_font(&win->ctx, &atlas->default_font->handle);
491 }
492
493 if(ttf)
494 {
495 nk_style_set_font(&win->ctx, &ttf->handle);
496 }
497
498 // to please compiler
499 (void)nk_cos;
500 (void)nk_sin;
501 (void)nk_sqrt;
502 }
503
504 static void
_nk_pugl_font_deinit(nk_pugl_window_t * win)505 _nk_pugl_font_deinit(nk_pugl_window_t *win)
506 {
507 nk_font_atlas_clear(&win->atlas);
508
509 if(win->font_tex)
510 {
511 glDeleteTextures(1, (const GLuint *)&win->font_tex);
512 }
513 }
514
515 static void
_nk_pugl_host_resize(nk_pugl_window_t * win)516 _nk_pugl_host_resize(nk_pugl_window_t *win)
517 {
518 nk_pugl_config_t *cfg = &win->cfg;
519
520 if(cfg->host_resize)
521 {
522 cfg->host_resize->ui_resize(cfg->host_resize->handle,
523 cfg->width, cfg->height);
524 }
525 }
526
527 static void
_nk_pugl_key_press(struct nk_context * ctx,enum nk_keys key)528 _nk_pugl_key_press(struct nk_context *ctx, enum nk_keys key)
529 {
530 nk_input_key(ctx, key, nk_true);
531 nk_input_key(ctx, key, nk_false);
532 }
533
534 static void
_nk_pugl_modifiers(nk_pugl_window_t * win,PuglMod state)535 _nk_pugl_modifiers(nk_pugl_window_t *win, PuglMod state)
536 {
537 struct nk_context *ctx = &win->ctx;
538
539 if(win->state != state) // modifiers changed
540 {
541 if( (win->state & PUGL_MOD_SHIFT) != (state & PUGL_MOD_SHIFT))
542 {
543 nk_input_key(ctx, NK_KEY_SHIFT, (state & PUGL_MOD_SHIFT) == PUGL_MOD_SHIFT);
544 }
545
546 if( (win->state & PUGL_MOD_CTRL) != (state & PUGL_MOD_CTRL))
547 {
548 nk_input_key(ctx, NK_KEY_CTRL, (state & PUGL_MOD_CTRL) == PUGL_MOD_CTRL);
549 }
550
551 if( (win->state & PUGL_MOD_ALT) != (state & PUGL_MOD_ALT))
552 {
553 // not yet supported in nuklear
554 }
555
556 if( (win->state & PUGL_MOD_SUPER) != (state & PUGL_MOD_SUPER))
557 {
558 // not yet supported in nuklear
559 }
560
561 win->state = state; // switch old and new modifier states
562 }
563 }
564
565 static bool
_nk_pugl_key_down(nk_pugl_window_t * win,const PuglEventKey * ev)566 _nk_pugl_key_down(nk_pugl_window_t *win, const PuglEventKey *ev)
567 {
568 struct nk_context *ctx = &win->ctx;
569 #if defined(__APPLE__)
570 const bool control = ev->state & PUGL_MOD_SUPER;
571 #else
572 const bool control = ev->state & PUGL_MOD_CTRL;
573 #endif
574 const bool shift = ev->state & PUGL_MOD_SHIFT;
575
576 switch(ev->key)
577 {
578 case PUGL_KEY_LEFT:
579 {
580 _nk_pugl_key_press(ctx, control ? NK_KEY_TEXT_WORD_LEFT : NK_KEY_LEFT);
581 } break;
582 case PUGL_KEY_RIGHT:
583 {
584 _nk_pugl_key_press(ctx, control ? NK_KEY_TEXT_WORD_RIGHT : NK_KEY_RIGHT);
585 } break;
586 case PUGL_KEY_UP:
587 {
588 _nk_pugl_key_press(ctx, NK_KEY_UP);
589 } break;
590 case PUGL_KEY_DOWN:
591 {
592 _nk_pugl_key_press(ctx, NK_KEY_DOWN);
593 } break;
594 case PUGL_KEY_PAGE_UP:
595 {
596 _nk_pugl_key_press(ctx, NK_KEY_SCROLL_UP);
597 } break;
598 case PUGL_KEY_PAGE_DOWN:
599 {
600 _nk_pugl_key_press(ctx, NK_KEY_SCROLL_DOWN);
601 } break;
602 case PUGL_KEY_HOME:
603 {
604 if(control)
605 {
606 _nk_pugl_key_press(ctx, NK_KEY_TEXT_START);
607 _nk_pugl_key_press(ctx, NK_KEY_SCROLL_START);
608 }
609 else
610 {
611 _nk_pugl_key_press(ctx, NK_KEY_TEXT_LINE_START);
612 }
613 } break;
614 case PUGL_KEY_END:
615 {
616 if(control)
617 {
618 _nk_pugl_key_press(ctx, NK_KEY_TEXT_END);
619 _nk_pugl_key_press(ctx, NK_KEY_SCROLL_END);
620 }
621 else
622 {
623 _nk_pugl_key_press(ctx, NK_KEY_TEXT_LINE_END);
624 }
625 } break;
626 case PUGL_KEY_INSERT:
627 {
628 _nk_pugl_key_press(ctx, NK_KEY_TEXT_INSERT_MODE);
629 } break;
630 case PUGL_KEY_SHIFT:
631 {
632 win->state |= PUGL_MOD_SHIFT;
633 nk_input_key(ctx, NK_KEY_SHIFT, nk_true);
634 } return true;
635 case PUGL_KEY_CTRL:
636 {
637 win->state |= PUGL_MOD_CTRL;
638 nk_input_key(ctx, NK_KEY_CTRL, nk_true);
639 } return true;
640 case KEY_NEWLINE:
641 // fall-through
642 case KEY_RETURN:
643 {
644 _nk_pugl_key_press(ctx, NK_KEY_ENTER);
645 } break;
646 case KEY_TAB:
647 {
648 _nk_pugl_key_press(ctx, NK_KEY_TAB);
649 } break;
650 case PUGL_KEY_DELETE:
651 {
652 #if defined(__APPLE__) // quirk around Apple's Delete key strangeness
653 _nk_pugl_key_press(ctx, NK_KEY_BACKSPACE);
654 #else
655 _nk_pugl_key_press(ctx, NK_KEY_DEL);
656 #endif
657 } break;
658 case PUGL_KEY_BACKSPACE:
659 {
660 #if defined(__APPLE__) // quirk around Apple's Delete key strangeness
661 _nk_pugl_key_press(ctx, NK_KEY_DEL);
662 #else
663 _nk_pugl_key_press(ctx, NK_KEY_BACKSPACE);
664 #endif
665 } break;
666 case PUGL_KEY_ESCAPE:
667 {
668 _nk_pugl_key_press(ctx, NK_KEY_TEXT_RESET_MODE);
669 } break;
670
671 default:
672 {
673 if(control)
674 {
675 switch(ev->key)
676 {
677 case KEY_C:
678 {
679 _nk_pugl_key_press(ctx, NK_KEY_COPY);
680 } break;
681 case KEY_V:
682 {
683 _nk_pugl_key_press(ctx, NK_KEY_PASTE);
684 } break;
685 case KEY_X:
686 {
687 _nk_pugl_key_press(ctx, NK_KEY_CUT);
688 } break;
689 case KEY_Z:
690 {
691 _nk_pugl_key_press(ctx, shift ? NK_KEY_TEXT_REDO : NK_KEY_TEXT_UNDO);
692 } break;
693 }
694 }
695 }
696 }
697
698 return false;
699 }
700
701 static bool
_nk_pugl_key_up(nk_pugl_window_t * win,const PuglEventKey * ev)702 _nk_pugl_key_up(nk_pugl_window_t *win, const PuglEventKey *ev)
703 {
704 struct nk_context *ctx = &win->ctx;
705
706 switch(ev->key)
707 {
708 case PUGL_KEY_SHIFT:
709 {
710 nk_input_key(ctx, NK_KEY_SHIFT, nk_false);
711 win->state &= ~PUGL_MOD_SHIFT;
712 } return true;
713 case PUGL_KEY_CTRL:
714 {
715 nk_input_key(ctx, NK_KEY_CTRL, nk_false);
716 win->state &= ~PUGL_MOD_CTRL;
717 } return true;
718 }
719
720 return false;
721 }
722
723 static inline void
_nk_pugl_expose(PuglView * view)724 _nk_pugl_expose(PuglView *view)
725 {
726 nk_pugl_window_t *win = puglGetHandle(view);
727 nk_pugl_config_t *cfg = &win->cfg;
728 struct nk_context *ctx = &win->ctx;
729
730 const struct nk_rect wbounds = nk_rect(0, 0, cfg->width, cfg->height);
731
732 if(nk_begin(ctx, "__bg__", wbounds, 0))
733 {
734 const struct nk_rect obounds = nk_window_get_bounds(ctx);
735
736 if( (obounds.x != wbounds.x) || (obounds.y != wbounds.y)
737 || (obounds.w != wbounds.w) || (obounds.h != wbounds.h) )
738 {
739 // size has changed
740 puglPostRedisplay(view);
741 }
742
743 // clears window with widget background color
744 }
745 nk_end(ctx);
746
747 if(cfg->expose)
748 {
749 cfg->expose(ctx, wbounds, cfg->data);
750 }
751
752 _nk_pugl_render_gl2(win);
753 }
754
755 static PuglStatus
_nk_pugl_event_func(PuglView * view,const PuglEvent * e)756 _nk_pugl_event_func(PuglView *view, const PuglEvent *e)
757 {
758 nk_pugl_window_t *win = puglGetHandle(view);
759 nk_pugl_config_t *cfg = &win->cfg;
760 struct nk_context *ctx = &win->ctx;
761
762 switch(e->type)
763 {
764 case PUGL_LOOP_ENTER:
765 {
766 // TODO
767 } break;
768 case PUGL_LOOP_LEAVE:
769 {
770 // TODO
771 } break;
772 case PUGL_BUTTON_PRESS:
773 {
774 const PuglEventButton *ev = (const PuglEventButton *)e;
775
776 _nk_pugl_modifiers(win, ev->state);
777 nk_input_button(ctx, ev->button - 1, ev->x, ev->y, 1);
778
779 puglPostRedisplay(win->view);
780 } break;
781 case PUGL_BUTTON_RELEASE:
782 {
783 const PuglEventButton *ev = (const PuglEventButton *)e;
784
785 _nk_pugl_modifiers(win, ev->state);
786 nk_input_button(ctx, ev->button - 1, ev->x, ev->y, 0);
787
788 puglPostRedisplay(win->view);
789 } break;
790 case PUGL_CONFIGURE:
791 {
792 const PuglEventConfigure *ev = (const PuglEventConfigure *)e;
793
794 // only redisplay if size has changed
795 if( (cfg->width == ev->width) && (cfg->height == ev->height) )
796 {
797 break;
798 }
799
800 cfg->width = ev->width;
801 cfg->height = ev->height;
802
803 puglPostRedisplay(win->view);
804 } break;
805 case PUGL_EXPOSE:
806 {
807 nk_input_end(ctx);
808 _nk_pugl_expose(win->view);
809 nk_input_begin(ctx);
810 } break;
811 case PUGL_CLOSE:
812 {
813 nk_pugl_quit(win);
814 } break;
815 case PUGL_KEY_PRESS:
816 {
817 const PuglEventKey *ev = (const PuglEventKey *)e;
818
819 if(!_nk_pugl_key_down(win, ev)) // no modifier change
820 {
821 _nk_pugl_modifiers(win, ev->state);
822 }
823
824 puglPostRedisplay(win->view);
825 } break;
826 case PUGL_KEY_RELEASE:
827 {
828 const PuglEventKey *ev = (const PuglEventKey *)e;
829
830 if(!_nk_pugl_key_up(win, ev)) // no modifier change
831 {
832 _nk_pugl_modifiers(win, ev->state);
833 }
834
835 puglPostRedisplay(win->view);
836 } break;
837 case PUGL_MOTION:
838 {
839 const PuglEventMotion *ev = (const PuglEventMotion *)e;
840
841 _nk_pugl_modifiers(win, ev->state);
842 nk_input_motion(ctx, ev->x, ev->y);
843
844 puglPostRedisplay(win->view);
845 } break;
846 case PUGL_SCROLL:
847 {
848 const PuglEventScroll *ev = (const PuglEventScroll *)e;
849
850 _nk_pugl_modifiers(win, ev->state);
851 nk_input_scroll(ctx, nk_vec2(0.f, ev->dy));
852
853 puglPostRedisplay(win->view);
854 } break;
855
856 case PUGL_POINTER_OUT:
857 {
858 const PuglEventCrossing *ev = (const PuglEventCrossing *)e;
859
860 _nk_pugl_modifiers(win, ev->state);
861 win->has_left = true;
862 puglPostRedisplay(win->view);
863 } break;
864 case PUGL_POINTER_IN:
865 {
866 const PuglEventCrossing *ev = (const PuglEventCrossing *)e;
867
868 _nk_pugl_modifiers(win, ev->state);
869 win->has_left = false;
870 win->has_entered = true;
871 puglPostRedisplay(win->view);
872 } break;
873
874 case PUGL_FOCUS_OUT:
875 case PUGL_FOCUS_IN:
876 {
877 // nothing
878 } break;
879
880 case PUGL_TEXT:
881 {
882 const PuglEventText *ev = (const PuglEventText *)e;
883
884 const bool control = ev->state & PUGL_MOD_CTRL;
885
886 const int ch = control ? ev->character | 0x60 : ev->character;
887
888 if(isprint(ch))
889 {
890 _nk_pugl_key_press(ctx, NK_KEY_TEXT_INSERT_MODE);
891 nk_input_unicode(ctx, ch);
892 }
893 } break;
894
895 case PUGL_CREATE:
896 {
897 // init glew
898 _nk_pugl_glew_init();
899
900 // init font system
901 _nk_pugl_font_init(win);
902
903 #pragma GCC diagnostic push
904 #pragma GCC diagnostic ignored "-Wpedantic"
905 win->glGenerateMipmap = GL_EXT(glGenerateMipmap);
906 #pragma GCC diagnostic pop
907 } break;
908 case PUGL_DESTROY:
909 {
910 // deinit font system
911 _nk_pugl_font_deinit(win);
912 } break;
913
914 case PUGL_MAP:
915 {
916 //nothing
917 } break;
918 case PUGL_UNMAP:
919 {
920 //nothing
921 } break;
922 case PUGL_UPDATE:
923 {
924 //nothing
925 } break;
926 case PUGL_CLIENT:
927 {
928 //nothing
929 } break;
930 case PUGL_TIMER:
931 {
932 //nothing
933 } break;
934 case PUGL_NOTHING:
935 {
936 // nothing
937 } break;
938 }
939
940 return PUGL_SUCCESS;
941 }
942
943 static void
_nk_pugl_editor_paste(nk_handle userdata,struct nk_text_edit * editor)944 _nk_pugl_editor_paste(nk_handle userdata, struct nk_text_edit* editor)
945 {
946 nk_pugl_window_t *win = userdata.ptr;
947
948 size_t len;
949 const char *selection = nk_pugl_paste_from_clipboard(win, &len);
950 if(selection)
951 {
952 nk_textedit_paste(editor, selection, len);
953 }
954 }
955
956 static void
_nk_pugl_editor_copy(nk_handle userdata,const char * buf,int len)957 _nk_pugl_editor_copy(nk_handle userdata, const char *buf, int len)
958 {
959 nk_pugl_window_t *win = userdata.ptr;
960
961 nk_pugl_copy_to_clipboard(win, buf, len);
962 }
963
964 NK_PUGL_API intptr_t
nk_pugl_init(nk_pugl_window_t * win)965 nk_pugl_init(nk_pugl_window_t *win)
966 {
967 nk_pugl_config_t *cfg = &win->cfg;
968 struct nk_convert_config *conv = &win->conv;
969
970 win->has_left = true;
971
972 // init pugl
973 win->world = puglNewWorld(cfg->parent ? PUGL_MODULE : PUGL_PROGRAM,
974 cfg->threads ? PUGL_WORLD_THREADS : 0);
975
976 #if defined(__APPLE__) || defined(_WIN32)
977 uint8_t bytes [0x10];
978
979 srand(time(NULL));
980 for(unsigned i=0x0; i<0x10; i++)
981 bytes[i] = rand() & 0xff;
982
983 bytes[6] = (bytes[6] & 0b00001111) | 0b01000000; // set four most significant bits of 7th byte to 0b0100
984 bytes[8] = (bytes[8] & 0b00111111) | 0b10000000; // set two most significant bits of 9th byte to 0b10
985
986 snprintf(win->urn, sizeof(win->urn),
987 "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
988 bytes[0x0], bytes[0x1], bytes[0x2], bytes[0x3],
989 bytes[0x4], bytes[0x5],
990 bytes[0x6], bytes[0x7],
991 bytes[0x8], bytes[0x9],
992 bytes[0xa], bytes[0xb], bytes[0xc], bytes[0xd], bytes[0xe], bytes[0xf]);
993 fprintf(stderr, "%s\n", win->urn);
994 puglSetClassName(win->world, win->urn);
995 #else
996 puglSetClassName(win->world, cfg->class ? cfg->class : "nuklear");
997 #endif
998
999 // init nuklear
1000 nk_buffer_init_default(&win->cmds);
1001 nk_buffer_init_default(&win->vbuf);
1002 nk_buffer_init_default(&win->ebuf);
1003 nk_init_default(&win->ctx, 0);
1004
1005 // fill convert configuration
1006 conv->vertex_layout = vertex_layout;
1007 conv->vertex_size = sizeof(nk_pugl_vertex_t);
1008 conv->vertex_alignment = NK_ALIGNOF(nk_pugl_vertex_t);
1009 conv->null = win->null;
1010 conv->circle_segment_count = 22;
1011 conv->curve_segment_count = 22;
1012 conv->arc_segment_count = 22;
1013 conv->global_alpha = 1.0f;
1014 conv->shape_AA = NK_ANTI_ALIASING_ON;
1015 conv->line_AA = NK_ANTI_ALIASING_ON;
1016
1017 nk_input_begin(&win->ctx);
1018
1019 win->ctx.clip.paste = _nk_pugl_editor_paste;
1020 win->ctx.clip.copy = _nk_pugl_editor_copy;
1021 win->ctx.clip.userdata.ptr = win;
1022
1023 win->view = puglNewView(win->world);
1024
1025 const PuglRect frame = {
1026 .x = 0,
1027 .y = 0,
1028 .width = cfg->width,
1029 .height = cfg->height
1030 };
1031
1032 puglSetFrame(win->view, frame);
1033 if(cfg->min_width && cfg->min_height)
1034 {
1035 puglSetMinSize(win->view, cfg->min_width, cfg->min_height);
1036 }
1037 if(cfg->parent)
1038 {
1039 puglSetParentWindow(win->view, cfg->parent);
1040 #if 0 // not yet implemented for mingw, darwin
1041 puglSetTransientFor(win->view, cfg->parent);
1042 #endif
1043 }
1044 if(cfg->fixed_aspect)
1045 {
1046 puglSetAspectRatio(win->view, cfg->width, cfg->height,
1047 cfg->width, cfg->height);
1048 }
1049 puglSetViewHint(win->view, PUGL_RESIZABLE, cfg->resizable);
1050 puglSetViewHint(win->view, PUGL_DOUBLE_BUFFER, true);
1051 puglSetViewHint(win->view, PUGL_SWAP_INTERVAL, 1);
1052 puglSetHandle(win->view, win);
1053 puglSetEventFunc(win->view, _nk_pugl_event_func);
1054 puglSetBackend(win->view, puglGlBackend());
1055 puglSetWindowTitle(win->view,
1056 cfg->title ? cfg->title : "Nuklear");
1057 const int stat = puglRealize(win->view);
1058 assert(stat == 0);
1059
1060 win->widget = puglGetNativeWindow(win->view);
1061 return win->widget;
1062 }
1063
1064 NK_PUGL_API void
nk_pugl_show(nk_pugl_window_t * win)1065 nk_pugl_show(nk_pugl_window_t *win)
1066 {
1067 if(!win->view)
1068 {
1069 return;
1070 }
1071
1072 puglShow(win->view);
1073 _nk_pugl_host_resize(win);
1074 }
1075
1076 NK_PUGL_API void
nk_pugl_hide(nk_pugl_window_t * win)1077 nk_pugl_hide(nk_pugl_window_t *win)
1078 {
1079 if(!win->view)
1080 {
1081 return;
1082 }
1083
1084 puglHide(win->view);
1085 }
1086
1087 NK_PUGL_API void
nk_pugl_shutdown(nk_pugl_window_t * win)1088 nk_pugl_shutdown(nk_pugl_window_t *win)
1089 {
1090 if(!win->view)
1091 {
1092 return;
1093 }
1094
1095 nk_input_end(&win->ctx);
1096
1097 if(win->last.buffer)
1098 {
1099 free(win->last.buffer);
1100 }
1101
1102 // shutdown nuklear
1103 nk_buffer_free(&win->cmds);
1104 nk_buffer_free(&win->vbuf);
1105 nk_buffer_free(&win->ebuf);
1106 nk_free(&win->ctx);
1107
1108 // shutdown pugl
1109 if(win->world)
1110 {
1111 if(win->view)
1112 {
1113 puglFreeView(win->view);
1114 }
1115
1116 puglFreeWorld(win->world);
1117 }
1118 }
1119
1120 NK_PUGL_API void
nk_pugl_wait_for_event(nk_pugl_window_t * win)1121 nk_pugl_wait_for_event(nk_pugl_window_t *win)
1122 {
1123 if(!win->view)
1124 {
1125 return;
1126 }
1127
1128 puglUpdate(win->world, -1.0); // blocking pooll
1129 }
1130
1131 NK_PUGL_API int
nk_pugl_process_events(nk_pugl_window_t * win)1132 nk_pugl_process_events(nk_pugl_window_t *win)
1133 {
1134 if(!win->view)
1135 {
1136 return 1; // quit
1137 }
1138
1139 PuglStatus stat = puglUpdate(win->world, 0.0);
1140 (void)stat;
1141
1142 return win->quit;
1143 }
1144
1145 NK_PUGL_API int
nk_pugl_resize(nk_pugl_window_t * win,int width,int height)1146 nk_pugl_resize(nk_pugl_window_t *win, int width, int height)
1147 {
1148 if(!win->view)
1149 {
1150 return 1; // quit
1151 }
1152
1153 win->cfg.width = width;
1154 win->cfg.height = height;
1155
1156 puglPostRedisplay(win->view);
1157
1158 return 0;
1159 }
1160
1161 NK_PUGL_API void
nk_pugl_post_redisplay(nk_pugl_window_t * win)1162 nk_pugl_post_redisplay(nk_pugl_window_t *win)
1163 {
1164 if(!win->view)
1165 {
1166 return;
1167 }
1168
1169 puglPostRedisplay(win->view);
1170 }
1171
1172 NK_PUGL_API void
nk_pugl_async_redisplay(nk_pugl_window_t * win)1173 nk_pugl_async_redisplay(nk_pugl_window_t *win)
1174 {
1175 if(!win->view)
1176 {
1177 return;
1178 }
1179
1180 #if defined(__APPLE__)
1181 // TODO
1182 #elif defined(_WIN32)
1183 const HWND widget = (HWND)win->widget;
1184 const int status = SendNotifyMessage(widget, WM_PAINT, 0, 0);
1185 (void)status;
1186 #else
1187 Display *disp = puglGetNativeWorld(win->world);
1188 const Window widget = (Window)win->widget;
1189 XExposeEvent xevent = {
1190 .type = Expose,
1191 .display = disp,
1192 .window = widget
1193 };
1194
1195 while(atomic_flag_test_and_set_explicit(&win->async, memory_order_acquire))
1196 {
1197 // spin
1198 }
1199
1200 const Status status = XSendEvent(disp, widget, false, ExposureMask,
1201 (XEvent *)&xevent);
1202 (void)status;
1203 XFlush(disp);
1204
1205 atomic_flag_clear_explicit(&win->async, memory_order_release);
1206 #endif
1207 }
1208
1209 NK_PUGL_API void
nk_pugl_quit(nk_pugl_window_t * win)1210 nk_pugl_quit(nk_pugl_window_t *win)
1211 {
1212 win->quit = 1;
1213 }
1214
1215 NK_PUGL_API struct nk_image
nk_pugl_icon_load(nk_pugl_window_t * win,const char * filename)1216 nk_pugl_icon_load(nk_pugl_window_t *win, const char *filename)
1217 {
1218 GLuint tex = 0;
1219
1220 if(!win->view)
1221 {
1222 return nk_image_id(tex);
1223 }
1224
1225 int w, h, n;
1226 uint8_t *data = stbi_load(filename, &w, &h, &n, 4);
1227 if(data)
1228 {
1229 #pragma GCC diagnostic push
1230 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1231 puglEnterContext(win->view);
1232 #pragma GCC diagnostic pop
1233 {
1234 glGenTextures(1, &tex);
1235 glBindTexture(GL_TEXTURE_2D, tex);
1236 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
1237 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1238 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1239 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1240 if(!win->glGenerateMipmap) // for GL >= 1.4 && < 3.1
1241 {
1242 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
1243 }
1244 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
1245 if(win->glGenerateMipmap) // for GL >= 3.1
1246 {
1247 win->glGenerateMipmap(GL_TEXTURE_2D);
1248 }
1249 }
1250 #pragma GCC diagnostic push
1251 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1252 puglLeaveContext(win->view);
1253 #pragma GCC diagnostic pop
1254
1255 stbi_image_free(data);
1256 }
1257
1258 return nk_image_id(tex);
1259 }
1260
1261 NK_PUGL_API void
nk_pugl_icon_unload(nk_pugl_window_t * win,struct nk_image img)1262 nk_pugl_icon_unload(nk_pugl_window_t *win, struct nk_image img)
1263 {
1264 if(!win->view)
1265 {
1266 return;
1267 }
1268
1269 if(img.handle.id)
1270 {
1271 #pragma GCC diagnostic push
1272 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1273 puglEnterContext(win->view);
1274 #pragma GCC diagnostic pop
1275 {
1276 glDeleteTextures(1, (const GLuint *)&img.handle.id);
1277 }
1278 #pragma GCC diagnostic push
1279 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1280 puglLeaveContext(win->view);
1281 #pragma GCC diagnostic pop
1282 }
1283 }
1284
1285 NK_PUGL_API bool
nk_pugl_is_shortcut_pressed(struct nk_input * in,char letter,bool clear)1286 nk_pugl_is_shortcut_pressed(struct nk_input *in, char letter, bool clear)
1287 {
1288 const bool control = nk_input_is_key_down(in, NK_KEY_CTRL);
1289
1290 if(control && (in->keyboard.text_len == 1) )
1291 {
1292 if(in->keyboard.text[0] == letter)
1293 {
1294 if(clear)
1295 {
1296 in->keyboard.text_len = 0;
1297 }
1298
1299 return true; // matching shortcut
1300 }
1301 }
1302
1303 return false;
1304 }
1305
1306 NK_PUGL_API void
nk_pugl_copy_to_clipboard(nk_pugl_window_t * win,const char * selection,size_t len)1307 nk_pugl_copy_to_clipboard(nk_pugl_window_t *win, const char *selection, size_t len)
1308 {
1309 const char *type = "text/plain";
1310
1311 puglSetClipboard(win->view, type, selection, len);
1312 }
1313
1314 NK_PUGL_API const char *
nk_pugl_paste_from_clipboard(nk_pugl_window_t * win,size_t * len)1315 nk_pugl_paste_from_clipboard(nk_pugl_window_t *win, size_t *len)
1316 {
1317 const char *type = NULL;
1318
1319 return puglGetClipboard(win->view, &type, len);
1320 }
1321
1322 NK_PUGL_API float
nk_pugl_get_scale()1323 nk_pugl_get_scale()
1324 {
1325 const char *NK_SCALE = getenv("NK_SCALE");
1326 const float scale = NK_SCALE ? atof(NK_SCALE) : 1.f;
1327 const float dpi0 = 96.f; // reference DPI we're designing for
1328 float dpi1 = dpi0;
1329
1330 #if defined(__APPLE__)
1331 // FIXME
1332 #elif defined(_WIN32)
1333 // GetDpiForSystem/Monitor/Window is Win10 only
1334 HDC screen = GetDC(NULL);
1335 dpi1 = GetDeviceCaps(screen, LOGPIXELSX);
1336 ReleaseDC(NULL, screen);
1337 #else
1338 Display *disp = XOpenDisplay(0);
1339 if(disp)
1340 {
1341 // modern X actually lies here, but proprietary nvidia
1342 dpi1 = XDisplayWidth(disp, 0) * 25.4f / XDisplayWidthMM(disp, 0);
1343
1344 // read DPI from users's ~/.Xresources
1345 char *resource_string = XResourceManagerString(disp);
1346 XrmInitialize();
1347 if(resource_string)
1348 {
1349 XrmDatabase db = XrmGetStringDatabase(resource_string);
1350 if(db)
1351 {
1352 char *type = NULL;
1353 XrmValue value;
1354
1355 XrmGetResource(db, "Xft.dpi", "String", &type, &value);
1356 if(value.addr)
1357 {
1358 dpi1 = atof(value.addr);
1359 }
1360
1361 XrmDestroyDatabase(db);
1362 }
1363 }
1364
1365 XCloseDisplay(disp);
1366 }
1367 #endif
1368
1369 return scale * dpi1 / dpi0;
1370 }
1371
1372 #ifdef __cplusplus
1373 }
1374 #endif
1375
1376 #endif // NK_PUGL_IMPLEMENTATION
1377