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 #include <unistd.h>
13 #include <dirent.h>
14 
15 #include <GL/glew.h>
16 #include <GLFW/glfw3.h>
17 
18 #define NK_INCLUDE_FIXED_TYPES
19 #define NK_INCLUDE_STANDARD_IO
20 #define NK_INCLUDE_DEFAULT_ALLOCATOR
21 #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
22 #define NK_INCLUDE_FONT_BAKING
23 #define NK_INCLUDE_DEFAULT_FONT
24 #define NK_IMPLEMENTATION
25 #include "../nuklear.h"
26 
27 #define STB_IMAGE_IMPLEMENTATION
28 #include "stb_image.h"
29 
30 /* macros */
31 #define WINDOW_WIDTH 1200
32 #define WINDOW_HEIGHT 800
33 
34 #define MAX_VERTEX_MEMORY 512 * 1024
35 #define MAX_ELEMENT_MEMORY 128 * 1024
36 
37 #define UNUSED(a) (void)a
38 #define MIN(a,b) ((a) < (b) ? (a) : (b))
39 #define MAX(a,b) ((a) < (b) ? (b) : (a))
40 #define LEN(a) (sizeof(a)/sizeof(a)[0])
41 
42 #ifdef __APPLE__
43   #define NK_SHADER_VERSION "#version 150\n"
44 #else
45   #define NK_SHADER_VERSION "#version 300 es\n"
46 #endif
47 
48 /* ===============================================================
49  *
50  *                          GUI
51  *
52  * ===============================================================*/
53 struct icons {
54     struct nk_image desktop;
55     struct nk_image home;
56     struct nk_image computer;
57     struct nk_image directory;
58 
59     struct nk_image default_file;
60     struct nk_image text_file;
61     struct nk_image music_file;
62     struct nk_image font_file;
63     struct nk_image img_file;
64     struct nk_image movie_file;
65 };
66 
67 enum file_groups {
68     FILE_GROUP_DEFAULT,
69     FILE_GROUP_TEXT,
70     FILE_GROUP_MUSIC,
71     FILE_GROUP_FONT,
72     FILE_GROUP_IMAGE,
73     FILE_GROUP_MOVIE,
74     FILE_GROUP_MAX
75 };
76 
77 enum file_types {
78     FILE_DEFAULT,
79     FILE_TEXT,
80     FILE_C_SOURCE,
81     FILE_CPP_SOURCE,
82     FILE_HEADER,
83     FILE_CPP_HEADER,
84     FILE_MP3,
85     FILE_WAV,
86     FILE_OGG,
87     FILE_TTF,
88     FILE_BMP,
89     FILE_PNG,
90     FILE_JPEG,
91     FILE_PCX,
92     FILE_TGA,
93     FILE_GIF,
94     FILE_MAX
95 };
96 
97 struct file_group {
98     enum file_groups group;
99     const char *name;
100     struct nk_image *icon;
101 };
102 
103 struct file {
104     enum file_types type;
105     const char *suffix;
106     enum file_groups group;
107 };
108 
109 struct media {
110     int font;
111     int icon_sheet;
112     struct icons icons;
113     struct file_group group[FILE_GROUP_MAX];
114     struct file files[FILE_MAX];
115 };
116 
117 #define MAX_PATH_LEN 512
118 struct file_browser {
119     /* path */
120     char file[MAX_PATH_LEN];
121     char home[MAX_PATH_LEN];
122     char desktop[MAX_PATH_LEN];
123     char directory[MAX_PATH_LEN];
124 
125     /* directory content */
126     char **files;
127     char **directories;
128     size_t file_count;
129     size_t dir_count;
130     struct media *media;
131 };
132 
133 #ifdef __unix__
134 #include <dirent.h>
135 #include <unistd.h>
136 #endif
137 
138 #ifndef _WIN32
139 # include <pwd.h>
140 #endif
141 
142 static void
die(const char * fmt,...)143 die(const char *fmt, ...)
144 {
145     va_list ap;
146     va_start(ap, fmt);
147     vfprintf(stderr, fmt, ap);
148     va_end(ap);
149     fputs("\n", stderr);
150     exit(EXIT_FAILURE);
151 }
152 
153 static char*
file_load(const char * path,size_t * siz)154 file_load(const char* path, size_t* siz)
155 {
156     char *buf;
157     FILE *fd = fopen(path, "rb");
158     if (!fd) die("Failed to open file: %s\n", path);
159     fseek(fd, 0, SEEK_END);
160     *siz = (size_t)ftell(fd);
161     fseek(fd, 0, SEEK_SET);
162     buf = (char*)calloc(*siz, 1);
163     fread(buf, *siz, 1, fd);
164     fclose(fd);
165     return buf;
166 }
167 
168 static char*
str_duplicate(const char * src)169 str_duplicate(const char *src)
170 {
171     char *ret;
172     size_t len = strlen(src);
173     if (!len) return 0;
174     ret = (char*)malloc(len+1);
175     if (!ret) return 0;
176     memcpy(ret, src, len);
177     ret[len] = '\0';
178     return ret;
179 }
180 
181 static void
dir_free_list(char ** list,size_t size)182 dir_free_list(char **list, size_t size)
183 {
184     size_t i;
185     for (i = 0; i < size; ++i)
186         free(list[i]);
187     free(list);
188 }
189 
190 static char**
dir_list(const char * dir,int return_subdirs,size_t * count)191 dir_list(const char *dir, int return_subdirs, size_t *count)
192 {
193     size_t n = 0;
194     char buffer[MAX_PATH_LEN];
195     char **results = NULL;
196     const DIR *none = NULL;
197     size_t capacity = 32;
198     size_t size;
199     DIR *z;
200 
201     assert(dir);
202     assert(count);
203     strncpy(buffer, dir, MAX_PATH_LEN);
204     n = strlen(buffer);
205 
206     if (n > 0 && (buffer[n-1] != '/'))
207         buffer[n++] = '/';
208 
209     size = 0;
210 
211     z = opendir(dir);
212     if (z != none) {
213         int nonempty = 1;
214         struct dirent *data = readdir(z);
215         nonempty = (data != NULL);
216         if (!nonempty) return NULL;
217 
218         do {
219             DIR *y;
220             char *p;
221             int is_subdir;
222             if (data->d_name[0] == '.')
223                 continue;
224 
225             strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n);
226             y = opendir(buffer);
227             is_subdir = (y != NULL);
228             if (y != NULL) closedir(y);
229 
230             if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)){
231                 if (!size) {
232                     results = (char**)calloc(sizeof(char*), capacity);
233                 } else if (size >= capacity) {
234                     void *old = results;
235                     capacity = capacity * 2;
236                     results = (char**)realloc(results, capacity * sizeof(char*));
237                     assert(results);
238                     if (!results) free(old);
239                 }
240                 p = str_duplicate(data->d_name);
241                 results[size++] = p;
242             }
243         } while ((data = readdir(z)) != NULL);
244     }
245 
246     if (z) closedir(z);
247     *count = size;
248     return results;
249 }
250 
251 static struct file_group
FILE_GROUP(enum file_groups group,const char * name,struct nk_image * icon)252 FILE_GROUP(enum file_groups group, const char *name, struct nk_image *icon)
253 {
254     struct file_group fg;
255     fg.group = group;
256     fg.name = name;
257     fg.icon = icon;
258     return fg;
259 }
260 
261 static struct file
FILE_DEF(enum file_types type,const char * suffix,enum file_groups group)262 FILE_DEF(enum file_types type, const char *suffix, enum file_groups group)
263 {
264     struct file fd;
265     fd.type = type;
266     fd.suffix = suffix;
267     fd.group = group;
268     return fd;
269 }
270 
271 static struct nk_image*
media_icon_for_file(struct media * media,const char * file)272 media_icon_for_file(struct media *media, const char *file)
273 {
274     int i = 0;
275     const char *s = file;
276     char suffix[4];
277     int found = 0;
278     memset(suffix, 0, sizeof(suffix));
279 
280     /* extract suffix .xxx from file */
281     while (*s++ != '\0') {
282         if (found && i < 3)
283             suffix[i++] = *s;
284 
285         if (*s == '.') {
286             if (found){
287                 found = 0;
288                 break;
289             }
290             found = 1;
291         }
292     }
293 
294     /* check for all file definition of all groups for fitting suffix*/
295     for (i = 0; i < FILE_MAX && found; ++i) {
296         struct file *d = &media->files[i];
297         {
298             const char *f = d->suffix;
299             s = suffix;
300             while (f && *f && *s && *s == *f) {
301                 s++; f++;
302             }
303 
304             /* found correct file definition so */
305             if (f && *s == '\0' && *f == '\0')
306                 return media->group[d->group].icon;
307         }
308     }
309     return &media->icons.default_file;
310 }
311 
312 static void
media_init(struct media * media)313 media_init(struct media *media)
314 {
315     /* file groups */
316     struct icons *icons = &media->icons;
317     media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file);
318     media->group[FILE_GROUP_TEXT] = FILE_GROUP(FILE_GROUP_TEXT, "textual", &icons->text_file);
319     media->group[FILE_GROUP_MUSIC] = FILE_GROUP(FILE_GROUP_MUSIC, "music", &icons->music_file);
320     media->group[FILE_GROUP_FONT] = FILE_GROUP(FILE_GROUP_FONT, "font", &icons->font_file);
321     media->group[FILE_GROUP_IMAGE] = FILE_GROUP(FILE_GROUP_IMAGE, "image", &icons->img_file);
322     media->group[FILE_GROUP_MOVIE] = FILE_GROUP(FILE_GROUP_MOVIE, "movie", &icons->movie_file);
323 
324     /* files */
325     media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT);
326     media->files[FILE_TEXT] = FILE_DEF(FILE_TEXT, "txt", FILE_GROUP_TEXT);
327     media->files[FILE_C_SOURCE] = FILE_DEF(FILE_C_SOURCE, "c", FILE_GROUP_TEXT);
328     media->files[FILE_CPP_SOURCE] = FILE_DEF(FILE_CPP_SOURCE, "cpp", FILE_GROUP_TEXT);
329     media->files[FILE_HEADER] = FILE_DEF(FILE_HEADER, "h", FILE_GROUP_TEXT);
330     media->files[FILE_CPP_HEADER] = FILE_DEF(FILE_HEADER, "hpp", FILE_GROUP_TEXT);
331     media->files[FILE_MP3] = FILE_DEF(FILE_MP3, "mp3", FILE_GROUP_MUSIC);
332     media->files[FILE_WAV] = FILE_DEF(FILE_WAV, "wav", FILE_GROUP_MUSIC);
333     media->files[FILE_OGG] = FILE_DEF(FILE_OGG, "ogg", FILE_GROUP_MUSIC);
334     media->files[FILE_TTF] = FILE_DEF(FILE_TTF, "ttf", FILE_GROUP_FONT);
335     media->files[FILE_BMP] = FILE_DEF(FILE_BMP, "bmp", FILE_GROUP_IMAGE);
336     media->files[FILE_PNG] = FILE_DEF(FILE_PNG, "png", FILE_GROUP_IMAGE);
337     media->files[FILE_JPEG] = FILE_DEF(FILE_JPEG, "jpg", FILE_GROUP_IMAGE);
338     media->files[FILE_PCX] = FILE_DEF(FILE_PCX, "pcx", FILE_GROUP_IMAGE);
339     media->files[FILE_TGA] = FILE_DEF(FILE_TGA, "tga", FILE_GROUP_IMAGE);
340     media->files[FILE_GIF] = FILE_DEF(FILE_GIF, "gif", FILE_GROUP_IMAGE);
341 }
342 
343 static void
file_browser_reload_directory_content(struct file_browser * browser,const char * path)344 file_browser_reload_directory_content(struct file_browser *browser, const char *path)
345 {
346     strncpy(browser->directory, path, MAX_PATH_LEN);
347     dir_free_list(browser->files, browser->file_count);
348     dir_free_list(browser->directories, browser->dir_count);
349     browser->files = dir_list(path, 0, &browser->file_count);
350     browser->directories = dir_list(path, 1, &browser->dir_count);
351 }
352 
353 static void
file_browser_init(struct file_browser * browser,struct media * media)354 file_browser_init(struct file_browser *browser, struct media *media)
355 {
356     memset(browser, 0, sizeof(*browser));
357     browser->media = media;
358     {
359         /* load files and sub-directory list */
360         const char *home = getenv("HOME");
361 #ifdef _WIN32
362         if (!home) home = getenv("USERPROFILE");
363 #else
364         if (!home) home = getpwuid(getuid())->pw_dir;
365         {
366             size_t l;
367             strncpy(browser->home, home, MAX_PATH_LEN);
368             l = strlen(browser->home);
369             strcpy(browser->home + l, "/");
370             strcpy(browser->directory, browser->home);
371         }
372 #endif
373         {
374             size_t l;
375             strcpy(browser->desktop, browser->home);
376             l = strlen(browser->desktop);
377             strcpy(browser->desktop + l, "desktop/");
378         }
379         browser->files = dir_list(browser->directory, 0, &browser->file_count);
380         browser->directories = dir_list(browser->directory, 1, &browser->dir_count);
381     }
382 }
383 
384 static void
file_browser_free(struct file_browser * browser)385 file_browser_free(struct file_browser *browser)
386 {
387     if (browser->files)
388         dir_free_list(browser->files, browser->file_count);
389     if (browser->directories)
390         dir_free_list(browser->directories, browser->dir_count);
391     browser->files = NULL;
392     browser->directories = NULL;
393     memset(browser, 0, sizeof(*browser));
394 }
395 
396 static int
file_browser_run(struct file_browser * browser,struct nk_context * ctx)397 file_browser_run(struct file_browser *browser, struct nk_context *ctx)
398 {
399     int ret = 0;
400     struct media *media = browser->media;
401     struct nk_rect total_space;
402 
403     if (nk_begin(ctx, "File Browser", nk_rect(50, 50, 800, 600),
404         NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_MOVABLE))
405     {
406         static float ratio[] = {0.25f, NK_UNDEFINED};
407         float spacing_x = ctx->style.window.spacing.x;
408 
409         /* output path directory selector in the menubar */
410         ctx->style.window.spacing.x = 0;
411         nk_menubar_begin(ctx);
412         {
413             char *d = browser->directory;
414             char *begin = d + 1;
415             nk_layout_row_dynamic(ctx, 25, 6);
416             while (*d++) {
417                 if (*d == '/') {
418                     *d = '\0';
419                     if (nk_button_label(ctx, begin)) {
420                         *d++ = '/'; *d = '\0';
421                         file_browser_reload_directory_content(browser, browser->directory);
422                         break;
423                     }
424                     *d = '/';
425                     begin = d + 1;
426                 }
427             }
428         }
429         nk_menubar_end(ctx);
430         ctx->style.window.spacing.x = spacing_x;
431 
432         /* window layout */
433         total_space = nk_window_get_content_region(ctx);
434         nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 2, ratio);
435         nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR);
436         {
437             struct nk_image home = media->icons.home;
438             struct nk_image desktop = media->icons.desktop;
439             struct nk_image computer = media->icons.computer;
440 
441             nk_layout_row_dynamic(ctx, 40, 1);
442             if (nk_button_image_label(ctx, home, "home", NK_TEXT_CENTERED))
443                 file_browser_reload_directory_content(browser, browser->home);
444             if (nk_button_image_label(ctx,desktop,"desktop",NK_TEXT_CENTERED))
445                 file_browser_reload_directory_content(browser, browser->desktop);
446             if (nk_button_image_label(ctx,computer,"computer",NK_TEXT_CENTERED))
447                 file_browser_reload_directory_content(browser, "/");
448             nk_group_end(ctx);
449         }
450 
451         /* output directory content window */
452         nk_group_begin(ctx, "Content", 0);
453         {
454             int index = -1;
455             size_t i = 0, j = 0, k = 0;
456             size_t rows = 0, cols = 0;
457             size_t count = browser->dir_count + browser->file_count;
458 
459             cols = 4;
460             rows = count / cols;
461             for (i = 0; i <= rows; i += 1) {
462                 {size_t n = j + cols;
463                 nk_layout_row_dynamic(ctx, 135, (int)cols);
464                 for (; j < count && j < n; ++j) {
465                     /* draw one row of icons */
466                     if (j < browser->dir_count) {
467                         /* draw and execute directory buttons */
468                         if (nk_button_image(ctx,media->icons.directory))
469                             index = (int)j;
470                     } else {
471                         /* draw and execute files buttons */
472                         struct nk_image *icon;
473                         size_t fileIndex = ((size_t)j - browser->dir_count);
474                         icon = media_icon_for_file(media,browser->files[fileIndex]);
475                         if (nk_button_image(ctx, *icon)) {
476                             strncpy(browser->file, browser->directory, MAX_PATH_LEN);
477                             n = strlen(browser->file);
478                             strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n);
479                             ret = 1;
480                         }
481                     }
482                 }}
483                 {size_t n = k + cols;
484                 nk_layout_row_dynamic(ctx, 20, (int)cols);
485                 for (; k < count && k < n; k++) {
486                     /* draw one row of labels */
487                     if (k < browser->dir_count) {
488                         nk_label(ctx, browser->directories[k], NK_TEXT_CENTERED);
489                     } else {
490                         size_t t = k-browser->dir_count;
491                         nk_label(ctx,browser->files[t],NK_TEXT_CENTERED);
492                     }
493                 }}
494             }
495 
496             if (index != -1) {
497                 size_t n = strlen(browser->directory);
498                 strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n);
499                 n = strlen(browser->directory);
500                 if (n < MAX_PATH_LEN - 1) {
501                     browser->directory[n] = '/';
502                     browser->directory[n+1] = '\0';
503                 }
504                 file_browser_reload_directory_content(browser, browser->directory);
505             }
506             nk_group_end(ctx);
507         }
508     }
509     nk_end(ctx);
510     return ret;
511 }
512 
513 /* ===============================================================
514  *
515  *                          DEVICE
516  *
517  * ===============================================================*/
518 struct nk_glfw_vertex {
519     float position[2];
520     float uv[2];
521     nk_byte col[4];
522 };
523 
524 struct device {
525     struct nk_buffer cmds;
526     struct nk_draw_null_texture null;
527     GLuint vbo, vao, ebo;
528     GLuint prog;
529     GLuint vert_shdr;
530     GLuint frag_shdr;
531     GLint attrib_pos;
532     GLint attrib_uv;
533     GLint attrib_col;
534     GLint uniform_tex;
535     GLint uniform_proj;
536     GLuint font_tex;
537 };
538 static struct nk_image
icon_load(const char * filename)539 icon_load(const char *filename)
540 {
541     int x,y,n;
542     GLuint tex;
543     unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
544     if (!data) die("[SDL]: failed to load image: %s", filename);
545 
546     glGenTextures(1, &tex);
547     glBindTexture(GL_TEXTURE_2D, tex);
548     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
549     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
550     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
551     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
552     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
553     glGenerateMipmap(GL_TEXTURE_2D);
554     stbi_image_free(data);
555     return nk_image_id((int)tex);
556 }
557 
558 static void
device_init(struct device * dev)559 device_init(struct device *dev)
560 {
561     GLint status;
562     static const GLchar *vertex_shader =
563         NK_SHADER_VERSION
564         "uniform mat4 ProjMtx;\n"
565         "in vec2 Position;\n"
566         "in vec2 TexCoord;\n"
567         "in vec4 Color;\n"
568         "out vec2 Frag_UV;\n"
569         "out vec4 Frag_Color;\n"
570         "void main() {\n"
571         "   Frag_UV = TexCoord;\n"
572         "   Frag_Color = Color;\n"
573         "   gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
574         "}\n";
575     static const GLchar *fragment_shader =
576         NK_SHADER_VERSION
577         "precision mediump float;\n"
578         "uniform sampler2D Texture;\n"
579         "in vec2 Frag_UV;\n"
580         "in vec4 Frag_Color;\n"
581         "out vec4 Out_Color;\n"
582         "void main(){\n"
583         "   Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
584         "}\n";
585 
586     nk_buffer_init_default(&dev->cmds);
587     dev->prog = glCreateProgram();
588     dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
589     dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
590     glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
591     glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
592     glCompileShader(dev->vert_shdr);
593     glCompileShader(dev->frag_shdr);
594     glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
595     assert(status == GL_TRUE);
596     glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
597     assert(status == GL_TRUE);
598     glAttachShader(dev->prog, dev->vert_shdr);
599     glAttachShader(dev->prog, dev->frag_shdr);
600     glLinkProgram(dev->prog);
601     glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
602     assert(status == GL_TRUE);
603 
604     dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
605     dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
606     dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
607     dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
608     dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
609 
610     {
611         /* buffer setup */
612         GLsizei vs = sizeof(struct nk_glfw_vertex);
613         size_t vp = offsetof(struct nk_glfw_vertex, position);
614         size_t vt = offsetof(struct nk_glfw_vertex, uv);
615         size_t vc = offsetof(struct nk_glfw_vertex, col);
616 
617         glGenBuffers(1, &dev->vbo);
618         glGenBuffers(1, &dev->ebo);
619         glGenVertexArrays(1, &dev->vao);
620 
621         glBindVertexArray(dev->vao);
622         glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
623         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
624 
625         glEnableVertexAttribArray((GLuint)dev->attrib_pos);
626         glEnableVertexAttribArray((GLuint)dev->attrib_uv);
627         glEnableVertexAttribArray((GLuint)dev->attrib_col);
628 
629         glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
630         glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
631         glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
632     }
633 
634     glBindTexture(GL_TEXTURE_2D, 0);
635     glBindBuffer(GL_ARRAY_BUFFER, 0);
636     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
637     glBindVertexArray(0);
638 }
639 
640 static void
device_upload_atlas(struct device * dev,const void * image,int width,int height)641 device_upload_atlas(struct device *dev, const void *image, int width, int height)
642 {
643     glGenTextures(1, &dev->font_tex);
644     glBindTexture(GL_TEXTURE_2D, dev->font_tex);
645     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
646     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
647     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
648                 GL_RGBA, GL_UNSIGNED_BYTE, image);
649 }
650 
651 static void
device_shutdown(struct device * dev)652 device_shutdown(struct device *dev)
653 {
654     glDetachShader(dev->prog, dev->vert_shdr);
655     glDetachShader(dev->prog, dev->frag_shdr);
656     glDeleteShader(dev->vert_shdr);
657     glDeleteShader(dev->frag_shdr);
658     glDeleteProgram(dev->prog);
659     glDeleteTextures(1, &dev->font_tex);
660     glDeleteBuffers(1, &dev->vbo);
661     glDeleteBuffers(1, &dev->ebo);
662     nk_buffer_free(&dev->cmds);
663 }
664 
665 static void
device_draw(struct device * dev,struct nk_context * ctx,int width,int height,struct nk_vec2 scale,enum nk_anti_aliasing AA)666 device_draw(struct device *dev, struct nk_context *ctx, int width, int height,
667     struct nk_vec2 scale, enum nk_anti_aliasing AA)
668 {
669     GLfloat ortho[4][4] = {
670         {2.0f, 0.0f, 0.0f, 0.0f},
671         {0.0f,-2.0f, 0.0f, 0.0f},
672         {0.0f, 0.0f,-1.0f, 0.0f},
673         {-1.0f,1.0f, 0.0f, 1.0f},
674     };
675     ortho[0][0] /= (GLfloat)width;
676     ortho[1][1] /= (GLfloat)height;
677 
678     /* setup global state */
679     glEnable(GL_BLEND);
680     glBlendEquation(GL_FUNC_ADD);
681     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
682     glDisable(GL_CULL_FACE);
683     glDisable(GL_DEPTH_TEST);
684     glEnable(GL_SCISSOR_TEST);
685     glActiveTexture(GL_TEXTURE0);
686 
687     /* setup program */
688     glUseProgram(dev->prog);
689     glUniform1i(dev->uniform_tex, 0);
690     glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
691     {
692         /* convert from command queue into draw list and draw to screen */
693         const struct nk_draw_command *cmd;
694         void *vertices, *elements;
695         const nk_draw_index *offset = NULL;
696 
697         /* allocate vertex and element buffer */
698         glBindVertexArray(dev->vao);
699         glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
700         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
701 
702         glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW);
703         glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW);
704 
705         /* load draw vertices & elements directly into vertex + element buffer */
706         vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
707         elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
708         {
709             /* fill convert configuration */
710             struct nk_convert_config config;
711             static const struct nk_draw_vertex_layout_element vertex_layout[] = {
712                 {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)},
713                 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)},
714                 {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)},
715                 {NK_VERTEX_LAYOUT_END}
716             };
717             NK_MEMSET(&config, 0, sizeof(config));
718             config.vertex_layout = vertex_layout;
719             config.vertex_size = sizeof(struct nk_glfw_vertex);
720             config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex);
721             config.null = dev->null;
722             config.circle_segment_count = 22;
723             config.curve_segment_count = 22;
724             config.arc_segment_count = 22;
725             config.global_alpha = 1.0f;
726             config.shape_AA = AA;
727             config.line_AA = AA;
728 
729             /* setup buffers to load vertices and elements */
730             {struct nk_buffer vbuf, ebuf;
731             nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY);
732             nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY);
733             nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);}
734         }
735         glUnmapBuffer(GL_ARRAY_BUFFER);
736         glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
737 
738         /* iterate over and execute each draw command */
739         nk_draw_foreach(cmd, ctx, &dev->cmds) {
740             if (!cmd->elem_count) continue;
741             glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
742             glScissor(
743                 (GLint)(cmd->clip_rect.x * scale.x),
744                 (GLint)((height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y),
745                 (GLint)(cmd->clip_rect.w * scale.x),
746                 (GLint)(cmd->clip_rect.h * scale.y));
747             glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
748             offset += cmd->elem_count;
749         }
750         nk_clear(ctx);
751     }
752 
753     /* default OpenGL state */
754     glUseProgram(0);
755     glBindBuffer(GL_ARRAY_BUFFER, 0);
756     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
757     glBindVertexArray(0);
758     glDisable(GL_BLEND);
759     glDisable(GL_SCISSOR_TEST);
760 }
761 
762 
763 /* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/
error_callback(int e,const char * d)764 static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);}
text_input(GLFWwindow * win,unsigned int codepoint)765 static void text_input(GLFWwindow *win, unsigned int codepoint)
766 {nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);}
scroll_input(GLFWwindow * win,double _,double yoff)767 static void scroll_input(GLFWwindow *win, double _, double yoff)
768 {UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), nk_vec2(0, (float)yoff));}
769 
main(int argc,char * argv[])770 int main(int argc, char *argv[])
771 {
772     /* Platform */
773     static GLFWwindow *win;
774     int width = 0, height = 0;
775     int display_width = 0, display_height = 0;
776 
777     /* GUI */
778     struct device device;
779     struct nk_context ctx;
780     struct nk_font *font;
781     struct nk_font_atlas atlas;
782     struct file_browser browser;
783     struct media media;
784 
785     /* GLFW */
786     glfwSetErrorCallback(error_callback);
787     if (!glfwInit()) {
788         fprintf(stdout, "[GFLW] failed to init!\n");
789         exit(1);
790     }
791     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
792     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
793     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
794 #ifdef __APPLE__
795     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
796 #endif
797     win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL);
798     glfwMakeContextCurrent(win);
799     glfwSetWindowUserPointer(win, &ctx);
800     glfwSetCharCallback(win, text_input);
801     glfwSetScrollCallback(win, scroll_input);
802 
803     /* OpenGL */
804     glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
805     glewExperimental = 1;
806     if (glewInit() != GLEW_OK) {
807         fprintf(stderr, "Failed to setup GLEW\n");
808         exit(1);
809     }
810 
811     {/* GUI */
812     device_init(&device);
813     {const void *image; int w, h;
814     const char *font_path = (argc > 1) ? argv[1]: 0;
815     nk_font_atlas_init_default(&atlas);
816     nk_font_atlas_begin(&atlas);
817     if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 13.0f, NULL);
818     else font = nk_font_atlas_add_default(&atlas, 13.0f, NULL);
819     image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
820     device_upload_atlas(&device, image, w, h);
821     nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);}
822     nk_init_default(&ctx, &font->handle);}
823 
824     /* icons */
825     glEnable(GL_TEXTURE_2D);
826     media.icons.home = icon_load("../icon/home.png");
827     media.icons.directory = icon_load("../icon/directory.png");
828     media.icons.computer = icon_load("../icon/computer.png");
829     media.icons.desktop = icon_load("../icon/desktop.png");
830     media.icons.default_file = icon_load("../icon/default.png");
831     media.icons.text_file = icon_load("../icon/text.png");
832     media.icons.music_file = icon_load("../icon/music.png");
833     media.icons.font_file =  icon_load("../icon/font.png");
834     media.icons.img_file = icon_load("../icon/img.png");
835     media.icons.movie_file = icon_load("../icon/movie.png");
836     media_init(&media);
837 
838     file_browser_init(&browser, &media);
839     while (!glfwWindowShouldClose(win))
840     {
841         /* High DPI displays */
842         struct nk_vec2 scale;
843         glfwGetWindowSize(win, &width, &height);
844         glfwGetFramebufferSize(win, &display_width, &display_height);
845         scale.x = (float)display_width/(float)width;
846         scale.y = (float)display_height/(float)height;
847 
848         /* Input */
849         {double x, y;
850         nk_input_begin(&ctx);
851         glfwPollEvents();
852         nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
853         nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
854         nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
855         nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
856         nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
857         nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
858         nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
859         nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
860         if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
861             glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
862             nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
863             nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS);
864             nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
865             nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
866             nk_input_key(&ctx, NK_KEY_SHIFT, 1);
867         } else {
868             nk_input_key(&ctx, NK_KEY_COPY, 0);
869             nk_input_key(&ctx, NK_KEY_PASTE, 0);
870             nk_input_key(&ctx, NK_KEY_CUT, 0);
871             nk_input_key(&ctx, NK_KEY_SHIFT, 0);
872         }
873         glfwGetCursorPos(win, &x, &y);
874         nk_input_motion(&ctx, (int)x, (int)y);
875         nk_input_button(&ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
876         nk_input_button(&ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
877         nk_input_button(&ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
878         nk_input_end(&ctx);}
879 
880         /* GUI */
881         file_browser_run(&browser, &ctx);
882 
883         /* Draw */
884         glViewport(0, 0, display_width, display_height);
885         glClear(GL_COLOR_BUFFER_BIT);
886         glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
887         device_draw(&device, &ctx, width, height, scale, NK_ANTI_ALIASING_ON);
888         glfwSwapBuffers(win);
889     }
890 
891     glDeleteTextures(1,(const GLuint*)&media.icons.home.handle.id);
892     glDeleteTextures(1,(const GLuint*)&media.icons.directory.handle.id);
893     glDeleteTextures(1,(const GLuint*)&media.icons.computer.handle.id);
894     glDeleteTextures(1,(const GLuint*)&media.icons.desktop.handle.id);
895     glDeleteTextures(1,(const GLuint*)&media.icons.default_file.handle.id);
896     glDeleteTextures(1,(const GLuint*)&media.icons.text_file.handle.id);
897     glDeleteTextures(1,(const GLuint*)&media.icons.music_file.handle.id);
898     glDeleteTextures(1,(const GLuint*)&media.icons.font_file.handle.id);
899     glDeleteTextures(1,(const GLuint*)&media.icons.img_file.handle.id);
900     glDeleteTextures(1,(const GLuint*)&media.icons.movie_file.handle.id);
901 
902     file_browser_free(&browser);
903     nk_font_atlas_clear(&atlas);
904     nk_free(&ctx);
905     device_shutdown(&device);
906     glfwTerminate();
907     return 0;
908 }
909 
910 
911