implemented a laser pointer
[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 void draw_node_name(FSNode *node, float angle, float ypos, float dist, bool full);
20
21 static IconRenderer *iconrend;
22
23 static std::map<std::string, FSNode*> node_cache;
24 static FSNode *cur_node;
25 static int start_child;
26
27 static dtx_font *fat_font;
28 #define FAT_FONT_SZ     32
29 static unsigned int glow_link_sdr;
30 static unsigned int chrome_font_sdr, glow_font_sdr;
31
32
33 bool init_fs(const char *path)
34 {
35         iconrend = new ShapesIcons;
36         if(!iconrend->init()) {
37                 return false;
38         }
39
40         if(!(fat_font = dtx_open_font_glyphmap("data/fat.glyphmap")) ||
41                         dtx_get_glyphmap_ptsize(dtx_get_glyphmap(fat_font, 0)) != FAT_FONT_SZ) {
42
43                 dtx_set(DTX_PADDING, 64);
44
45                 if(!(fat_font = dtx_open_font("data/fat.font", 0))) {
46                         fprintf(stderr, "failed to open font file data/fat.font\n");
47                         return false;
48                 }
49                 dtx_prepare_range(fat_font, FAT_FONT_SZ * 8, 32, 127);
50                 dtx_calc_font_distfield(fat_font, 1, 8);
51                 dtx_save_glyphmap("data/fat.glyphmap", dtx_get_glyphmap(fat_font, 0));
52         }
53         dtx_use_font(fat_font, FAT_FONT_SZ);
54
55         struct dtx_glyphmap *fat_gmap = dtx_get_glyphmap(fat_font, 0);
56         Vec2 pixsz;
57         pixsz.x = 1.0 / dtx_get_glyphmap_width(fat_gmap);
58         pixsz.y = 1.0 / dtx_get_glyphmap_height(fat_gmap);
59
60         if(!(chrome_font_sdr = create_program_load("sdr/chrome_font.v.glsl", "sdr/chrome_font.p.glsl"))) {
61                 return false;
62         }
63         set_uniform_float(chrome_font_sdr, "height", dtx_line_height());
64         set_uniform_float(chrome_font_sdr, "smoothness", 0.01);
65         set_uniform_float2(chrome_font_sdr, "pix_sz", pixsz.x, pixsz.y);
66
67         if(!(glow_font_sdr = create_program_load("sdr/dfont.v.glsl", "sdr/glow_font.p.glsl"))) {
68                 return false;
69         }
70         set_uniform_float(glow_font_sdr, "smoothness", 0.01);
71         set_uniform_float2(glow_font_sdr, "pix_sz", pixsz.x, pixsz.y);
72
73         if(!(glow_link_sdr = create_program_load("sdr/glink.v.glsl", "sdr/glink.p.glsl"))) {
74                 return false;
75         }
76
77         if(!(cur_node = get_fsnode(path))) {
78                 return false;
79         }
80         cur_node->expand();
81         return true;
82 }
83
84 void cleanup_fs()
85 {
86         std::map<std::string, FSNode*>::iterator it = node_cache.begin();
87         while(it != node_cache.end()) {
88                 FSNode *node = it++->second;
89                 delete node;
90         }
91         node_cache.clear();
92         dtx_close_font(fat_font);
93         delete iconrend;
94 }
95
96 static float icon_angle(int col, int ncols, float max_angle = 0.0f)
97 {
98         if(max_angle > 0) {
99                 return max_angle * ((float)col / (float)(ncols - 1) - 0.5);
100         }
101         return 2.0 * M_PI * (float)col / (float)ncols;
102 }
103
104 void draw_fs()
105 {
106         static const float row_spacing = 0.25;
107         static const float radius = 0.6;
108         static const float umax = 0.42;
109         static const float max_icon_angle = M_PI * 2.0 * umax;
110         static const float first_row_y = -row_spacing;
111
112         int max_ncols = std::max<int>(1, umax * 12);
113
114         glPushMatrix();
115         glTranslatef(0, cam_height, 0);
116
117         Mat4 rot_xform;
118         rot_xform.rotate(time_sec, 0, 0);
119         rot_xform.rotate(0, 0, time_sec * 0.5);
120
121         glDisable(GL_TEXTURE_2D);
122         glEnable(GL_BLEND);
123         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
124
125         int nchildren = (int)cur_node->children.size();
126         int ncols = std::max(std::min(cur_node->nfiles, max_ncols), 1);
127
128         int first = start_child % ncols;
129         int col = 0, row = 0;
130         int num_dirs = 0;
131
132         // count directories ...
133         for(int i=0; i<nchildren; i++) {
134                 FSNode *node = cur_node->children[i];
135                 if(node->type == FSTYPE_DIR) {
136                         ++num_dirs;
137                 }
138         }
139
140         // draw the directory link lines
141         glUseProgram(glow_link_sdr);
142         set_uniform_float(glow_link_sdr, "tsec", time_sec);
143         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
144
145         for(int i=0; i<nchildren; i++) {
146                 FSNode *node = cur_node->children[i];
147
148                 if(node->type != FSTYPE_DIR) {
149                         continue;
150                 }
151
152                 float angle = (float)col++ / (float)(num_dirs - 1) * max_icon_angle - max_icon_angle * 0.5;
153
154                 Mat4 xform;
155                 xform.rotate_y(angle);
156                 xform.translate(0, -0.6, 0);
157
158                 glPushMatrix();
159                 glMultMatrixf(xform[0]);
160                 glDepthMask(0);
161
162                 set_uniform_float(glow_link_sdr, "phase", col * 42.0);
163
164                 glBegin(GL_QUADS);
165                 glColor3f(0.2, 0.3, 0.8);
166                 glTexCoord2f(0, 0);
167                 glVertex3f(-0.2, 0, 0.05);
168                 glTexCoord2f(1, 0);
169                 glVertex3f(0.2, 0, 0.05);
170                 glTexCoord2f(1, 1);
171                 glVertex3f(0.2, 0, -10.0);
172                 glTexCoord2f(0, 1);
173                 glVertex3f(-0.2, 0, -10.0);
174                 glColor3f(1, 1, 1);
175                 glEnd();
176                 glPopMatrix();
177
178                 glDepthMask(1);
179         }
180
181         // draw the directory labels
182         glUseProgram(glow_font_sdr);
183         col = 0;
184         for(int i=0; i<nchildren; i++) {
185                 FSNode *node = cur_node->children[i];
186
187                 if(node->type != FSTYPE_DIR) {
188                         continue;
189                 }
190
191                 float angle = (float)col++ / (float)(num_dirs - 1) * max_icon_angle - max_icon_angle * 0.5;
192
193                 draw_node_name(node, angle, -0.6, radius * 1.2, false);
194         }
195
196         // then draw file icons
197         glDisable(GL_BLEND);
198         glUseProgram(0);
199         glLineWidth(2.0);
200         col = 0;
201         for(int i=0; i<nchildren; i++) {
202                 int idx = (i + first) % nchildren;
203                 FSNode *node = cur_node->children[idx];
204
205                 if(node->type == FSTYPE_DIR) {
206                         ++num_dirs;
207                         continue;
208                 }
209
210                 float angle = icon_angle(col, ncols, max_icon_angle);
211
212                 Mat4 xform = rot_xform;
213                 xform.translate(0, row * row_spacing + first_row_y, -radius);
214                 xform.rotate_y(angle);
215
216                 glPushMatrix();
217                 glMultMatrixf(xform[0]);
218                 iconrend->draw(node);
219                 glPopMatrix();
220
221                 if(++col >= ncols) {
222                         col = 0;
223                         ++row;
224                 }
225         }
226         glLineWidth(1.0);
227
228         // then draw the file labels
229         glUseProgram(chrome_font_sdr);
230         col = 0;
231         row = 0;
232         for(int i=0; i<nchildren; i++) {
233                 int idx = (i + first) % nchildren;
234                 FSNode *node = cur_node->children[idx];
235
236                 if(node->type == FSTYPE_DIR) {
237                         ++num_dirs;
238                         continue;
239                 }
240
241                 float angle = icon_angle(col, ncols, max_icon_angle);
242
243                 draw_node_name(node, angle, row * row_spacing + first_row_y - 0.08, radius, false);
244
245                 if(++col >= ncols) {
246                         col = 0;
247                         ++row;
248                 }
249         }
250
251         glPopMatrix();
252 }
253
254 static void draw_node_name(FSNode *node, float angle, float ypos, float dist, bool full)
255 {
256         dtx_use_font(fat_font, FAT_FONT_SZ);
257         int line_height = dtx_line_height();
258
259         int nlines = full ? node->name_lines.size() : 1;
260         for(int i=0; i<nlines; i++) {
261                 const char *name = full ? node->name_lines[i].c_str() : node->short_name.c_str();
262                 glPushMatrix();
263                 Mat4 xform;
264                 xform.translate(-dtx_string_width(name) / 2.0, -line_height * i - line_height * 0.5, 0);
265                 if(node->type == FSTYPE_DIR) {
266                         xform.rotate_z(deg_to_rad(90));
267                         xform.rotate_x(deg_to_rad(-90));
268                         xform.scale(0.0018);
269                 } else {
270                         xform.scale(0.0012);
271                 }
272                 xform.translate(0, ypos, -dist);
273                 xform.rotate_y(angle);
274                 glMultMatrixf(xform[0]);
275
276                 dtx_string(name);
277                 glPopMatrix();
278         }
279 }
280
281 #define MAX_NAME_SZ     16
282
283 FSNode *get_fsnode(const char *path)
284 {
285         char *abspath = make_abs_path(path);
286
287         FSNode *node = node_cache[abspath];
288         if(!node) {
289                 node = new FSNode;
290                 node->path = path;
291
292                 const char *name = node->path.get_name();
293                 if(name) {
294                         const char *ptr = name;
295                         while(*ptr) {
296                                 if(ptr - name >= MAX_NAME_SZ) {
297                                         int len = ptr - name;
298                                         std::string s = std::string(name, len);
299                                         if(node->short_name.empty()) {
300                                                 node->short_name = s;
301                                                 node->short_name[len - 1] = node->short_name[len - 2] = '.';
302                                         }
303                                         node->name_lines.push_back(s);
304                                         name = ptr;
305                                 }
306                                 ++ptr;
307                         }
308                         if(*name) {
309                                 if(node->short_name.empty()) {
310                                         node->short_name = name;
311                                 }
312                                 node->name_lines.push_back(name);
313                         }
314                 }
315
316                 struct stat st;
317                 if(stat(node->path, &st) == -1) {
318                         fprintf(stderr, "failed to stat: %s\n", node->path.get_path());
319                         delete node;
320                         return 0;
321                 }
322                 node->size = st.st_size;
323
324                 switch(st.st_mode & S_IFMT) {
325                 case S_IFREG:
326                         node->type = FSTYPE_FILE;
327                         break;
328
329                 case S_IFDIR:
330                         node->type = FSTYPE_DIR;
331                         break;
332
333                 case S_IFBLK:
334                 case S_IFCHR:
335                         node->type = FSTYPE_DEV;
336                         break;
337
338                 default:
339                         node->type = FSTYPE_UNKNOWN;
340                 }
341                 node_cache[abspath] = node;
342         }
343
344         return node;
345 }
346
347 FSNode *get_fsnode(const char *dir, const char *name)
348 {
349         if(!dir) {
350                 return get_fsnode(name);
351         }
352         if(!name || *name == '/') {
353                 return 0;
354         }
355
356         int len = strlen(dir) + 1 + strlen(name);
357         char *buf = new char[len + 1];
358         sprintf(buf, "%s/%s", dir, name);
359         FSNode *res = get_fsnode(buf);
360         delete [] buf;
361         return res;
362 }
363
364 // ---- FSNode implementation ----
365 FSNode::FSNode()
366 {
367         type = FSTYPE_UNKNOWN;
368         size = 0;
369         parent = 0;
370         nfiles = ndirs = 0;
371 }
372
373 bool FSNode::expand()
374 {
375         if(type != FSTYPE_DIR) {
376                 return false;
377         }
378
379         DIR *dir = opendir(path);
380         if(!dir) {
381                 fprintf(stderr, "failed to open dir: %s: %s\n", path.get_path(), strerror(errno));
382                 return false;
383         }
384
385         struct dirent *dent;
386         while((dent = readdir(dir))) {
387                 if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
388                         continue;
389                 }
390
391                 FSNode *node = get_fsnode(path, dent->d_name);
392                 if(!node) continue;
393
394                 children.push_back(node);
395                 switch(node->type) {
396                 case FSTYPE_FILE:
397                         ++nfiles;
398                         break;
399                 case FSTYPE_DIR:
400                         ++ndirs;
401                 default:
402                         break;
403                 }
404         }
405         printf("expanded %d children\n", (int)children.size());
406
407         parent = get_fsnode(path.get_parent());
408         return true;
409 }