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