1 /* nuklear - v1.05 - public domain */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <stdarg.h>
6 #include <string.h>
7 #include <math.h>
8 #include <assert.h>
9 #include <math.h>
10 #include <time.h>
11 #include <limits.h>
12 
13 #include <GL/glew.h>
14 #include <GLFW/glfw3.h>
15 
16 #define NK_INCLUDE_FIXED_TYPES
17 #define NK_INCLUDE_STANDARD_IO
18 #define NK_INCLUDE_DEFAULT_ALLOCATOR
19 #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
20 #define NK_INCLUDE_FONT_BAKING
21 #define NK_INCLUDE_DEFAULT_FONT
22 #define NK_IMPLEMENTATION
23 #include "../nuklear.h"
24 
25 #define STB_IMAGE_IMPLEMENTATION
26 #include "stb_image.h"
27 
28 /* macros */
29 #define WINDOW_WIDTH 1200
30 #define WINDOW_HEIGHT 800
31 
32 #define MAX_VERTEX_MEMORY 512 * 1024
33 #define MAX_ELEMENT_MEMORY 128 * 1024
34 
35 #define UNUSED(a) (void)a
36 #define MIN(a,b) ((a) < (b) ? (a) : (b))
37 #define MAX(a,b) ((a) < (b) ? (b) : (a))
38 #define LEN(a) (sizeof(a)/sizeof(a)[0])
39 
40 #ifdef __APPLE__
41   #define NK_SHADER_VERSION "#version 150\n"
42 #else
43   #define NK_SHADER_VERSION "#version 300 es\n"
44 #endif
45 
46 struct media {
47     struct nk_font *font_14;
48     struct nk_font *font_18;
49     struct nk_font *font_20;
50     struct nk_font *font_22;
51 
52     struct nk_image unchecked;
53     struct nk_image checked;
54     struct nk_image rocket;
55     struct nk_image cloud;
56     struct nk_image pen;
57     struct nk_image play;
58     struct nk_image pause;
59     struct nk_image stop;
60     struct nk_image prev;
61     struct nk_image next;
62     struct nk_image tools;
63     struct nk_image dir;
64     struct nk_image copy;
65     struct nk_image convert;
66     struct nk_image del;
67     struct nk_image edit;
68     struct nk_image images[9];
69     struct nk_image menu[6];
70 };
71 
72 /* ===============================================================
73  *
74  *                          CUSTOM WIDGET
75  *
76  * ===============================================================*/
77 static int
ui_piemenu(struct nk_context * ctx,struct nk_vec2 pos,float radius,struct nk_image * icons,int item_count)78 ui_piemenu(struct nk_context *ctx, struct nk_vec2 pos, float radius,
79             struct nk_image *icons, int item_count)
80 {
81     int ret = -1;
82     struct nk_rect total_space;
83     struct nk_rect bounds;
84     int active_item = 0;
85 
86     /* pie menu popup */
87     struct nk_color border = ctx->style.window.border_color;
88     struct nk_style_item background = ctx->style.window.fixed_background;
89     ctx->style.window.fixed_background = nk_style_item_hide();
90     ctx->style.window.border_color = nk_rgba(0,0,0,0);
91 
92     total_space  = nk_window_get_content_region(ctx);
93     ctx->style.window.spacing = nk_vec2(0,0);
94     ctx->style.window.padding = nk_vec2(0,0);
95 
96     if (nk_popup_begin(ctx, NK_POPUP_STATIC, "piemenu", NK_WINDOW_NO_SCROLLBAR,
97         nk_rect(pos.x - total_space.x - radius, pos.y - radius - total_space.y,
98         2*radius,2*radius)))
99     {
100         int i = 0;
101         struct nk_command_buffer* out = nk_window_get_canvas(ctx);
102         const struct nk_input *in = &ctx->input;
103 
104         total_space = nk_window_get_content_region(ctx);
105         ctx->style.window.spacing = nk_vec2(4,4);
106         ctx->style.window.padding = nk_vec2(8,8);
107         nk_layout_row_dynamic(ctx, total_space.h, 1);
108         nk_widget(&bounds, ctx);
109 
110         /* outer circle */
111         nk_fill_circle(out, bounds, nk_rgb(50,50,50));
112         {
113             /* circle buttons */
114             float step = (2 * 3.141592654f) / (float)(MAX(1,item_count));
115             float a_min = 0; float a_max = step;
116 
117             struct nk_vec2 center = nk_vec2(bounds.x + bounds.w / 2.0f, bounds.y + bounds.h / 2.0f);
118             struct nk_vec2 drag = nk_vec2(in->mouse.pos.x - center.x, in->mouse.pos.y - center.y);
119             float angle = (float)atan2(drag.y, drag.x);
120             if (angle < -0.0f) angle += 2.0f * 3.141592654f;
121             active_item = (int)(angle/step);
122 
123             for (i = 0; i < item_count; ++i) {
124                 struct nk_rect content;
125                 float rx, ry, dx, dy, a;
126                 nk_fill_arc(out, center.x, center.y, (bounds.w/2.0f),
127                     a_min, a_max, (active_item == i) ? nk_rgb(45,100,255): nk_rgb(60,60,60));
128 
129                 /* separator line */
130                 rx = bounds.w/2.0f; ry = 0;
131                 dx = rx * (float)cos(a_min) - ry * (float)sin(a_min);
132                 dy = rx * (float)sin(a_min) + ry * (float)cos(a_min);
133                 nk_stroke_line(out, center.x, center.y,
134                     center.x + dx, center.y + dy, 1.0f, nk_rgb(50,50,50));
135 
136                 /* button content */
137                 a = a_min + (a_max - a_min)/2.0f;
138                 rx = bounds.w/2.5f; ry = 0;
139                 content.w = 30; content.h = 30;
140                 content.x = center.x + ((rx * (float)cos(a) - ry * (float)sin(a)) - content.w/2.0f);
141                 content.y = center.y + (rx * (float)sin(a) + ry * (float)cos(a) - content.h/2.0f);
142                 nk_draw_image(out, content, &icons[i], nk_rgb(255,255,255));
143                 a_min = a_max; a_max += step;
144             }
145         }
146         {
147             /* inner circle */
148             struct nk_rect inner;
149             inner.x = bounds.x + bounds.w/2 - bounds.w/4;
150             inner.y = bounds.y + bounds.h/2 - bounds.h/4;
151             inner.w = bounds.w/2; inner.h = bounds.h/2;
152             nk_fill_circle(out, inner, nk_rgb(45,45,45));
153 
154             /* active icon content */
155             bounds.w = inner.w / 2.0f;
156             bounds.h = inner.h / 2.0f;
157             bounds.x = inner.x + inner.w/2 - bounds.w/2;
158             bounds.y = inner.y + inner.h/2 - bounds.h/2;
159             nk_draw_image(out, bounds, &icons[active_item], nk_rgb(255,255,255));
160         }
161         nk_layout_space_end(ctx);
162         if (!nk_input_is_mouse_down(&ctx->input, NK_BUTTON_RIGHT)) {
163             nk_popup_close(ctx);
164             ret = active_item;
165         }
166     } else ret = -2;
167     ctx->style.window.spacing = nk_vec2(4,4);
168     ctx->style.window.padding = nk_vec2(8,8);
169     nk_popup_end(ctx);
170 
171     ctx->style.window.fixed_background = background;
172     ctx->style.window.border_color = border;
173     return ret;
174 }
175 
176 /* ===============================================================
177  *
178  *                          GRID
179  *
180  * ===============================================================*/
181 static void
grid_demo(struct nk_context * ctx,struct media * media)182 grid_demo(struct nk_context *ctx, struct media *media)
183 {
184     static char text[3][64];
185     static int text_len[3];
186     static const char *items[] = {"Item 0","item 1","item 2"};
187     static int selected_item = 0;
188     static int check = 1;
189 
190     int i;
191     nk_style_set_font(ctx, &media->font_20->handle);
192     if (nk_begin(ctx, "Grid Demo", nk_rect(600, 350, 275, 250),
193         NK_WINDOW_TITLE|NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|
194         NK_WINDOW_NO_SCROLLBAR))
195     {
196         nk_style_set_font(ctx, &media->font_18->handle);
197         nk_layout_row_dynamic(ctx, 30, 2);
198         nk_label(ctx, "Floating point:", NK_TEXT_RIGHT);
199         nk_edit_string(ctx, NK_EDIT_FIELD, text[0], &text_len[0], 64, nk_filter_float);
200         nk_label(ctx, "Hexadecimal:", NK_TEXT_RIGHT);
201         nk_edit_string(ctx, NK_EDIT_FIELD, text[1], &text_len[1], 64, nk_filter_hex);
202         nk_label(ctx, "Binary:", NK_TEXT_RIGHT);
203         nk_edit_string(ctx, NK_EDIT_FIELD, text[2], &text_len[2], 64, nk_filter_binary);
204         nk_label(ctx, "Checkbox:", NK_TEXT_RIGHT);
205         nk_checkbox_label(ctx, "Check me", &check);
206         nk_label(ctx, "Combobox:", NK_TEXT_RIGHT);
207         if (nk_combo_begin_label(ctx, items[selected_item], nk_vec2(nk_widget_width(ctx), 200))) {
208             nk_layout_row_dynamic(ctx, 25, 1);
209             for (i = 0; i < 3; ++i)
210                 if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT))
211                     selected_item = i;
212             nk_combo_end(ctx);
213         }
214     }
215     nk_end(ctx);
216     nk_style_set_font(ctx, &media->font_14->handle);
217 }
218 
219 /* ===============================================================
220  *
221  *                          BUTTON DEMO
222  *
223  * ===============================================================*/
224 static void
ui_header(struct nk_context * ctx,struct media * media,const char * title)225 ui_header(struct nk_context *ctx, struct media *media, const char *title)
226 {
227     nk_style_set_font(ctx, &media->font_18->handle);
228     nk_layout_row_dynamic(ctx, 20, 1);
229     nk_label(ctx, title, NK_TEXT_LEFT);
230 }
231 
232 static void
ui_widget(struct nk_context * ctx,struct media * media,float height)233 ui_widget(struct nk_context *ctx, struct media *media, float height)
234 {
235     static const float ratio[] = {0.15f, 0.85f};
236     nk_style_set_font(ctx, &media->font_22->handle);
237     nk_layout_row(ctx, NK_DYNAMIC, height, 2, ratio);
238     nk_spacing(ctx, 1);
239 }
240 
241 static void
ui_widget_centered(struct nk_context * ctx,struct media * media,float height)242 ui_widget_centered(struct nk_context *ctx, struct media *media, float height)
243 {
244     static const float ratio[] = {0.15f, 0.50f, 0.35f};
245     nk_style_set_font(ctx, &media->font_22->handle);
246     nk_layout_row(ctx, NK_DYNAMIC, height, 3, ratio);
247     nk_spacing(ctx, 1);
248 }
249 
250 static void
button_demo(struct nk_context * ctx,struct media * media)251 button_demo(struct nk_context *ctx, struct media *media)
252 {
253     static int option = 1;
254     static int toggle0 = 1;
255     static int toggle1 = 0;
256     static int toggle2 = 1;
257 
258     nk_style_set_font(ctx, &media->font_20->handle);
259     nk_begin(ctx, "Button Demo", nk_rect(50,50,255,610),
260         NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE);
261 
262     /*------------------------------------------------
263      *                  MENU
264      *------------------------------------------------*/
265     nk_menubar_begin(ctx);
266     {
267         /* toolbar */
268         nk_layout_row_static(ctx, 40, 40, 4);
269         if (nk_menu_begin_image(ctx, "Music", media->play, nk_vec2(110,120)))
270         {
271             /* settings */
272             nk_layout_row_dynamic(ctx, 25, 1);
273             nk_menu_item_image_label(ctx, media->play, "Play", NK_TEXT_RIGHT);
274             nk_menu_item_image_label(ctx, media->stop, "Stop", NK_TEXT_RIGHT);
275             nk_menu_item_image_label(ctx, media->pause, "Pause", NK_TEXT_RIGHT);
276             nk_menu_item_image_label(ctx, media->next, "Next", NK_TEXT_RIGHT);
277             nk_menu_item_image_label(ctx, media->prev, "Prev", NK_TEXT_RIGHT);
278             nk_menu_end(ctx);
279         }
280         nk_button_image(ctx, media->tools);
281         nk_button_image(ctx, media->cloud);
282         nk_button_image(ctx, media->pen);
283     }
284     nk_menubar_end(ctx);
285 
286     /*------------------------------------------------
287      *                  BUTTON
288      *------------------------------------------------*/
289     ui_header(ctx, media, "Push buttons");
290     ui_widget(ctx, media, 35);
291     if (nk_button_label(ctx, "Push me"))
292         fprintf(stdout, "pushed!\n");
293     ui_widget(ctx, media, 35);
294     if (nk_button_image_label(ctx, media->rocket, "Styled", NK_TEXT_CENTERED))
295         fprintf(stdout, "rocket!\n");
296 
297     /*------------------------------------------------
298      *                  REPEATER
299      *------------------------------------------------*/
300     ui_header(ctx, media, "Repeater");
301     ui_widget(ctx, media, 35);
302     if (nk_button_label(ctx, "Press me"))
303         fprintf(stdout, "pressed!\n");
304 
305     /*------------------------------------------------
306      *                  TOGGLE
307      *------------------------------------------------*/
308     ui_header(ctx, media, "Toggle buttons");
309     ui_widget(ctx, media, 35);
310     if (nk_button_image_label(ctx, (toggle0) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT))
311         toggle0 = !toggle0;
312 
313     ui_widget(ctx, media, 35);
314     if (nk_button_image_label(ctx, (toggle1) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT))
315         toggle1 = !toggle1;
316 
317     ui_widget(ctx, media, 35);
318     if (nk_button_image_label(ctx, (toggle2) ? media->checked: media->unchecked, "Toggle", NK_TEXT_LEFT))
319         toggle2 = !toggle2;
320 
321     /*------------------------------------------------
322      *                  RADIO
323      *------------------------------------------------*/
324     ui_header(ctx, media, "Radio buttons");
325     ui_widget(ctx, media, 35);
326     if (nk_button_symbol_label(ctx, (option == 0)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT))
327         option = 0;
328     ui_widget(ctx, media, 35);
329     if (nk_button_symbol_label(ctx, (option == 1)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT))
330         option = 1;
331     ui_widget(ctx, media, 35);
332     if (nk_button_symbol_label(ctx, (option == 2)?NK_SYMBOL_CIRCLE_OUTLINE:NK_SYMBOL_CIRCLE_SOLID, "Select", NK_TEXT_LEFT))
333         option = 2;
334 
335     /*------------------------------------------------
336      *                  CONTEXTUAL
337      *------------------------------------------------*/
338     nk_style_set_font(ctx, &media->font_18->handle);
339     if (nk_contextual_begin(ctx, NK_WINDOW_NO_SCROLLBAR, nk_vec2(150, 300), nk_window_get_bounds(ctx))) {
340         nk_layout_row_dynamic(ctx, 30, 1);
341         if (nk_contextual_item_image_label(ctx, media->copy, "Clone", NK_TEXT_RIGHT))
342             fprintf(stdout, "pressed clone!\n");
343         if (nk_contextual_item_image_label(ctx, media->del, "Delete", NK_TEXT_RIGHT))
344             fprintf(stdout, "pressed delete!\n");
345         if (nk_contextual_item_image_label(ctx, media->convert, "Convert", NK_TEXT_RIGHT))
346             fprintf(stdout, "pressed convert!\n");
347         if (nk_contextual_item_image_label(ctx, media->edit, "Edit", NK_TEXT_RIGHT))
348             fprintf(stdout, "pressed edit!\n");
349         nk_contextual_end(ctx);
350     }
351     nk_style_set_font(ctx, &media->font_14->handle);
352     nk_end(ctx);
353 }
354 
355 /* ===============================================================
356  *
357  *                          BASIC DEMO
358  *
359  * ===============================================================*/
360 static void
basic_demo(struct nk_context * ctx,struct media * media)361 basic_demo(struct nk_context *ctx, struct media *media)
362 {
363     static int image_active;
364     static int check0 = 1;
365     static int check1 = 0;
366     static size_t prog = 80;
367     static int selected_item = 0;
368     static int selected_image = 3;
369     static int selected_icon = 0;
370     static const char *items[] = {"Item 0","item 1","item 2"};
371     static int piemenu_active = 0;
372     static struct nk_vec2 piemenu_pos;
373 
374     int i = 0;
375     nk_style_set_font(ctx, &media->font_20->handle);
376     nk_begin(ctx, "Basic Demo", nk_rect(320, 50, 275, 610),
377         NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_TITLE);
378 
379     /*------------------------------------------------
380      *                  POPUP BUTTON
381      *------------------------------------------------*/
382     ui_header(ctx, media, "Popup & Scrollbar & Images");
383     ui_widget(ctx, media, 35);
384     if (nk_button_image_label(ctx, media->dir, "Images", NK_TEXT_CENTERED))
385         image_active = !image_active;
386 
387     /*------------------------------------------------
388      *                  SELECTED IMAGE
389      *------------------------------------------------*/
390     ui_header(ctx, media, "Selected Image");
391     ui_widget_centered(ctx, media, 100);
392     nk_image(ctx, media->images[selected_image]);
393 
394     /*------------------------------------------------
395      *                  IMAGE POPUP
396      *------------------------------------------------*/
397     if (image_active) {
398         struct nk_panel popup;
399         if (nk_popup_begin(ctx, NK_POPUP_STATIC, "Image Popup", 0, nk_rect(265, 0, 320, 220))) {
400             nk_layout_row_static(ctx, 82, 82, 3);
401             for (i = 0; i < 9; ++i) {
402                 if (nk_button_image(ctx, media->images[i])) {
403                     selected_image = i;
404                     image_active = 0;
405                     nk_popup_close(ctx);
406                 }
407             }
408             nk_popup_end(ctx);
409         }
410     }
411     /*------------------------------------------------
412      *                  COMBOBOX
413      *------------------------------------------------*/
414     ui_header(ctx, media, "Combo box");
415     ui_widget(ctx, media, 40);
416     if (nk_combo_begin_label(ctx, items[selected_item], nk_vec2(nk_widget_width(ctx), 200))) {
417         nk_layout_row_dynamic(ctx, 35, 1);
418         for (i = 0; i < 3; ++i)
419             if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT))
420                 selected_item = i;
421         nk_combo_end(ctx);
422     }
423 
424     ui_widget(ctx, media, 40);
425     if (nk_combo_begin_image_label(ctx, items[selected_icon], media->images[selected_icon], nk_vec2(nk_widget_width(ctx), 200))) {
426         nk_layout_row_dynamic(ctx, 35, 1);
427         for (i = 0; i < 3; ++i)
428             if (nk_combo_item_image_label(ctx, media->images[i], items[i], NK_TEXT_RIGHT))
429                 selected_icon = i;
430         nk_combo_end(ctx);
431     }
432 
433     /*------------------------------------------------
434      *                  CHECKBOX
435      *------------------------------------------------*/
436     ui_header(ctx, media, "Checkbox");
437     ui_widget(ctx, media, 30);
438     nk_checkbox_label(ctx, "Flag 1", &check0);
439     ui_widget(ctx, media, 30);
440     nk_checkbox_label(ctx, "Flag 2", &check1);
441 
442     /*------------------------------------------------
443      *                  PROGRESSBAR
444      *------------------------------------------------*/
445     ui_header(ctx, media, "Progressbar");
446     ui_widget(ctx, media, 35);
447     nk_progress(ctx, &prog, 100, nk_true);
448 
449     /*------------------------------------------------
450      *                  PIEMENU
451      *------------------------------------------------*/
452     if (nk_input_is_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_RIGHT,
453         nk_window_get_bounds(ctx),nk_true)){
454         piemenu_pos = ctx->input.mouse.pos;
455         piemenu_active = 1;
456     }
457 
458     if (piemenu_active) {
459         int ret = ui_piemenu(ctx, piemenu_pos, 140, &media->menu[0], 6);
460         if (ret == -2) piemenu_active = 0;
461         if (ret != -1) {
462             fprintf(stdout, "piemenu selected: %d\n", ret);
463             piemenu_active = 0;
464         }
465     }
466     nk_style_set_font(ctx, &media->font_14->handle);
467     nk_end(ctx);
468 }
469 
470 /* ===============================================================
471  *
472  *                          DEVICE
473  *
474  * ===============================================================*/
475 struct nk_glfw_vertex {
476     float position[2];
477     float uv[2];
478     nk_byte col[4];
479 };
480 
481 struct device {
482     struct nk_buffer cmds;
483     struct nk_draw_null_texture null;
484     GLuint vbo, vao, ebo;
485     GLuint prog;
486     GLuint vert_shdr;
487     GLuint frag_shdr;
488     GLint attrib_pos;
489     GLint attrib_uv;
490     GLint attrib_col;
491     GLint uniform_tex;
492     GLint uniform_proj;
493     GLuint font_tex;
494 };
495 
496 static void
die(const char * fmt,...)497 die(const char *fmt, ...)
498 {
499     va_list ap;
500     va_start(ap, fmt);
501     vfprintf(stderr, fmt, ap);
502     va_end(ap);
503     fputs("\n", stderr);
504     exit(EXIT_FAILURE);
505 }
506 
507 static struct nk_image
icon_load(const char * filename)508 icon_load(const char *filename)
509 {
510     int x,y,n;
511     GLuint tex;
512     unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
513     if (!data) die("[SDL]: failed to load image: %s", filename);
514 
515     glGenTextures(1, &tex);
516     glBindTexture(GL_TEXTURE_2D, tex);
517     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
518     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
519     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
520     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
521     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
522     glGenerateMipmap(GL_TEXTURE_2D);
523     stbi_image_free(data);
524     return nk_image_id((int)tex);
525 }
526 
527 static void
device_init(struct device * dev)528 device_init(struct device *dev)
529 {
530     GLint status;
531     static const GLchar *vertex_shader =
532         NK_SHADER_VERSION
533         "uniform mat4 ProjMtx;\n"
534         "in vec2 Position;\n"
535         "in vec2 TexCoord;\n"
536         "in vec4 Color;\n"
537         "out vec2 Frag_UV;\n"
538         "out vec4 Frag_Color;\n"
539         "void main() {\n"
540         "   Frag_UV = TexCoord;\n"
541         "   Frag_Color = Color;\n"
542         "   gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
543         "}\n";
544     static const GLchar *fragment_shader =
545         NK_SHADER_VERSION
546         "precision mediump float;\n"
547         "uniform sampler2D Texture;\n"
548         "in vec2 Frag_UV;\n"
549         "in vec4 Frag_Color;\n"
550         "out vec4 Out_Color;\n"
551         "void main(){\n"
552         "   Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
553         "}\n";
554 
555     nk_buffer_init_default(&dev->cmds);
556     dev->prog = glCreateProgram();
557     dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
558     dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
559     glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
560     glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
561     glCompileShader(dev->vert_shdr);
562     glCompileShader(dev->frag_shdr);
563     glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
564     assert(status == GL_TRUE);
565     glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
566     assert(status == GL_TRUE);
567     glAttachShader(dev->prog, dev->vert_shdr);
568     glAttachShader(dev->prog, dev->frag_shdr);
569     glLinkProgram(dev->prog);
570     glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
571     assert(status == GL_TRUE);
572 
573     dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
574     dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
575     dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
576     dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
577     dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
578 
579     {
580         /* buffer setup */
581         GLsizei vs = sizeof(struct nk_glfw_vertex);
582         size_t vp = offsetof(struct nk_glfw_vertex, position);
583         size_t vt = offsetof(struct nk_glfw_vertex, uv);
584         size_t vc = offsetof(struct nk_glfw_vertex, col);
585 
586         glGenBuffers(1, &dev->vbo);
587         glGenBuffers(1, &dev->ebo);
588         glGenVertexArrays(1, &dev->vao);
589 
590         glBindVertexArray(dev->vao);
591         glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
592         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
593 
594         glEnableVertexAttribArray((GLuint)dev->attrib_pos);
595         glEnableVertexAttribArray((GLuint)dev->attrib_uv);
596         glEnableVertexAttribArray((GLuint)dev->attrib_col);
597 
598         glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
599         glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
600         glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
601     }
602 
603     glBindTexture(GL_TEXTURE_2D, 0);
604     glBindBuffer(GL_ARRAY_BUFFER, 0);
605     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
606     glBindVertexArray(0);
607 }
608 
609 static void
device_upload_atlas(struct device * dev,const void * image,int width,int height)610 device_upload_atlas(struct device *dev, const void *image, int width, int height)
611 {
612     glGenTextures(1, &dev->font_tex);
613     glBindTexture(GL_TEXTURE_2D, dev->font_tex);
614     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
615     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
616     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
617                 GL_RGBA, GL_UNSIGNED_BYTE, image);
618 }
619 
620 static void
device_shutdown(struct device * dev)621 device_shutdown(struct device *dev)
622 {
623     glDetachShader(dev->prog, dev->vert_shdr);
624     glDetachShader(dev->prog, dev->frag_shdr);
625     glDeleteShader(dev->vert_shdr);
626     glDeleteShader(dev->frag_shdr);
627     glDeleteProgram(dev->prog);
628     glDeleteTextures(1, &dev->font_tex);
629     glDeleteBuffers(1, &dev->vbo);
630     glDeleteBuffers(1, &dev->ebo);
631     nk_buffer_free(&dev->cmds);
632 }
633 
634 static void
device_draw(struct device * dev,struct nk_context * ctx,int width,int height,struct nk_vec2 scale,enum nk_anti_aliasing AA)635 device_draw(struct device *dev, struct nk_context *ctx, int width, int height,
636     struct nk_vec2 scale, enum nk_anti_aliasing AA)
637 {
638     GLfloat ortho[4][4] = {
639         {2.0f, 0.0f, 0.0f, 0.0f},
640         {0.0f,-2.0f, 0.0f, 0.0f},
641         {0.0f, 0.0f,-1.0f, 0.0f},
642         {-1.0f,1.0f, 0.0f, 1.0f},
643     };
644     ortho[0][0] /= (GLfloat)width;
645     ortho[1][1] /= (GLfloat)height;
646 
647     /* setup global state */
648     glEnable(GL_BLEND);
649     glBlendEquation(GL_FUNC_ADD);
650     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
651     glDisable(GL_CULL_FACE);
652     glDisable(GL_DEPTH_TEST);
653     glEnable(GL_SCISSOR_TEST);
654     glActiveTexture(GL_TEXTURE0);
655 
656     /* setup program */
657     glUseProgram(dev->prog);
658     glUniform1i(dev->uniform_tex, 0);
659     glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
660     {
661         /* convert from command queue into draw list and draw to screen */
662         const struct nk_draw_command *cmd;
663         void *vertices, *elements;
664         const nk_draw_index *offset = NULL;
665 
666         /* allocate vertex and element buffer */
667         glBindVertexArray(dev->vao);
668         glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
669         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
670 
671         glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW);
672         glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW);
673 
674         /* load draw vertices & elements directly into vertex + element buffer */
675         vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
676         elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
677         {
678             /* fill convert configuration */
679             struct nk_convert_config config;
680             static const struct nk_draw_vertex_layout_element vertex_layout[] = {
681                 {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)},
682                 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)},
683                 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)},
684                 {NK_VERTEX_LAYOUT_END}
685             };
686             NK_MEMSET(&config, 0, sizeof(config));
687             config.vertex_layout = vertex_layout;
688             config.vertex_size = sizeof(struct nk_glfw_vertex);
689             config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
690             config.null = dev->null;
691             config.circle_segment_count = 22;
692             config.curve_segment_count = 22;
693             config.arc_segment_count = 22;
694             config.global_alpha = 1.0f;
695             config.shape_AA = AA;
696             config.line_AA = AA;
697 
698             /* setup buffers to load vertices and elements */
699             {struct nk_buffer vbuf, ebuf;
700             nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY);
701             nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY);
702             nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);}
703         }
704         glUnmapBuffer(GL_ARRAY_BUFFER);
705         glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
706 
707         /* iterate over and execute each draw command */
708         nk_draw_foreach(cmd, ctx, &dev->cmds)
709         {
710             if (!cmd->elem_count) continue;
711             glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
712             glScissor(
713                 (GLint)(cmd->clip_rect.x * scale.x),
714                 (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y),
715                 (GLint)(cmd->clip_rect.w * scale.x),
716                 (GLint)(cmd->clip_rect.h * scale.y));
717             glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
718             offset += cmd->elem_count;
719         }
720         nk_clear(ctx);
721     }
722 
723     /* default OpenGL state */
724     glUseProgram(0);
725     glBindBuffer(GL_ARRAY_BUFFER, 0);
726     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
727     glBindVertexArray(0);
728     glDisable(GL_BLEND);
729     glDisable(GL_SCISSOR_TEST);
730 }
731 
732 /* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/
error_callback(int e,const char * d)733 static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);}
text_input(GLFWwindow * win,unsigned int codepoint)734 static void text_input(GLFWwindow *win, unsigned int codepoint)
735 {nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);}
scroll_input(GLFWwindow * win,double _,double yoff)736 static void scroll_input(GLFWwindow *win, double _, double yoff)
737 {UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), nk_vec2(0, (float)yoff));}
738 
main(int argc,char * argv[])739 int main(int argc, char *argv[])
740 {
741     /* Platform */
742     static GLFWwindow *win;
743     int width = 0, height = 0;
744     int display_width=0, display_height=0;
745 
746     /* GUI */
747     struct device device;
748     struct nk_font_atlas atlas;
749     struct media media;
750     struct nk_context ctx;
751 
752     /* GLFW */
753     glfwSetErrorCallback(error_callback);
754     if (!glfwInit()) {
755         fprintf(stdout, "[GFLW] failed to init!\n");
756         exit(1);
757     }
758     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
759     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
760     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
761 #ifdef __APPLE__
762     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
763 #endif
764     win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL);
765     glfwMakeContextCurrent(win);
766     glfwSetWindowUserPointer(win, &ctx);
767     glfwSetCharCallback(win, text_input);
768     glfwSetScrollCallback(win, scroll_input);
769     glfwGetWindowSize(win, &width, &height);
770     glfwGetFramebufferSize(win, &display_width, &display_height);
771 
772     /* OpenGL */
773     glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
774     glewExperimental = 1;
775     if (glewInit() != GLEW_OK) {
776         fprintf(stderr, "Failed to setup GLEW\n");
777         exit(1);
778     }
779 
780     {/* GUI */
781     device_init(&device);
782     {const void *image; int w, h;
783     struct nk_font_config cfg = nk_font_config(0);
784     cfg.oversample_h = 3; cfg.oversample_v = 2;
785     /* Loading one font with different heights is only required if you want higher
786      * quality text otherwise you can just set the font height directly
787      * e.g.: ctx->style.font.height = 20. */
788     nk_font_atlas_init_default(&atlas);
789     nk_font_atlas_begin(&atlas);
790     media.font_14 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 14.0f, &cfg);
791     media.font_18 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 18.0f, &cfg);
792     media.font_20 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 20.0f, &cfg);
793     media.font_22 = nk_font_atlas_add_from_file(&atlas, "../../extra_font/Roboto-Regular.ttf", 22.0f, &cfg);
794     image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
795     device_upload_atlas(&device, image, w, h);
796     nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);}
797     nk_init_default(&ctx, &media.font_14->handle);}
798 
799     /* icons */
800     glEnable(GL_TEXTURE_2D);
801     media.unchecked = icon_load("../icon/unchecked.png");
802     media.checked = icon_load("../icon/checked.png");
803     media.rocket = icon_load("../icon/rocket.png");
804     media.cloud = icon_load("../icon/cloud.png");
805     media.pen = icon_load("../icon/pen.png");
806     media.play = icon_load("../icon/play.png");
807     media.pause = icon_load("../icon/pause.png");
808     media.stop = icon_load("../icon/stop.png");
809     media.next =  icon_load("../icon/next.png");
810     media.prev =  icon_load("../icon/prev.png");
811     media.tools = icon_load("../icon/tools.png");
812     media.dir = icon_load("../icon/directory.png");
813     media.copy = icon_load("../icon/copy.png");
814     media.convert = icon_load("../icon/export.png");
815     media.del = icon_load("../icon/delete.png");
816     media.edit = icon_load("../icon/edit.png");
817     media.menu[0] = icon_load("../icon/home.png");
818     media.menu[1] = icon_load("../icon/phone.png");
819     media.menu[2] = icon_load("../icon/plane.png");
820     media.menu[3] = icon_load("../icon/wifi.png");
821     media.menu[4] = icon_load("../icon/settings.png");
822     media.menu[5] = icon_load("../icon/volume.png");
823 
824     {int i;
825     for (i = 0; i < 9; ++i) {
826         char buffer[256];
827         sprintf(buffer, "../images/image%d.png", (i+1));
828         media.images[i] = icon_load(buffer);
829     }}
830 
831     while (!glfwWindowShouldClose(win))
832     {
833         /* High DPI displays */
834         struct nk_vec2 scale;
835         glfwGetWindowSize(win, &width, &height);
836         glfwGetFramebufferSize(win, &display_width, &display_height);
837         scale.x = (float)display_width/(float)width;
838         scale.y = (float)display_height/(float)height;
839 
840         /* Input */
841         {double x, y;
842         nk_input_begin(&ctx);
843         glfwPollEvents();
844         nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
845         nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
846         nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
847         nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
848         nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
849         nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
850         nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
851         nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
852         if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
853             glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
854             nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
855             nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS);
856             nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
857             nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
858             nk_input_key(&ctx, NK_KEY_SHIFT, 1);
859         } else {
860             nk_input_key(&ctx, NK_KEY_COPY, 0);
861             nk_input_key(&ctx, NK_KEY_PASTE, 0);
862             nk_input_key(&ctx, NK_KEY_CUT, 0);
863             nk_input_key(&ctx, NK_KEY_SHIFT, 0);
864         }
865         glfwGetCursorPos(win, &x, &y);
866         nk_input_motion(&ctx, (int)x, (int)y);
867         nk_input_button(&ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
868         nk_input_button(&ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
869         nk_input_button(&ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
870         nk_input_end(&ctx);}
871 
872         /* GUI */
873         basic_demo(&ctx, &media);
874         button_demo(&ctx, &media);
875         grid_demo(&ctx, &media);
876 
877         /* Draw */
878         glViewport(0, 0, display_width, display_height);
879         glClear(GL_COLOR_BUFFER_BIT);
880         glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
881         device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON);
882         glfwSwapBuffers(win);
883     }
884 
885     glDeleteTextures(1,(const GLuint*)&media.unchecked.handle.id);
886     glDeleteTextures(1,(const GLuint*)&media.checked.handle.id);
887     glDeleteTextures(1,(const GLuint*)&media.rocket.handle.id);
888     glDeleteTextures(1,(const GLuint*)&media.cloud.handle.id);
889     glDeleteTextures(1,(const GLuint*)&media.pen.handle.id);
890     glDeleteTextures(1,(const GLuint*)&media.play.handle.id);
891     glDeleteTextures(1,(const GLuint*)&media.pause.handle.id);
892     glDeleteTextures(1,(const GLuint*)&media.stop.handle.id);
893     glDeleteTextures(1,(const GLuint*)&media.next.handle.id);
894     glDeleteTextures(1,(const GLuint*)&media.prev.handle.id);
895     glDeleteTextures(1,(const GLuint*)&media.tools.handle.id);
896     glDeleteTextures(1,(const GLuint*)&media.dir.handle.id);
897     glDeleteTextures(1,(const GLuint*)&media.del.handle.id);
898 
899     nk_font_atlas_clear(&atlas);
900     nk_free(&ctx);
901 
902     device_shutdown(&device);
903     glfwTerminate();
904     return 0;
905 }
906 
907