#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <string>
+#include <map>
+#include <algorithm>
#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
#include "fs.h"
#include "icon.h"
#include "gmath/gmath.h"
#include "opengl.h"
#include "app.h"
+#include "drawtext.h"
+#include "sdr.h"
+
+static void draw_node_name(FSNode *node, float angle, float ypos, float dist, bool full);
static IconRenderer *iconrend;
-bool init_fs()
+static std::map<std::string, FSNode*> node_cache;
+static FSNode *cur_node;
+static int start_child;
+
+static dtx_font *fat_font;
+#define FAT_FONT_SZ 32
+static unsigned int glow_link_sdr;
+static unsigned int chrome_font_sdr, glow_font_sdr;
+
+
+bool init_fs(const char *path)
{
iconrend = new ShapesIcons;
if(!iconrend->init()) {
return false;
}
+ if(!(fat_font = dtx_open_font_glyphmap("data/fat.glyphmap")) ||
+ dtx_get_glyphmap_ptsize(dtx_get_glyphmap(fat_font, 0)) != FAT_FONT_SZ) {
+
+ dtx_set(DTX_PADDING, 64);
+
+ if(!(fat_font = dtx_open_font("data/fat.font", 0))) {
+ fprintf(stderr, "failed to open font file data/fat.font\n");
+ return false;
+ }
+ dtx_prepare_range(fat_font, FAT_FONT_SZ * 8, 32, 127);
+ dtx_calc_font_distfield(fat_font, 1, 8);
+ dtx_save_glyphmap("data/fat.glyphmap", dtx_get_glyphmap(fat_font, 0));
+ }
+ dtx_use_font(fat_font, FAT_FONT_SZ);
+
+ struct dtx_glyphmap *fat_gmap = dtx_get_glyphmap(fat_font, 0);
+ Vec2 pixsz;
+ pixsz.x = 1.0 / dtx_get_glyphmap_width(fat_gmap);
+ pixsz.y = 1.0 / dtx_get_glyphmap_height(fat_gmap);
+
+ if(!(chrome_font_sdr = create_program_load("sdr/chrome_font.v.glsl", "sdr/chrome_font.p.glsl"))) {
+ return false;
+ }
+ set_uniform_float(chrome_font_sdr, "height", dtx_line_height());
+ set_uniform_float(chrome_font_sdr, "smoothness", 0.01);
+ set_uniform_float2(chrome_font_sdr, "pix_sz", pixsz.x, pixsz.y);
+
+ if(!(glow_font_sdr = create_program_load("sdr/dfont.v.glsl", "sdr/glow_font.p.glsl"))) {
+ return false;
+ }
+ set_uniform_float(glow_font_sdr, "smoothness", 0.01);
+ set_uniform_float2(glow_font_sdr, "pix_sz", pixsz.x, pixsz.y);
+
+ if(!(glow_link_sdr = create_program_load("sdr/glink.v.glsl", "sdr/glink.p.glsl"))) {
+ return false;
+ }
+
+ if(!(cur_node = get_fsnode(path))) {
+ return false;
+ }
+ cur_node->expand();
return true;
}
void cleanup_fs()
{
+ std::map<std::string, FSNode*>::iterator it = node_cache.begin();
+ while(it != node_cache.end()) {
+ FSNode *node = it++->second;
+ delete node;
+ }
+ node_cache.clear();
+ dtx_close_font(fat_font);
delete iconrend;
}
+static float icon_angle(int col, int ncols, float max_angle = 0.0f)
+{
+ if(max_angle > 0) {
+ return max_angle * ((float)col / (float)(ncols - 1) - 0.5);
+ }
+ return 2.0 * M_PI * (float)col / (float)ncols;
+}
+
void draw_fs()
{
- FSNode test;
- test.type = FSNODE_DIR;
+ static const float row_spacing = 0.25;
+ static const float radius = 0.6;
+ static const float umax = 0.42;
+ static const float max_icon_angle = M_PI * 2.0 * umax;
- Mat4 xform;
- xform.rotate(time_sec, 0, 0);
- xform.rotate(0, 0, time_sec * 0.5);
- xform.translate(0, 2, -5);
+ int max_ncols = std::max<int>(1, umax * 12);
glPushMatrix();
- glMultMatrixf(xform[0]);
+ glTranslatef(0, cam_height, 0);
+
+ Mat4 rot_xform;
+ rot_xform.rotate(time_sec, 0, 0);
+ rot_xform.rotate(0, 0, time_sec * 0.5);
- glUseProgram(0);
glDisable(GL_TEXTURE_2D);
- iconrend->draw(&test);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ int nchildren = (int)cur_node->children.size();
+ int ncols = std::min(cur_node->nfiles, max_ncols);
+
+ int first = start_child % ncols;
+ int col = 0, row = 0;
+ int num_dirs = 0;
+
+ // count directories ...
+ for(int i=0; i<nchildren; i++) {
+ FSNode *node = cur_node->children[i];
+ if(node->type == FSTYPE_DIR) {
+ ++num_dirs;
+ }
+ }
+
+ // draw the directory link lines
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+ for(int i=0; i<nchildren; i++) {
+ FSNode *node = cur_node->children[i];
+
+ if(node->type != FSTYPE_DIR) {
+ continue;
+ }
+
+ float angle = (float)col++ / (float)(num_dirs - 1) * max_icon_angle - max_icon_angle * 0.5;
+
+ Mat4 xform;
+ xform.rotate_y(angle);
+ xform.translate(0, -0.3, 0);
+
+ glUseProgram(glow_link_sdr);
+ glPushMatrix();
+ glMultMatrixf(xform[0]);
+ glDepthMask(0);
+
+ glBegin(GL_QUADS);
+ glColor3f(0.2, 0.3, 0.8);
+ glTexCoord2f(0, 0);
+ glVertex3f(-0.25, 0, 0.05);
+ glTexCoord2f(1, 0);
+ glVertex3f(0.25, 0, 0.05);
+ glTexCoord2f(1, 1);
+ glVertex3f(0.25, 0, -2.0);
+ glTexCoord2f(0, 1);
+ glVertex3f(-0.25, 0, -2.0);
+ glColor3f(1, 1, 1);
+ glEnd();
+ glPopMatrix();
+
+ glDepthMask(1);
+ }
+
+ // draw the directory labels
+ glUseProgram(glow_font_sdr);
+ col = 0;
+ for(int i=0; i<nchildren; i++) {
+ FSNode *node = cur_node->children[i];
+
+ if(node->type != FSTYPE_DIR) {
+ continue;
+ }
+
+ float angle = (float)col++ / (float)(num_dirs - 1) * max_icon_angle - max_icon_angle * 0.5;
+
+ draw_node_name(node, angle, -0.3, radius, false);
+ }
+
+ // then draw file icons
+ glDisable(GL_BLEND);
+ glUseProgram(0);
+ col = 0;
+ for(int i=0; i<nchildren; i++) {
+ int idx = (i + first) % nchildren;
+ FSNode *node = cur_node->children[idx];
+
+ if(node->type == FSTYPE_DIR) {
+ ++num_dirs;
+ continue;
+ }
+
+ float angle = icon_angle(col, ncols, max_icon_angle);
+
+ Mat4 xform = rot_xform;
+ xform.translate(0, row * row_spacing, -radius);
+ xform.rotate_y(angle);
+
+ glPushMatrix();
+ glMultMatrixf(xform[0]);
+ iconrend->draw(node);
+ glPopMatrix();
+
+ if(++col >= ncols) {
+ col = 0;
+ ++row;
+ }
+ }
+
+ // then draw the file labels
+ glUseProgram(chrome_font_sdr);
+ col = 0;
+ row = 0;
+ for(int i=0; i<nchildren; i++) {
+ int idx = (i + first) % nchildren;
+ FSNode *node = cur_node->children[idx];
+
+ if(node->type == FSTYPE_DIR) {
+ ++num_dirs;
+ continue;
+ }
+
+ float angle = icon_angle(col, ncols, max_icon_angle);
+
+ draw_node_name(node, angle, row * row_spacing - 0.1, radius, false);
+
+ if(++col >= ncols) {
+ col = 0;
+ ++row;
+ }
+ }
glPopMatrix();
}
-// ---- FSNode implementation ----
-FSNode::FSNode()
+static void draw_node_name(FSNode *node, float angle, float ypos, float dist, bool full)
{
- type = FSNODE_UNKNOWN;
- abs_path = name = suffix = 0;
-}
+ dtx_use_font(fat_font, FAT_FONT_SZ);
+ int line_height = dtx_line_height();
-FSNode::~FSNode()
-{
- if(abs_path) {
- delete [] abs_path;
+ int nlines = full ? node->name_lines.size() : 1;
+ for(int i=0; i<nlines; i++) {
+ const char *name = full ? node->name_lines[i].c_str() : node->short_name.c_str();
+ glPushMatrix();
+ Mat4 xform;
+ xform.translate(-dtx_string_width(name) / 2.0, -line_height * i, 0);
+ if(node->type == FSTYPE_DIR) {
+ xform.rotate_z(deg_to_rad(90));
+ xform.rotate_x(deg_to_rad(-90));
+ xform.scale(0.0017);
+ } else {
+ xform.scale(0.0012);
+ }
+ xform.translate(0, ypos, -dist);
+ xform.rotate_y(angle);
+ glMultMatrixf(xform[0]);
+
+ dtx_string(name);
+ glPopMatrix();
}
}
-void FSNode::set_path(const char *s)
+#define MAX_NAME_SZ 16
+
+FSNode *get_fsnode(const char *path)
{
- int len = strlen(s);
- if(!len) return;
+ char *abspath = make_abs_path(path);
- delete [] abs_path;
+ FSNode *node = node_cache[abspath];
+ if(!node) {
+ node = new FSNode;
+ node->path = path;
- const char *slash = s + len - 1;
- while(slash > s && *slash != '/') {
- --slash;
- }
- if(name == s) { // no slashes found
- char buf[1024];
- if(!getcwd(buf, sizeof buf)) {
- abort();
+ const char *name = node->path.get_name();
+ if(name) {
+ const char *ptr = name;
+ while(*ptr) {
+ if(ptr - name >= MAX_NAME_SZ) {
+ int len = ptr - name;
+ std::string s = std::string(name, len);
+ if(node->short_name.empty()) {
+ node->short_name = s;
+ node->short_name[len - 1] = node->short_name[len - 2] = '.';
+ }
+ node->name_lines.push_back(s);
+ name = ptr;
+ }
+ ++ptr;
+ }
+ if(*name) {
+ if(node->short_name.empty()) {
+ node->short_name = name;
+ }
+ node->name_lines.push_back(name);
+ }
+ }
+
+ struct stat st;
+ if(stat(node->path, &st) == -1) {
+ fprintf(stderr, "failed to stat: %s\n", node->path.get_path());
+ delete node;
+ return 0;
+ }
+ node->size = st.st_size;
+
+ switch(st.st_mode & S_IFMT) {
+ case S_IFREG:
+ node->type = FSTYPE_FILE;
+ break;
+
+ case S_IFDIR:
+ node->type = FSTYPE_DIR;
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ node->type = FSTYPE_DEV;
+ break;
+
+ default:
+ node->type = FSTYPE_UNKNOWN;
}
+ node_cache[abspath] = node;
+ }
- int dirlen = strlen(buf);
- abs_path = new char[len + dirlen + 2];
- sprintf(abs_path, "%s/%s", buf, s);
+ return node;
+}
- name = abs_path + dirlen + 1;
- suffix = abs_path + len + dirlen - 1;
- } else {
- abs_path = new char[len + 1];
- memcpy(abs_path, s, len + 1);
+FSNode *get_fsnode(const char *dir, const char *name)
+{
+ if(!dir) {
+ return get_fsnode(name);
+ }
+ if(!name || *name == '/') {
+ return 0;
+ }
+
+ int len = strlen(dir) + 1 + strlen(name);
+ char *buf = new char[len + 1];
+ sprintf(buf, "%s/%s", dir, name);
+ FSNode *res = get_fsnode(buf);
+ delete [] buf;
+ return res;
+}
+
+// ---- FSNode implementation ----
+FSNode::FSNode()
+{
+ type = FSTYPE_UNKNOWN;
+ size = 0;
+ parent = 0;
+ nfiles = ndirs = 0;
+}
- name = abs_path + (slash - s);
- suffix = abs_path + len - 1;
+bool FSNode::expand()
+{
+ if(type != FSTYPE_DIR) {
+ return false;
}
- while(suffix > name && *suffix != '.') {
- --suffix;
+ DIR *dir = opendir(path);
+ if(!dir) {
+ fprintf(stderr, "failed to open dir: %s: %s\n", path.get_path(), strerror(errno));
+ return false;
}
- if(suffix == name) {
- suffix = 0;
+
+ struct dirent *dent;
+ while((dent = readdir(dir))) {
+ if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
+ continue;
+ }
+
+ FSNode *node = get_fsnode(path, dent->d_name);
+ if(!node) continue;
+
+ children.push_back(node);
+ switch(node->type) {
+ case FSTYPE_FILE:
+ ++nfiles;
+ break;
+ case FSTYPE_DIR:
+ ++ndirs;
+ default:
+ break;
+ }
}
+ printf("expanded %d children\n", (int)children.size());
+
+ parent = get_fsnode(path.get_parent());
+ return true;
}