| @@ -83,6 +83,68 @@ void* nese_alloc(int size) { | |||||
| * Platform-specific features and controls | * Platform-specific features and controls | ||||
| */ | */ | ||||
| #define ACTION_DELAY (60U * 1U) | |||||
| #define ACTION_NOTIFY (60U * 3U) | |||||
| typedef struct { | |||||
| int timer; | |||||
| int msg_id; | |||||
| nese_Action action; | |||||
| } Action_State; | |||||
| static void action_immediate(Action_State* state, nese_Action action) { | |||||
| state->action = action; | |||||
| state->timer = 0; | |||||
| } | |||||
| static void action_start(Action_State* state, nese_Action action, | |||||
| Overlay* overlay) { | |||||
| if (state->action != Action_OK) { | |||||
| // We can't do two things at once - cancel the current action | |||||
| overlay_clear_message(overlay, state->msg_id); | |||||
| state->action = Action_OK; | |||||
| } else { | |||||
| state->action = action; | |||||
| const char* op = NULL; | |||||
| if (Action_Load == action) op = "Restoring ..."; | |||||
| else if (Action_Save == action) op = "Saving ..."; | |||||
| else if (Action_Reset == action) op = "Resetting ..."; | |||||
| if (NULL != op) { | |||||
| state->timer = ACTION_DELAY; | |||||
| state->msg_id = overlay_add_message(overlay, op, state->timer); | |||||
| } else { | |||||
| state->timer = 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| static nese_Action action_tick(Action_State* state, Overlay* overlay) { | |||||
| nese_Action action = Action_OK; | |||||
| if (state->action != Action_OK && --state->timer <= 0) { | |||||
| action = state->action; | |||||
| state->action = Action_OK; | |||||
| overlay_clear_message(overlay, state->msg_id); | |||||
| } | |||||
| return action; | |||||
| } | |||||
| static int action_notify(nese_Action action, int status, Overlay* overlay) { | |||||
| int msg_id = 0; | |||||
| const char* op = NULL; | |||||
| if (Action_Load == action) op = (status < 0 ? "Restore failed" : "Restored"); | |||||
| else if (Action_Save == action) op = (status < 0 ? "Save failed" : "Saved"); | |||||
| else if (Action_Reset == action) op = "Reset"; | |||||
| if (NULL != op) { | |||||
| msg_id = overlay_add_message(overlay, op, ACTION_NOTIFY); | |||||
| } | |||||
| return msg_id; | |||||
| } | |||||
| #define PLAT_FILENAME_SIZE (1024U) | #define PLAT_FILENAME_SIZE (1024U) | ||||
| typedef enum { | typedef enum { | ||||
| @@ -98,6 +160,7 @@ typedef struct { | |||||
| int fps_msg_id; | int fps_msg_id; | ||||
| Platform_Flags flags; | Platform_Flags flags; | ||||
| char filename[PLAT_FILENAME_SIZE]; | char filename[PLAT_FILENAME_SIZE]; | ||||
| Action_State delayed_action; | |||||
| } platform_data; | } platform_data; | ||||
| @@ -221,12 +284,21 @@ static nese_Action process_events(nes* sys) { | |||||
| input->gamepads[0].buttons &= ~mask; | input->gamepads[0].buttons &= ~mask; | ||||
| } | } | ||||
| } else if (SDL_KEYDOWN == event.type) { | |||||
| } else { | |||||
| index = keycode_index( | index = keycode_index( | ||||
| event.key.keysym.sym, | event.key.keysym.sym, | ||||
| sdl_action_keycodes, Action_Max | sdl_action_keycodes, Action_Max | ||||
| ); | ); | ||||
| if (index >= 0) action = index; | |||||
| if (index >= 0) { | |||||
| if (SDL_KEYDOWN == event.type) { | |||||
| action = index; | |||||
| } else if ( Action_Load == index || | |||||
| Action_Save == index || | |||||
| Action_Reset == index) { | |||||
| // On key release, cancel certain pending delayed actions | |||||
| action = Action_Cancel; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| // TODO: Controller inputs | // TODO: Controller inputs | ||||
| @@ -314,6 +386,8 @@ int nese_frame_ready(void* plat_data) { | |||||
| status = render_frame_end(&plat->renderer); | status = render_frame_end(&plat->renderer); | ||||
| } | } | ||||
| overlay_step(&plat->overlay); | |||||
| // TODO: Check status first? | // TODO: Check status first? | ||||
| @@ -324,11 +398,10 @@ int nese_frame_ready(void* plat_data) { | |||||
| break; | break; | ||||
| case Action_Save: | case Action_Save: | ||||
| status = save_state(plat->sys, plat->cart.filename); | |||||
| break; | |||||
| case Action_Load: | case Action_Load: | ||||
| status = load_state(plat->sys, plat->cart.filename); | |||||
| case Action_Reset: | |||||
| case Action_Cancel: | |||||
| action_start(&plat->delayed_action, action, &plat->overlay); | |||||
| break; | break; | ||||
| case Action_FPS: | case Action_FPS: | ||||
| @@ -346,14 +419,17 @@ int nese_frame_ready(void* plat_data) { | |||||
| case Action_Menu: | case Action_Menu: | ||||
| action = game_menu(plat); | action = game_menu(plat); | ||||
| if (Action_Reset == action) { | |||||
| nes_reset(plat->sys); | |||||
| } else if (Action_Save == action) { | |||||
| status = save_state(plat->sys, plat->cart.filename); | |||||
| } else if (Action_Load == action) { | |||||
| status = load_state(plat->sys, plat->cart.filename); | |||||
| } else if (Action_Quit == action || NULL == plat->sys->cart_header) { | |||||
| switch (action) { | |||||
| case Action_Reset: | |||||
| case Action_Load: | |||||
| case Action_Save: | |||||
| action_immediate(&plat->delayed_action, action); | |||||
| break; | |||||
| default: | |||||
| if (NULL != plat->sys->cart_header) break; | |||||
| case Action_Quit: | |||||
| status = -1; | status = -1; | ||||
| break; | |||||
| } | } | ||||
| // TODO: Other Actions? | // TODO: Other Actions? | ||||
| break; | break; | ||||
| @@ -362,6 +438,20 @@ int nese_frame_ready(void* plat_data) { | |||||
| } | } | ||||
| // TODO: Perform more actions? | // TODO: Perform more actions? | ||||
| int action_status = 1; | |||||
| action = action_tick(&plat->delayed_action, &plat->overlay); | |||||
| if (Action_Reset == action) { | |||||
| nes_reset(plat->sys); | |||||
| action_status = 0; | |||||
| } else if (Action_Save == action) { | |||||
| action_status = save_state(plat->sys, plat->cart.filename); | |||||
| } else if (Action_Load == action) { | |||||
| action_status = load_state(plat->sys, plat->cart.filename); | |||||
| } | |||||
| if (action_status <= 0) { | |||||
| action_notify(action, action_status, &plat->overlay); | |||||
| } | |||||
| if (0 == status) { | if (0 == status) { | ||||
| plat->t_target += FRAME_TIME_NS; | plat->t_target += FRAME_TIME_NS; | ||||