Nuklear/example/file_browser.c

896 lines
30 KiB
C
Raw Normal View History

Release Version 1.0 This is the first release version of nuklear (previously: zahnrad). As for those who no the old version will notice: a lot has changed. Most obvious should be the two biggest changes. First the name change because I got critique that the name is hard to comprehend and remember (understandable for non-germans) and the second is the transistion from four files (zahnrad.h, zahnrad.c, stb_truetype and stb_rect_pack) to one single header library file nuklear.h. I am not 100% convinced that using a single header library is the right choice here but so far I haven't encountered any problems. Noticable should be as well that nuklear now directly embeds three stb libraries: stb_truetype, stb_rect_pack and stb_textedit. Like in previous versions the first two are optional and the library can be compiled without. stb_textedit on the other hand powers the text edit implementation for single as well as multiline text manipulation. The text edit implementation is still relative new and untested so you can expect some bugs I have not found yet. In the demo department a lot changed as well. All platform demos now don't compile one big demo but instead contain a simple demo and small abstraction layer over the platform. Main benefit is better understandablity improved ease of use. The old demo is now split up and transfered into the example folder while each part is self contained and compileable. (All examples use glfw I don't now if this is the best platform but it is at least the simplest. I also removed the apple demo because I don't have an apple system and cannot make sure the new version runs with the old version. Finally a lot of small bugs have been fixed as well as bugs found by clang analyzer and coverity.
2016-04-11 02:34:59 +03:00
/* nuklear - v1.00 - public domain */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <dirent.h>
Release Version 1.0 This is the first release version of nuklear (previously: zahnrad). As for those who no the old version will notice: a lot has changed. Most obvious should be the two biggest changes. First the name change because I got critique that the name is hard to comprehend and remember (understandable for non-germans) and the second is the transistion from four files (zahnrad.h, zahnrad.c, stb_truetype and stb_rect_pack) to one single header library file nuklear.h. I am not 100% convinced that using a single header library is the right choice here but so far I haven't encountered any problems. Noticable should be as well that nuklear now directly embeds three stb libraries: stb_truetype, stb_rect_pack and stb_textedit. Like in previous versions the first two are optional and the library can be compiled without. stb_textedit on the other hand powers the text edit implementation for single as well as multiline text manipulation. The text edit implementation is still relative new and untested so you can expect some bugs I have not found yet. In the demo department a lot changed as well. All platform demos now don't compile one big demo but instead contain a simple demo and small abstraction layer over the platform. Main benefit is better understandablity improved ease of use. The old demo is now split up and transfered into the example folder while each part is self contained and compileable. (All examples use glfw I don't now if this is the best platform but it is at least the simplest. I also removed the apple demo because I don't have an apple system and cannot make sure the new version runs with the old version. Finally a lot of small bugs have been fixed as well as bugs found by clang analyzer and coverity.
2016-04-11 02:34:59 +03:00
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#define NK_IMPLEMENTATION
#include "../nuklear.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
/* macros */
#define WINDOW_WIDTH 1200
#define WINDOW_HEIGHT 800
#define MAX_VERTEX_MEMORY 512 * 1024
#define MAX_ELEMENT_MEMORY 128 * 1024
#define UNUSED(a) (void)a
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) < (b) ? (b) : (a))
#define LEN(a) (sizeof(a)/sizeof(a)[0])
#ifdef __APPLE__
#define NK_SHADER_VERSION "#version 150\n"
#else
#define NK_SHADER_VERSION "#version 300 es\n"
#endif
Release Version 1.0 This is the first release version of nuklear (previously: zahnrad). As for those who no the old version will notice: a lot has changed. Most obvious should be the two biggest changes. First the name change because I got critique that the name is hard to comprehend and remember (understandable for non-germans) and the second is the transistion from four files (zahnrad.h, zahnrad.c, stb_truetype and stb_rect_pack) to one single header library file nuklear.h. I am not 100% convinced that using a single header library is the right choice here but so far I haven't encountered any problems. Noticable should be as well that nuklear now directly embeds three stb libraries: stb_truetype, stb_rect_pack and stb_textedit. Like in previous versions the first two are optional and the library can be compiled without. stb_textedit on the other hand powers the text edit implementation for single as well as multiline text manipulation. The text edit implementation is still relative new and untested so you can expect some bugs I have not found yet. In the demo department a lot changed as well. All platform demos now don't compile one big demo but instead contain a simple demo and small abstraction layer over the platform. Main benefit is better understandablity improved ease of use. The old demo is now split up and transfered into the example folder while each part is self contained and compileable. (All examples use glfw I don't now if this is the best platform but it is at least the simplest. I also removed the apple demo because I don't have an apple system and cannot make sure the new version runs with the old version. Finally a lot of small bugs have been fixed as well as bugs found by clang analyzer and coverity.
2016-04-11 02:34:59 +03:00
/* ===============================================================
*
* GUI
*
* ===============================================================*/
struct icons {
struct nk_image desktop;
struct nk_image home;
struct nk_image computer;
struct nk_image directory;
struct nk_image default_file;
struct nk_image text_file;
struct nk_image music_file;
struct nk_image font_file;
struct nk_image img_file;
struct nk_image movie_file;
};
enum file_groups {
FILE_GROUP_DEFAULT,
FILE_GROUP_TEXT,
FILE_GROUP_MUSIC,
FILE_GROUP_FONT,
FILE_GROUP_IMAGE,
FILE_GROUP_MOVIE,
FILE_GROUP_MAX
};
enum file_types {
FILE_DEFAULT,
FILE_TEXT,
FILE_C_SOURCE,
FILE_CPP_SOURCE,
FILE_HEADER,
FILE_CPP_HEADER,
FILE_MP3,
FILE_WAV,
FILE_OGG,
FILE_TTF,
FILE_BMP,
FILE_PNG,
FILE_JPEG,
FILE_PCX,
FILE_TGA,
FILE_GIF,
FILE_MAX
};
struct file_group {
enum file_groups group;
const char *name;
struct nk_image *icon;
};
struct file {
enum file_types type;
const char *suffix;
enum file_groups group;
};
struct media {
int font;
int icon_sheet;
struct icons icons;
struct file_group group[FILE_GROUP_MAX];
struct file files[FILE_MAX];
};
#define MAX_PATH_LEN 512
struct file_browser {
/* path */
char file[MAX_PATH_LEN];
char home[MAX_PATH_LEN];
char desktop[MAX_PATH_LEN];
char directory[MAX_PATH_LEN];
/* directory content */
char **files;
char **directories;
size_t file_count;
size_t dir_count;
struct media *media;
};
#ifdef __unix__
#include <dirent.h>
#include <unistd.h>
#endif
#ifndef _WIN32
# include <pwd.h>
#endif
static void
die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputs("\n", stderr);
exit(EXIT_FAILURE);
}
static char*
file_load(const char* path, size_t* siz)
{
char *buf;
FILE *fd = fopen(path, "rb");
if (!fd) die("Failed to open file: %s\n", path);
fseek(fd, 0, SEEK_END);
*siz = (size_t)ftell(fd);
fseek(fd, 0, SEEK_SET);
buf = (char*)calloc(*siz, 1);
fread(buf, *siz, 1, fd);
fclose(fd);
return buf;
}
static char*
str_duplicate(const char *src)
{
char *ret;
size_t len = strlen(src);
if (!len) return 0;
ret = (char*)malloc(len+1);
if (!ret) return 0;
memcpy(ret, src, len);
ret[len] = '\0';
return ret;
}
static void
dir_free_list(char **list, size_t size)
{
size_t i;
for (i = 0; i < size; ++i)
free(list[i]);
free(list);
}
static char**
dir_list(const char *dir, int return_subdirs, size_t *count)
{
size_t n = 0;
char buffer[MAX_PATH_LEN];
char **results = NULL;
const DIR *none = NULL;
size_t capacity = 32;
size_t size;
DIR *z;
assert(dir);
assert(count);
strncpy(buffer, dir, MAX_PATH_LEN);
n = strlen(buffer);
if (n > 0 && (buffer[n-1] != '/'))
buffer[n++] = '/';
size = 0;
z = opendir(dir);
if (z != none) {
int nonempty = 1;
struct dirent *data = readdir(z);
nonempty = (data != NULL);
if (!nonempty) return NULL;
do {
DIR *y;
char *p;
int is_subdir;
if (data->d_name[0] == '.')
continue;
strncpy(buffer + n, data->d_name, MAX_PATH_LEN-n);
y = opendir(buffer);
is_subdir = (y != NULL);
if (y != NULL) closedir(y);
if ((return_subdirs && is_subdir) || (!is_subdir && !return_subdirs)){
if (!size) {
results = (char**)calloc(sizeof(char*), capacity);
} else if (size >= capacity) {
capacity = capacity * 2;
results = (char**)realloc(results, capacity * sizeof(char*));
}
p = str_duplicate(data->d_name);
results[size++] = p;
}
} while ((data = readdir(z)) != NULL);
}
if (z) closedir(z);
*count = size;
return results;
}
static struct file_group
FILE_GROUP(enum file_groups group, const char *name, struct nk_image *icon)
{
struct file_group fg;
fg.group = group;
fg.name = name;
fg.icon = icon;
return fg;
}
static struct file
FILE_DEF(enum file_types type, const char *suffix, enum file_groups group)
{
struct file fd;
fd.type = type;
fd.suffix = suffix;
fd.group = group;
return fd;
}
static struct nk_image*
media_icon_for_file(struct media *media, const char *file)
{
int i = 0;
const char *s = file;
char suffix[4];
int found = 0;
memset(suffix, 0, sizeof(suffix));
/* extract suffix .xxx from file */
while (*s++ != '\0') {
if (found && i < 3)
suffix[i++] = *s;
if (*s == '.') {
if (found){
found = 0;
break;
}
found = 1;
}
}
/* check for all file definition of all groups for fitting suffix*/
for (i = 0; i < FILE_MAX && found; ++i) {
struct file *d = &media->files[i];
{
const char *f = d->suffix;
s = suffix;
while (f && *f && *s && *s == *f) {
s++; f++;
}
/* found correct file definition so */
if (f && *s == '\0' && *f == '\0')
return media->group[d->group].icon;
}
}
return &media->icons.default_file;
}
static void
media_init(struct media *media)
{
/* file groups */
struct icons *icons = &media->icons;
media->group[FILE_GROUP_DEFAULT] = FILE_GROUP(FILE_GROUP_DEFAULT,"default",&icons->default_file);
media->group[FILE_GROUP_TEXT] = FILE_GROUP(FILE_GROUP_TEXT, "textual", &icons->text_file);
media->group[FILE_GROUP_MUSIC] = FILE_GROUP(FILE_GROUP_MUSIC, "music", &icons->music_file);
media->group[FILE_GROUP_FONT] = FILE_GROUP(FILE_GROUP_FONT, "font", &icons->font_file);
media->group[FILE_GROUP_IMAGE] = FILE_GROUP(FILE_GROUP_IMAGE, "image", &icons->img_file);
media->group[FILE_GROUP_MOVIE] = FILE_GROUP(FILE_GROUP_MOVIE, "movie", &icons->movie_file);
/* files */
media->files[FILE_DEFAULT] = FILE_DEF(FILE_DEFAULT, NULL, FILE_GROUP_DEFAULT);
media->files[FILE_TEXT] = FILE_DEF(FILE_TEXT, "txt", FILE_GROUP_TEXT);
media->files[FILE_C_SOURCE] = FILE_DEF(FILE_C_SOURCE, "c", FILE_GROUP_TEXT);
media->files[FILE_CPP_SOURCE] = FILE_DEF(FILE_CPP_SOURCE, "cpp", FILE_GROUP_TEXT);
media->files[FILE_HEADER] = FILE_DEF(FILE_HEADER, "h", FILE_GROUP_TEXT);
media->files[FILE_CPP_HEADER] = FILE_DEF(FILE_HEADER, "hpp", FILE_GROUP_TEXT);
media->files[FILE_MP3] = FILE_DEF(FILE_MP3, "mp3", FILE_GROUP_MUSIC);
media->files[FILE_WAV] = FILE_DEF(FILE_WAV, "wav", FILE_GROUP_MUSIC);
media->files[FILE_OGG] = FILE_DEF(FILE_OGG, "ogg", FILE_GROUP_MUSIC);
media->files[FILE_TTF] = FILE_DEF(FILE_TTF, "ttf", FILE_GROUP_FONT);
media->files[FILE_BMP] = FILE_DEF(FILE_BMP, "bmp", FILE_GROUP_IMAGE);
media->files[FILE_PNG] = FILE_DEF(FILE_PNG, "png", FILE_GROUP_IMAGE);
media->files[FILE_JPEG] = FILE_DEF(FILE_JPEG, "jpg", FILE_GROUP_IMAGE);
media->files[FILE_PCX] = FILE_DEF(FILE_PCX, "pcx", FILE_GROUP_IMAGE);
media->files[FILE_TGA] = FILE_DEF(FILE_TGA, "tga", FILE_GROUP_IMAGE);
media->files[FILE_GIF] = FILE_DEF(FILE_GIF, "gif", FILE_GROUP_IMAGE);
}
static void
file_browser_reload_directory_content(struct file_browser *browser, const char *path)
{
strncpy(browser->directory, path, MAX_PATH_LEN);
dir_free_list(browser->files, browser->file_count);
dir_free_list(browser->directories, browser->dir_count);
browser->files = dir_list(path, 0, &browser->file_count);
browser->directories = dir_list(path, 1, &browser->dir_count);
}
static void
file_browser_init(struct file_browser *browser, struct media *media)
{
memset(browser, 0, sizeof(*browser));
browser->media = media;
{
/* load files and sub-directory list */
const char *home = getenv("HOME");
#ifdef _WIN32
if (!home) home = getenv("USERPROFILE");
#else
if (!home) home = getpwuid(getuid())->pw_dir;
{
size_t l;
strncpy(browser->home, home, MAX_PATH_LEN);
l = strlen(browser->home);
strcpy(browser->home + l, "/");
strcpy(browser->directory, browser->home);
}
#endif
{
size_t l;
strcpy(browser->desktop, browser->home);
l = strlen(browser->desktop);
strcpy(browser->desktop + l, "desktop/");
}
browser->files = dir_list(browser->directory, 0, &browser->file_count);
browser->directories = dir_list(browser->directory, 1, &browser->dir_count);
}
}
static void
file_browser_free(struct file_browser *browser)
{
if (browser->files)
dir_free_list(browser->files, browser->file_count);
if (browser->directories)
dir_free_list(browser->directories, browser->dir_count);
browser->files = NULL;
browser->directories = NULL;
memset(browser, 0, sizeof(*browser));
}
static int
file_browser_run(struct file_browser *browser, struct nk_context *ctx)
{
int ret = 0;
struct nk_panel layout;
struct media *media = browser->media;
struct nk_rect total_space;
if (nk_begin(ctx, &layout, "File Browser", nk_rect(50, 50, 800, 600),
NK_WINDOW_BORDER|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_CLOSABLE|NK_WINDOW_MOVABLE))
{
struct nk_panel sub;
static float ratio[] = {0.25f, NK_UNDEFINED};
float spacing_x = ctx->style.window.spacing.x;
/* output path directory selector in the menubar */
ctx->style.window.spacing.x = 0;
nk_menubar_begin(ctx);
{
char *d = browser->directory;
char *begin = d + 1;
nk_layout_row_dynamic(ctx, 25, 6);
while (*d++) {
if (*d == '/') {
*d = '\0';
if (nk_button_label(ctx, begin, NK_BUTTON_DEFAULT)) {
*d++ = '/'; *d = '\0';
file_browser_reload_directory_content(browser, browser->directory);
break;
}
*d = '/';
begin = d + 1;
}
}
}
nk_menubar_end(ctx);
ctx->style.window.spacing.x = spacing_x;
/* window layout */
total_space = nk_window_get_content_region(ctx);
nk_layout_row(ctx, NK_DYNAMIC, total_space.h, 2, ratio);
nk_group_begin(ctx, &sub, "Special", NK_WINDOW_NO_SCROLLBAR);
{
struct nk_image home = media->icons.home;
struct nk_image desktop = media->icons.desktop;
struct nk_image computer = media->icons.computer;
nk_layout_row_dynamic(ctx, 40, 1);
if (nk_button_image_label(ctx, home, "home", NK_TEXT_CENTERED, NK_BUTTON_DEFAULT))
file_browser_reload_directory_content(browser, browser->home);
if (nk_button_image_label(ctx,desktop,"desktop",NK_TEXT_CENTERED, NK_BUTTON_DEFAULT))
file_browser_reload_directory_content(browser, browser->desktop);
if (nk_button_image_label(ctx,computer,"computer",NK_TEXT_CENTERED,NK_BUTTON_DEFAULT))
file_browser_reload_directory_content(browser, "/");
nk_group_end(ctx);
}
/* output directory content window */
nk_group_begin(ctx, &sub, "Content", 0);
{
int index = -1;
size_t i = 0, j = 0, k = 0;
size_t rows = 0, cols = 0;
size_t count = browser->dir_count + browser->file_count;
cols = 4;
rows = count / cols;
for (i = 0; i <= rows; i += 1) {
{size_t n = j + cols;
nk_layout_row_dynamic(ctx, 135, (int)cols);
for (; j < count && j < n; ++j) {
/* draw one row of icons */
if (j < browser->dir_count) {
/* draw and execute directory buttons */
if (nk_button_image(ctx,media->icons.directory,NK_BUTTON_DEFAULT))
index = (int)j;
} else {
/* draw and execute files buttons */
struct nk_image *icon;
size_t fileIndex = ((size_t)j - browser->dir_count);
icon = media_icon_for_file(media,browser->files[fileIndex]);
if (nk_button_image(ctx, *icon, NK_BUTTON_DEFAULT)) {
strncpy(browser->file, browser->directory, MAX_PATH_LEN);
n = strlen(browser->file);
strncpy(browser->file + n, browser->files[fileIndex], MAX_PATH_LEN - n);
ret = 1;
}
}
}}
{size_t n = k + cols;
nk_layout_row_dynamic(ctx, 20, (int)cols);
for (; k < count && k < n; k++) {
/* draw one row of labels */
if (k < browser->dir_count) {
nk_label(ctx, browser->directories[k], NK_TEXT_CENTERED);
} else {
size_t t = k-browser->dir_count;
nk_label(ctx,browser->files[t],NK_TEXT_CENTERED);
}
}}
}
if (index != -1) {
size_t n = strlen(browser->directory);
strncpy(browser->directory + n, browser->directories[index], MAX_PATH_LEN - n);
n = strlen(browser->directory);
if (n < MAX_PATH_LEN - 1) {
browser->directory[n] = '/';
browser->directory[n+1] = '\0';
}
file_browser_reload_directory_content(browser, browser->directory);
sub.offset->y = 0;
}
nk_group_end(ctx);
}
}
nk_end(ctx);
return ret;
}
/* ===============================================================
*
* DEVICE
*
* ===============================================================*/
struct device {
struct nk_buffer cmds;
struct nk_draw_null_texture null;
GLuint vbo, vao, ebo;
GLuint prog;
GLuint vert_shdr;
GLuint frag_shdr;
GLint attrib_pos;
GLint attrib_uv;
GLint attrib_col;
GLint uniform_tex;
GLint uniform_proj;
GLuint font_tex;
};
static struct nk_image
icon_load(const char *filename)
{
int x,y,n;
GLuint tex;
unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
if (!data) die("[SDL]: failed to load image: %s", filename);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
return nk_image_id((int)tex);
}
static void
device_init(struct device *dev)
{
GLint status;
static const GLchar *vertex_shader =
NK_SHADER_VERSION
Release Version 1.0 This is the first release version of nuklear (previously: zahnrad). As for those who no the old version will notice: a lot has changed. Most obvious should be the two biggest changes. First the name change because I got critique that the name is hard to comprehend and remember (understandable for non-germans) and the second is the transistion from four files (zahnrad.h, zahnrad.c, stb_truetype and stb_rect_pack) to one single header library file nuklear.h. I am not 100% convinced that using a single header library is the right choice here but so far I haven't encountered any problems. Noticable should be as well that nuklear now directly embeds three stb libraries: stb_truetype, stb_rect_pack and stb_textedit. Like in previous versions the first two are optional and the library can be compiled without. stb_textedit on the other hand powers the text edit implementation for single as well as multiline text manipulation. The text edit implementation is still relative new and untested so you can expect some bugs I have not found yet. In the demo department a lot changed as well. All platform demos now don't compile one big demo but instead contain a simple demo and small abstraction layer over the platform. Main benefit is better understandablity improved ease of use. The old demo is now split up and transfered into the example folder while each part is self contained and compileable. (All examples use glfw I don't now if this is the best platform but it is at least the simplest. I also removed the apple demo because I don't have an apple system and cannot make sure the new version runs with the old version. Finally a lot of small bugs have been fixed as well as bugs found by clang analyzer and coverity.
2016-04-11 02:34:59 +03:00
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 TexCoord;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main() {\n"
" Frag_UV = TexCoord;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n"
"}\n";
static const GLchar *fragment_shader =
NK_SHADER_VERSION
Release Version 1.0 This is the first release version of nuklear (previously: zahnrad). As for those who no the old version will notice: a lot has changed. Most obvious should be the two biggest changes. First the name change because I got critique that the name is hard to comprehend and remember (understandable for non-germans) and the second is the transistion from four files (zahnrad.h, zahnrad.c, stb_truetype and stb_rect_pack) to one single header library file nuklear.h. I am not 100% convinced that using a single header library is the right choice here but so far I haven't encountered any problems. Noticable should be as well that nuklear now directly embeds three stb libraries: stb_truetype, stb_rect_pack and stb_textedit. Like in previous versions the first two are optional and the library can be compiled without. stb_textedit on the other hand powers the text edit implementation for single as well as multiline text manipulation. The text edit implementation is still relative new and untested so you can expect some bugs I have not found yet. In the demo department a lot changed as well. All platform demos now don't compile one big demo but instead contain a simple demo and small abstraction layer over the platform. Main benefit is better understandablity improved ease of use. The old demo is now split up and transfered into the example folder while each part is self contained and compileable. (All examples use glfw I don't now if this is the best platform but it is at least the simplest. I also removed the apple demo because I don't have an apple system and cannot make sure the new version runs with the old version. Finally a lot of small bugs have been fixed as well as bugs found by clang analyzer and coverity.
2016-04-11 02:34:59 +03:00
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main(){\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
nk_buffer_init_default(&dev->cmds);
dev->prog = glCreateProgram();
dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER);
dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0);
glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0);
glCompileShader(dev->vert_shdr);
glCompileShader(dev->frag_shdr);
glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status);
assert(status == GL_TRUE);
glAttachShader(dev->prog, dev->vert_shdr);
glAttachShader(dev->prog, dev->frag_shdr);
glLinkProgram(dev->prog);
glGetProgramiv(dev->prog, GL_LINK_STATUS, &status);
assert(status == GL_TRUE);
dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture");
dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx");
dev->attrib_pos = glGetAttribLocation(dev->prog, "Position");
dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord");
dev->attrib_col = glGetAttribLocation(dev->prog, "Color");
{
/* buffer setup */
GLsizei vs = sizeof(struct nk_draw_vertex);
size_t vp = offsetof(struct nk_draw_vertex, position);
size_t vt = offsetof(struct nk_draw_vertex, uv);
size_t vc = offsetof(struct nk_draw_vertex, col);
glGenBuffers(1, &dev->vbo);
glGenBuffers(1, &dev->ebo);
glGenVertexArrays(1, &dev->vao);
glBindVertexArray(dev->vao);
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
glEnableVertexAttribArray((GLuint)dev->attrib_pos);
glEnableVertexAttribArray((GLuint)dev->attrib_uv);
glEnableVertexAttribArray((GLuint)dev->attrib_col);
glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp);
glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt);
glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc);
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
static void
device_upload_atlas(struct device *dev, const void *image, int width, int height)
{
glGenTextures(1, &dev->font_tex);
glBindTexture(GL_TEXTURE_2D, dev->font_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image);
}
static void
device_shutdown(struct device *dev)
{
glDetachShader(dev->prog, dev->vert_shdr);
glDetachShader(dev->prog, dev->frag_shdr);
glDeleteShader(dev->vert_shdr);
glDeleteShader(dev->frag_shdr);
glDeleteProgram(dev->prog);
glDeleteTextures(1, &dev->font_tex);
glDeleteBuffers(1, &dev->vbo);
glDeleteBuffers(1, &dev->ebo);
nk_buffer_free(&dev->cmds);
}
static void
device_draw(struct device *dev, struct nk_context *ctx, int width, int height,
enum nk_anti_aliasing AA)
{
GLint last_prog, last_tex;
GLint last_ebo, last_vbo, last_vao;
GLfloat ortho[4][4] = {
{2.0f, 0.0f, 0.0f, 0.0f},
{0.0f,-2.0f, 0.0f, 0.0f},
{0.0f, 0.0f,-1.0f, 0.0f},
{-1.0f,1.0f, 0.0f, 1.0f},
};
ortho[0][0] /= (GLfloat)width;
ortho[1][1] /= (GLfloat)height;
/* save previous opengl state */
glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_vao);
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_ebo);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vbo);
/* setup global state */
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glActiveTexture(GL_TEXTURE0);
/* setup program */
glUseProgram(dev->prog);
glUniform1i(dev->uniform_tex, 0);
glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]);
{
/* convert from command queue into draw list and draw to screen */
const struct nk_draw_command *cmd;
void *vertices, *elements;
const nk_draw_index *offset = NULL;
/* allocate vertex and element buffer */
glBindVertexArray(dev->vao);
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
glBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_MEMORY, NULL, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_MEMORY, NULL, GL_STREAM_DRAW);
/* load draw vertices & elements directly into vertex + element buffer */
vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
{
/* fill converting configuration */
struct nk_convert_config config;
memset(&config, 0, sizeof(config));
config.global_alpha = 1.0f;
config.shape_AA = AA;
config.line_AA = AA;
config.circle_segment_count = 22;
config.curve_segment_count = 22;
config.arc_segment_count = 22;
config.null = dev->null;
/* setup buffers to load vertices and elements */
{struct nk_buffer vbuf, ebuf;
nk_buffer_init_fixed(&vbuf, vertices, MAX_VERTEX_MEMORY);
nk_buffer_init_fixed(&ebuf, elements, MAX_ELEMENT_MEMORY);
nk_convert(ctx, &dev->cmds, &vbuf, &ebuf, &config);}
}
glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
/* iterate over and execute each draw command */
nk_draw_foreach(cmd, ctx, &dev->cmds) {
if (!cmd->elem_count) continue;
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor((GLint)cmd->clip_rect.x,
height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h),
(GLint)cmd->clip_rect.w, (GLint)cmd->clip_rect.h);
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
offset += cmd->elem_count;
}
nk_clear(ctx);
}
/* restore old state */
glUseProgram((GLuint)last_prog);
glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex);
glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo);
glBindVertexArray((GLuint)last_vao);
glDisable(GL_SCISSOR_TEST);
}
/* glfw callbacks (I don't know if there is a easier way to access text and scroll )*/
static void error_callback(int e, const char *d){printf("Error %d: %s\n", e, d);}
static void text_input(GLFWwindow *win, unsigned int codepoint)
{nk_input_unicode((struct nk_context*)glfwGetWindowUserPointer(win), codepoint);}
static void scroll_input(GLFWwindow *win, double _, double yoff)
{UNUSED(_);nk_input_scroll((struct nk_context*)glfwGetWindowUserPointer(win), (float)yoff);}
int main(int argc, char *argv[])
{
/* Platform */
static GLFWwindow *win;
int width = 0, height = 0;
/* GUI */
struct device device;
struct nk_context ctx;
struct nk_font *font;
struct nk_font_atlas atlas;
struct file_browser browser;
struct media media;
/* GLFW */
glfwSetErrorCallback(error_callback);
if (!glfwInit()) {
fprintf(stdout, "[GFLW] failed to init!\n");
exit(1);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL);
glfwMakeContextCurrent(win);
glfwSetWindowUserPointer(win, &ctx);
glfwSetCharCallback(win, text_input);
glfwSetScrollCallback(win, scroll_input);
/* OpenGL */
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
glewExperimental = 1;
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to setup GLEW\n");
exit(1);
}
{/* GUI */
device_init(&device);
{const void *image; int w, h;
const char *font_path = (argc > 1) ? argv[1]: 0;
nk_font_atlas_init_default(&atlas);
nk_font_atlas_begin(&atlas);
if (font_path) font = nk_font_atlas_add_from_file(&atlas, font_path, 14.0f, NULL);
else font = nk_font_atlas_add_default(&atlas, 14.0f, NULL);
image = nk_font_atlas_bake(&atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
device_upload_atlas(&device, image, w, h);
nk_font_atlas_end(&atlas, nk_handle_id((int)device.font_tex), &device.null);}
nk_init_default(&ctx, &font->handle);}
/* icons */
glEnable(GL_TEXTURE_2D);
media.icons.home = icon_load("../icon/home.png");
media.icons.directory = icon_load("../icon/directory.png");
media.icons.computer = icon_load("../icon/computer.png");
media.icons.desktop = icon_load("../icon/desktop.png");
media.icons.default_file = icon_load("../icon/default.png");
media.icons.text_file = icon_load("../icon/text.png");
media.icons.music_file = icon_load("../icon/music.png");
media.icons.font_file = icon_load("../icon/font.png");
media.icons.img_file = icon_load("../icon/img.png");
media.icons.movie_file = icon_load("../icon/movie.png");
media_init(&media);
file_browser_init(&browser, &media);
while (!glfwWindowShouldClose(win))
{
/* Input */
{double x, y;
nk_input_begin(&ctx);
glfwPollEvents();
nk_input_key(&ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS);
if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL)) {
nk_input_key(&ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_P) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS);
nk_input_key(&ctx, NK_KEY_SHIFT, 1);
} else {
nk_input_key(&ctx, NK_KEY_COPY, 0);
nk_input_key(&ctx, NK_KEY_PASTE, 0);
nk_input_key(&ctx, NK_KEY_CUT, 0);
nk_input_key(&ctx, NK_KEY_SHIFT, 0);
}
glfwGetCursorPos(win, &x, &y);
nk_input_motion(&ctx, (int)x, (int)y);
nk_input_button(&ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
nk_input_button(&ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS);
nk_input_button(&ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
nk_input_end(&ctx);}
/* GUI */
file_browser_run(&browser, &ctx);
/* Draw */
glfwGetWindowSize(win, &width, &height);
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
device_draw(&device, &ctx, width, height, NK_ANTI_ALIASING_ON);
glfwSwapBuffers(win);
}
glDeleteTextures(1,(const GLuint*)&media.icons.home.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.directory.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.computer.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.desktop.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.default_file.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.text_file.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.music_file.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.font_file.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.img_file.handle.id);
glDeleteTextures(1,(const GLuint*)&media.icons.movie_file.handle.id);
file_browser_free(&browser);
nk_font_atlas_clear(&atlas);
nk_free(&ctx);
device_shutdown(&device);
glfwTerminate();
return 0;
}