~ruther/CTU-FEE-B0B35APO-Semestral-project

817c1f98f5c94d30b0d270d53a5f9ec5cbb59b5c — František Boháček 3 years ago 4a2108e
feat: implement basic directory browser window
M file-browser/include/file_access.h => file-browser/include/file_access.h +5 -1
@@ 5,6 5,7 @@
#include <stdint.h>
#include <sys/types.h>
#include "file_execute.h"
#include "logger.h"

typedef struct directory_t directory_t;



@@ 52,6 53,8 @@ typedef enum {
  FILOPER_UNKNOWN,
} file_operation_error_t;

extern const char *file_operation_error_strings[];

typedef struct {
  bool error;
  union {


@@ 156,9 159,10 @@ extern const fileaccess_t
extern const fileaccess_t
    temp_file_access; // state is /tmp directory descriptor

extern uint8_t fileaccess_connectors_count;
extern fileaccess_connector_t fileaccess_connectors[(FA_COUNT-1)*FA_COUNT];

void fileaccess_log_error(logger_t *logger, file_operation_error_t error);

fileaccess_state_t fileaccess_init(const fileaccess_t *fileaccess, void *data);
bool fileaccess_deinit(fileaccess_state_t state);


A file-browser/include/window_browser.h => file-browser/include/window_browser.h +11 -0
@@ 0,0 1,11 @@
#ifndef __WINDOW_BROWSER_H__
#define __WINDOW_BROWSER_H__

#include "gui.h"
#include <stdbool.h>
#include "file_access.h"

bool window_browser_open_local(gui_t *gui, font_t *font);
bool window_browser_open(gui_t *gui, font_t *font, fileaccess_state_t state);

#endif // __WINDOW_BROWSER_H__

M file-browser/src/file_access.c => file-browser/src/file_access.c +24 -1
@@ 1,8 1,31 @@
#include "file_access.h"
#include "logger.h"
#include <string.h>
#include <errno.h>
/*
  FILOPER_SUCCESS,
  FILOPER_PERMISSIONS,
  FILOPER_DOES_NOT_EXIST,
  FILOPER_USED,
  FILOPER_ALREADY_EXISTS,
  FILOPER_NOT_ENOUGH_SPACE,
  FILOPER_UNKNOWN,
 */

uint8_t connectors_count = 1;
const char *file_operation_error_strings[] = {
  "Success",
  "No permissions",
  "No such file or directory",
  "File is in use",
  "File already exists",
  "Not enough space on device",
  "Unknown error",
};

void fileaccess_log_error(logger_t *logger, file_operation_error_t error) {
  logger_error(logger, __FILE__, __FUNCTION__, __LINE__,
               "File operation error: %s", file_operation_error_strings[error]);
}

fileaccess_state_t fileaccess_init(const fileaccess_t *fileaccess, void *data) {
  return fileaccess->init(data);

M file-browser/src/gui_list_commands.c => file-browser/src/gui_list_commands.c +2 -0
@@ 56,6 56,8 @@ static void command_handler_move_up(void *state, int amount) {

void gui_list_commands_register(commands_t *commands, gui_list_command_state_t *state) {
  commands_register(commands, IN_KEYBOARD, 13, command_handler_gui_list_clicked, state);
  commands_register(commands, IN_KEYBOARD, 'v', command_handler_gui_list_clicked,
                    state);

  commands_register(commands, IN_KEYBOARD, KEYBOARD_DOWN,
                    command_handler_move_down, state);

M file-browser/src/local_file_access.c => file-browser/src/local_file_access.c +45 -31
@@ 23,18 23,28 @@ bool local_fileaccess_deinit_state(fileaccess_state_t data) {
  return true;
}

static file_operation_error_t file_get_information(void **malloced,
                                                   uint64_t *offset, uint64_t *bytes_malloced,
static uint64_t directory_get_needed_bytes(char *name, uint32_t *dirs_count, DIR *dirptr) {
  uint64_t size = sizeof(directory_t) + strlen(name) + 1;

  struct dirent *dir;
  errno = 0;
  *dirs_count = 0;
  while ((dir = readdir(dirptr)) != NULL) {
    size += sizeof(file_t);
    size += strlen(dir->d_name) + 1;
    (*dirs_count)++;
  }

  rewinddir(dirptr);
  return size;
}

static file_operation_error_t file_get_information(void *malloced,
                                                   uint64_t *file_offset,
                                                   uint64_t *names_offset,
                                                   fileaccess_state_t state,
                                                   file_t file) {
  size_t name_len = strlen(file.name);
  bytes_malloced += sizeof(file_t) + name_len + 1;
  void *new = realloc(*malloced, *bytes_malloced);
  if (new == NULL) {
    free(*malloced);
    return FILOPER_UNKNOWN;
  }
  *malloced = new;

  char full_path[file_get_full_path_memory_size(state, file.directory, &file)];
  file_get_full_path(state, file.directory, &file, full_path);


@@ 44,8 54,8 @@ static file_operation_error_t file_get_information(void **malloced,
  int status = stat(full_path, &stats);

  if (status == -1) {
    free(new);
    return file_operation_error_from_errno(errno);
    //free(new);
    //return file_operation_error_from_errno(errno);
  }

  file.size = stats.st_size;


@@ 53,13 63,13 @@ static file_operation_error_t file_get_information(void **malloced,
  file.uid = stats.st_uid;
  file.permissions = stats.st_mode;

  file_t *stored = new + *offset;
  file_t *stored = malloced + *file_offset;
  *stored = file;
  *offset += sizeof(file_t);
  *file_offset += sizeof(file_t);

  strcpy(new + *offset, file.name);
  stored->name = new + *offset;
  *offset += name_len + 1;
  strcpy(malloced + *names_offset, file.name);
  stored->name = malloced + *names_offset;
  *names_offset += name_len + 1;

  return FILOPER_SUCCESS;
}


@@ 70,9 80,18 @@ directory_or_error_t local_fileaccess_directory_list(fileaccess_state_t state,
  char full_path[path_join_memory_size(state.payload.local.path, path)];
  path_join((char *)state.payload.local.path, path, full_path);

  uint64_t malloc_offset = sizeof(directory_t) + strlen(path) + 1;
  uint64_t bytes_malloced = sizeof(directory_t) + strlen(path) + 1;
  directory_t *directory = malloc(malloc_offset);
  DIR *dirptr = opendir(full_path);
  if (dirptr == NULL) {
    ret.error = true;
    ret.payload.error = file_operation_error_from_errno(errno);
    return ret;
  }

  uint32_t files_count = 0;
  uint64_t size = directory_get_needed_bytes(path, &files_count, dirptr);
  uint64_t files_offset = sizeof(directory_t) + strlen(path) + 1;
  uint64_t names_offset = files_count * sizeof(file_t) + files_offset;
  directory_t *directory = malloc(size);
  void *malloced = directory;

  if (directory == NULL) {


@@ 82,17 101,10 @@ directory_or_error_t local_fileaccess_directory_list(fileaccess_state_t state,
  }

  directory->path = malloced + sizeof(directory_t);
  directory->files = malloced + files_offset;
  directory->files_count = 0;
  strcpy(directory->path, path);


  DIR *dirptr = opendir(full_path);
  if (dirptr == NULL) {
    ret.error = true;
    ret.payload.error = file_operation_error_from_errno(errno);
    free(malloced);
    return ret;
  }

  struct dirent * dir;
  errno = 0;
  while ((dir = readdir(dirptr)) != NULL) {


@@ 115,17 127,19 @@ directory_or_error_t local_fileaccess_directory_list(fileaccess_state_t state,
      break;
    }

    ret.payload.error = file_get_information(&malloced, &malloc_offset,
                                             &bytes_malloced, state, file);
    ret.payload.error = file_get_information(malloced, &files_offset,
                                             &names_offset, state, file);
    errno = 0;
    if (ret.payload.error != FILOPER_SUCCESS) {
      ret.error = true;
      free(malloced);
      return ret;
    }

    directory->files_count++;
  }

  closedir(dirptr);

  if (errno != 0) {
    ret.error = true;
    ret.payload.error = file_operation_error_from_errno(errno);

M file-browser/src/local_file_utils.c => file-browser/src/local_file_utils.c +5 -1
@@ 10,7 10,11 @@
static int nfw_callback(const char *fpath, const struct stat *sb, int typeflag);

size_t file_get_full_path_memory_size(fileaccess_state_t state, directory_t *directory, file_t *file) {
  return strlen(file->name) + strlen(state.payload.local.path) + strlen(file->name) + 3;
  size_t root = strlen(state.payload.local.path);
  size_t dir = strlen(directory->path);
  size_t file_name = strlen(file->name);

  return root + 1 + dir + 1 + file_name + 1;
}

bool file_get_full_path(fileaccess_state_t state,

A file-browser/src/window_browser.c => file-browser/src/window_browser.c +181 -0
@@ 0,0 1,181 @@
#include "window_browser.h"
#include "display_utils.h"
#include "file_access.h"
#include "gui.h"
#include "gui_component_text.h"
#include "gui_container_info.h"
#include "gui_list_commands.h"
#include "gui_component_line.h"
#include "gui_window_info.h"
#include "input.h"
#include "logger.h"
#include "renderer.h"

typedef struct {
  bool running;
  gui_t *gui;

  container_t *list_container;
  window_t *browser_window;

  font_t *font;

  gui_list_command_state_t click_state;
  text_t text_state;

  directory_t *current_directory;
  fileaccess_state_t state;
} browser_window_state_t;

static bool browser_window_list_render_item(void *state, uint32_t index,
                                            renderer_t *renderer, int16_t beg_x,
                                            int16_t beg_y,
                                            display_pixel_t color);

static bool browser_window_list_render_header(void *state, uint32_t index,
                                              renderer_t *renderer,
                                              int16_t beg_x, int16_t beg_y,
                                              display_pixel_t color);

static void browser_window_item_clicked(container_t *container, void *state,
                                        uint32_t selected_index);

static void *browser_window_construct(window_t *window, void *state);
static bool browser_window_running(void *state);
static void browser_window_job(void *state);

gui_container_info_t window_browser_containers[] = {
    {.type = CONT_TABLE,
     .payload.list = {.render_item_fn = browser_window_list_render_item,
                      .render_header_fn = browser_window_list_render_header,
                      .item_height = 16}},
    {.type = CONT_GROUP, .payload.group.components_count = 2},
};

window_info_t window_browser_info = {
    .construct = browser_window_construct,
    .containers_count = 2,
    .containers = window_browser_containers,
};

bool window_browser_open_local(gui_t *gui, font_t *font) {
  fileaccess_state_t state = fileaccess_init(&local_file_access, NULL);
  return window_browser_open(gui, font, state);
}

bool window_browser_open(gui_t *gui, font_t *font, fileaccess_state_t state) {
  directory_or_error_t root = fileaccess_root_list(state);

  if (root.error) {
    fileaccess_log_error(gui->logger, root.error);
    // TODO: dialog
    return false;
  }

  browser_window_state_t bstate = {
      .state = state,
      .gui = gui,
      .font = font,
      .current_directory = root.payload.directory,
      .running = true,
  };

  uint16_t commands_state = commands_save_state(gui->commands);
  gui_window_init_and_loop(gui, &bstate, window_browser_info, browser_window_running,
                           browser_window_job);
  commands_restore_state(gui->commands, commands_state);

  return true;
}

static void command_handler_exit(void *state, int amount) {
  browser_window_state_t *bstate = (browser_window_state_t *)state;
  if (bstate->gui->active_window == bstate->browser_window) {
    bstate->running = false;
  }
}

static void *browser_window_construct(window_t *window, void *state) {
  browser_window_state_t *bstate = (browser_window_state_t *)state;
  logger_t *logger = bstate->gui->logger;
  logger_info(logger, __FILE__, __FUNCTION__, __LINE__,
              "Constructing browser window");
  bstate->list_container = &window->containers[0];
  bstate->browser_window = window;

  bstate->click_state.container = bstate->list_container;
  bstate->click_state.state = state;
  bstate->click_state.clicked = browser_window_item_clicked;
  bstate->click_state.font = bstate->font;
  bstate->click_state.gui = bstate->gui;
  bstate->click_state.window = window;

  bstate->text_state.font = bstate->font;
  bstate->text_state.line = bstate->current_directory->path;
  bstate->text_state.color = WHITE_PIXEL;

  // containers init
  // group components init
  component_t path_text = gui_text_create(&bstate->text_state, 3, 3, 0, 0);
  component_t line_component = gui_line_create(&WHITE_PIXEL, 0, path_text.height + path_text.y + 3, 1000, 1);
  
  gui_group_container_add_component(&window->containers[1], path_text);
  gui_group_container_add_component(&window->containers[1], line_component);

  // list init
  gui_container_info_init(bstate->list_container, bstate,
                          bstate->current_directory->files_count, 5,
                          5 + line_component.height + 5);
  bstate->list_container->width = bstate->gui->size.x - 20;
  bstate->list_container->height = bstate->gui->size.y - bstate->list_container->y - 20;

  // commands register
  gui_list_commands_register(bstate->gui->commands, &bstate->click_state);
  commands_register(bstate->gui->commands, IN_KEYBOARD, 'e',
                    command_handler_exit, state);

  return state;
}

static void browser_window_item_clicked(container_t *container, void *state,
                                        uint32_t selected_index) {

  browser_window_state_t *bstate = (browser_window_state_t *)state;
  logger_t *logger = bstate->gui->logger;

  logger_info(logger, __FILE__, __FUNCTION__, __LINE__, "Item was clicked.");
}

static bool browser_window_list_render_item(void *state, uint32_t index,
                                            renderer_t *renderer, int16_t beg_x,
                                            int16_t beg_y,
                                            display_pixel_t color) {
  browser_window_state_t *bstate = (browser_window_state_t *)state;
  file_t file = bstate->current_directory->files[index];
  renderer_write_string(renderer, beg_x, beg_y, 0, bstate->font, file.name,
                        color);
  return true;
}

static bool browser_window_list_render_header(void *state, uint32_t index,
                                              renderer_t *renderer,
                                              int16_t beg_x, int16_t beg_y,
                                              display_pixel_t color) {

  browser_window_state_t *bstate = (browser_window_state_t *)state;
  renderer_write_string(renderer, beg_x, beg_y, 0, bstate->font, "This is header", color);
  return true;
}

static bool browser_window_running(void *state) {
  browser_window_state_t *bstate = (browser_window_state_t*)state;
  return bstate->running;
}

static void browser_window_job(void *state) {
  browser_window_state_t *bstate = (browser_window_state_t *)state;
  if (!bstate->running) {
    // cleanup
    fileaccess_directory_close(bstate->state, bstate->current_directory);
  }
}

M file-browser/src/window_initial.c => file-browser/src/window_initial.c +7 -1
@@ 9,6 9,7 @@
#include "input.h"
#include "logger.h"
#include "renderer.h"
#include "window_browser.h"

#include <stdbool.h>



@@ 100,7 101,9 @@ static bool initial_window_list_render_item(void *state, uint32_t index,

static void command_handler_exit(void *state, int amount) {
  initial_window_state_t *istate = (initial_window_state_t *)state;
  istate->running = false;
  if (istate->gui->active_window == istate->initial_window) {
    istate->running = false;
  }
}

static void *initial_window_construct(window_t *window, void *state) {


@@ 140,6 143,8 @@ static void initial_window_job(void *state) {
      (initial_window_state_t *)state;

  initial_window_state->list_container->inner.list.scroll_x = 0;
  gui_list_container_set_item_height(initial_window_state->list_container,
                                     initial_window_state->font.size);
  // do nothing?
}



@@ 156,6 161,7 @@ static void initial_window_item_clicked(container_t *container, void *state,
  case INITIAL_WINDOW_LOCAL_INDEX:
    logger_info(logger, __FILE__, __FUNCTION__, __LINE__,
                "Clicked local root filesystem");
    window_browser_open_local(istate->gui, &istate->font);
    break;
  case INITIAL_WINDOW_MOUNT_INDEX:
    logger_info(logger, __FILE__, __FUNCTION__, __LINE__,

M lib-gui/src/gui_list_container.c => lib-gui/src/gui_list_container.c +34 -26
@@ 7,30 7,28 @@ container_t gui_list_container_create(void *state, uint32_t items_count,
                                      render_item render_it,
                                      render_item render_header) {
  list_container_t list = {
    .item_height = item_height,
    .items_count = items_count,
    .state = state,
    .scroll_x = 0,
    .scroll_y = 0,
    .selected_index = 0,
    .render_header_fn = render_header,
    .render_item_fn = render_it,
    .regular_background = BLACK_PIXEL,
    .regular_foreground = WHITE_PIXEL,
    .selected_background = WHITE_PIXEL,
    .selected_foreground = BLACK_PIXEL,
    .item_padding = 3,
      .item_height = item_height,
      .items_count = items_count,
      .state = state,
      .scroll_x = 0,
      .scroll_y = 0,
      .selected_index = 0,
      .render_header_fn = render_header,
      .render_item_fn = render_it,
      .regular_background = BLACK_PIXEL,
      .regular_foreground = WHITE_PIXEL,
      .selected_background = WHITE_PIXEL,
      .selected_foreground = BLACK_PIXEL,
      .item_padding = 3,
  };

  container_t container = {
    .focusable = true,
    .focused = false,
    .height = item_height * items_count,
    .width = 0,
    .inner.list = list,
    .x = 0,
    .y = 0
  };
  container_t container = {.focusable = true,
                           .focused = false,
                           .height = item_height * items_count,
                           .width = 0,
                           .inner.list = list,
                           .x = 0,
                           .y = 0};

  return container;
}


@@ 57,7 55,8 @@ bool gui_list_container_set_item_height(container_t *container,
  return true;
}

bool gui_list_container_set_render_function(container_t *container, render_item render_it,
bool gui_list_container_set_render_function(container_t *container,
                                            render_item render_it,
                                            render_item render_header) {
  container->inner.list.render_item_fn = render_it;
  container->inner.list.render_header_fn = render_header;


@@ 94,6 93,14 @@ void gui_list_container_render(gui_t *gui, container_t *container) {

  uint32_t selected_index = gui_list_get_selected_index(container);

  if (list.render_header_fn &&
      list.render_header_fn(list.state, 0, gui->renderer, 0 + list.item_padding,
                            0 + list.item_padding, WHITE_PIXEL)) {
    // if header was rendered, translate initial position
    renderer_translate(gui->renderer, 0, item_full_height);
    renderer_set_draw_area(gui->renderer, container->width, container->height - item_full_height);
  }

  for (uint32_t i = first_index; i < end_index && i < list.items_count; i++) {
    int32_t y = beg_y + (i - first_index) * item_full_height;
    display_pixel_t fgcolor = list.regular_foreground;


@@ 104,8 111,10 @@ void gui_list_container_render(gui_t *gui, container_t *container) {
      bgcolor = list.selected_background;
    }

    renderer_render_rectangle(gui->renderer, beg_x, y, 1000, item_full_height, bgcolor);
    list.render_item_fn(list.state, i, gui->renderer, beg_x + list.item_padding, y + list.item_padding, fgcolor);
    renderer_render_rectangle(gui->renderer, beg_x, y, 1000, item_full_height,
                              bgcolor);
    list.render_item_fn(list.state, i, gui->renderer, beg_x + list.item_padding,
                        y + list.item_padding, fgcolor);
  }
}



@@ 138,6 147,5 @@ void gui_list_container_update(gui_t *gui, container_t *container) {
    list.scroll_y = (selected_index - items_count) * item_full_height;
  }


  container->inner.list = list;
}

M lib-gui/src/gui_window_info.c => lib-gui/src/gui_window_info.c +1 -0
@@ 1,6 1,7 @@
#include "gui_window_info.h"
#include "gui.h"
#include "gui_container_info.h"
#include "logger.h"

bool gui_window_init_and_loop(gui_t *gui, void *state, window_info_t info,
                              gui_loop_running_fn loop_running,

M lib-pheripherals/src/nonblocking_io.c => lib-pheripherals/src/nonblocking_io.c +3 -1
@@ 35,7 35,9 @@ int file_set_nonblocking(int file, struct termios *old)
    tcgetattr(file, old);
  }

  cfmakeraw(&attrs);
  attrs.c_lflag &= ~ICANON;
  attrs.c_lflag &= ~ECHO;
  //cfmakeraw(&attrs);

  tcsetattr(file, TCSANOW, &attrs);
  return 1;

Do not follow this link