/main/browse.c
#include <dirent.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "cwalk.h"
#include "esp_vfs_fat.h"
#include "gfx.h"
#include "keyboard.h"
#include "sd.h"
#include "browse.h"
typedef struct browse_file_entry {
char* name;
unsigned char type;
} browse_file_entry_t;
typedef struct browse_file_list {
browse_file_entry_t** files;
size_t count;
size_t capacity;
} browse_file_list_t;
void bfl_init(browse_file_list_t* bfl) {
bfl->count = 0;
bfl->capacity = 10;
bfl->files = (browse_file_entry_t**) malloc(bfl->capacity * sizeof(browse_file_entry_t*));
}
void bfl_free_entry(browse_file_list_t* bfl, size_t index) {
browse_file_entry_t* be = bfl->files[index];
free(be->name);
free(be);
}
void bfl_free(browse_file_list_t* bfl) {
for (int i = 0; i < bfl->count; i++) {
bfl_free_entry(bfl, i);
}
free(bfl->files);
}
void bfl_resize(browse_file_list_t* bfl, size_t new_capacity) {
if (new_capacity < bfl->count) {
for (int i = new_capacity; i < bfl->count; i++) {
bfl_free_entry(bfl, i);
}
bfl->count = new_capacity;
}
bfl->capacity = new_capacity;
bfl->files = (browse_file_entry_t**) realloc(bfl->files, bfl->capacity * sizeof(browse_file_entry_t*));
}
void bfl_clear(browse_file_list_t* bfl) {
bfl_free(bfl);
bfl_init(bfl);
}
void bfl_add_entry(browse_file_list_t* bfl, const char* name, unsigned char type) {
browse_file_entry_t* be = (browse_file_entry_t*) malloc(sizeof(browse_file_entry_t));
size_t l = strlen(name);
be->name = (char*) malloc(l + 1);
strncpy(be->name, name, l);
be->name[l] = 0;
be->type = type;
if (bfl->count == bfl->capacity) {
bfl_resize(bfl, bfl->capacity * 2);
}
bfl->files[bfl->count] = be;
bfl->count++;
}
void bfl_fetch(browse_file_list_t* bfl, const char* path) {
struct dirent *e;
DIR* d = opendir(path);
bfl_clear(bfl);
while ((e = readdir(d)) != NULL) {
bfl_add_entry(bfl, e->d_name, e->d_type);
}
closedir(d);
}
void browse() {
const char TAG[] = "browse_files";
browse_file_list_t bfl;
char* path = (char*) malloc(FILENAME_MAX);
strcpy(path, "/sd");
FATFS* fs = sd_get();
if (fs == NULL) {
printf("No SD card\n");
return;
}
bfl_init(&bfl);
bfl_fetch(&bfl, path);
int cursor = 0;
key_event_t k;
while (1) {
gfx_data_t *gd = gfx_acquire_data(SCENE_BROWSE);
gd->browse.state = BROWSE_NONE;
if (cursor == 0) {
gd->browse.state = BROWSE_AT_TOP;
}
if (cursor >= bfl.count - 4) {
gd->browse.state = BROWSE_AT_BOTTOM;
}
for (int i = 0; i < 4; i++) {
if (cursor + i < bfl.count) {
gd->browse.names[i] = bfl.files[cursor + i]->name;
gd->browse.types[i] = bfl.files[cursor + i]->type;
} else {
gd->browse.names[i] = NULL;
}
}
gfx_release_data();
int selected = -1;
k = key_get_event(portMAX_DELAY);
if (k.type == KEY_PRESSED) {
switch (k.code) {
case KEY_BAND:
if (strncmp(path, "/sd", FILENAME_MAX) == 0) {
goto browse_end;
}
size_t l;
cwk_path_get_dirname(path, &l);
path[l - 1] = 0;
ESP_LOGI(TAG, "new path = %s", path);
bfl_clear(&bfl);
bfl_fetch(&bfl, path);
cursor = 0;
break;
case KEY_TUNE_PLUS:
if (cursor >= 4) {
cursor -= 4;
}
break;
case KEY_TUNE_MINUS:
if (cursor < bfl.count - 4) {
cursor += 4;
}
break;
case KEY_1:
selected = 0;
break;
case KEY_2:
selected = 1;
break;
case KEY_3:
selected = 2;
break;
case KEY_4:
selected = 3;
break;
default:
break;
}
}
if (selected != -1 && cursor + selected < bfl.count) {
char* name = bfl.files[cursor + selected]->name;
unsigned char t = bfl.files[cursor + selected]->type;
if (strlen(path) + strlen(name) + 2 > FILENAME_MAX) {
ESP_LOGE(TAG, "File path would be too long; cannot navigate to %s", name);
continue;
}
char* fullpath = (char*) malloc(FILENAME_MAX);
cwk_path_join(path, name, fullpath, FILENAME_MAX);
if (t == DT_REG) {
play_file(fullpath);
free(fullpath);
break;
} else if (t == DT_DIR) {
free(path);
path = fullpath;
ESP_LOGI(TAG, "new path = %s", path);
bfl_clear(&bfl);
bfl_fetch(&bfl, path);
cursor = 0;
}
}
}
browse_end:
bfl_free(&bfl);
free(path);
}