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