#include #include "input.h" #include "state.h" #define DBG_LOG(...) printf(__VA_ARGS__) #define ERR_LOG(...) DBG_LOG(__VA_ARGS__) #ifdef DEBUG_INPUT #define INPUT_DBG DBG_LOG #define INPUT_INFO DBG_LOG #else #define INPUT_DBG(...) #define INPUT_INFO(...) #endif #define INPUT_ERR ERR_LOG #define axis_threshold (8192L) typedef struct { int16_t last_x_axis; int16_t last_y_axis; SDL_GameController* gamepad; } sdl_input_data; static sdl_input_data the_input_data = {0}; static SDL_GameController* sdl_find_gamepad() { int i = SDL_NumJoysticks() - 1; INPUT_INFO("Found %d joysticks\n", i + 1); for ( ; i >= 0 && !SDL_IsGameController(i); --i); if (i >= 0) INPUT_INFO("Joystick %d is a gamepad\n", i); return (i < 0 ? NULL : SDL_GameControllerOpen(i)); } static void sdl_lose_gamepad(SDL_GameController* gamepad) { if (NULL != gamepad) { SDL_GameControllerClose(gamepad); } } static int sdl_match_gamepad(SDL_JoystickID id, SDL_GameController* gamepad) { return ( id == SDL_JoystickInstanceID( SDL_GameControllerGetJoystick(gamepad))); } /* static int sdl_event_filter(void*, SDL_Event* event) { return ( SDL_QUIT == event->type || SDL_KEYDOWN == event->type || SDL_KEYUP == event->type || SDL_CONTROLLERBUTTONDOWN == event->type || SDL_CONTROLLERBUTTONUP == event->type || SDL_CONTROLLERDEVICEADDED == event->type || SDL_CONTROLLERDEVICEREMOVED == event->type ); } */ static int sdl_input_init(nes_Input_Reader* reader) { reader->data = &the_input_data; sdl_input_data* data = (sdl_input_data*)reader->data; int status = SDL_Init(SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER); if (status == 0) { data->gamepad = sdl_find_gamepad(); // SDL_SetEventFilter(sdl_event_filter, NULL); if (NULL != data->gamepad) { INPUT_INFO("Gamepad found\n"); } } return status; } static void sdl_input_done(nes_Input_Reader* reader) { sdl_input_data* data = (sdl_input_data*)reader->data; sdl_lose_gamepad(data->gamepad); } static const int sdl_menu_key = SDLK_ESCAPE; static const int sdl_save_key = SDLK_F1; static const int sdl_load_key = SDLK_F2; static const int sdl_alt_start_key = SDLK_RETURN; static const int sdl_keycodes[nes_controller_num_buttons] = { SDLK_a, SDLK_s, SDLK_q, SDLK_w, SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, }; static const int sdl_menu_button = SDL_CONTROLLER_BUTTON_X; static const int sdl_save_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; static const int sdl_load_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; static const int sdl_buttons[nes_controller_num_buttons] = { SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, }; static int button_index(int keycode, const int* codes) { int index = nes_controller_num_buttons - 1; for ( ; index >= 0 && keycode != codes[index]; --index); return index; } #define input_debug(o, ...) INPUT_DBG(__VA_ARGS__) /* static void input_debug(Overlay* overlay, const char* fmt, ...) { char msg[100] = {0}; va_list args; va_start(args, fmt); vsnprintf(msg, sizeof(msg) - 1, fmt, args); va_end(args); overlay_add_message(overlay, msg, 60); } */ static int sdl_input_update(nes_Input_Reader* reader, nese_Components* comp) { int status = input_Result_OK; sdl_input_data* data = (sdl_input_data*)reader->data; if (reader->menu_timer > 0 && --reader->menu_timer == 0) { status = input_Result_Menu; } SDL_Event event = {0}; while (0 == status && 0 != SDL_PollEvent(&event)) { if (SDL_QUIT == event.type) { status = input_Result_Quit; } else if (SDL_WINDOWEVENT == event.type) { if ( SDL_WINDOWEVENT_EXPOSED == event.window.event) { nes_render_refresh(comp->rend); status = input_Result_Refresh; } } else if ( ( SDL_KEYDOWN == event.type || SDL_KEYUP == event.type) && 0 == event.key.repeat ) { input_debug(&comp->rend->overlay, "Input: K %d S %d\n", SDL_KEYDOWN == event.type ? 1 : 0, event.key.keysym.sym); int index = ( sdl_alt_start_key == event.key.keysym.sym) ? Button_Start : button_index( event.key.keysym.sym, sdl_keycodes); if (index >= 0) { uint8_t mask = (1 << index); if (SDL_KEYDOWN == event.type) { comp->sys->input.controllers[0].buttons |= mask; } else { comp->sys->input.controllers[0].buttons &= ~mask; } /* input_debug( &comp->rend->overlay, "Input: Key: %s %02x\n", (SDL_KEYDOWN == event.type) ? "Set" : "Clear", mask ); */ } else if ( sdl_menu_key == event.key.keysym.sym && SDL_KEYDOWN == event.type) { status = input_Result_Menu; } else if (sdl_save_key == event.key.keysym.sym) { if (SDL_KEYDOWN == event.type) { status = input_Result_Save; } else { status = input_Result_Cancel; } } else if (sdl_load_key == event.key.keysym.sym) { if (SDL_KEYDOWN == event.type) { status = input_Result_Load; } else { status = input_Result_Cancel; } } } else if ( SDL_CONTROLLERBUTTONDOWN == event.type || SDL_CONTROLLERBUTTONUP == event.type) { input_debug(&comp->rend->overlay, "Input: B %d B %d\n", SDL_CONTROLLERBUTTONDOWN == event.type ? 1 : 0, event.cbutton.button); int index = button_index(event.cbutton.button, sdl_buttons); if (index >= 0) { uint8_t mask = (1 << index); if (SDL_CONTROLLERBUTTONDOWN == event.type) { comp->sys->input.controllers[0].buttons |= mask; } else { comp->sys->input.controllers[0].buttons &= ~mask; } /* input_debug( &comp->rend->overlay, "Input: Button: %s %02x\n", (SDL_CONTROLLERBUTTONDOWN == event.type) ? "Set" : "Clear", mask ); */ if (Button_Start == index) { if (SDL_CONTROLLERBUTTONDOWN == event.type) { reader->menu_timer = 60; } else { reader->menu_timer = 0; } } } else if (sdl_menu_button == event.cbutton.button) { if (SDL_CONTROLLERBUTTONDOWN == event.type) { status = input_Result_Menu; } } else if (sdl_save_button == event.cbutton.button) { if (SDL_CONTROLLERBUTTONDOWN == event.type) { status = input_Result_Save; } else { status = input_Result_Cancel; } } else if (sdl_load_button == event.cbutton.button) { if (SDL_CONTROLLERBUTTONDOWN == event.type) { status = input_Result_Load; } else { status = input_Result_Cancel; } } } else if (SDL_CONTROLLERAXISMOTION == event.type) { const uint8_t axis = event.caxis.axis; const int16_t value = event.caxis.value; uint8_t mask_set = 0; uint8_t mask_clear = 0; int dir = 0; if (value <= -axis_threshold) dir = -1; else if (value >= axis_threshold) dir = 1; if (SDL_CONTROLLER_AXIS_LEFTX == axis) { if (dir != data->last_x_axis) { mask_clear = (1 << Button_Left) | (1 << Button_Right); if (dir < 0) { mask_set = (1 << Button_Left); } else if (dir > 0) { mask_set = (1 << Button_Right); } } data->last_x_axis = dir; } else if (SDL_CONTROLLER_AXIS_LEFTY == axis) { if (dir != data->last_y_axis) { mask_clear = (1 << Button_Down) | (1 << Button_Up); if (dir < 0) { mask_set = (1 << Button_Up); } else if (dir > 0) { mask_set = (1 << Button_Down); } } data->last_y_axis = dir; } uint32_t old_buttons = comp->sys->input.controllers[0].buttons; uint32_t new_buttons = ((old_buttons & ~mask_clear) | mask_set); comp->sys->input.controllers[0].buttons = new_buttons; /* if (old_buttons != new_buttons) { input_debug(&comp->rend->overlay, "Input: Axis: S %02x C %02x V %d\n", mask_set, mask_clear, value); } */ } else if (SDL_CONTROLLERDEVICEADDED == event.type) { if (NULL == data->gamepad) { INPUT_INFO("New gamepad connected\n"); data->gamepad = sdl_find_gamepad(); if (data->gamepad) INPUT_INFO("Using new gamepad\n"); } else { INPUT_INFO("Redundant gamepad connected\n"); } } else if (SDL_CONTROLLERDEVICEREMOVED == event.type) { if (sdl_match_gamepad(event.cdevice.which, data->gamepad)) { INPUT_INFO("Gamepad disconnected\n"); sdl_lose_gamepad(reader->data); data->gamepad = sdl_find_gamepad(); if (data->gamepad) INPUT_INFO("Using another gamepad\n"); } else { INPUT_INFO("Redundant gamepad disconnected\n"); } } } if (0 != status) { input_debug(&comp->rend->overlay, "Input: Returning %d\n", status); } return status; } nes_Input_Reader sdl_input = { .init = sdl_input_init, .done = sdl_input_done, .update = sdl_input_update, };