diff --git a/src/linux/port.c b/src/linux/port.c index f8b4bcd..ed46a9c 100644 --- a/src/linux/port.c +++ b/src/linux/port.c @@ -83,6 +83,68 @@ void* nese_alloc(int size) { * 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) typedef enum { @@ -98,6 +160,7 @@ typedef struct { int fps_msg_id; Platform_Flags flags; char filename[PLAT_FILENAME_SIZE]; + Action_State delayed_action; } platform_data; @@ -221,12 +284,21 @@ static nese_Action process_events(nes* sys) { input->gamepads[0].buttons &= ~mask; } - } else if (SDL_KEYDOWN == event.type) { + } else { index = keycode_index( event.key.keysym.sym, 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 @@ -314,6 +386,8 @@ int nese_frame_ready(void* plat_data) { status = render_frame_end(&plat->renderer); } + overlay_step(&plat->overlay); + // TODO: Check status first? @@ -324,11 +398,10 @@ int nese_frame_ready(void* plat_data) { break; case Action_Save: - status = save_state(plat->sys, plat->cart.filename); - break; - 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; case Action_FPS: @@ -346,14 +419,17 @@ int nese_frame_ready(void* plat_data) { case Action_Menu: 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; + break; } // TODO: Other Actions? break; @@ -362,6 +438,20 @@ int nese_frame_ready(void* plat_data) { } // 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) { plat->t_target += FRAME_TIME_NS;