diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b101f09a..9d68a28a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: runCmd: | make all -j env: - FLAGS: -DMENU_RELEASE + FLAGS: -DNDEBUG - name: Upload artifact (Standard ROM) uses: actions/upload-artifact@v3 diff --git a/libdragon b/libdragon index e37421e8..e3b756ec 160000 --- a/libdragon +++ b/libdragon @@ -1 +1 @@ -Subproject commit e37421e8e392e51470bb9dc6b5cd7a7419eeb10c +Subproject commit e3b756ecdac6200ff8b6ed255d8f9959137fd325 diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index 0be8a62a..08cb8d95 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -38,12 +38,19 @@ static flashcart_t *flashcart = &((flashcart_t) { .set_save_writeback = NULL, }); +#ifdef NDEBUG + // HACK: libdragon mocks every debug function if NDEBUG flag is enabled. + // Code below reverts that and point to real function instead. + #undef debug_init_sdfs + bool debug_init_sdfs (const char *prefix, int npart); +#endif + flashcart_error_t flashcart_init (void) { bool sd_initialized; flashcart_error_t error; -#ifndef MENU_RELEASE +#ifndef NDEBUG // NOTE: Some flashcarts doesn't have USB port, can't throw error here debug_init_usblog(); #endif @@ -116,6 +123,7 @@ flashcart_error_t flashcart_load_file (char *file_path, uint32_t start_offset_ad if ((file_path == NULL) || (!file_exists(file_path))) { return FLASHCART_ERROR_ARGS; } + return flashcart->load_file(file_path, start_offset_address); } diff --git a/src/menu/actions.c b/src/menu/actions.c index 27bdc2cc..d1ab0496 100644 --- a/src/menu/actions.c +++ b/src/menu/actions.c @@ -3,8 +3,7 @@ #include "actions.h" -#define ACTIONS_REPEAT_DELAY 16 -#define ACTIONS_REPEAT_RATE 2 +#define ACTIONS_REPEAT_DELAY 10 #define JOYSTICK_DEADZONE 32 @@ -49,7 +48,7 @@ void actions_update (menu_t *menu) { } } else if (held.c[0].up || held.c[0].C_up) { menu->actions.vertical_held_counter += 1; - if ((menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY) && (menu->actions.vertical_held_counter % ACTIONS_REPEAT_RATE)) { + if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY) { menu->actions.go_up = true; if (held.c[0].C_up) { menu->actions.fast = true; @@ -57,7 +56,7 @@ void actions_update (menu_t *menu) { } } else if (held.c[0].down || held.c[0].C_down) { menu->actions.vertical_held_counter += 1; - if ((menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY) && (menu->actions.vertical_held_counter % ACTIONS_REPEAT_RATE)) { + if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY) { menu->actions.go_down = true; if (held.c[0].C_down) { menu->actions.fast = true; @@ -65,7 +64,7 @@ void actions_update (menu_t *menu) { } } else if (pressed.c[0].y > +JOYSTICK_DEADZONE) { // TODO: requires improvement for responsiveness menu->actions.vertical_held_counter += 1; - if ((menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY / 2) && (menu->actions.vertical_held_counter % ACTIONS_REPEAT_RATE)) { + if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY / 2) { menu->actions.go_up = true; if (pressed.c[0].y < +75) { menu->actions.vertical_held_counter = 0; @@ -73,7 +72,7 @@ void actions_update (menu_t *menu) { } } else if (pressed.c[0].y < -JOYSTICK_DEADZONE) { // TODO: requires improvement for responsiveness menu->actions.vertical_held_counter += 1; - if ((menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY / 2) && (menu->actions.vertical_held_counter % ACTIONS_REPEAT_RATE)) { + if (menu->actions.vertical_held_counter >= ACTIONS_REPEAT_DELAY / 2) { menu->actions.go_down = true; if (pressed.c[0].y > -75) { menu->actions.vertical_held_counter = 0; @@ -89,12 +88,12 @@ void actions_update (menu_t *menu) { menu->actions.horizontal_held_counter = 0; } else if (held.c[0].left) { menu->actions.horizontal_held_counter += 1; - if ((menu->actions.horizontal_held_counter >= ACTIONS_REPEAT_DELAY) && (menu->actions.horizontal_held_counter % ACTIONS_REPEAT_RATE)) { + if (menu->actions.horizontal_held_counter >= ACTIONS_REPEAT_DELAY) { menu->actions.go_left = true; } } else if (held.c[0].right) { menu->actions.horizontal_held_counter += 1; - if ((menu->actions.horizontal_held_counter >= ACTIONS_REPEAT_DELAY) && (menu->actions.horizontal_held_counter % ACTIONS_REPEAT_RATE)) { + if (menu->actions.horizontal_held_counter >= ACTIONS_REPEAT_DELAY) { menu->actions.go_right = true; } } diff --git a/src/menu/components/file_list.c b/src/menu/components/file_list.c index fd4a1ea4..5da8d1e9 100644 --- a/src/menu/components/file_list.c +++ b/src/menu/components/file_list.c @@ -8,15 +8,15 @@ static const char *dir_prefix = "/"; -static void format_file_size (char *buffer, int size) { +static int format_file_size (char *buffer, int size) { if (size < 8 * 1024) { - sprintf(buffer, "%d B", size); + return sprintf(buffer, "%d B", size); } else if (size < 8 * 1024 * 1024) { - sprintf(buffer, "%d kB", size / 1024); + return sprintf(buffer, "%d kB", size / 1024); } else if (size < 1 * 1024 * 1024 * 1024) { - sprintf(buffer, "%d MB", size / 1024 / 1024); + return sprintf(buffer, "%d MB", size / 1024 / 1024); } else { - sprintf(buffer, "%d GB", size / 1024 / 1024 / 1024); + return sprintf(buffer, "%d GB", size / 1024 / 1024 / 1024); } } @@ -40,11 +40,27 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { STL_UNKNOWN ); } else { - const int list_max_characters = (256 + strlen(dir_prefix)) * FILE_LIST_ENTRIES; - const int paragraph_size = sizeof(rdpq_paragraph_t) + sizeof(rdpq_paragraph_char_t) * list_max_characters; + rdpq_paragraph_t *file_list_layout; + rdpq_paragraph_t *layout; - rdpq_paragraph_t *paragraph = calloc(1, paragraph_size); - paragraph->capacity = list_max_characters; + size_t name_lengths[FILE_LIST_ENTRIES]; + size_t total_length = 1; + + for (int i = 0; i < FILE_LIST_ENTRIES; i++) { + int entry_index = starting_position + i; + + if (entry_index >= entries) { + name_lengths[i] = 0; + } else { + size_t length = strlen(list[entry_index].name); + name_lengths[i] = length; + total_length += length + (list[entry_index].type == ENTRY_TYPE_DIR ? strlen(dir_prefix) : 0); + } + } + + file_list_layout = malloc(sizeof(rdpq_paragraph_t) + (sizeof(rdpq_paragraph_char_t) * total_length)); + memset(file_list_layout, 0, sizeof(rdpq_paragraph_t)); + file_list_layout->capacity = total_length; rdpq_paragraph_builder_begin( &(rdpq_textparms_t) { @@ -53,15 +69,13 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { .wrap = WRAP_ELLIPSES, }, FNT_DEFAULT, - paragraph + file_list_layout ); - for (int i = starting_position; i < entries; i++) { - if (i == (starting_position + FILE_LIST_ENTRIES)) { - break; - } + for (int i = 0; i < FILE_LIST_ENTRIES; i++) { + int entry_index = starting_position + i; - entry_t *entry = &list[i]; + entry_t *entry = &list[entry_index]; menu_font_style_t style; @@ -80,14 +94,18 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { rdpq_paragraph_builder_span(dir_prefix, strlen(dir_prefix)); } - rdpq_paragraph_builder_span(entry->name, strlen(entry->name)); + rdpq_paragraph_builder_span(entry->name, name_lengths[i]); + + if ((entry_index + 1) >= entries) { + break; + } rdpq_paragraph_builder_newline(); } - rdpq_paragraph_builder_end(); + layout = rdpq_paragraph_builder_end(); - int highlight_height = (paragraph->bbox[3] - paragraph->bbox[1]) / paragraph->nlines; + int highlight_height = (layout->bbox[3] - layout->bbox[1]) / layout->nlines; int highlight_y = VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL + ((selected - starting_position) * highlight_height); component_box_draw( @@ -99,12 +117,12 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { ); rdpq_paragraph_render( - paragraph, + layout, VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL ); - memset(paragraph, 0, paragraph_size); + rdpq_paragraph_free(layout); rdpq_paragraph_builder_begin( &(rdpq_textparms_t) { @@ -114,32 +132,33 @@ void component_file_list_draw (entry_t *list, int entries, int selected) { .wrap = WRAP_ELLIPSES, }, FNT_DEFAULT, - paragraph + NULL ); - char file_size[16]; + char file_size[8]; for (int i = starting_position; i < entries; i++) { - if (i == (starting_position + FILE_LIST_ENTRIES)) { - break; - } - entry_t *entry = &list[i]; if (entry->type != ENTRY_TYPE_DIR) { - format_file_size(file_size, entry->size); - rdpq_paragraph_builder_span(file_size, strlen(file_size)); + rdpq_paragraph_builder_span(file_size, format_file_size(file_size, entry->size)); + } + + if ((i + 1) == (starting_position + FILE_LIST_ENTRIES)) { + break; } rdpq_paragraph_builder_newline(); } + layout = rdpq_paragraph_builder_end(); + rdpq_paragraph_render( - paragraph, + layout, VISIBLE_AREA_X0 + TEXT_MARGIN_HORIZONTAL, VISIBLE_AREA_Y0 + TEXT_MARGIN_VERTICAL ); - rdpq_paragraph_free(paragraph); + rdpq_paragraph_free(layout); } } diff --git a/src/menu/menu.c b/src/menu/menu.c index f82dd897..2329d06b 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -24,8 +24,17 @@ static menu_t *menu; static bool boot_pending; static tv_type_t tv_type; +static volatile int frame_counter = 0; +static void frame_counter_handler (void) { + frame_counter += 1; +} + +static void frame_counter_reset (void) { + frame_counter = 0; +} + static void menu_init (boot_params_t *boot_params) { controller_init(); timer_init(); @@ -74,6 +83,8 @@ static void menu_init (boot_params_t *boot_params) { } display_init(RESOLUTION_640x480, DEPTH_16_BPP, 2, GAMMA_NONE, ANTIALIAS_OFF); + + register_VI_handler(frame_counter_handler); } static void menu_deinit (menu_t *menu) { @@ -94,126 +105,61 @@ static void menu_deinit (menu_t *menu) { rtc_close(); timer_close(); + unregister_VI_handler(frame_counter_handler); + display_close(); } +// NOTE: Keep this array in sync with menu_mode_t +static struct views_s { + void (*init) (menu_t *menu); + void (*show) (menu_t *menu, surface_t *display); +} views[__MENU_MODE_COUNT] = { + { NULL, NULL }, // MENU_MODE_NONE + { view_startup_init, view_startup_display }, // MENU_MODE_STARTUP + { view_browser_init, view_browser_display }, // MENU_MODE_BROWSER + { view_file_info_init, view_file_info_display }, // MENU_MODE_FILE_INFO + { view_system_info_init, view_system_info_display }, // MENU_MODE_SYSTEM_INFO + { view_image_viewer_init, view_image_viewer_display }, // MENU_MODE_IMAGE_VIEWER + { view_music_player_init, view_music_player_display }, // MENU_MODE_MUSIC_PLAYER + { view_credits_init, view_credits_display }, // MENU_MODE_CREDITS + { view_load_init, view_load_display }, // MENU_MODE_LOAD + { view_load_emulator_init, view_load_emulator_display }, // MENU_MODE_EMULATOR_LOAD + { view_error_init, view_error_display }, // MENU_MODE_ERROR + { view_fault_init, view_fault_display }, // MENU_MODE_FAULT + { NULL, NULL }, // MENU_MODE_BOOT +}; + void menu_run (boot_params_t *boot_params) { menu_init(boot_params); int audio_buffer_length = audio_get_buffer_length(); while (!boot_pending && (exception_reset_time() < RESET_TIME_LENGTH)) { - surface_t *display = display_try_get(); + surface_t *display = (frame_counter >= 2) ? display_try_get() : NULL; if (display != NULL) { + frame_counter_reset(); + actions_update(menu); - switch (menu->mode) { - case MENU_MODE_STARTUP: - view_startup_display(menu, display); - break; - - case MENU_MODE_BROWSER: - view_browser_display(menu, display); - break; - - case MENU_MODE_FILE_INFO: - view_file_info_display(menu, display); - break; - - case MENU_MODE_SYSTEM_INFO: - view_system_info_display(menu, display); - break; - - case MENU_MODE_IMAGE_VIEWER: - view_image_viewer_display(menu, display); - break; - - case MENU_MODE_MUSIC_PLAYER: - view_music_player_display(menu, display); - break; - - case MENU_MODE_CREDITS: - view_credits_display(menu, display); - break; - - case MENU_MODE_LOAD: - view_load_display(menu, display); - break; - - case MENU_MODE_EMULATOR_LOAD: - view_load_emulator_display(menu, display); - break; - - case MENU_MODE_ERROR: - view_error_display(menu, display); - break; - - case MENU_MODE_FAULT: - view_fault_display(menu, display); - break; - - default: - rdpq_attach_clear(display, NULL); - rdpq_detach_show(); - break; + if (views[menu->mode].show) { + views[menu->mode].show(menu, display); + } else { + rdpq_attach_clear(display, NULL); + rdpq_detach_show(); } while (menu->mode != menu->next_mode) { menu->mode = menu->next_mode; - switch (menu->next_mode) { - case MENU_MODE_STARTUP: - view_startup_init(menu); - break; + if (views[menu->mode].init) { + views[menu->mode].init(menu); + } - case MENU_MODE_BROWSER: - view_browser_init(menu); - break; - - case MENU_MODE_FILE_INFO: - view_file_info_init(menu); - break; - - case MENU_MODE_SYSTEM_INFO: - view_system_info_init(menu); - break; - - case MENU_MODE_IMAGE_VIEWER: - view_image_viewer_init(menu); - break; - - case MENU_MODE_MUSIC_PLAYER: - view_music_player_init(menu); - break; - - case MENU_MODE_CREDITS: - view_credits_init(menu); - break; - - case MENU_MODE_LOAD: - view_load_init(menu); - break; - - case MENU_MODE_EMULATOR_LOAD: - view_load_emulator_init(menu); - break; - - case MENU_MODE_ERROR: - view_error_init(menu); - break; - - case MENU_MODE_FAULT: - view_fault_init(menu); - break; - - case MENU_MODE_BOOT: - boot_pending = true; - break; - - default: - break; + if (menu->mode == MENU_MODE_BOOT) { + boot_pending = true; } } } diff --git a/src/menu/menu_state.h b/src/menu/menu_state.h index 074f5dba..148cf002 100644 --- a/src/menu/menu_state.h +++ b/src/menu/menu_state.h @@ -32,6 +32,7 @@ typedef enum { MENU_MODE_ERROR, MENU_MODE_FAULT, MENU_MODE_BOOT, + __MENU_MODE_COUNT, } menu_mode_t; /** @brief File entry type enumeration */ diff --git a/src/menu/views/system_info.c b/src/menu/views/system_info.c index d371fdca..e60f1466 100644 --- a/src/menu/views/system_info.c +++ b/src/menu/views/system_info.c @@ -67,6 +67,7 @@ static void draw (menu_t *menu, surface_t *d) { component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, + "\n" "B: Exit" );