diff options
Diffstat (limited to 'backends/events/sdl/sdl-events.cpp')
-rw-r--r-- | backends/events/sdl/sdl-events.cpp | 309 |
1 files changed, 233 insertions, 76 deletions
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index 48854437ad..a3dee67f22 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -30,17 +30,8 @@ #include "common/config-manager.h" #include "common/textconsole.h" -#ifdef JOY_ANALOG -#include "math.h" -#endif - // FIXME move joystick defines out and replace with confile file options // we should really allow users to map any key to a joystick button -#define JOY_DEADZONE 3200 - -#ifndef __SYMBIAN32__ // Symbian wants dialog joystick i.e cursor for movement/selection - #define JOY_ANALOG -#endif // #define JOY_INVERT_Y #define JOY_XAXIS 0 @@ -77,7 +68,7 @@ static uint32 convUTF8ToUTF32(const char *src) { SdlEventSource::SdlEventSource() : EventSource(), _scrollLock(false), _joystick(0), _lastScreenID(0), _graphicsManager(0), _queuedFakeMouseMove(false) #if SDL_VERSION_ATLEAST(2, 0, 0) - , _queuedFakeKeyUp(false), _fakeKeyUp() + , _queuedFakeKeyUp(false), _fakeKeyUp(), _controller(nullptr) #endif { // Reset mouse state @@ -90,25 +81,18 @@ SdlEventSource::SdlEventSource() error("Could not initialize SDL: %s", SDL_GetError()); } - // Enable joystick - if (SDL_NumJoysticks() > joystick_num) { - _joystick = SDL_JoystickOpen(joystick_num); - debug("Using joystick: %s", #if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_JoystickName(_joystick) -#else - SDL_JoystickName(joystick_num) -#endif - ); - } else { - warning("Invalid joystick: %d", joystick_num); + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1) { + error("Could not initialize SDL: %s", SDL_GetError()); } +#endif + + openJoystick(joystick_num); } } SdlEventSource::~SdlEventSource() { - if (_joystick) - SDL_JoystickClose(_joystick); + closeJoystick(); } int SdlEventSource::mapKey(SDLKey sdlKey, SDLMod mod, Uint16 unicode) { @@ -534,12 +518,6 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { return handleMouseButtonDown(ev, event); case SDL_MOUSEBUTTONUP: return handleMouseButtonUp(ev, event); - case SDL_JOYBUTTONDOWN: - return handleJoyButtonDown(ev, event); - case SDL_JOYBUTTONUP: - return handleJoyButtonUp(ev, event); - case SDL_JOYAXISMOTION: - return handleJoyAxisMotion(ev, event); #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_MOUSEWHEEL: { @@ -607,6 +585,12 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { default: return false; } + + case SDL_JOYDEVICEADDED: + return handleJoystickAdded(ev.jdevice); + + case SDL_JOYDEVICEREMOVED: + return handleJoystickRemoved(ev.jdevice); #else case SDL_VIDEOEXPOSE: if (_graphicsManager) @@ -623,6 +607,30 @@ bool SdlEventSource::dispatchSDLEvent(SDL_Event &ev, Common::Event &event) { } + if (_joystick) { + switch (ev.type) { + case SDL_JOYBUTTONDOWN: + return handleJoyButtonDown(ev, event); + case SDL_JOYBUTTONUP: + return handleJoyButtonUp(ev, event); + case SDL_JOYAXISMOTION: + return handleJoyAxisMotion(ev, event); + } + } + +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (_controller) { + switch (ev.type) { + case SDL_CONTROLLERBUTTONDOWN: + return handleControllerButton(ev, event, false); + case SDL_CONTROLLERBUTTONUP: + return handleControllerButton(ev, event, true); + case SDL_CONTROLLERAXISMOTION: + return handleControllerAxisMotion(ev, event); + } + } +#endif + return false; } @@ -797,6 +805,42 @@ bool SdlEventSource::handleMouseButtonUp(SDL_Event &ev, Common::Event &event) { return processMouseEvent(event, ev.button.x, ev.button.y); } +void SdlEventSource::openJoystick(int joystickIndex) { + if (SDL_NumJoysticks() > joystickIndex) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (SDL_IsGameController(joystickIndex)) { + _controller = SDL_GameControllerOpen(joystickIndex); + debug("Using game controller: %s", SDL_GameControllerName(_controller)); + } else +#endif + { + _joystick = SDL_JoystickOpen(joystickIndex); + debug("Using joystick: %s", +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_JoystickName(_joystick) +#else + SDL_JoystickName(joystickIndex) +#endif + ); + } + } else { + warning("Invalid joystick: %d", joystickIndex); + } +} + +void SdlEventSource::closeJoystick() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (_controller) { + SDL_GameControllerClose(_controller); + _controller = nullptr; + } +#endif + if (_joystick) { + SDL_JoystickClose(_joystick); + _joystick = nullptr; + } +} + bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) { if (ev.jbutton.button == JOY_BUT_LMOUSE) { event.type = Common::EVENT_LBUTTONDOWN; @@ -860,71 +904,184 @@ bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) { } bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) { + if (ev.jaxis.axis == JOY_XAXIS) { + _km.joy_x = ev.jaxis.value; + return handleAxisToMouseMotion(_km.joy_x, _km.joy_y); + } else if (ev.jaxis.axis == JOY_YAXIS) { + _km.joy_y = ev.jaxis.value; + return handleAxisToMouseMotion(_km.joy_x, _km.joy_y); + } - int axis = ev.jaxis.value; -#ifdef JOY_ANALOG - // conversion factor between keyboard mouse and joy axis value - int vel_to_axis = (1500 / MULTIPLIER); + return false; +} + +#if SDL_VERSION_ATLEAST(2, 0, 0) +bool SdlEventSource::handleJoystickAdded(const SDL_JoyDeviceEvent &device) { + debug(5, "SdlEventSource: Received joystick added event for index '%d'", device.which); + + int joystick_num = ConfMan.getInt("joystick_num"); + if (joystick_num == device.which) { + debug(5, "SdlEventSource: Newly added joystick with index '%d' matches 'joysticky_num', trying to use it", device.which); + + closeJoystick(); + openJoystick(joystick_num); + } + + return false; +} + +bool SdlEventSource::handleJoystickRemoved(const SDL_JoyDeviceEvent &device) { + debug(5, "SdlEventSource: Received joystick removed event for instance id '%d'", device.which); + + SDL_Joystick *joystick; + if (_controller) { + joystick = SDL_GameControllerGetJoystick(_controller); + } else { + joystick = _joystick; + } + + if (!joystick) { + return false; + } + + if (SDL_JoystickInstanceID(joystick) == device.which) { + debug(5, "SdlEventSource: Newly removed joystick with instance id '%d' matches currently used joystick, closing current joystick", device.which); + + closeJoystick(); + } + + return false; +} + +bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event &event, bool buttonUp) { + using namespace Common; + + struct ControllerEventMapping { + EventType normalType; + KeyState normalKeystate; + EventType modifierType; + KeyState modifierKeystate; + }; + + static const ControllerEventMapping mapping[] = { + // SDL_CONTROLLER_BUTTON_A: Left mouse button + { EVENT_LBUTTONDOWN, KeyState(), EVENT_LBUTTONDOWN, KeyState() }, + // SDL_CONTROLLER_BUTTON_B: Right mouse button + { EVENT_RBUTTONDOWN, KeyState(), EVENT_RBUTTONDOWN, KeyState() }, + // SDL_CONTROLLER_BUTTON_X: Period (+R_trigger: Space) + { EVENT_KEYDOWN, KeyState(KEYCODE_PERIOD, '.'), EVENT_KEYDOWN, KeyState(KEYCODE_SPACE, ASCII_SPACE) }, + // SDL_CONTROLLER_BUTTON_Y: Escape (+R_trigger: Return) + { EVENT_KEYDOWN, KeyState(KEYCODE_ESCAPE, ASCII_ESCAPE), EVENT_KEYDOWN, KeyState(KEYCODE_RETURN, ASCII_RETURN) }, + // SDL_CONTROLLER_BUTTON_BACK: Virtual keyboard (+R_trigger: Predictive Input Dialog) +#ifdef ENABLE_VKEYBD + { EVENT_VIRTUAL_KEYBOARD, KeyState(), EVENT_PREDICTIVE_DIALOG, KeyState() }, #else - if (axis > JOY_DEADZONE) { - axis -= JOY_DEADZONE; - } else if (axis < -JOY_DEADZONE) { - axis += JOY_DEADZONE; - } else - axis = 0; + { EVENT_INVALID, KeyState(), EVENT_PREDICTIVE_DIALOG, KeyState() }, #endif + // SDL_CONTROLLER_BUTTON_GUIDE: Unmapped + { EVENT_INVALID, KeyState(), EVENT_INVALID, KeyState() }, + // SDL_CONTROLLER_BUTTON_START: ScummVM in game menu + { EVENT_MAINMENU, KeyState(), EVENT_MAINMENU, KeyState() }, + // SDL_CONTROLLER_BUTTON_LEFTSTICK: Unmapped + { EVENT_INVALID, KeyState(), EVENT_INVALID, KeyState() }, + // SDL_CONTROLLER_BUTTON_RIGHTSTICK: Unmapped + { EVENT_INVALID, KeyState(), EVENT_INVALID, KeyState() }, + // SDL_CONTROLLER_BUTTON_LEFTSHOULDER: Game menu + { EVENT_KEYDOWN, KeyState(KEYCODE_F5, ASCII_F5), EVENT_KEYDOWN, KeyState(KEYCODE_F5, ASCII_F5) }, + // SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: Modifier + Shift + { EVENT_KEYDOWN, KeyState(KEYCODE_INVALID, 0, KBD_SHIFT), EVENT_KEYDOWN, KeyState(KEYCODE_INVALID, 0, 0) }, + // SDL_CONTROLLER_BUTTON_DPAD_UP: Up (+R_trigger: Up+Right) + { EVENT_KEYDOWN, KeyState(KEYCODE_KP8, 0), EVENT_KEYDOWN, KeyState(KEYCODE_KP9, 0) }, + // SDL_CONTROLLER_BUTTON_DPAD_DOWN: Down (+R_trigger: Down+Left) + { EVENT_KEYDOWN, KeyState(KEYCODE_KP2, 0), EVENT_KEYDOWN, KeyState(KEYCODE_KP1, 0) }, + // SDL_CONTROLLER_BUTTON_DPAD_LEFT: Left (+R_trigger: Up+Left) + { EVENT_KEYDOWN, KeyState(KEYCODE_KP4, 0), EVENT_KEYDOWN, KeyState(KEYCODE_KP7, 0) }, + // SDL_CONTROLLER_BUTTON_DPAD_RIGHT: Right (+R_trigger: Down+Right) + { EVENT_KEYDOWN, KeyState(KEYCODE_KP6, 0), EVENT_KEYDOWN, KeyState(KEYCODE_KP3, 0) } + }; + + if (ev.cbutton.button > SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { + warning("Unknown SDL controller button: '%d'", ev.cbutton.button); + return false; + } - if (ev.jaxis.axis == JOY_XAXIS) { -#ifdef JOY_ANALOG - _km.joy_x = axis; -#else - if (axis != 0) { - _km.x_vel = (axis > 0) ? 1 * MULTIPLIER:-1 * MULTIPLIER; - _km.x_down_count = 1; + if (!_km.modifier) { + event.type = mapping[ev.cbutton.button].normalType; + event.kbd = mapping[ev.cbutton.button].normalKeystate; + } else { + event.type = mapping[ev.cbutton.button].modifierType; + event.kbd = mapping[ev.cbutton.button].modifierKeystate; + } + + // Setting the mouse speed modifier after filling the event structure above + // ensures that the shift key events are correctly handled + if (ev.cbutton.button == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) { + // Right shoulder is the modifier button that makes the mouse go slower + // and allows access to an extended layout while pressed. + _km.modifier = !buttonUp; + } + + if (event.type == EVENT_LBUTTONDOWN || event.type == EVENT_RBUTTONDOWN) { + processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER); + } + + if (buttonUp) { + // The event mapping table is for button down events. If we received a button up event, + // transform the event type to the corresponding up type. + if (event.type == EVENT_KEYDOWN) { + event.type = EVENT_KEYUP; + } else if (event.type == EVENT_LBUTTONDOWN) { + event.type = EVENT_LBUTTONUP; + } else if (event.type == EVENT_RBUTTONDOWN) { + event.type = EVENT_RBUTTONUP; } else { - _km.x_vel = 0; - _km.x_down_count = 0; + // Handled in key down + event.type = EVENT_INVALID; } + } + + return event.type != EVENT_INVALID; +} + +bool SdlEventSource::handleControllerAxisMotion(const SDL_Event &ev, Common::Event &event) { + if (ev.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX) { + _km.joy_x = ev.caxis.value; + return handleAxisToMouseMotion(_km.joy_x, _km.joy_y); + } else if (ev.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY) { + _km.joy_y = ev.caxis.value; + return handleAxisToMouseMotion(_km.joy_x, _km.joy_y); + } + + return false; +} #endif - } else if (ev.jaxis.axis == JOY_YAXIS) { -#ifndef JOY_INVERT_Y - axis = -axis; -#endif -#ifdef JOY_ANALOG - _km.joy_y = -axis; -#else - if (axis != 0) { - _km.y_vel = (-axis > 0) ? 1 * MULTIPLIER: -1 * MULTIPLIER; - _km.y_down_count = 1; - } else { - _km.y_vel = 0; - _km.y_down_count = 0; - } + +bool SdlEventSource::handleAxisToMouseMotion(int16 xAxis, int16 yAxis) { +#ifdef JOY_INVERT_Y + yAxis = -yAxis; #endif - } -#ifdef JOY_ANALOG - // radial and scaled analog joystick deadzone - float analogX = (float)_km.joy_x; - float analogY = (float)_km.joy_y; - float deadZone = (float)JOY_DEADZONE; - if (g_system->hasFeature(OSystem::kFeatureJoystickDeadzone)) - deadZone = (float)ConfMan.getInt("joystick_deadzone") * 1000.0f; - float scalingFactor = 1.0f; - float magnitude = 0.0f; - - magnitude = sqrt(analogX * analogX + analogY * analogY); + + // conversion factor between keyboard mouse and joy axis value + int vel_to_axis = (1500 / MULTIPLIER); + + // radial and scaled deadzone + + float analogX = (float)xAxis; + float analogY = (float)yAxis; + float deadZone = (float)ConfMan.getInt("joystick_deadzone") * 1000.0f; + + float magnitude = sqrt(analogX * analogX + analogY * analogY); if (magnitude >= deadZone) { _km.x_down_count = 0; _km.y_down_count = 0; - scalingFactor = 1.0f / magnitude * (magnitude - deadZone) / (32769.0f - deadZone); + float scalingFactor = 1.0f / magnitude * (magnitude - deadZone) / (32769.0f - deadZone); _km.x_vel = (int16)(analogX * scalingFactor * 32768.0f / vel_to_axis); _km.y_vel = (int16)(analogY * scalingFactor * 32768.0f / vel_to_axis); } else { _km.x_vel = 0; _km.y_vel = 0; } -#endif return false; } |