og-odamex/client/sdl/i_input_sdl20.cpp
2026-01-05 01:52:47 +00:00

1057 lines
26 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id$
//
// Copyright (C) 2006-2026 by The Odamex Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
//
//-----------------------------------------------------------------------------
#include "odamex.h"
#include "i_input_sdl20.h"
#include "i_sdl.h"
#include "i_input.h"
#include "i_video.h"
#include "d_event.h"
#include "doomkeys.h"
#include <queue>
#include <cassert>
static constexpr int MAX_SDL_EVENTS_PER_TIC = 8192;
EXTERN_CVAR(joy_deadzone)
EXTERN_CVAR(joy_lefttrigger_deadzone)
EXTERN_CVAR(joy_righttrigger_deadzone)
// ============================================================================
//
// SDL 2.x Implementation
//
// ============================================================================
#ifdef SDL20
// ============================================================================
//
// ISDL20KeyboardInputDevice implementation
//
// ============================================================================
//
// ISDL20KeyboardInputDevice::ISDL20KeyboardInputDevice
//
ISDL20KeyboardInputDevice::ISDL20KeyboardInputDevice(int id) :
mActive(false), mTextEntry(false)
{
// enable keyboard input
resume();
}
//
// ISDL20KeyboardInputDevice::~ISDL20KeyboardInputDevice
//
ISDL20KeyboardInputDevice::~ISDL20KeyboardInputDevice()
{
pause();
}
//
// ISDL20KeyboardInputDevice::active
//
bool ISDL20KeyboardInputDevice::active() const
{
return mActive && I_GetWindow()->isFocused();
}
//
// ISDL20KeyboardInputDevice::flushEvents
//
void ISDL20KeyboardInputDevice::flushEvents()
{
gatherEvents();
while (!mEvents.empty())
mEvents.pop();
}
//
// ISDL20KeyboardInputDevice::reset
//
void ISDL20KeyboardInputDevice::reset()
{
flushEvents();
}
//
// ISDL20KeyboardInputDevice::pause
//
// Sets the internal state to ignore all input for this device.
//
// NOTE: SDL_EventState clears the SDL event queue so only call this after all
// SDL events have been processed in all SDL input device instances.
//
void ISDL20KeyboardInputDevice::pause()
{
mActive = false;
SDL_EventState(SDL_KEYDOWN, SDL_IGNORE);
SDL_EventState(SDL_KEYUP, SDL_IGNORE);
SDL_EventState(SDL_TEXTINPUT, SDL_IGNORE);
}
//
// ISDL20KeyboardInputDevice::resume
//
// Sets the internal state to enable all input for this device.
//
// NOTE: SDL_EventState clears the SDL event queue so only call this after all
// SDL events have been processed in all SDL input device instances.
//
void ISDL20KeyboardInputDevice::resume()
{
mActive = true;
reset();
SDL_EventState(SDL_KEYDOWN, SDL_ENABLE);
SDL_EventState(SDL_KEYUP, SDL_ENABLE);
SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
}
//
// ISDL20KeyboardInputDevice::enableTextEntry
//
// Enables text entry for this device.
//
//
void ISDL20KeyboardInputDevice::enableTextEntry()
{
mTextEntry = true;
SDL_StartTextInput();
}
//
// ISDL20KeyboardInputDevice::disableTextEntry
//
// Disables text entry for this device.
//
//
void ISDL20KeyboardInputDevice::disableTextEntry()
{
mTextEntry = false;
SDL_StopTextInput();
}
//
// ISDL20KeyboardInputDevice::getTextEventValue
//
// Look for an SDL_TEXTINPUT event following this SDL_KEYDOWN event.
//
// On Windows platforms, SDL receives the translation from a key scancode
// to a localized text representation as a separate event. SDL 1.2 used to
// combine these separate events into a single SDL_Event however SDL 2.0
// creates two separate SDL_Events for this.
//
// When SDL2 receives a Windows event message for a key press from the
// Windows message queue, it calls "TranslateMessage" to generate
// a WM_CHAR event message that contains the localized text representation
// of the keypress. The WM_CHAR event ends up as the next event in the
// Windows message queue in practice (though there is no guarantee of this).
//
// Thus SDL2 inserts the key press event into its own queue as SDL_KEYDOWN
// and follows it with the text event SDL_TEXTINPUT.
//
// [SL] Based on GPL v2 code from ScummVM
//
// ScummVM - Graphic Adventure Engine
//
// ScummVM is the legal property of its developers, whose names
// are too numerous to list here. Please refer to the COPYRIGHT
// file distributed with this source distribution.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
int ISDL20KeyboardInputDevice::getTextEventValue()
{
static constexpr size_t max_events = 32;
SDL_Event sdl_events[max_events];
SDL_PumpEvents();
const size_t num_events = SDL_PeepEvents(sdl_events, max_events, SDL_PEEKEVENT, SDL_KEYDOWN, SDL_TEXTINPUT);
for (size_t i = 0; i < num_events; i++)
{
// If we found another SDL_KEYDOWN event prior to SDL_TEXTINPUT event,
// we must assume the next SDL_TEXTINPUT event does not correspond to
// our original SDL_KEYDOWN event.
if (sdl_events[i].type == SDL_KEYDOWN)
return 0;
// Looks like we found a corresponding SDL_TEXTINPUT event
if (sdl_events[i].type == SDL_TEXTINPUT)
{
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
const char output_type[] = "UTF-32BE";
#else
const char output_type[] = "UTF-32LE";
#endif
const char* src = sdl_events[i].text.text;
uint32_t utf32 = 0;
char* dst = SDL_iconv_string(output_type, "UTF-8", src, SDL_strlen(src) + 1);
if (dst)
{
utf32 = *((uint32_t *)dst);
SDL_free(dst);
}
return utf32;
}
}
return 0;
}
//
// ISDL20KeyboardInputDevice::translateKey
//
// Performs translation of an SDL_Keysym event to
// to Odamex's internal key representation (which is identical
// to SDL 2.0's key representation).
//
int ISDL20KeyboardInputDevice::translateKey(SDL_Keysym keysym)
{
return keysym.sym;
}
//
// ISDL20KeyboardInputDevice::gatherEvents
//
// Pumps the SDL Event queue and retrieves any mouse events and puts them into
// this instance's event queue.
//
void ISDL20KeyboardInputDevice::gatherEvents()
{
if (!active())
return;
// Force SDL to gather events from input devices. This is called
// implicitly from SDL_PollEvent but since we're using SDL_PeepEvents to
// process only keyboard events, SDL_PumpEvents is necessary.
SDL_PumpEvents();
SDL_Event sdl_ev;
while (SDL_PeepEvents(&sdl_ev, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_TEXTINPUT))
{
// Process SDL_KEYDOWN / SDL_KEYUP events. SDL_TEXTINPUT events will
// be implicitly ignored unless handled below.
if (sdl_ev.type == SDL_KEYDOWN || sdl_ev.type == SDL_KEYUP)
{
const int sym = sdl_ev.key.keysym.sym;
const int mod = sdl_ev.key.keysym.mod;
event_t ev;
ev.type = (sdl_ev.type == SDL_KEYDOWN) ? ev_keydown : ev_keyup;
// Get the Odamex key code from the scancode
ev.data1 = translateKey(sdl_ev.key.keysym);
// From Chocolate Doom:
// Get the localized version of the key press. This takes into account the
// keyboard layout, but does not apply any changes due to modifiers, (eg.
// shift-, alt-, etc.)
//
// [SL] not sure if this is actually useful...
if (sym > 0 && sym < 128)
ev.data2 = sym;
// Get the unicode value for the key using the localized keyboard layout
// and includes modification via shift, etc.
if (sdl_ev.type == SDL_KEYDOWN)
ev.data3 = getTextEventValue();
// Ch0wW : Fixes a problem of ultra-fast repeats.
if (sdl_ev.key.repeat != 0)
continue;
// drop ALT-TAB events - they're handled elsewhere
if (sym == SDLK_TAB && mod & (KMOD_LALT | KMOD_RALT))
continue;
// HeX9109: Alt+F4 for cheats! Thanks Spleen
// Translate the ALT+F4 key combo event into a SDL_QUIT event and push
// it back into SDL's event queue so that it can be handled elsewhere.
if (sym == SDLK_F4 && mod & (KMOD_LALT | KMOD_RALT))
{
SDL_Event sdl_quit_ev;
sdl_quit_ev.type = SDL_QUIT;
SDL_PushEvent(&sdl_quit_ev);
continue;
}
// Add the mod in
ev.mod = mod;
// Normal game keyboard event - insert it into our internal queue
if (ev.data1)
mEvents.push(ev);
}
}
}
//
// ISDL20KeyboardInputDevice::getEvent
//
// Removes the first event from the queue and returns it.
// This makes no checks to ensure there actually is an event in the queue and
// if there is not, the behavior is undefined.
//
void ISDL20KeyboardInputDevice::getEvent(event_t* ev)
{
assert(hasEvent());
*ev = mEvents.front();
mEvents.pop();
}
// ============================================================================
//
// ISDL20MouseInputDevice implementation
//
// ============================================================================
//
// ISDL20MouseInputDevice::ISDL20MouseInputDevice
//
ISDL20MouseInputDevice::ISDL20MouseInputDevice(int id) :
mActive(false)
{
reset();
}
//
// ISDL20MouseInputDevice::~ISDL20MouseInputDevice
ISDL20MouseInputDevice::~ISDL20MouseInputDevice()
{
pause();
}
//
// ISDL20MouseInputDevice::active
//
bool ISDL20MouseInputDevice::active() const
{
return mActive && I_GetWindow()->isFocused();
}
//
// ISDL20MouseInputDevice::flushEvents
//
void ISDL20MouseInputDevice::flushEvents()
{
gatherEvents();
while (!mEvents.empty())
mEvents.pop();
}
//
// ISDL20MouseInputDevice::reset
//
void ISDL20MouseInputDevice::reset()
{
flushEvents();
}
//
// ISDL20MouseInputDevice::pause
//
// Sets the internal state to ignore all input for this device.
//
// NOTE: SDL_EventState clears the SDL event queue so only call this after all
// SDL events have been processed in all SDL input device instances.
//
void ISDL20MouseInputDevice::pause()
{
mActive = false;
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
SDL_SetRelativeMouseMode(SDL_FALSE);
}
//
// ISDL20MouseInputDevice::resume
//
// Sets the internal state to enable all input for this device.
//
// NOTE: SDL_EventState clears the SDL event queue so only call this after all
// SDL events have been processed in all SDL input device instances.
//
void ISDL20MouseInputDevice::resume()
{
mActive = true;
reset();
// [RV] Always use relative mouse mode and
// force unscaled relative motion across supported SDL versions
SDL_SetRelativeMouseMode(SDL_TRUE);
#if SDL_VERSION_ATLEAST(2, 0, 14)
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_SCALING, "0", SDL_HINT_OVERRIDE);
#endif
#if SDL_VERSION_ATLEAST(2, 26, 0)
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE, "0", SDL_HINT_OVERRIDE);
#endif
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE);
}
//
// ISDL20MouseInputDevice::gatherEvents
//
// Pumps the SDL Event queue and retrieves any mouse events and puts them into
// this instance's event queue.
//
void ISDL20MouseInputDevice::gatherEvents()
{
if (!active())
return;
// Force SDL to gather events from input devices. This is called
// implicitly from SDL_PollEvent but since we're using SDL_PeepEvents to
// process only mouse events, SDL_PumpEvents is necessary.
int num_events;
SDL_Event sdl_events[MAX_SDL_EVENTS_PER_TIC];
SDL_PumpEvents();
// Retrieve mouse movement events from SDL
// [SL] accumulate the total mouse movement over all events polled
// and post one aggregate mouse movement event to Doom's event queue
// after all are polled.
event_t movement_event(ev_mouse);
while ((num_events = SDL_PeepEvents(sdl_events, MAX_SDL_EVENTS_PER_TIC, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)))
{
for (int i = 0; i < num_events; i++)
{
const SDL_Event& sdl_ev = sdl_events[i];
movement_event.data2 += sdl_ev.motion.xrel;
movement_event.data3 -= sdl_ev.motion.yrel;
}
}
if (movement_event.data2 || movement_event.data3)
mEvents.push(movement_event);
// Retrieve mouse button and wheel events from SDL and post
// as separate events to Doom's event queue.
while ((num_events = SDL_PeepEvents(sdl_events, MAX_SDL_EVENTS_PER_TIC, SDL_GETEVENT, SDL_MOUSEBUTTONDOWN, SDL_MOUSEWHEEL)))
{
for (int i = 0; i < num_events; i++)
{
event_t ev;
const SDL_Event& sdl_ev = sdl_events[i];
if (sdl_ev.type == SDL_MOUSEWHEEL)
{
ev.type = ev_keydown;
int direction = 1;
#if (SDL_VERSION >= SDL_VERSIONNUM(2, 0, 4))
if (sdl_ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
direction = -1;
#endif
if (direction * sdl_ev.wheel.y > 0)
ev.data1 = OKEY_MWHEELUP;
else if (direction * sdl_ev.wheel.y < 0)
ev.data1 = OKEY_MWHEELDOWN;
}
else if (sdl_ev.type == SDL_MOUSEBUTTONDOWN || sdl_ev.type == SDL_MOUSEBUTTONUP)
{
ev.type = (sdl_ev.type == SDL_MOUSEBUTTONDOWN) ? ev_keydown : ev_keyup;
if (sdl_ev.button.button == SDL_BUTTON_LEFT)
ev.data1 = OKEY_MOUSE1;
else if (sdl_ev.button.button == SDL_BUTTON_RIGHT)
ev.data1 = OKEY_MOUSE2;
else if (sdl_ev.button.button == SDL_BUTTON_MIDDLE)
ev.data1 = OKEY_MOUSE3;
else if (sdl_ev.button.button == SDL_BUTTON_X1)
ev.data1 = OKEY_MOUSE4; // [Xyltol 07/21/2011] - Add support for MOUSE4
else if (sdl_ev.button.button == SDL_BUTTON_X2)
ev.data1 = OKEY_MOUSE5; // [Xyltol 07/21/2011] - Add support for MOUSE5
}
if (ev.data1)
mEvents.push(ev);
}
}
}
//
// ISDL20MouseInputDevice::getEvent
//
// Removes the first event from the queue and returns it.
// This makes no checks to ensure there actually is an event in the queue and
// if there is not, the behavior is undefined.
//
void ISDL20MouseInputDevice::getEvent(event_t* ev)
{
assert(hasEvent());
*ev = mEvents.front();
mEvents.pop();
}
// ============================================================================
//
// ISDL20JoystickInputDevice implementation
//
// ============================================================================
//
// ISDL20JoystickInputDevice::ISDL20JoystickInputDevice
//
ISDL20JoystickInputDevice::ISDL20JoystickInputDevice(int id) :
mActive(false), mJoystickId(id), mJoystick(NULL)
{
assert(SDL_WasInit(SDL_INIT_GAMECONTROLLER));
assert(mJoystickId >= 0 && mJoystickId < SDL_NumJoysticks());
mJoystick = SDL_GameControllerOpen(mJoystickId);
if (mJoystick == NULL)
return;
// This turns on automatic event polling for joysticks so that the state
// of each button and axis doesn't need to be manually queried each tick. -- Hyper_Eye
SDL_GameControllerEventState(SDL_ENABLE);
resume();
}
//
// ISDL20JoystickInputDevice::~ISDL20JoystickInputDevice
//
ISDL20JoystickInputDevice::~ISDL20JoystickInputDevice()
{
pause();
SDL_GameControllerEventState(SDL_IGNORE);
if (mJoystick != NULL)
SDL_GameControllerClose(mJoystick);
}
//
// ISDL20JoystickInputDevice::active
//
bool ISDL20JoystickInputDevice::active() const
{
return mJoystick != NULL && mActive && I_GetWindow()->isFocused();
}
//
// ISDL20JoystickInputDevice::flushEvents
//
void ISDL20JoystickInputDevice::flushEvents()
{
gatherEvents();
while (!mEvents.empty())
mEvents.pop();
}
//
// ISDL20JoystickInputDevice::reset
//
void ISDL20JoystickInputDevice::reset()
{
flushEvents();
}
//
// ISDL20JoystickInputDevice::pause
//
// Sets the internal state to ignore all input for this device.
//
// NOTE: SDL_EventState clears the SDL event queue so only call this after all
// SDL events have been processed in all SDL input device instances.
//
void ISDL20JoystickInputDevice::pause()
{
mActive = false;
SDL_EventState(SDL_CONTROLLERAXISMOTION, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERBUTTONDOWN, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERBUTTONUP, SDL_IGNORE);
}
//
// ISDL20JoystickInputDevice::resume
//
// Sets the internal state to enable all input for this device.
//
// NOTE: SDL_EventState clears the SDL event queue so only call this after all
// SDL events have been processed in all SDL input device instances.
//
void ISDL20JoystickInputDevice::resume()
{
mActive = true;
reset();
SDL_EventState(SDL_CONTROLLERAXISMOTION, SDL_ENABLE);
SDL_EventState(SDL_CONTROLLERBUTTONDOWN, SDL_ENABLE);
SDL_EventState(SDL_CONTROLLERBUTTONUP, SDL_ENABLE);
}
//
// ISDL20JoystickInputDevice::gatherEvents
//
// Pumps the SDL Event queue and retrieves any joystick events and translates
// them to an event_t instances before putting them into this instance's
// event queue.
//
void ISDL20JoystickInputDevice::gatherEvents()
{
if (!active())
return;
// Force SDL to gather events from input devices. This is called
// implicitly from SDL_PollEvent but since we're using SDL_PeepEvents to
// process only mouse events, SDL_PumpEvents is necessary.
SDL_PumpEvents();
// Retrieve events from SDL
int num_events = 0;
SDL_Event sdl_events[MAX_SDL_EVENTS_PER_TIC];
while ((num_events = SDL_PeepEvents(sdl_events, MAX_SDL_EVENTS_PER_TIC, SDL_GETEVENT,
SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONUP)))
{
for (int i = 0; i < num_events; i++)
{
const SDL_Event& sdl_ev = sdl_events[i];
assert(sdl_ev.type == SDL_CONTROLLERBUTTONDOWN ||
sdl_ev.type == SDL_CONTROLLERBUTTONUP ||
sdl_ev.type == SDL_CONTROLLERAXISMOTION);
if ((sdl_ev.type == SDL_CONTROLLERBUTTONDOWN ||
sdl_ev.type == SDL_CONTROLLERBUTTONUP) &&
sdl_ev.jbutton.which == mJoystickId)
{
event_t button_event;
button_event.type =
(sdl_ev.type == SDL_CONTROLLERBUTTONDOWN) ? ev_keydown : ev_keyup;
button_event.data1 = sdl_ev.cbutton.button + OKEY_JOY1;
mEvents.push(button_event);
}
else if (sdl_ev.type == SDL_CONTROLLERAXISMOTION &&
sdl_ev.caxis.which == mJoystickId)
{
float deadzone;
bool bPressed = false;
if (sdl_ev.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT)
{
event_t button_event;
deadzone = (joy_lefttrigger_deadzone * 32767);
if ((sdl_ev.caxis.value >= deadzone) ||
(sdl_ev.caxis.value <= -deadzone)){
bPressed = true;
}
button_event.type = bPressed ? ev_keydown : ev_keyup;
button_event.data1 = OKEY_JOY20; // LEFT TRIGGER
mEvents.push(button_event);
}
else if (sdl_ev.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
{
event_t button_event;
deadzone = (joy_righttrigger_deadzone * 32767);
if ((sdl_ev.caxis.value >= deadzone) ||
(sdl_ev.caxis.value <= -deadzone)){
bPressed = true;
}
button_event.type = bPressed ? ev_keydown : ev_keyup;
button_event.data1 = OKEY_JOY21; // RIGHT TRIGGER
mEvents.push(button_event);
}
else
{
event_t motion_event(ev_joystick);
motion_event.type = ev_joystick;
motion_event.data2 = sdl_ev.caxis.axis;
motion_event.data3 = calcAxisValue(sdl_ev.caxis.value);
mEvents.push(motion_event);
}
}
}
}
// Flush all remaining joystick and game controller events.
SDL_FlushEvents(SDL_JOYAXISMOTION, SDL_CONTROLLERDEVICEREMAPPED);
}
int ISDL20JoystickInputDevice::calcAxisValue(int raw_value)
{
float value;
// Normalize.
if (raw_value > 0)
{
value = (float)raw_value / (float)SDL_JOYSTICK_AXIS_MAX;
}
else if (raw_value < 0)
{
value = (float)raw_value / (float)abs(SDL_JOYSTICK_AXIS_MIN);
}
else
{
value = 0.0f;
}
// Apply deadzone.
if (value > joy_deadzone)
{
value = (value - joy_deadzone) / (1.0f - joy_deadzone);
}
else if (value < -joy_deadzone)
{
value = (value + joy_deadzone) / (1.0f - joy_deadzone);
}
else
{
value = 0.0f;
}
// Scale value to the range used for calculations in G_BuildTiccmd().
return lroundf(value * 32767.0f);
}
//
// ISDL20JoystickInputDevice::getEvent
//
// Removes the first event from the queue and returns it.
// This makes no checks to ensure there actually is an event in the queue and
// if there is not, the behavior is undefined.
//
void ISDL20JoystickInputDevice::getEvent(event_t* ev)
{
assert(hasEvent());
*ev = mEvents.front();
mEvents.pop();
}
// ============================================================================
//
// ISDL20InputSubsystem implementation
//
// ============================================================================
//
// ISDL20InputSubsystem::ISDL20InputSubsystem
//
ISDL20InputSubsystem::ISDL20InputSubsystem() :
IInputSubsystem(),
mInputGrabbed(false)
{
// Initialize the joystick subsystem and open a joystick if use_joystick is enabled. -- Hyper_Eye
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
// Tell SDL to ignore events from the input devices
// IInputDevice constructors will enable these events when they're initialized.
SDL_EventState(SDL_KEYDOWN, SDL_IGNORE);
SDL_EventState(SDL_KEYUP, SDL_IGNORE);
SDL_EventState(SDL_TEXTINPUT, SDL_IGNORE);
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERAXISMOTION, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERBUTTONDOWN, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERBUTTONUP, SDL_IGNORE);
// Ignore unsupported game controller events.
#if (SDL_MINOR_VERSION > 0 || SDL_PATCHLEVEL >= 14)
SDL_EventState(SDL_CONTROLLERTOUCHPADDOWN, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERTOUCHPADMOTION, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERTOUCHPADUP, SDL_IGNORE);
SDL_EventState(SDL_CONTROLLERSENSORUPDATE, SDL_IGNORE);
#endif
grabInput();
}
//
// ISDL20InputSubsystem::~ISDL20InputSubsystem
//
ISDL20InputSubsystem::~ISDL20InputSubsystem()
{
if (getKeyboardInputDevice())
shutdownKeyboard(0);
if (getMouseInputDevice())
shutdownMouse(0);
if (getJoystickInputDevice())
shutdownJoystick(0);
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
}
//
// ISDL20InputSubsystem::getKeyboardDevices
//
// SDL only allows for one logical keyboard so just return a dummy device
// description.
//
std::vector<IInputDeviceInfo> ISDL20InputSubsystem::getKeyboardDevices() const
{
std::vector<IInputDeviceInfo> devices;
IInputDeviceInfo& device_info = devices.emplace_back();
device_info.mId = 0;
device_info.mDeviceName = "SDL 2.0 keyboard";
return devices;
}
//
// ISDL20InputSubsystem::initKeyboard
//
void ISDL20InputSubsystem::initKeyboard(int id)
{
shutdownKeyboard(0);
const std::vector<IInputDeviceInfo> devices = getKeyboardDevices();
std::string device_name;
for (const auto& device : devices)
{
if (device.mId == id)
device_name = device.mDeviceName;
}
PrintFmt(PRINT_HIGH, "I_InitInput: intializing {:s}\n", device_name);
setKeyboardInputDevice(new ISDL20KeyboardInputDevice(id));
registerInputDevice(getKeyboardInputDevice());
getKeyboardInputDevice()->resume();
}
//
// ISDL20InputSubsystem::shutdownKeyboard
//
void ISDL20InputSubsystem::shutdownKeyboard(int id)
{
IInputDevice* device = getKeyboardInputDevice();
if (device)
{
unregisterInputDevice(device);
delete device;
setKeyboardInputDevice(NULL);
}
}
//
// ISDL20InputSubsystem::getMouseDevices
//
// SDL only allows for one logical mouse so just return a dummy device
// description.
//
std::vector<IInputDeviceInfo> ISDL20InputSubsystem::getMouseDevices() const
{
std::vector<IInputDeviceInfo> devices;
devices.push_back(IInputDeviceInfo());
IInputDeviceInfo& sdl_device_info = devices.back();
sdl_device_info.mId = 0;
sdl_device_info.mDeviceName = "SDL 2.0 mouse";
return devices;
}
//
// ISDL20InputSubsystem::initMouse
//
void ISDL20InputSubsystem::initMouse(int id)
{
shutdownMouse(0);
const std::vector<IInputDeviceInfo> devices = getMouseDevices();
std::string device_name;
for (const auto& device : devices)
{
if (device.mId == id)
device_name = device.mDeviceName;
}
PrintFmt(PRINT_HIGH, "I_InitInput: intializing {:s}\n", device_name);
setMouseInputDevice(new ISDL20MouseInputDevice(id));
assert(getMouseInputDevice() != NULL);
registerInputDevice(getMouseInputDevice());
getMouseInputDevice()->resume();
}
//
// ISDL20InputSubsystem::shutdownMouse
//
void ISDL20InputSubsystem::shutdownMouse(int id)
{
IInputDevice* device = getMouseInputDevice();
if (device)
{
unregisterInputDevice(device);
delete device;
setMouseInputDevice(NULL);
}
}
//
//
// ISDL20InputSubsystem::getJoystickDevices
//
//
std::vector<IInputDeviceInfo> ISDL20InputSubsystem::getJoystickDevices() const
{
// TODO: does the SDL Joystick subsystem need to be initialized?
std::vector<IInputDeviceInfo> devices;
for (int i = 0; i < SDL_NumJoysticks(); i++)
{
devices.push_back(IInputDeviceInfo());
IInputDeviceInfo& device_info = devices.back();
device_info.mId = i;
const char* name = SDL_GameControllerNameForIndex(i);
device_info.mDeviceName = fmt::format("SDL 2.0 joystick ({})", name ? name : "unknown");
}
return devices;
}
// ISDL20InputSubsystem::initJoystick
//
void ISDL20InputSubsystem::initJoystick(int id)
{
shutdownJoystick(0);
const std::vector<IInputDeviceInfo> devices = getJoystickDevices();
std::string device_name;
for (const auto& device : devices)
{
if (device.mId == id)
device_name = device.mDeviceName;
}
PrintFmt(PRINT_HIGH, "I_InitInput: intializing {:s}\n", device_name);
setJoystickInputDevice(new ISDL20JoystickInputDevice(id));
registerInputDevice(getJoystickInputDevice());
getJoystickInputDevice()->resume();
}
//
// ISDL20InputSubsystem::shutdownJoystick
//
void ISDL20InputSubsystem::shutdownJoystick(int id)
{
IInputDevice* device = getJoystickInputDevice();
if (device)
{
unregisterInputDevice(device);
delete device;
setJoystickInputDevice(NULL);
}
}
//
// ISDL20InputSubsystem::grabInput
//
void ISDL20InputSubsystem::grabInput()
{
mInputGrabbed = true;
IInputDevice* device = getMouseInputDevice();
if (device)
device->resume();
}
//
// ISDL20InputSubsystem::releaseInput
//
void ISDL20InputSubsystem::releaseInput()
{
mInputGrabbed = false;
IInputDevice* device = getMouseInputDevice();
if (device)
device->pause();
}
#endif // SDL20
VERSION_CONTROL(i_input_sdl20_cpp, "$Id$")