1d3b46d2fd5be7df8c6f5a7ccbf6a4630868b079
[vrfileman] / src / fs.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <string>
6 #include <map>
7 #include <algorithm>
8 #include <unistd.h>
9 #include <dirent.h>
10 #include <sys/stat.h>
11 #include "fs.h"
12 #include "icon.h"
13 #include "gmath/gmath.h"
14 #include "opengl.h"
15 #include "app.h"
16 #include "drawtext.h"
17 #include "sdr.h"
18
19 static IconRenderer *iconrend;
20
21 static std::map<std::string, FSNode*> node_cache;
22 static FSNode *cur_node;
23 static int start_child;
24
25 static dtx_font *fat_font;
26 #define FAT_FONT_SZ     32
27 static unsigned int font_sdr;
28
29
30 bool init_fs(const char *path)
31 {
32         iconrend = new ShapesIcons;
33         if(!iconrend->init()) {
34                 return false;
35         }
36
37         if(!(fat_font = dtx_open_font_glyphmap("data/fat.glyphmap")) ||
38                         dtx_get_glyphmap_ptsize(dtx_get_glyphmap(fat_font, 0)) != FAT_FONT_SZ) {
39
40                 dtx_set(DTX_PADDING, 64);
41
42                 if(!(fat_font = dtx_open_font("data/fat.font", 0))) {
43                         fprintf(stderr, "failed to open font file data/fat.font\n");
44                         return false;
45                 }
46                 dtx_prepare_range(fat_font, FAT_FONT_SZ * 8, 32, 127);
47                 dtx_calc_font_distfield(fat_font, 1, 8);
48                 dtx_save_glyphmap("data/fat.glyphmap", dtx_get_glyphmap(fat_font, 0));
49         }
50         dtx_use_font(fat_font, FAT_FONT_SZ);
51
52         if(!(font_sdr = create_program_load("sdr/dfont.v.glsl", "sdr/dfont.p.glsl"))) {
53                 return false;
54         }
55         set_uniform_float(font_sdr, "smoothness", 0.01);
56
57         if(!(cur_node = get_fsnode(path))) {
58                 return false;
59         }
60         cur_node->expand();
61         return true;
62 }
63
64 void cleanup_fs()
65 {
66         std::map<std::string, FSNode*>::iterator it = node_cache.begin();
67         while(it != node_cache.end()) {
68                 FSNode *node = it++->second;
69                 delete node;
70         }
71         node_cache.clear();
72         dtx_close_font(fat_font);
73         delete iconrend;
74 }
75
76 static Vec3 icon_pos(int row, int col, int ncols, float row_spacing, float radius)
77 {
78         float angle = 2.0 * M_PI * (float)col / (float)ncols;
79         float x = sin(angle) * radius;
80         float z = -cos(angle) * radius;
81         float y = (float)row * row_spacing;
82         return Vec3(x, y, z);
83 }
84
85 static float icon_angle(int col, int ncols, float max_angle = 0.0f)
86 {
87         if(max_angle > 0) {
88                 return max_angle * ((float)col / (float)(ncols - 1) - 0.5);
89         }
90         return 2.0 * M_PI * (float)col / (float)ncols;
91 }
92
93 void draw_fs()
94 {
95         static const float row_spacing = 0.25;
96         static const float radius = 0.6;
97         static const float umax = 0.42;
98         static const float max_icon_angle = M_PI * 2.0 * umax;
99
100         int max_ncols = std::max<int>(1, umax * 12);
101
102         Mat4 base_xform;
103         base_xform.rotate(time_sec, 0, 0);
104         base_xform.rotate(0, 0, time_sec * 0.5);
105         base_xform.translate(0, 1.65, 0);
106
107         glDisable(GL_TEXTURE_2D);
108
109         int nchildren = (int)cur_node->children.size();
110         int ncols = std::min(cur_node->nfiles, max_ncols);
111
112         int first = start_child % ncols;
113         int col = 0, row = 0;
114
115         for(int i=0; i<nchildren; i++) {
116                 int idx = (i + first) % nchildren;
117                 FSNode *node = cur_node->children[idx];
118
119                 if(node->type == FSTYPE_DIR) {
120                         continue;
121                 }
122
123                 glUseProgram(0);
124
125                 float angle = icon_angle(col, ncols, max_icon_angle);
126
127                 Mat4 xform = base_xform;
128                 xform.translate(0, row * row_spacing, -radius);
129                 xform.rotate_y(angle);
130
131                 glPushMatrix();
132                 glMultMatrixf(xform[0]);
133                 iconrend->draw(node);
134                 glPopMatrix();
135
136                 glPushMatrix();
137                 xform = Mat4::identity;
138                 xform.translate(-dtx_string_width(node->path.get_name()) / 2.0, 0, 0);
139                 xform.scale(0.0012);
140                 xform.translate(0, 1.54 + row * row_spacing, -radius);
141                 xform.rotate_y(angle);
142                 glMultMatrixf(xform[0]);
143
144                 glUseProgram(font_sdr);
145                 set_uniform_float(font_sdr, "height", dtx_line_height());
146                 dtx_string(node->path.get_name());
147                 glPopMatrix();
148
149                 if(++col >= ncols) {
150                         col = 0;
151                         ++row;
152                 }
153         }
154 }
155
156 FSNode *get_fsnode(const char *path)
157 {
158         char *abspath = make_abs_path(path);
159
160         FSNode *node = node_cache[abspath];
161         if(!node) {
162                 node = new FSNode;
163                 node->path = path;
164
165                 struct stat st;
166                 if(stat(node->path, &st) == -1) {
167                         fprintf(stderr, "failed to stat: %s\n", node->path.get_path());
168                         delete node;
169                         return 0;
170                 }
171                 node->size = st.st_size;
172
173                 switch(st.st_mode & S_IFMT) {
174                 case S_IFREG:
175                         node->type = FSTYPE_FILE;
176                         break;
177
178                 case S_IFDIR:
179                         node->type = FSTYPE_DIR;
180                         break;
181
182                 case S_IFBLK:
183                 case S_IFCHR:
184                         node->type = FSTYPE_DEV;
185                         break;
186
187                 default:
188                         node->type = FSTYPE_UNKNOWN;
189                 }
190                 node_cache[abspath] = node;
191         }
192
193         return node;
194 }
195
196 FSNode *get_fsnode(const char *dir, const char *name)
197 {
198         if(!dir) {
199                 return get_fsnode(name);
200         }
201         if(!name || *name == '/') {
202                 return 0;
203         }
204
205         int len = strlen(dir) + 1 + strlen(name);
206         char *buf = new char[len + 1];
207         sprintf(buf, "%s/%s", dir, name);
208         FSNode *res = get_fsnode(buf);
209         delete [] buf;
210         return res;
211 }
212
213 // ---- FSNode implementation ----
214 FSNode::FSNode()
215 {
216         type = FSTYPE_UNKNOWN;
217         size = 0;
218         parent = 0;
219         nfiles = ndirs = 0;
220 }
221
222 bool FSNode::expand()
223 {
224         if(type != FSTYPE_DIR) {
225                 return false;
226         }
227
228         DIR *dir = opendir(path);
229         if(!dir) {
230                 fprintf(stderr, "failed to open dir: %s: %s\n", path.get_path(), strerror(errno));
231                 return false;
232         }
233
234         struct dirent *dent;
235         while((dent = readdir(dir))) {
236                 if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
237                         continue;
238                 }
239
240                 FSNode *node = get_fsnode(path, dent->d_name);
241                 if(!node) continue;
242
243                 children.push_back(node);
244                 switch(node->type) {
245                 case FSTYPE_FILE:
246                         ++nfiles;
247                         break;
248                 case FSTYPE_DIR:
249                         ++ndirs;
250                 default:
251                         break;
252                 }
253         }
254         printf("expanded %d children\n", (int)children.size());
255
256         parent = get_fsnode(path.get_parent());
257         return true;
258 }