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