1 /**
2  * @file lv_fs_stdio.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../../lvgl.h"
10 #if LV_USE_FS_STDIO != '\0'
11 
12 #include <stdio.h>
13 #ifndef WIN32
14     #include <dirent.h>
15     #include <unistd.h>
16 #else
17     #include <windows.h>
18 #endif
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 #define MAX_PATH_LEN 256
24 
25 /**********************
26  *      TYPEDEFS
27  **********************/
28 typedef struct {
29 #ifdef WIN32
30     HANDLE dir_p;
31     char next_fn[MAX_PATH_LEN];
32 #else
33     DIR * dir_p;
34 #endif
35 } dir_handle_t;
36 
37 /**********************
38  *  STATIC PROTOTYPES
39  **********************/
40 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
41 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
42 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
43 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
44 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
45 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
46 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
47 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
48 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
49 
50 /**********************
51  *  STATIC VARIABLES
52  **********************/
53 
54 /**********************
55  *      MACROS
56  **********************/
57 
58 /**********************
59  *   GLOBAL FUNCTIONS
60  **********************/
61 
62 /**
63  * Register a driver for the File system interface
64  */
lv_fs_stdio_init(void)65 void lv_fs_stdio_init(void)
66 {
67     /*---------------------------------------------------
68      * Register the file system interface in LVGL
69      *--------------------------------------------------*/
70 
71     /*Add a simple drive to open images*/
72     static lv_fs_drv_t fs_drv; /*A driver descriptor*/
73     lv_fs_drv_init(&fs_drv);
74 
75     /*Set up fields...*/
76     fs_drv.letter = LV_FS_STDIO_LETTER;
77     fs_drv.cache_size = LV_FS_STDIO_CACHE_SIZE;
78 
79     fs_drv.open_cb = fs_open;
80     fs_drv.close_cb = fs_close;
81     fs_drv.read_cb = fs_read;
82     fs_drv.write_cb = fs_write;
83     fs_drv.seek_cb = fs_seek;
84     fs_drv.tell_cb = fs_tell;
85 
86     fs_drv.dir_close_cb = fs_dir_close;
87     fs_drv.dir_open_cb = fs_dir_open;
88     fs_drv.dir_read_cb = fs_dir_read;
89 
90     lv_fs_drv_register(&fs_drv);
91 }
92 
93 /**********************
94  *   STATIC FUNCTIONS
95  **********************/
96 
97 /**
98  * Open a file
99  * @param drv pointer to a driver where this function belongs
100  * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
101  * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
102  * @return pointer to FIL struct or NULL in case of fail
103  */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)104 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
105 {
106     LV_UNUSED(drv);
107 
108     const char * flags = "";
109 
110     if(mode == LV_FS_MODE_WR) flags = "wb";
111     else if(mode == LV_FS_MODE_RD) flags = "rb";
112     else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+";
113 
114     /*Make the path relative to the current directory (the projects root folder)*/
115 
116     char buf[MAX_PATH_LEN];
117     lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
118 
119     return fopen(buf, flags);
120 }
121 
122 /**
123  * Close an opened file
124  * @param drv pointer to a driver where this function belongs
125  * @param file_p pointer to a FILE variable. (opened with fs_open)
126  * @return LV_FS_RES_OK: no error, the file is read
127  *         any error from lv_fs_res_t enum
128  */
fs_close(lv_fs_drv_t * drv,void * file_p)129 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
130 {
131     LV_UNUSED(drv);
132     fclose(file_p);
133     return LV_FS_RES_OK;
134 }
135 
136 /**
137  * Read data from an opened file
138  * @param drv pointer to a driver where this function belongs
139  * @param file_p pointer to a FILE variable.
140  * @param buf pointer to a memory block where to store the read data
141  * @param btr number of Bytes To Read
142  * @param br the real number of read bytes (Byte Read)
143  * @return LV_FS_RES_OK: no error, the file is read
144  *         any error from lv_fs_res_t enum
145  */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)146 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
147 {
148     LV_UNUSED(drv);
149     *br = fread(buf, 1, btr, file_p);
150     return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
151 }
152 
153 /**
154  * Write into a file
155  * @param drv pointer to a driver where this function belongs
156  * @param file_p pointer to a FILE variable
157  * @param buf pointer to a buffer with the bytes to write
158  * @param btw Bytes To Write
159  * @param bw the number of real written bytes (Bytes Written). NULL if unused.
160  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
161  */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)162 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
163 {
164     LV_UNUSED(drv);
165     *bw = fwrite(buf, 1, btw, file_p);
166     return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
167 }
168 
169 /**
170  * Set the read write pointer. Also expand the file size if necessary.
171  * @param drv pointer to a driver where this function belongs
172  * @param file_p pointer to a FILE variable. (opened with fs_open )
173  * @param pos the new position of read write pointer
174  * @return LV_FS_RES_OK: no error, the file is read
175  *         any error from lv_fs_res_t enum
176  */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)177 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
178 {
179     LV_UNUSED(drv);
180     fseek(file_p, pos, whence);
181     return LV_FS_RES_OK;
182 }
183 
184 /**
185  * Give the position of the read write pointer
186  * @param drv pointer to a driver where this function belongs
187  * @param file_p pointer to a FILE variable.
188  * @param pos_p pointer to to store the result
189  * @return LV_FS_RES_OK: no error, the file is read
190  *         any error from lv_fs_res_t enum
191  */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)192 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
193 {
194     LV_UNUSED(drv);
195     *pos_p = ftell(file_p);
196     return LV_FS_RES_OK;
197 }
198 
199 /**
200  * Initialize a 'DIR' or 'HANDLE' variable for directory reading
201  * @param drv pointer to a driver where this function belongs
202  * @param path path to a directory
203  * @return pointer to an initialized 'DIR' or 'HANDLE' variable
204  */
fs_dir_open(lv_fs_drv_t * drv,const char * path)205 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
206 {
207     LV_UNUSED(drv);
208     dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t));
209 #ifndef WIN32
210     /*Make the path relative to the current directory (the projects root folder)*/
211     char buf[MAX_PATH_LEN];
212     lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
213     handle->dir_p = opendir(buf);
214     if(handle->dir_p == NULL) {
215         lv_mem_free(handle);
216         return NULL;
217     }
218     return handle;
219 #else
220     handle->dir_p = INVALID_HANDLE_VALUE;
221     WIN32_FIND_DATAA fdata;
222 
223     /*Make the path relative to the current directory (the projects root folder)*/
224     char buf[MAX_PATH_LEN];
225     lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s\\*", path);
226 
227     strcpy(handle->next_fn, "");
228     handle->dir_p = FindFirstFileA(buf, &fdata);
229     do {
230         if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
231             continue;
232         }
233         else {
234             if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
235                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
236             }
237             else {
238                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
239             }
240             break;
241         }
242     } while(FindNextFileA(handle->dir_p, &fdata));
243 
244     if(handle->dir_p == INVALID_HANDLE_VALUE) {
245         lv_mem_free(handle);
246         return INVALID_HANDLE_VALUE;
247     }
248     return handle;
249 #endif
250 }
251 
252 /**
253  * Read the next filename form a directory.
254  * The name of the directories will begin with '/'
255  * @param drv pointer to a driver where this function belongs
256  * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
257  * @param fn pointer to a buffer to store the filename
258  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
259  */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn)260 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
261 {
262     LV_UNUSED(drv);
263     dir_handle_t * handle = (dir_handle_t *)dir_p;
264 #ifndef WIN32
265     struct dirent * entry;
266     do {
267         entry = readdir(handle->dir_p);
268         if(entry) {
269             if(entry->d_type == DT_DIR) lv_snprintf(fn, MAX_PATH_LEN, "/%s", entry->d_name);
270             else strcpy(fn, entry->d_name);
271         }
272         else {
273             strcpy(fn, "");
274         }
275     } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
276 #else
277     strcpy(fn, handle->next_fn);
278 
279     strcpy(handle->next_fn, "");
280     WIN32_FIND_DATAA fdata;
281 
282     if(FindNextFileA(handle->dir_p, &fdata) == false) return LV_FS_RES_OK;
283     do {
284         if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
285             continue;
286         }
287         else {
288             if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
289                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
290             }
291             else {
292                 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
293             }
294             break;
295         }
296     } while(FindNextFileA(handle->dir_p, &fdata));
297 
298 #endif
299     return LV_FS_RES_OK;
300 }
301 
302 /**
303  * Close the directory reading
304  * @param drv pointer to a driver where this function belongs
305  * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
306  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
307  */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)308 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
309 {
310     LV_UNUSED(drv);
311     dir_handle_t * handle = (dir_handle_t *)dir_p;
312 #ifndef WIN32
313     closedir(handle->dir_p);
314 #else
315     FindClose(handle->dir_p);
316 #endif
317     lv_mem_free(handle);
318     return LV_FS_RES_OK;
319 }
320 
321 #else /*LV_USE_FS_STDIO == 0*/
322 
323 #if defined(LV_FS_STDIO_LETTER) && LV_FS_STDIO_LETTER != '\0'
324     #warning "LV_USE_FS_STDIO is not enabled but LV_FS_STDIO_LETTER is set"
325 #endif
326 
327 #endif /*LV_USE_FS_POSIX*/
328