diff --git a/.clangd b/.clangd index d926d2fe..ec0b8903 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,2 @@ CompileFlags: - CompilationDatabase: build/win32-debug + CompilationDatabase: build diff --git a/Makefile b/Makefile index 37ea06af..1cc54b52 100644 --- a/Makefile +++ b/Makefile @@ -52,9 +52,16 @@ endif reinstall/third-party/linux reinstall/third-party/linux-debug \ reinstall/third-party/macosx reinstall/third-party/macosx-debug \ reinstall/third-party/android reinstall/third-party/android-debug \ - reinstall/third-party/ios reinstall/third-party/ios-debug + reinstall/third-party/ios reinstall/third-party/ios-debug \ + link-compile-commands + +# clangd: build/compile_commands.json -> host platform debug CDB +link-compile-commands: + @mkdir -p build + ln -sfn $(PLATFORM)-debug/compile_commands.json build/compile_commands.json debug: build/$(PLATFORM)-debug + @$(MAKE) link-compile-commands release: build/$(PLATFORM) build/win32: build/win32/EVEngine.sln diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 08cf2763..f28bc7b0 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -112,9 +112,9 @@ else() endif() if(ANDROID OR (CMAKE_SYSTEM_NAME STREQUAL "Android") OR CMAKE_SYSTEM_NAME STREQUAL "iOS" OR EVENGINE_IOS) - set(EVELIBS EVWindow EVMouse EVEvent EVTimer EVTouch EVCmdLine EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio) + set(EVELIBS EVWindow EVMouse EVKeyboard EVJoystick EVEvent EVTimer EVTouch EVCmdLine EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio) else() - set(EVELIBS EVWindow EVMouse EVEvent EVTimer EVTouch EVCmdLine EVDevTools EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio) + set(EVELIBS EVWindow EVMouse EVKeyboard EVJoystick EVEvent EVTimer EVTouch EVCmdLine EVDevTools EVScripts EVCommon EVGraphics EVImage EVFileSystem EVNetwork EVData EVSound EVAudio) endif() if(EVENGINE_PLATFORM_LIB) diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 0ae5ad9c..06fe3e96 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -5,6 +5,8 @@ create_module(EVWindow window) create_module(EVEvent event) create_module(EVTimer timer) create_module(EVMouse mouse) +create_module(EVKeyboard keyboard) +create_module(EVJoystick joystick) create_module(EVTouch touch) create_module(EVData data) target_compile_definitions(EVData PUBLIC POCO_STATIC POCO_NO_AUTOMATIC_LIBS) diff --git a/src/modules/event/sdl/Event.cpp b/src/modules/event/sdl/Event.cpp index c2d360d9..e3ea68e6 100644 --- a/src/modules/event/sdl/Event.cpp +++ b/src/modules/event/sdl/Event.cpp @@ -8,6 +8,9 @@ #include "audio/Audio.h" #include "touch/Touch.h" #include "touch/sdl/Touch.h" +#include "keyboard/Keyboard.h" +#include "joystick/Joystick.h" +#include "joystick/Pad.h" #include "common/Exception.h" #include "common/Module.h" #include "common/config.h" @@ -193,6 +196,109 @@ Message *Event::convert(const SDL_Event &e) break; } break; + case SDL_KEYDOWN: { + if (e.key.repeat) { + auto *kb = getModInst(keyboard, Keyboard); + if (kb && !kb->hasKeyRepeat()) + break; + } + const char *keyName = SDL_GetKeyName(e.key.keysym.sym); + const char *scanName = SDL_GetScancodeName(e.key.keysym.scancode); + return new Message("keypressed", + {Variant::makeString(keyName ? keyName : ""), + Variant::makeString(scanName ? scanName : ""), + Variant::makeInt(e.key.repeat ? 1 : 0)}); + } + case SDL_KEYUP: { + const char *keyName = SDL_GetKeyName(e.key.keysym.sym); + const char *scanName = SDL_GetScancodeName(e.key.keysym.scancode); + return new Message("keyreleased", + {Variant::makeString(keyName ? keyName : ""), + Variant::makeString(scanName ? scanName : "")}); + } + case SDL_TEXTINPUT: + return new Message("textinput", {Variant::makeString(e.text.text)}); + case SDL_TEXTEDITING: + return new Message("textedited", + {Variant::makeString(e.edit.text), + Variant::makeInt(e.edit.start), + Variant::makeInt(e.edit.length)}); + case SDL_JOYDEVICEADDED: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->addJoystick(e.jdevice.which); + if (!pad) break; + return new Message("joystickadded", + {Variant::makeInt(pad->getID()), + Variant::makeString(pad->getName()), + Variant::makeInt(pad->isGamepad() ? 1 : 0)}); + } + case SDL_JOYDEVICEREMOVED: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->getJoystickFromID(e.jdevice.which); + if (!pad) break; + int id = pad->getID(); + std::string name = pad->getName(); + joy->removeJoystick(pad); + return new Message("joystickremoved", + {Variant::makeInt(id), Variant::makeString(name)}); + } + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->getJoystickFromID(e.jbutton.which); + if (!pad) break; + return new Message(e.type == SDL_JOYBUTTONDOWN ? "joystickpressed" : "joystickreleased", + {Variant::makeInt(pad->getID()), Variant::makeInt(e.jbutton.button)}); + } + case SDL_JOYAXISMOTION: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->getJoystickFromID(e.jaxis.which); + if (!pad) break; + float value = eve::joystick::clampAxis(e.jaxis.value / 32768.0f); + return new Message("joystickaxis", + {Variant::makeInt(pad->getID()), Variant::makeInt(e.jaxis.axis), + Variant::makeInt(static_cast(std::lround(value * 1000.0f)))}); + } + case SDL_JOYHATMOTION: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->getJoystickFromID(e.jhat.which); + if (!pad) break; + std::string hat = pad->getHat(e.jhat.hat); + // Prefer live hat from the event mask via re-query; getHat reads current state. + return new Message("joystickhat", + {Variant::makeInt(pad->getID()), Variant::makeInt(e.jhat.hat), + Variant::makeString(hat)}); + } + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->getJoystickFromID(e.cbutton.which); + if (!pad) break; + const char *btn = + SDL_GameControllerGetStringForButton(static_cast(e.cbutton.button)); + return new Message(e.type == SDL_CONTROLLERBUTTONDOWN ? "gamepadpressed" : "gamepadreleased", + {Variant::makeInt(pad->getID()), + Variant::makeString(btn ? btn : "")}); + } + case SDL_CONTROLLERAXISMOTION: { + auto *joy = getModInst(joystick, Joystick); + if (!joy) break; + auto *pad = joy->getJoystickFromID(e.caxis.which); + if (!pad) break; + const char *axis = + SDL_GameControllerGetStringForAxis(static_cast(e.caxis.axis)); + float value = eve::joystick::clampAxis(e.caxis.value / 32768.0f); + return new Message("gamepadaxis", + {Variant::makeInt(pad->getID()), + Variant::makeString(axis ? axis : ""), + Variant::makeInt(static_cast(std::lround(value * 1000.0f)))}); + } case SDL_FINGERDOWN: case SDL_FINGERUP: case SDL_FINGERMOTION: { diff --git a/src/modules/joystick/Joystick.cpp b/src/modules/joystick/Joystick.cpp index b56ba31a..e324de57 100644 --- a/src/modules/joystick/Joystick.cpp +++ b/src/modules/joystick/Joystick.cpp @@ -1,143 +1,34 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ +#include "joystick/Joystick.h" +#include "joystick/sdl/Joystick.h" +#include "joystick/Pad.h" -// LOVE -#include "Joystick.h" - -// STL #include +#include -namespace eve -{ -namespace joystick -{ +namespace eve::joystick { -love::Type Joystick::type("Joystick", &Object::type); +Module_IMPL(Joystick, new sdl::Joystick()); -float Joystick::clampval(float x) -{ - if (fabsf(x) < 0.01) - return 0.0f; +Joystick::~Joystick() {} - if (x < -0.99f) return -1.0f; - if (x > 0.99f) return 1.0f; - - return x; +float clampAxis(float x) { + if (std::fabs(x) < 0.01f) return 0.0f; + if (x < -0.99f) return -1.0f; + if (x > 0.99f) return 1.0f; + return x; } -bool Joystick::getConstant(const char *in, Joystick::Hat &out) -{ - return hats.find(in, out); +void Joystick::expose(ssq::Table& table) { + auto cls = table.addClass(name, Joystick::create, false); + expose(cls); } -bool Joystick::getConstant(Joystick::Hat in, const char *&out) -{ - return hats.find(in, out); +void Joystick::expose(ssq::Class& cls) { + cls.addFunc("getName", &Joystick::getName); + cls.addFunc("getJoystickCount", &Joystick::getJoystickCount); + cls.addFunc("loadGamepadMappings", &Joystick::loadGamepadMappings); + cls.addFunc("saveGamepadMappings", &Joystick::saveGamepadMappings); + cls.addFunc("getGamepadMappingString", &Joystick::getGamepadMappingString); } -bool Joystick::getConstant(const char *in, Joystick::GamepadAxis &out) -{ - return gpAxes.find(in, out); -} - -bool Joystick::getConstant(Joystick::GamepadAxis in, const char *&out) -{ - return gpAxes.find(in, out); -} - -bool Joystick::getConstant(const char *in, Joystick::GamepadButton &out) -{ - return gpButtons.find(in, out); -} - -bool Joystick::getConstant(Joystick::GamepadButton in, const char *&out) -{ - return gpButtons.find(in, out); -} - -bool Joystick::getConstant(const char *in, Joystick::InputType &out) -{ - return inputTypes.find(in, out); -} - -bool Joystick::getConstant(Joystick::InputType in, const char *&out) -{ - return inputTypes.find(in, out); -} - -StringMap::Entry Joystick::hatEntries[] = -{ - {"c", Joystick::HAT_CENTERED}, - {"u", Joystick::HAT_UP}, - {"r", Joystick::HAT_RIGHT}, - {"d", Joystick::HAT_DOWN}, - {"l", Joystick::HAT_LEFT}, - {"ru", Joystick::HAT_RIGHTUP}, - {"rd", Joystick::HAT_RIGHTDOWN}, - {"lu", Joystick::HAT_LEFTUP}, - {"ld", Joystick::HAT_LEFTDOWN}, -}; - -StringMap Joystick::hats(Joystick::hatEntries, sizeof(Joystick::hatEntries)); - -StringMap::Entry Joystick::gpAxisEntries[] = -{ - {"leftx", GAMEPAD_AXIS_LEFTX}, - {"lefty", GAMEPAD_AXIS_LEFTY}, - {"rightx", GAMEPAD_AXIS_RIGHTX}, - {"righty", GAMEPAD_AXIS_RIGHTY}, - {"triggerleft", GAMEPAD_AXIS_TRIGGERLEFT}, - {"triggerright", GAMEPAD_AXIS_TRIGGERRIGHT}, -}; - -StringMap Joystick::gpAxes(Joystick::gpAxisEntries, sizeof(Joystick::gpAxisEntries)); - -StringMap::Entry Joystick::gpButtonEntries[] = -{ - {"a", GAMEPAD_BUTTON_A}, - {"b", GAMEPAD_BUTTON_B}, - {"x", GAMEPAD_BUTTON_X}, - {"y", GAMEPAD_BUTTON_Y}, - {"back", GAMEPAD_BUTTON_BACK}, - {"guide", GAMEPAD_BUTTON_GUIDE}, - {"start", GAMEPAD_BUTTON_START}, - {"leftstick", GAMEPAD_BUTTON_LEFTSTICK}, - {"rightstick", GAMEPAD_BUTTON_RIGHTSTICK}, - {"leftshoulder", GAMEPAD_BUTTON_LEFTSHOULDER}, - {"rightshoulder", GAMEPAD_BUTTON_RIGHTSHOULDER}, - {"dpup", GAMEPAD_BUTTON_DPAD_UP}, - {"dpdown", GAMEPAD_BUTTON_DPAD_DOWN}, - {"dpleft", GAMEPAD_BUTTON_DPAD_LEFT}, - {"dpright", GAMEPAD_BUTTON_DPAD_RIGHT}, -}; - -StringMap Joystick::gpButtons(Joystick::gpButtonEntries, sizeof(Joystick::gpButtonEntries)); - -StringMap::Entry Joystick::inputTypeEntries[] = -{ - {"axis", Joystick::INPUT_TYPE_AXIS}, - {"button", Joystick::INPUT_TYPE_BUTTON}, - {"hat", Joystick::INPUT_TYPE_HAT}, -}; - -StringMap Joystick::inputTypes(Joystick::inputTypeEntries, sizeof(Joystick::inputTypeEntries)); - -} // joystick -} // eve +} // namespace eve::joystick diff --git a/src/modules/joystick/Joystick.h b/src/modules/joystick/Joystick.h index 3c095581..eba41ce2 100644 --- a/src/modules/joystick/Joystick.h +++ b/src/modules/joystick/Joystick.h @@ -1,205 +1,34 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ +#pragma once -#ifndef LOVE_JOYSTICK_JOYSTICK_H -#define LOVE_JOYSTICK_JOYSTICK_H +#include "common/Module.h" +#include "joystick/Pad.h" -// LOVE -#include "common/Object.h" -#include "common/StringMap.h" - -// stdlib -#include #include -namespace eve -{ -namespace joystick -{ +namespace eve::joystick { -class Joystick -{ +/** + * Joystick / gamepad manager. Tracks connected pads and gamecontroller mappings. + */ +class Joystick : public Module { public: + Module_REG(Joystick); - static love::Type type; + virtual ~Joystick(); - // Joystick hat values. - enum Hat - { - HAT_INVALID, - HAT_CENTERED, - HAT_UP, - HAT_RIGHT, - HAT_DOWN, - HAT_LEFT, - HAT_RIGHTUP, - HAT_RIGHTDOWN, - HAT_LEFTUP, - HAT_LEFTDOWN, - HAT_MAX_ENUM = 16 - }; - - // Valid Gamepad axes. - enum GamepadAxis - { - GAMEPAD_AXIS_INVALID, - GAMEPAD_AXIS_LEFTX, - GAMEPAD_AXIS_LEFTY, - GAMEPAD_AXIS_RIGHTX, - GAMEPAD_AXIS_RIGHTY, - GAMEPAD_AXIS_TRIGGERLEFT, - GAMEPAD_AXIS_TRIGGERRIGHT, - GAMEPAD_AXIS_MAX_ENUM - }; - - // Valid Gamepad buttons. - enum GamepadButton - { - GAMEPAD_BUTTON_INVALID, - GAMEPAD_BUTTON_A, - GAMEPAD_BUTTON_B, - GAMEPAD_BUTTON_X, - GAMEPAD_BUTTON_Y, - GAMEPAD_BUTTON_BACK, - GAMEPAD_BUTTON_GUIDE, - GAMEPAD_BUTTON_START, - GAMEPAD_BUTTON_LEFTSTICK, - GAMEPAD_BUTTON_RIGHTSTICK, - GAMEPAD_BUTTON_LEFTSHOULDER, - GAMEPAD_BUTTON_RIGHTSHOULDER, - GAMEPAD_BUTTON_DPAD_UP, - GAMEPAD_BUTTON_DPAD_DOWN, - GAMEPAD_BUTTON_DPAD_LEFT, - GAMEPAD_BUTTON_DPAD_RIGHT, - GAMEPAD_BUTTON_MAX_ENUM - }; - - // Different types of inputs for a joystick. - enum InputType - { - INPUT_TYPE_AXIS, - INPUT_TYPE_BUTTON, - INPUT_TYPE_HAT, - INPUT_TYPE_MAX_ENUM - }; - - // Represents a gamepad input value, e.g. the "x" button or the left trigger. - struct GamepadInput - { - InputType type; - union - { - GamepadAxis axis; - GamepadButton button; - }; - }; - - // Represents a joystick input value, e.g. button 6 or axis 1. - struct JoystickInput - { - InputType type; - union - { - int axis; - int button; - struct - { - int index; - Hat value; - } hat; - }; - }; - - virtual ~Joystick() {} - - virtual bool open(int deviceindex) = 0; - virtual void close() = 0; - - virtual bool isConnected() const = 0; - - virtual const char *getName() const = 0; - - virtual int getAxisCount() const = 0; - virtual int getButtonCount() const = 0; - virtual int getHatCount() const = 0; - - virtual float getAxis(int axisindex) const = 0; - virtual std::vector getAxes() const = 0; - virtual Hat getHat(int hatindex) const = 0; - - virtual bool isDown(const std::vector &buttonlist) const = 0; - - virtual bool openGamepad(int deviceindex) = 0; - virtual bool isGamepad() const = 0; - - virtual float getGamepadAxis(GamepadAxis axis) const = 0; - virtual bool isGamepadDown(const std::vector &blist) const = 0; - - virtual JoystickInput getGamepadMapping(const GamepadInput &input) const = 0; - virtual std::string getGamepadMappingString() const = 0; - - virtual void *getHandle() const = 0; - - virtual std::string getGUID() const = 0; - virtual int getInstanceID() const = 0; - virtual int getID() const = 0; - - virtual void getDeviceInfo(int &vendorID, int &productID, int &productVersion) const = 0; - - virtual bool isVibrationSupported() = 0; - virtual bool setVibration(float left, float right, float duration = -1.0f) = 0; - virtual bool setVibration() = 0; - virtual void getVibration(float &left, float &right) = 0; - - static bool getConstant(const char *in, Hat &out); - static bool getConstant(Hat in, const char *&out); - - static bool getConstant(const char *in, GamepadAxis &out); - static bool getConstant(GamepadAxis in, const char *&out); - - static bool getConstant(const char *in, GamepadButton &out); - static bool getConstant(GamepadButton in, const char *&out); - - static bool getConstant(const char *in, InputType &out); - static bool getConstant(InputType in, const char *&out); - - static float clampval(float x); - -private: - - static StringMap::Entry hatEntries[]; - static StringMap hats; - - static StringMap::Entry gpAxisEntries[]; - static StringMap gpAxes; - - static StringMap::Entry gpButtonEntries[]; - static StringMap gpButtons; - - static StringMap::Entry inputTypeEntries[]; - static StringMap inputTypes; - -}; // Joystick - -} // joystick -} // eve + /** Open a device by SDL device index; returns null on failure. */ + virtual Pad* addJoystick(int deviceindex) = 0; + virtual void removeJoystick(Pad* pad) = 0; + virtual Pad* getJoystickFromID(int instanceid) = 0; + virtual Pad* getJoystick(int joyindex) = 0; + virtual int getIndex(const Pad* pad) = 0; + virtual int getJoystickCount() const = 0; + /** Load SDL GameController mapping database (newline-separated). */ + virtual void loadGamepadMappings(const std::string& mappings) = 0; + virtual std::string saveGamepadMappings() = 0; + virtual std::string getGamepadMappingString(const std::string& guid) const = 0; +}; +} // namespace eve::joystick diff --git a/src/modules/joystick/JoystickModule.h b/src/modules/joystick/JoystickModule.h deleted file mode 100644 index 0820bbb1..00000000 --- a/src/modules/joystick/JoystickModule.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "Joystick.h" -#include "common/Module.h" - -namespace eve { -namespace joystick { - -class JoystickModule : public Module { -public: - virtual ~JoystickModule() {} - - // Implements Module. - ModuleType getModuleType() const override { return M_JOYSTICK; } - - /** - * Adds a connected Joystick device and opens it for use. - * Returns NULL if the Joystick could not be added. - **/ - virtual Joystick *addJoystick(int deviceindex) = 0; - - /** - * Removes a disconnected Joystick device. - **/ - virtual void removeJoystick(Joystick *joystick) = 0; - - /** - * Gets a connected Joystick from its unique Instance ID. - * Returns NULL if the instance ID does not correspond to a connected stick. - **/ - virtual Joystick *getJoystickFromID(int instanceid) = 0; - - /** - * Gets a connected Joystick. - * Returns NULL if joyindex is not in the range of [0, getJoystickCount()). - **/ - virtual Joystick *getJoystick(int joyindex) = 0; - - /** - * Gets the index of a connected joystick. - * Returns -1 if the joystick is not connected. - **/ - virtual int getIndex(const Joystick *joystick) = 0; - - /** - * Gets the number of currently connected Joysticks. - **/ - virtual int getJoystickCount() const = 0; - - /** - * Sets the virtual Gamepad mapping for a joystick input value for all - * joystick devices with the specified joystick product GUID. - * If any joysticks with the specified GUID are connected, they will be - * added as Gamepads if they aren't already, otherwise their Gamepad mapping - * will be updated. - **/ - virtual bool setGamepadMapping(const std::string &pguid, Joystick::GamepadInput gpinput, - Joystick::JoystickInput joyinput) = 0; - - /** - * Loads a newline-separated list of virtual Gamepad mapping strings for - * multiple joysticks at a time. The mapping strings must have been - * generated with saveGamepadMappings, via Steam, or some other tool which - * generates SDL GameController mappings. - **/ - virtual void loadGamepadMappings(const std::string &mappings) = 0; - - /** - * Gets a newline-separated list of virtual Gamepad mapping strings for - * all used or modified Joysticks which are identified as Gamepads. - **/ - virtual std::string saveGamepadMappings() = 0; - - /** - * Gets the gamepad mapping string for the given GUID. - **/ - virtual std::string getGamepadMappingString(const std::string &guid) const = 0; - -}; // JoystickModule - -} // namespace joystick -} // namespace eve diff --git a/src/modules/joystick/Pad.h b/src/modules/joystick/Pad.h new file mode 100644 index 00000000..2d868a83 --- /dev/null +++ b/src/modules/joystick/Pad.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +namespace eve::joystick { + +/** + * One connected (or previously connected) joystick / gamepad device. + * Gamepad axis/button names follow SDL GameController strings + * (e.g. "leftx", "a", "dpup"). Hat directions: c/u/d/l/r/lu/ld/ru/rd. + */ +class Pad { +public: + virtual ~Pad() = default; + + virtual bool open(int deviceindex) = 0; + virtual void close() = 0; + virtual bool isConnected() const = 0; + + virtual std::string getName() const = 0; + + virtual int getAxisCount() const = 0; + virtual int getButtonCount() const = 0; + virtual int getHatCount() const = 0; + + virtual float getAxis(int axisindex) const = 0; + virtual std::vector getAxes() const = 0; + virtual std::string getHat(int hatindex) const = 0; + + virtual bool isDown(int button) const = 0; + virtual bool isDown(const std::vector& buttons) const = 0; + + virtual bool openGamepad(int deviceindex) = 0; + virtual bool isGamepad() const = 0; + + virtual float getGamepadAxis(const std::string& axis) const = 0; + virtual bool isGamepadDown(const std::string& button) const = 0; + virtual bool isGamepadDown(const std::vector& buttons) const = 0; + + virtual std::string getGamepadMappingString() const = 0; + + virtual void* getHandle() const = 0; + virtual std::string getGUID() const = 0; + virtual int getInstanceID() const = 0; + virtual int getID() const = 0; + + virtual void getDeviceInfo(int& vendorID, int& productID, int& productVersion) const = 0; + + virtual bool isVibrationSupported() = 0; + virtual bool setVibration(float left, float right, float duration = -1.0f) = 0; + virtual bool setVibration() = 0; + virtual void getVibration(float& left, float& right) = 0; +}; + +float clampAxis(float x); + +} // namespace eve::joystick diff --git a/src/modules/joystick/sdl/Joystick.cpp b/src/modules/joystick/sdl/Joystick.cpp index 53acdaee..b67f8f94 100644 --- a/src/modules/joystick/sdl/Joystick.cpp +++ b/src/modules/joystick/sdl/Joystick.cpp @@ -1,645 +1,200 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ +#include "joystick/sdl/Joystick.h" -// LOVE -#include "common/config.h" -#include "Joystick.h" -#include "common/int.h" +#include "common/Exception.h" + +#include -// C++ #include -#include +#include +#include -#ifndef SDL_TICKS_PASSED -#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0) -#endif +namespace eve::joystick::sdl { -namespace eve -{ -namespace joystick -{ -namespace sdl -{ +Joystick::Joystick() { + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) + throw eve::Exception("Could not initialize SDL joystick subsystem (%s)", SDL_GetError()); -Joystick::Joystick(int id) - : joyhandle(nullptr) - , controller(nullptr) - , haptic(nullptr) - , instanceid(-1) - , id(id) - , vibration() -{ + for (int i = 0; i < SDL_NumJoysticks(); i++) addJoystick(i); + + SDL_JoystickEventState(SDL_ENABLE); + SDL_GameControllerEventState(SDL_ENABLE); } -Joystick::Joystick(int id, int joyindex) - : joyhandle(nullptr) - , controller(nullptr) - , haptic(nullptr) - , instanceid(-1) - , id(id) - , vibration() -{ - open(joyindex); +Joystick::~Joystick() { + for (auto* stick : joysticks_) { + stick->close(); + delete stick; + } + joysticks_.clear(); + activeSticks_.clear(); + + if (SDL_WasInit(SDL_INIT_HAPTIC) != 0) SDL_QuitSubSystem(SDL_INIT_HAPTIC); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); } -Joystick::~Joystick() -{ - close(); +Pad* Joystick::getJoystick(int joyindex) { + if (joyindex < 0 || static_cast(joyindex) >= activeSticks_.size()) return nullptr; + return activeSticks_[static_cast(joyindex)]; } -bool Joystick::open(int deviceindex) -{ - close(); - - joyhandle = SDL_JoystickOpen(deviceindex); - - if (joyhandle) - { - instanceid = SDL_JoystickInstanceID(joyhandle); - - // SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator. - char cstr[33]; - - SDL_JoystickGUID sdlguid = SDL_JoystickGetGUID(joyhandle); - SDL_JoystickGetGUIDString(sdlguid, cstr, (int) sizeof(cstr)); - - pguid = std::string(cstr); - - // See if SDL thinks this is a Game Controller. - openGamepad(deviceindex); - - // Prefer the Joystick name for consistency. - const char *joyname = SDL_JoystickName(joyhandle); - if (!joyname && controller) - joyname = SDL_GameControllerName(controller); - - if (joyname) - name = joyname; - } - - return isConnected(); +int Joystick::getIndex(const eve::joystick::Pad* pad) { + for (int i = 0; i < static_cast(activeSticks_.size()); i++) { + if (activeSticks_[static_cast(i)] == pad) return i; + } + return -1; } -void Joystick::close() -{ - if (haptic) - SDL_HapticClose(haptic); - - if (controller) - SDL_GameControllerClose(controller); - - if (joyhandle) - SDL_JoystickClose(joyhandle); - - joyhandle = nullptr; - controller = nullptr; - haptic = nullptr; - instanceid = -1; - vibration = Vibration(); +int Joystick::getJoystickCount() const { + return static_cast(activeSticks_.size()); } -bool Joystick::isConnected() const -{ - return joyhandle != nullptr && SDL_JoystickGetAttached(joyhandle); +Pad* Joystick::getJoystickFromID(int instanceid) { + for (auto* stick : activeSticks_) { + if (stick->getInstanceID() == instanceid) return stick; + } + return nullptr; } -const char *Joystick::getName() const -{ - return name.c_str(); +Pad* Joystick::addJoystick(int deviceindex) { + if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) return nullptr; + + std::string guidstr = getDeviceGUID(deviceindex); + Pad* pad = nullptr; + bool reused = false; + + for (auto* stick : joysticks_) { + if (!stick->isConnected() && stick->getGUID() == guidstr) { + pad = stick; + reused = true; + break; + } + } + + if (!pad) { + pad = new Pad(static_cast(joysticks_.size())); + joysticks_.push_back(pad); + } + + removeJoystick(pad); + + if (!pad->open(deviceindex)) return nullptr; + + for (auto* active : activeSticks_) { + if (pad->getHandle() == active->getHandle()) { + pad->close(); + if (!reused) { + joysticks_.remove(pad); + delete pad; + } + return active; + } + } + + if (pad->isGamepad()) recentGamepadGUIDs_[pad->getGUID()] = true; + + activeSticks_.push_back(pad); + return pad; } -int Joystick::getAxisCount() const -{ - return isConnected() ? SDL_JoystickNumAxes(joyhandle) : 0; +void Joystick::removeJoystick(eve::joystick::Pad* pad) { + if (!pad) return; + auto it = std::find(activeSticks_.begin(), activeSticks_.end(), pad); + if (it != activeSticks_.end()) { + (*it)->close(); + activeSticks_.erase(it); + } } -int Joystick::getButtonCount() const -{ - return isConnected() ? SDL_JoystickNumButtons(joyhandle) : 0; +void Joystick::checkGamepads(const std::string& guid) const { + for (int d_index = 0; d_index < SDL_NumJoysticks(); d_index++) { + if (!SDL_IsGameController(d_index)) continue; + if (guid.compare(getDeviceGUID(d_index)) != 0) continue; + + for (auto* stick : activeSticks_) { + if (guid.compare(stick->getGUID()) != 0) continue; + + SDL_GameController* controller = SDL_GameControllerOpen(d_index); + if (!controller) continue; + + SDL_Joystick* sdlstick = SDL_GameControllerGetJoystick(controller); + bool open_gamepad = (sdlstick == static_cast(stick->getHandle())); + SDL_GameControllerClose(controller); + + if (open_gamepad) stick->openGamepad(d_index); + } + } } -int Joystick::getHatCount() const -{ - return isConnected() ? SDL_JoystickNumHats(joyhandle) : 0; +std::string Joystick::getDeviceGUID(int deviceindex) const { + if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) return ""; + + char guidstr[33] = {'\0'}; + SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(deviceindex); + SDL_JoystickGetGUIDString(guid, guidstr, sizeof(guidstr)); + return guidstr; } -float Joystick::getAxis(int axisindex) const -{ - if (!isConnected() || axisindex < 0 || axisindex >= getAxisCount()) - return 0; +void Joystick::loadGamepadMappings(const std::string& mappings) { + std::stringstream ss(mappings); + std::string mapping; + bool success = false; - return clampval(((float) SDL_JoystickGetAxis(joyhandle, axisindex))/32768.0f); + while (std::getline(ss, mapping)) { + if (mapping.empty()) continue; + if (mapping[0] == '#') continue; + + size_t pstartpos = mapping.find("platform:"); + if (pstartpos != std::string::npos) { + pstartpos += strlen("platform:"); + size_t pendpos = mapping.find_first_of(',', pstartpos); + std::string platform = mapping.substr(pstartpos, pendpos - pstartpos); + if (platform.compare(SDL_GetPlatform()) != 0) { + success = true; + continue; + } + pstartpos -= strlen("platform:"); + mapping.erase(pstartpos, pendpos - pstartpos + 1); + } + + if (SDL_GameControllerAddMapping(mapping.c_str()) != -1) { + success = true; + std::string guid = mapping.substr(0, mapping.find_first_of(',')); + recentGamepadGUIDs_[guid] = true; + checkGamepads(guid); + } + } + + if (!success && !mappings.empty()) throw eve::Exception("Invalid gamepad mappings."); } -std::vector Joystick::getAxes() const -{ - std::vector axes; - int count = getAxisCount(); +std::string Joystick::getGamepadMappingString(const std::string& guid) const { + SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str()); + char* sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); + if (!sdlmapping) return ""; - if (!isConnected() || count <= 0) - return axes; + std::string mapping(sdlmapping); + SDL_free(sdlmapping); - axes.reserve(count); - - for (int i = 0; i < count; i++) - axes.push_back(clampval(((float) SDL_JoystickGetAxis(joyhandle, i))/32768.0f)); - - return axes; + if (mapping.find_last_of(',') != mapping.length() - 1) mapping += ","; + mapping += "platform:" + std::string(SDL_GetPlatform()); + return mapping; } -Joystick::Hat Joystick::getHat(int hatindex) const -{ - Hat h = HAT_INVALID; +std::string Joystick::saveGamepadMappings() { + std::string mappings; + for (const auto& g : recentGamepadGUIDs_) { + SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(g.first.c_str()); + char* sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); + if (!sdlmapping) continue; - if (!isConnected() || hatindex < 0 || hatindex >= getHatCount()) - return h; + std::string mapping = sdlmapping; + SDL_free(sdlmapping); - getConstant(SDL_JoystickGetHat(joyhandle, hatindex), h); - - return h; + if (mapping.find_last_of(',') != mapping.length() - 1) mapping += ","; + mapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n"; + mappings += mapping; + } + return mappings; } -bool Joystick::isDown(const std::vector &buttonlist) const -{ - if (!isConnected()) - return false; - - int numbuttons = getButtonCount(); - - for (int button : buttonlist) - { - if (button < 0 || button >= numbuttons) - continue; - - if (SDL_JoystickGetButton(joyhandle, button) == 1) - return true; - } - - return false; -} - -bool Joystick::openGamepad(int deviceindex) -{ - if (!SDL_IsGameController(deviceindex)) - return false; - - if (isGamepad()) - { - SDL_GameControllerClose(controller); - controller = nullptr; - } - - controller = SDL_GameControllerOpen(deviceindex); - return isGamepad(); -} - -bool Joystick::isGamepad() const -{ - return controller != nullptr; -} - -float Joystick::getGamepadAxis(love::joystick::Joystick::GamepadAxis axis) const -{ - if (!isConnected() || !isGamepad()) - return 0.f; - - SDL_GameControllerAxis sdlaxis; - if (!getConstant(axis, sdlaxis)) - return 0.f; - - Sint16 value = SDL_GameControllerGetAxis(controller, sdlaxis); - - return clampval((float) value / 32768.0f); -} - -bool Joystick::isGamepadDown(const std::vector &blist) const -{ - if (!isConnected() || !isGamepad()) - return false; - - SDL_GameControllerButton sdlbutton; - - for (GamepadButton button : blist) - { - if (!getConstant(button, sdlbutton)) - continue; - - if (SDL_GameControllerGetButton(controller, sdlbutton) == 1) - return true; - } - - return false; -} - -Joystick::JoystickInput Joystick::getGamepadMapping(const GamepadInput &input) const -{ - Joystick::JoystickInput jinput; - jinput.type = INPUT_TYPE_MAX_ENUM; - - if (!isGamepad()) - return jinput; - - SDL_GameControllerButtonBind sdlbind = {}; - sdlbind.bindType = SDL_CONTROLLER_BINDTYPE_NONE; - - SDL_GameControllerButton sdlbutton; - SDL_GameControllerAxis sdlaxis; - - switch (input.type) - { - case INPUT_TYPE_BUTTON: - if (getConstant(input.button, sdlbutton)) - sdlbind = SDL_GameControllerGetBindForButton(controller, sdlbutton); - break; - case INPUT_TYPE_AXIS: - if (getConstant(input.axis, sdlaxis)) - sdlbind = SDL_GameControllerGetBindForAxis(controller, sdlaxis); - break; - default: - break; - } - - switch (sdlbind.bindType) - { - case SDL_CONTROLLER_BINDTYPE_BUTTON: - jinput.type = INPUT_TYPE_BUTTON; - jinput.button = sdlbind.value.button; - break; - case SDL_CONTROLLER_BINDTYPE_AXIS: - jinput.type = INPUT_TYPE_AXIS; - jinput.axis = sdlbind.value.axis; - break; - case SDL_CONTROLLER_BINDTYPE_HAT: - if (getConstant(sdlbind.value.hat.hat_mask, jinput.hat.value)) - { - jinput.type = INPUT_TYPE_HAT; - jinput.hat.index = sdlbind.value.hat.hat; - } - break; - case SDL_CONTROLLER_BINDTYPE_NONE: - default: - break; - } - - return jinput; -} - -std::string Joystick::getGamepadMappingString() const -{ - char *sdlmapping = nullptr; - - if (controller != nullptr) - sdlmapping = SDL_GameControllerMapping(controller); - - if (sdlmapping == nullptr) - { - SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(pguid.c_str()); - sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); - } - - if (sdlmapping == nullptr) - return ""; - - std::string mappingstr(sdlmapping); - SDL_free(sdlmapping); - - // Matches SDL_GameControllerAddMappingsFromRW. - if (mappingstr.find_last_of(',') != mappingstr.length() - 1) - mappingstr += ","; - mappingstr += "platform:" + std::string(SDL_GetPlatform()); - - return mappingstr; -} - -void *Joystick::getHandle() const -{ - return joyhandle; -} - -std::string Joystick::getGUID() const -{ - // SDL2's GUIDs identify *classes* of devices, instead of unique devices. - return pguid; -} - -int Joystick::getInstanceID() const -{ - return instanceid; -} - -int Joystick::getID() const -{ - return id; -} - -void Joystick::getDeviceInfo(int &vendorID, int &productID, int &productVersion) const -{ -#if SDL_VERSION_ATLEAST(2, 0, 6) - if (joyhandle != nullptr) - { - vendorID = SDL_JoystickGetVendor(joyhandle); - productID = SDL_JoystickGetProduct(joyhandle); - productVersion = SDL_JoystickGetProductVersion(joyhandle); - } - else -#endif - { - vendorID = 0; - productID = 0; - productVersion = 0; - } -} - -bool Joystick::checkCreateHaptic() -{ - if (!isConnected()) - return false; - - if (!SDL_WasInit(SDL_INIT_HAPTIC) && SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) - return false; - - if (haptic && SDL_HapticIndex(haptic) != -1) - return true; - - if (haptic) - { - SDL_HapticClose(haptic); - haptic = nullptr; - } - - haptic = SDL_HapticOpenFromJoystick(joyhandle); - vibration = Vibration(); - - return haptic != nullptr; -} - -bool Joystick::isVibrationSupported() -{ - if (!checkCreateHaptic()) - return false; - - unsigned int features = SDL_HapticQuery(haptic); - - if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) - return true; - - // Some gamepad drivers only support left/right motors via a custom effect. - if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0) - return true; - - // Test for simple sine wave support as a last resort. - if ((features & SDL_HAPTIC_SINE) != 0) - return true; - - return false; -} - -bool Joystick::runVibrationEffect() -{ - if (vibration.id != -1) - { - if (SDL_HapticUpdateEffect(haptic, vibration.id, &vibration.effect) == 0) - { - if (SDL_HapticRunEffect(haptic, vibration.id, 1) == 0) - return true; - } - - // If the effect fails to update, we should destroy and re-create it. - SDL_HapticDestroyEffect(haptic, vibration.id); - vibration.id = -1; - } - - vibration.id = SDL_HapticNewEffect(haptic, &vibration.effect); - - if (vibration.id != -1 && SDL_HapticRunEffect(haptic, vibration.id, 1) == 0) - return true; - - return false; -} - -bool Joystick::setVibration(float left, float right, float duration) -{ - left = std::min(std::max(left, 0.0f), 1.0f); - right = std::min(std::max(right, 0.0f), 1.0f); - - if (left == 0.0f && right == 0.0f) - return setVibration(); - - if (!checkCreateHaptic()) - return false; - - Uint32 length = SDL_HAPTIC_INFINITY; - if (duration >= 0.0f) - { - float maxduration = std::numeric_limits::max() / 1000.0f; - length = Uint32(std::min(duration, maxduration) * 1000); - } - - bool success = false; - unsigned int features = SDL_HapticQuery(haptic); - int axes = SDL_HapticNumAxes(haptic); - - if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) - { - memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); - vibration.effect.type = SDL_HAPTIC_LEFTRIGHT; - - vibration.effect.leftright.length = length; - vibration.effect.leftright.large_magnitude = Uint16(left * LOVE_UINT16_MAX); - vibration.effect.leftright.small_magnitude = Uint16(right * LOVE_UINT16_MAX); - - success = runVibrationEffect(); - } - - // Some gamepad drivers only give support for controlling individual motors - // through a custom FF effect. - if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2) - { - // NOTE: this may cause issues with drivers which support custom effects - // but aren't similar to https://github.com/d235j/360Controller . - - // Custom effect data is clamped to 0x7FFF in SDL. - vibration.data[0] = vibration.data[2] = Uint16(left * 0x7FFF); - vibration.data[1] = vibration.data[3] = Uint16(right * 0x7FFF); - - memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); - vibration.effect.type = SDL_HAPTIC_CUSTOM; - - vibration.effect.custom.length = length; - vibration.effect.custom.channels = 2; - vibration.effect.custom.period = 10; - vibration.effect.custom.samples = 2; - vibration.effect.custom.data = vibration.data; - - success = runVibrationEffect(); - } - - // Fall back to a simple sine wave if all else fails. This only supports a - // single strength value. - if (!success && (features & SDL_HAPTIC_SINE) != 0) - { - memset(&vibration.effect, 0, sizeof(SDL_HapticEffect)); - vibration.effect.type = SDL_HAPTIC_SINE; - - vibration.effect.periodic.length = length; - vibration.effect.periodic.period = 10; - - float strength = std::max(left, right); - vibration.effect.periodic.magnitude = Sint16(strength * 0x7FFF); - - success = runVibrationEffect(); - } - - if (success) - { - vibration.left = left; - vibration.right = right; - - if (length == SDL_HAPTIC_INFINITY) - vibration.endtime = SDL_HAPTIC_INFINITY; - else - vibration.endtime = SDL_GetTicks() + length; - } - else - { - vibration.left = vibration.right = 0.0f; - vibration.endtime = SDL_HAPTIC_INFINITY; - } - - return success; -} - -bool Joystick::setVibration() -{ - bool success = true; - - if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic && SDL_HapticIndex(haptic) != -1) - success = (SDL_HapticStopEffect(haptic, vibration.id) == 0); - - if (success) - vibration.left = vibration.right = 0.0f; - - return success; -} - -void Joystick::getVibration(float &left, float &right) -{ - if (vibration.endtime != SDL_HAPTIC_INFINITY) - { - // With some drivers, the effect physically stops at the right time, but - // SDL_HapticGetEffectStatus still thinks it's playing. So we explicitly - // stop it once it's done, just to be sure. - if (SDL_TICKS_PASSED(SDL_GetTicks(), vibration.endtime)) - { - setVibration(); - vibration.endtime = SDL_HAPTIC_INFINITY; - } - } - - // Check if the haptic effect has stopped playing. - int id = vibration.id; - if (!haptic || id == -1 || SDL_HapticGetEffectStatus(haptic, id) != 1) - vibration.left = vibration.right = 0.0f; - - left = vibration.left; - right = vibration.right; -} - -bool Joystick::getConstant(Uint8 in, Joystick::Hat &out) -{ - return hats.find(in, out); -} - -bool Joystick::getConstant(Joystick::Hat in, Uint8 &out) -{ - return hats.find(in, out); -} - -bool Joystick::getConstant(SDL_GameControllerAxis in, Joystick::GamepadAxis &out) -{ - return gpAxes.find(in, out); -} - -bool Joystick::getConstant(Joystick::GamepadAxis in, SDL_GameControllerAxis &out) -{ - return gpAxes.find(in, out); -} - -bool Joystick::getConstant(SDL_GameControllerButton in, Joystick::GamepadButton &out) -{ - return gpButtons.find(in, out); -} - -bool Joystick::getConstant(Joystick::GamepadButton in, SDL_GameControllerButton &out) -{ - return gpButtons.find(in, out); -} - -EnumMap::Entry Joystick::hatEntries[] = -{ - {Joystick::HAT_CENTERED, SDL_HAT_CENTERED}, - {Joystick::HAT_UP, SDL_HAT_UP}, - {Joystick::HAT_RIGHT, SDL_HAT_RIGHT}, - {Joystick::HAT_DOWN, SDL_HAT_DOWN}, - {Joystick::HAT_LEFT, SDL_HAT_LEFT}, - {Joystick::HAT_RIGHTUP, SDL_HAT_RIGHTUP}, - {Joystick::HAT_RIGHTDOWN, SDL_HAT_RIGHTDOWN}, - {Joystick::HAT_LEFTUP, SDL_HAT_LEFTUP}, - {Joystick::HAT_LEFTDOWN, SDL_HAT_LEFTDOWN}, -}; - -EnumMap Joystick::hats(Joystick::hatEntries, sizeof(Joystick::hatEntries)); - -EnumMap::Entry Joystick::gpAxisEntries[] = -{ - {Joystick::GAMEPAD_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTX}, - {Joystick::GAMEPAD_AXIS_LEFTY, SDL_CONTROLLER_AXIS_LEFTY}, - {Joystick::GAMEPAD_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTX}, - {Joystick::GAMEPAD_AXIS_RIGHTY, SDL_CONTROLLER_AXIS_RIGHTY}, - {Joystick::GAMEPAD_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERLEFT}, - {Joystick::GAMEPAD_AXIS_TRIGGERRIGHT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT}, -}; - -EnumMap Joystick::gpAxes(Joystick::gpAxisEntries, sizeof(Joystick::gpAxisEntries)); - -EnumMap::Entry Joystick::gpButtonEntries[] = -{ - {Joystick::GAMEPAD_BUTTON_A, SDL_CONTROLLER_BUTTON_A}, - {Joystick::GAMEPAD_BUTTON_B, SDL_CONTROLLER_BUTTON_B}, - {Joystick::GAMEPAD_BUTTON_X, SDL_CONTROLLER_BUTTON_X}, - {Joystick::GAMEPAD_BUTTON_Y, SDL_CONTROLLER_BUTTON_Y}, - {Joystick::GAMEPAD_BUTTON_BACK, SDL_CONTROLLER_BUTTON_BACK}, - {Joystick::GAMEPAD_BUTTON_GUIDE, SDL_CONTROLLER_BUTTON_GUIDE}, - {Joystick::GAMEPAD_BUTTON_START, SDL_CONTROLLER_BUTTON_START}, - {Joystick::GAMEPAD_BUTTON_LEFTSTICK, SDL_CONTROLLER_BUTTON_LEFTSTICK}, - {Joystick::GAMEPAD_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, - {Joystick::GAMEPAD_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, - {Joystick::GAMEPAD_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, - {Joystick::GAMEPAD_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_UP}, - {Joystick::GAMEPAD_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, - {Joystick::GAMEPAD_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, - {Joystick::GAMEPAD_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, -}; - -EnumMap Joystick::gpButtons(Joystick::gpButtonEntries, sizeof(Joystick::gpButtonEntries)); - -} // sdl -} // joystick -} // eve +} // namespace eve::joystick::sdl diff --git a/src/modules/joystick/sdl/Joystick.h b/src/modules/joystick/sdl/Joystick.h index bc6ad468..8eb2b538 100644 --- a/src/modules/joystick/sdl/Joystick.h +++ b/src/modules/joystick/sdl/Joystick.h @@ -1,144 +1,38 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ +#pragma once -#ifndef LOVE_JOYSTICK_SDL_JOYSTICK_H -#define LOVE_JOYSTICK_SDL_JOYSTICK_H - -// LOVE #include "joystick/Joystick.h" -#include "common/EnumMap.h" +#include "joystick/sdl/Pad.h" -// SDL -#include +#include +#include +#include +#include -namespace eve -{ -namespace joystick -{ -namespace sdl -{ +namespace eve::joystick::sdl { -class Joystick : public love::joystick::Joystick -{ +class Joystick : public eve::joystick::Joystick { public: + Joystick(); + ~Joystick() override; - Joystick(int id); - Joystick(int id, int joyindex); + Pad* addJoystick(int deviceindex) override; + void removeJoystick(eve::joystick::Pad* pad) override; + Pad* getJoystickFromID(int instanceid) override; + Pad* getJoystick(int joyindex) override; + int getIndex(const eve::joystick::Pad* pad) override; + int getJoystickCount() const override; - virtual ~Joystick(); - - bool open(int deviceindex) override; - void close() override; - - bool isConnected() const override; - - const char *getName() const override; - - int getAxisCount() const override; - int getButtonCount() const override; - int getHatCount() const override; - - float getAxis(int axisindex) const override; - std::vector getAxes() const override; - Hat getHat(int hatindex) const override; - - bool isDown(const std::vector &buttonlist) const override; - - bool openGamepad(int deviceindex) override; - bool isGamepad() const override; - - float getGamepadAxis(GamepadAxis axis) const override; - bool isGamepadDown(const std::vector &blist) const override; - - JoystickInput getGamepadMapping(const GamepadInput &input) const override; - std::string getGamepadMappingString() const override; - - void *getHandle() const override; - - std::string getGUID() const override; - int getInstanceID() const override; - int getID() const override; - - void getDeviceInfo(int &vendorID, int &productID, int &productVersion) const override; - - bool isVibrationSupported() override; - bool setVibration(float left, float right, float duration = -1.0f) override; - bool setVibration() override; - void getVibration(float &left, float &right) override; - - static bool getConstant(Hat in, Uint8 &out); - static bool getConstant(Uint8 in, Hat &out); - - static bool getConstant(SDL_GameControllerAxis in, GamepadAxis &out); - static bool getConstant(GamepadAxis in, SDL_GameControllerAxis &out); - - static bool getConstant(SDL_GameControllerButton in, GamepadButton &out); - static bool getConstant(GamepadButton in, SDL_GameControllerButton &out); + void loadGamepadMappings(const std::string& mappings) override; + std::string saveGamepadMappings() override; + std::string getGamepadMappingString(const std::string& guid) const override; private: + void checkGamepads(const std::string& guid) const; + std::string getDeviceGUID(int deviceindex) const; - Joystick() {} - - bool checkCreateHaptic(); - bool runVibrationEffect(); - - SDL_Joystick *joyhandle; - SDL_GameController *controller; - SDL_Haptic *haptic; - - SDL_JoystickID instanceid; - std::string pguid; - int id; - - std::string name; - - struct Vibration - { - float left = 0.0f; - float right = 0.0f; - SDL_HapticEffect effect; - Uint16 data[4]; - int id = -1; - Uint32 endtime = SDL_HAPTIC_INFINITY; - - // Old versions of VS2013 have trouble with initializing these in-line. - Vibration() - : effect() - , data() - {} - - } vibration; - - static EnumMap::Entry hatEntries[]; - static EnumMap hats; - - static EnumMap::Entry gpAxisEntries[]; - static EnumMap gpAxes; - - static EnumMap::Entry gpButtonEntries[]; - static EnumMap gpButtons; - + std::vector activeSticks_; + std::list joysticks_; + std::map recentGamepadGUIDs_; }; -} // sdl -} // joystick -} // eve - - +} // namespace eve::joystick::sdl diff --git a/src/modules/joystick/sdl/JoystickModule.cpp b/src/modules/joystick/sdl/JoystickModule.cpp deleted file mode 100644 index 8b3bd40a..00000000 --- a/src/modules/joystick/sdl/JoystickModule.cpp +++ /dev/null @@ -1,501 +0,0 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#include "common/config.h" -#include "JoystickModule.h" -#include "Joystick.h" - -// SDL -#include - -// C++ -#include -#include - -// C -#include - -namespace eve -{ -namespace joystick -{ -namespace sdl -{ - -JoystickModule::JoystickModule() -{ - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) - throw love::Exception("Could not initialize SDL joystick subsystem (%s)", SDL_GetError()); - - // Initialize any joysticks which are already connected. - for (int i = 0; i < SDL_NumJoysticks(); i++) - addJoystick(i); - - // Start joystick event watching. Joysticks are automatically added and - // removed via love.event. - SDL_JoystickEventState(SDL_ENABLE); - SDL_GameControllerEventState(SDL_ENABLE); -} - -JoystickModule::~JoystickModule() -{ - // Close any open Joysticks. - for (auto stick : joysticks) - { - stick->close(); - stick->release(); - } - - if (SDL_WasInit(SDL_INIT_HAPTIC) != 0) - SDL_QuitSubSystem(SDL_INIT_HAPTIC); - - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); -} - -const char *JoystickModule::getName() const -{ - return "love.joystick.sdl"; -} - -love::joystick::Joystick *JoystickModule::getJoystick(int joyindex) -{ - if (joyindex < 0 || (size_t) joyindex >= activeSticks.size()) - return nullptr; - - return activeSticks[joyindex]; -} - -int JoystickModule::getIndex(const love::joystick::Joystick *joystick) -{ - for (int i = 0; i < (int) activeSticks.size(); i++) - { - if (activeSticks[i] == joystick) - return i; - } - - // Joystick is not connected. - return -1; -} - -int JoystickModule::getJoystickCount() const -{ - return (int) activeSticks.size(); -} - -love::joystick::Joystick *JoystickModule::getJoystickFromID(int instanceid) -{ - for (auto stick : activeSticks) - { - if (stick->getInstanceID() == instanceid) - return stick; - } - - return nullptr; -} - -love::joystick::Joystick *JoystickModule::addJoystick(int deviceindex) -{ - if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) - return nullptr; - - std::string guidstr = getDeviceGUID(deviceindex); - joystick::Joystick *joystick = 0; - bool reused = false; - - for (auto stick : joysticks) - { - // Try to re-use a disconnected Joystick with the same GUID. - if (!stick->isConnected() && stick->getGUID() == guidstr) - { - joystick = stick; - reused = true; - break; - } - } - - if (!joystick) - { - joystick = new Joystick((int) joysticks.size()); - joysticks.push_back(joystick); - } - - // Make sure the Joystick object isn't in the active list already. - removeJoystick(joystick); - - if (!joystick->open(deviceindex)) - return nullptr; - - // Make sure multiple instances of the same physical joystick aren't added - // to the active list. - for (auto activestick : activeSticks) - { - if (joystick->getHandle() == activestick->getHandle()) - { - joystick->close(); - - // If we just created the stick, remove it since it's a duplicate. - if (!reused) - { - joysticks.remove(joystick); - joystick->release(); - } - - return activestick; - } - } - - if (joystick->isGamepad()) - recentGamepadGUIDs[joystick->getGUID()] = true; - - activeSticks.push_back(joystick); - return joystick; -} - -void JoystickModule::removeJoystick(love::joystick::Joystick *joystick) -{ - if (!joystick) - return; - - // Close the Joystick and remove it from the active joystick list. - auto it = std::find(activeSticks.begin(), activeSticks.end(), joystick); - if (it != activeSticks.end()) - { - (*it)->close(); - activeSticks.erase(it); - } -} - -bool JoystickModule::setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput) -{ - // All SDL joystick GUID strings are 32 characters. - if (guid.length() != 32) - throw love::Exception("Invalid joystick GUID: %s", guid.c_str()); - - SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str()); - std::string mapstr; - - char *sdlmapstr = SDL_GameControllerMappingForGUID(sdlguid); - if (sdlmapstr) - { - mapstr = sdlmapstr; - SDL_free(sdlmapstr); - } - else - { - std::string name = "Controller"; - - for (love::joystick::Joystick *stick : joysticks) - { - if (stick->getGUID() == guid) - { - name = stick->getName(); - break; - } - } - - mapstr = guid + "," + name + ","; - } - - std::stringstream joyinputstream; - Uint8 sdlhat; - - // We can't have negative int values in the bind string. - switch (joyinput.type) - { - case Joystick::INPUT_TYPE_AXIS: - if (joyinput.axis >= 0) - joyinputstream << "a" << joyinput.axis; - break; - case Joystick::INPUT_TYPE_BUTTON: - if (joyinput.button >= 0) - joyinputstream << "b" << joyinput.button; - break; - case Joystick::INPUT_TYPE_HAT: - if (joyinput.hat.index >= 0 && Joystick::getConstant(joyinput.hat.value, sdlhat)) - joyinputstream << "h" << joyinput.hat.index << "." << int(sdlhat); - break; - default: - break; - } - - std::string joyinputstr = joyinputstream.str(); - - if (joyinputstr.length() == 0) - throw love::Exception("Invalid joystick input value."); - - // SDL's name for the gamepad input value, e.g. "guide". - std::string gpinputname = stringFromGamepadInput(gpinput); - - // We should remove any existing joystick bind for this gamepad buttton/axis - // so SDL's parser doesn't get mixed up. - removeBindFromMapString(mapstr, joyinputstr); - - // The string we'll be adding to the mapping string, e.g. "guide:b10," - std::string insertstr = gpinputname + ":" + joyinputstr + ","; - - // We should replace any existing gamepad bind. - size_t findpos = mapstr.find("," + gpinputname + ":"); - if (findpos != std::string::npos) - { - // The bind string ends at the next comma, or the end of the string. - size_t endpos = mapstr.find_first_of(',', findpos + 1); - if (endpos == std::string::npos) - endpos = mapstr.length() - 1; - - mapstr.replace(findpos + 1, endpos - findpos + 1, insertstr); - } - else - { - // Just append to the end if we don't need to replace anything. - mapstr += insertstr; - } - - // 1 == added, 0 == updated, -1 == error. - int status = SDL_GameControllerAddMapping(mapstr.c_str()); - - if (status != -1) - recentGamepadGUIDs[guid] = true; - - // FIXME: massive hack until missing APIs are added to SDL 2: - // https://bugzilla.libsdl.org/show_bug.cgi?id=1975 - if (status == 1) - checkGamepads(guid); - - return status >= 0; -} - -std::string JoystickModule::stringFromGamepadInput(Joystick::GamepadInput gpinput) const -{ - SDL_GameControllerAxis sdlaxis; - SDL_GameControllerButton sdlbutton; - - const char *gpinputname = nullptr; - - switch (gpinput.type) - { - case Joystick::INPUT_TYPE_AXIS: - if (Joystick::getConstant(gpinput.axis, sdlaxis)) - gpinputname = SDL_GameControllerGetStringForAxis(sdlaxis); - break; - case Joystick::INPUT_TYPE_BUTTON: - if (Joystick::getConstant(gpinput.button, sdlbutton)) - gpinputname = SDL_GameControllerGetStringForButton(sdlbutton); - break; - default: - break; - } - - if (!gpinputname) - throw love::Exception("Invalid gamepad axis/button."); - - return std::string(gpinputname); -} - -void JoystickModule::removeBindFromMapString(std::string &mapstr, const std::string &joybindstr) const -{ - // Find the joystick part of the bind in the string. - size_t joybindpos = mapstr.find(joybindstr + ","); - if (joybindpos == std::string::npos) - { - joybindpos = mapstr.rfind(joybindstr); - if (joybindpos != mapstr.length() - joybindstr.length()) - return; - } - - if (joybindpos == std::string::npos) - return; - - // Find the start of the entire bind by looking for the separator between - // the end of one section of the map string and the start of this section. - size_t bindstart = mapstr.rfind(',', joybindpos); - if (bindstart != std::string::npos && bindstart < mapstr.length() - 1) - { - // The start of the bind is directly after the separator. - bindstart++; - - size_t bindend = mapstr.find(',', bindstart + 1); - if (bindend == std::string::npos) - bindend = mapstr.length() - 1; - - // Replace it with an empty string (remove it.) - mapstr.replace(bindstart, bindend - bindstart + 1, ""); - } -} - -void JoystickModule::checkGamepads(const std::string &guid) const -{ - // FIXME: massive hack until missing APIs are added to SDL 2: - // https://bugzilla.libsdl.org/show_bug.cgi?id=1975 - - // Make sure all connected joysticks of a certain guid that are - // gamepad-capable are opened as such. - for (int d_index = 0; d_index < SDL_NumJoysticks(); d_index++) - { - if (!SDL_IsGameController(d_index)) - continue; - - if (guid.compare(getDeviceGUID(d_index)) != 0) - continue; - - for (auto stick : activeSticks) - { - if (guid.compare(stick->getGUID()) != 0) - continue; - - // Big hack time: open the index as a game controller and compare - // the underlying joystick handle to the active stick's. - SDL_GameController *controller = SDL_GameControllerOpen(d_index); - if (controller == nullptr) - continue; - - // GameController objects are reference-counted in SDL, so we don't want to - // have a joystick open when trying to re-initialize it - SDL_Joystick *sdlstick = SDL_GameControllerGetJoystick(controller); - bool open_gamepad = (sdlstick == (SDL_Joystick *) stick->getHandle()); - SDL_GameControllerClose(controller); - - // open as gamepad if necessary - if (open_gamepad) - stick->openGamepad(d_index); - } - } -} - -std::string JoystickModule::getDeviceGUID(int deviceindex) const -{ - if (deviceindex < 0 || deviceindex >= SDL_NumJoysticks()) - return std::string(""); - - // SDL_JoystickGetGUIDString uses 32 bytes plus the null terminator. - char guidstr[33] = {'\0'}; - - // SDL2's GUIDs identify *classes* of devices, instead of unique devices. - SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(deviceindex); - SDL_JoystickGetGUIDString(guid, guidstr, sizeof(guidstr)); - - return std::string(guidstr); -} - -void JoystickModule::loadGamepadMappings(const std::string &mappings) -{ - // TODO: We should use SDL_GameControllerAddMappingsFromRW. We're - // duplicating its functionality for now because it was added after - // SDL 2.0.0's release, and we want runtime compat with 2.0.0 on Linux... - - std::stringstream ss(mappings); - std::string mapping; - bool success = false; - - // The mappings string contains newline-separated mappings. - while (std::getline(ss, mapping)) - { - if (mapping.empty()) - continue; - - // Lines starting with "#" are comments. - if (mapping[0] == '#') - continue; - - // Strip out and compare any "platform:XYZ," in the mapping. - size_t pstartpos = mapping.find("platform:"); - if (pstartpos != std::string::npos) - { - pstartpos += strlen("platform:"); - - size_t pendpos = mapping.find_first_of(',', pstartpos); - std::string platform = mapping.substr(pstartpos, pendpos - pstartpos); - - if (platform.compare(SDL_GetPlatform()) != 0) - { - // Ignore the mapping but still acknowledge that it is one. - success = true; - continue; - } - - pstartpos -= strlen("platform:"); - mapping.erase(pstartpos, pendpos - pstartpos + 1); - } - - if (SDL_GameControllerAddMapping(mapping.c_str()) != -1) - { - success = true; - std::string guid = mapping.substr(0, mapping.find_first_of(',')); - recentGamepadGUIDs[guid] = true; - - // FIXME: massive hack until missing APIs are added to SDL 2: - // https://bugzilla.libsdl.org/show_bug.cgi?id=1975 - checkGamepads(guid); - } - } - - // Don't error when an empty string is given, since saveGamepadMappings can - // produce an empty string if there are no recently seen gamepads to save. - if (!success && !mappings.empty()) - throw love::Exception("Invalid gamepad mappings."); -} - -std::string JoystickModule::getGamepadMappingString(const std::string &guid) const -{ - SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid.c_str()); - - char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); - if (sdlmapping == nullptr) - return ""; - - std::string mapping(sdlmapping); - SDL_free(sdlmapping); - - // Matches SDL_GameControllerAddMappingsFromRW. - if (mapping.find_last_of(',') != mapping.length() - 1) - mapping += ","; - mapping += "platform:" + std::string(SDL_GetPlatform()); - - return mapping; -} - -std::string JoystickModule::saveGamepadMappings() -{ - std::string mappings; - - for (const auto &g : recentGamepadGUIDs) - { - SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(g.first.c_str()); - - char *sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); - if (sdlmapping == nullptr) - continue; - - std::string mapping = sdlmapping; - SDL_free(sdlmapping); - - if (mapping.find_last_of(',') != mapping.length() - 1) - mapping += ","; - - // Matches SDL_GameControllerAddMappingsFromRW. - mapping += "platform:" + std::string(SDL_GetPlatform()) + ",\n"; - mappings += mapping; - } - - return mappings; -} - -} // sdl -} // joystick -} // eve diff --git a/src/modules/joystick/sdl/JoystickModule.h b/src/modules/joystick/sdl/JoystickModule.h deleted file mode 100644 index 5ac34078..00000000 --- a/src/modules/joystick/sdl/JoystickModule.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#ifndef LOVE_JOYSTICK_SDL_JOYSTICK_MODULE_H -#define LOVE_JOYSTICK_SDL_JOYSTICK_MODULE_H - -// LOVE -#include "joystick/JoystickModule.h" - -// C++ -#include -#include -#include -#include - -namespace eve -{ -namespace joystick -{ -namespace sdl -{ - -class JoystickModule : public love::joystick::JoystickModule -{ -public: - - JoystickModule(); - virtual ~JoystickModule(); - - // Implements Module. - const char *getName() const override; - - // Implements JoystickModule. - love::joystick::Joystick *addJoystick(int deviceindex) override; - void removeJoystick(love::joystick::Joystick *joystick) override; - love::joystick::Joystick *getJoystickFromID(int instanceid) override; - love::joystick::Joystick *getJoystick(int joyindex) override; - int getIndex(const love::joystick::Joystick *joystick) override; - int getJoystickCount() const override; - - bool setGamepadMapping(const std::string &guid, Joystick::GamepadInput gpinput, Joystick::JoystickInput joyinput) override; - void loadGamepadMappings(const std::string &mappings) override; - - std::string getGamepadMappingString(const std::string &guid) const override; - std::string saveGamepadMappings() override; - -private: - - std::string stringFromGamepadInput(Joystick::GamepadInput gpinput) const; - void removeBindFromMapString(std::string &mapstr, const std::string &joybindstr) const; - - void checkGamepads(const std::string &guid) const; - - // SDL2's GUIDs identify *classes* of devices, instead of unique devices. - std::string getDeviceGUID(int deviceindex) const; - - // Lists of currently connected Joysticks. - std::vector activeSticks; - - // Persistent list of all Joysticks which have been connected at some point. - std::list joysticks; - - // Persistent map indicating GUIDs for Gamepads which have been connected or - // modified at some point. - std::map recentGamepadGUIDs; - -}; // JoystickModule - -} // sdl -} // joystick -} // eve - - diff --git a/src/modules/joystick/sdl/Pad.cpp b/src/modules/joystick/sdl/Pad.cpp new file mode 100644 index 00000000..eba9d135 --- /dev/null +++ b/src/modules/joystick/sdl/Pad.cpp @@ -0,0 +1,338 @@ +#include "joystick/sdl/Pad.h" + +#include "common/Exception.h" + +#include +#include +#include + +#ifndef SDL_TICKS_PASSED +#define SDL_TICKS_PASSED(A, B) ((Sint32)((B) - (A)) <= 0) +#endif + +namespace eve::joystick::sdl { +namespace { + +std::string hatToString(Uint8 value) { + switch (value) { + case SDL_HAT_CENTERED: return "c"; + case SDL_HAT_UP: return "u"; + case SDL_HAT_RIGHT: return "r"; + case SDL_HAT_DOWN: return "d"; + case SDL_HAT_LEFT: return "l"; + case SDL_HAT_RIGHTUP: return "ru"; + case SDL_HAT_RIGHTDOWN: return "rd"; + case SDL_HAT_LEFTUP: return "lu"; + case SDL_HAT_LEFTDOWN: return "ld"; + default: return ""; + } +} + +} // namespace + +Pad::Pad(int id) : id_(id) {} + +Pad::Pad(int id, int joyindex) : id_(id) { + open(joyindex); +} + +Pad::~Pad() { + close(); +} + +bool Pad::open(int deviceindex) { + close(); + + joyhandle_ = SDL_JoystickOpen(deviceindex); + if (!joyhandle_) return false; + + instanceid_ = SDL_JoystickInstanceID(joyhandle_); + + char cstr[33]; + SDL_JoystickGUID sdlguid = SDL_JoystickGetGUID(joyhandle_); + SDL_JoystickGetGUIDString(sdlguid, cstr, static_cast(sizeof(cstr))); + guid_ = cstr; + + openGamepad(deviceindex); + + const char* joyname = SDL_JoystickName(joyhandle_); + if (!joyname && controller_) joyname = SDL_GameControllerName(controller_); + name_ = joyname ? joyname : ""; + + return isConnected(); +} + +void Pad::close() { + if (haptic_) SDL_HapticClose(haptic_); + if (controller_) SDL_GameControllerClose(controller_); + if (joyhandle_) SDL_JoystickClose(joyhandle_); + + joyhandle_ = nullptr; + controller_ = nullptr; + haptic_ = nullptr; + instanceid_ = -1; + vibration_ = Vibration{}; +} + +bool Pad::isConnected() const { + return joyhandle_ != nullptr && SDL_JoystickGetAttached(joyhandle_); +} + +std::string Pad::getName() const { + return name_; +} + +int Pad::getAxisCount() const { + return isConnected() ? SDL_JoystickNumAxes(joyhandle_) : 0; +} + +int Pad::getButtonCount() const { + return isConnected() ? SDL_JoystickNumButtons(joyhandle_) : 0; +} + +int Pad::getHatCount() const { + return isConnected() ? SDL_JoystickNumHats(joyhandle_) : 0; +} + +float Pad::getAxis(int axisindex) const { + if (!isConnected() || axisindex < 0 || axisindex >= getAxisCount()) return 0.f; + return clampAxis(static_cast(SDL_JoystickGetAxis(joyhandle_, axisindex)) / 32768.0f); +} + +std::vector Pad::getAxes() const { + std::vector axes; + int count = getAxisCount(); + if (!isConnected() || count <= 0) return axes; + axes.reserve(static_cast(count)); + for (int i = 0; i < count; i++) + axes.push_back(clampAxis(static_cast(SDL_JoystickGetAxis(joyhandle_, i)) / 32768.0f)); + return axes; +} + +std::string Pad::getHat(int hatindex) const { + if (!isConnected() || hatindex < 0 || hatindex >= getHatCount()) return ""; + return hatToString(SDL_JoystickGetHat(joyhandle_, hatindex)); +} + +bool Pad::isDown(int button) const { + return isDown(std::vector{button}); +} + +bool Pad::isDown(const std::vector& buttons) const { + if (!isConnected()) return false; + int numbuttons = getButtonCount(); + for (int button : buttons) { + if (button < 0 || button >= numbuttons) continue; + if (SDL_JoystickGetButton(joyhandle_, button) == 1) return true; + } + return false; +} + +bool Pad::openGamepad(int deviceindex) { + if (!SDL_IsGameController(deviceindex)) return false; + + if (controller_) { + SDL_GameControllerClose(controller_); + controller_ = nullptr; + } + + controller_ = SDL_GameControllerOpen(deviceindex); + return isGamepad(); +} + +bool Pad::isGamepad() const { + return controller_ != nullptr; +} + +float Pad::getGamepadAxis(const std::string& axis) const { + if (!isConnected() || !isGamepad()) return 0.f; + SDL_GameControllerAxis sdlaxis = SDL_GameControllerGetAxisFromString(axis.c_str()); + if (sdlaxis == SDL_CONTROLLER_AXIS_INVALID) return 0.f; + Sint16 value = SDL_GameControllerGetAxis(controller_, sdlaxis); + return clampAxis(static_cast(value) / 32768.0f); +} + +bool Pad::isGamepadDown(const std::string& button) const { + return isGamepadDown(std::vector{button}); +} + +bool Pad::isGamepadDown(const std::vector& buttons) const { + if (!isConnected() || !isGamepad()) return false; + for (const auto& name : buttons) { + SDL_GameControllerButton sdlbutton = SDL_GameControllerGetButtonFromString(name.c_str()); + if (sdlbutton == SDL_CONTROLLER_BUTTON_INVALID) continue; + if (SDL_GameControllerGetButton(controller_, sdlbutton) == 1) return true; + } + return false; +} + +std::string Pad::getGamepadMappingString() const { + char* sdlmapping = nullptr; + if (controller_) sdlmapping = SDL_GameControllerMapping(controller_); + if (!sdlmapping) { + SDL_JoystickGUID sdlguid = SDL_JoystickGetGUIDFromString(guid_.c_str()); + sdlmapping = SDL_GameControllerMappingForGUID(sdlguid); + } + if (!sdlmapping) return ""; + + std::string mappingstr(sdlmapping); + SDL_free(sdlmapping); + + if (mappingstr.find_last_of(',') != mappingstr.length() - 1) mappingstr += ","; + mappingstr += "platform:" + std::string(SDL_GetPlatform()); + return mappingstr; +} + +void* Pad::getHandle() const { + return joyhandle_; +} + +std::string Pad::getGUID() const { + return guid_; +} + +int Pad::getInstanceID() const { + return static_cast(instanceid_); +} + +int Pad::getID() const { + return id_; +} + +void Pad::getDeviceInfo(int& vendorID, int& productID, int& productVersion) const { +#if SDL_VERSION_ATLEAST(2, 0, 6) + if (joyhandle_) { + vendorID = SDL_JoystickGetVendor(joyhandle_); + productID = SDL_JoystickGetProduct(joyhandle_); + productVersion = SDL_JoystickGetProductVersion(joyhandle_); + return; + } +#endif + vendorID = productID = productVersion = 0; +} + +bool Pad::checkCreateHaptic() { + if (!isConnected()) return false; + + if (!SDL_WasInit(SDL_INIT_HAPTIC) && SDL_InitSubSystem(SDL_INIT_HAPTIC) < 0) return false; + + if (haptic_ && SDL_HapticIndex(haptic_) != -1) return true; + + if (haptic_) { + SDL_HapticClose(haptic_); + haptic_ = nullptr; + } + + haptic_ = SDL_HapticOpenFromJoystick(joyhandle_); + vibration_ = Vibration{}; + return haptic_ != nullptr; +} + +bool Pad::isVibrationSupported() { + if (!checkCreateHaptic()) return false; + unsigned int features = SDL_HapticQuery(haptic_); + if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) return true; + if (isGamepad() && (features & SDL_HAPTIC_CUSTOM) != 0) return true; + if ((features & SDL_HAPTIC_SINE) != 0) return true; + return false; +} + +bool Pad::runVibrationEffect() { + if (vibration_.id != -1) { + if (SDL_HapticUpdateEffect(haptic_, vibration_.id, &vibration_.effect) == 0) { + if (SDL_HapticRunEffect(haptic_, vibration_.id, 1) == 0) return true; + } + SDL_HapticDestroyEffect(haptic_, vibration_.id); + vibration_.id = -1; + } + + vibration_.id = SDL_HapticNewEffect(haptic_, &vibration_.effect); + return vibration_.id != -1 && SDL_HapticRunEffect(haptic_, vibration_.id, 1) == 0; +} + +bool Pad::setVibration(float left, float right, float duration) { + left = std::min(std::max(left, 0.0f), 1.0f); + right = std::min(std::max(right, 0.0f), 1.0f); + + if (left == 0.0f && right == 0.0f) return setVibration(); + if (!checkCreateHaptic()) return false; + + Uint32 length = SDL_HAPTIC_INFINITY; + if (duration >= 0.0f) { + float maxduration = static_cast(std::numeric_limits::max()) / 1000.0f; + length = static_cast(std::min(duration, maxduration) * 1000.0f); + } + + bool success = false; + unsigned int features = SDL_HapticQuery(haptic_); + int axes = SDL_HapticNumAxes(haptic_); + + if ((features & SDL_HAPTIC_LEFTRIGHT) != 0) { + memset(&vibration_.effect, 0, sizeof(SDL_HapticEffect)); + vibration_.effect.type = SDL_HAPTIC_LEFTRIGHT; + vibration_.effect.leftright.length = length; + vibration_.effect.leftright.large_magnitude = static_cast(left * 0xFFFF); + vibration_.effect.leftright.small_magnitude = static_cast(right * 0xFFFF); + success = runVibrationEffect(); + } + + if (!success && isGamepad() && (features & SDL_HAPTIC_CUSTOM) && axes == 2) { + vibration_.data[0] = vibration_.data[2] = static_cast(left * 0x7FFF); + vibration_.data[1] = vibration_.data[3] = static_cast(right * 0x7FFF); + memset(&vibration_.effect, 0, sizeof(SDL_HapticEffect)); + vibration_.effect.type = SDL_HAPTIC_CUSTOM; + vibration_.effect.custom.length = length; + vibration_.effect.custom.channels = 2; + vibration_.effect.custom.period = 10; + vibration_.effect.custom.samples = 2; + vibration_.effect.custom.data = vibration_.data; + success = runVibrationEffect(); + } + + if (!success && (features & SDL_HAPTIC_SINE) != 0) { + memset(&vibration_.effect, 0, sizeof(SDL_HapticEffect)); + vibration_.effect.type = SDL_HAPTIC_SINE; + vibration_.effect.periodic.length = length; + vibration_.effect.periodic.period = 10; + float strength = std::max(left, right); + vibration_.effect.periodic.magnitude = static_cast(strength * 0x7FFF); + success = runVibrationEffect(); + } + + if (success) { + vibration_.left = left; + vibration_.right = right; + vibration_.endtime = + (length == SDL_HAPTIC_INFINITY) ? SDL_HAPTIC_INFINITY : (SDL_GetTicks() + length); + } else { + vibration_.left = vibration_.right = 0.0f; + vibration_.endtime = SDL_HAPTIC_INFINITY; + } + return success; +} + +bool Pad::setVibration() { + bool success = true; + if (SDL_WasInit(SDL_INIT_HAPTIC) && haptic_ && SDL_HapticIndex(haptic_) != -1) + success = (SDL_HapticStopEffect(haptic_, vibration_.id) == 0); + if (success) vibration_.left = vibration_.right = 0.0f; + return success; +} + +void Pad::getVibration(float& left, float& right) { + if (vibration_.endtime != SDL_HAPTIC_INFINITY) { + if (SDL_TICKS_PASSED(SDL_GetTicks(), vibration_.endtime)) { + setVibration(); + vibration_.endtime = SDL_HAPTIC_INFINITY; + } + } + + int id = vibration_.id; + if (!haptic_ || id == -1 || SDL_HapticGetEffectStatus(haptic_, id) != 1) + vibration_.left = vibration_.right = 0.0f; + + left = vibration_.left; + right = vibration_.right; +} + +} // namespace eve::joystick::sdl diff --git a/src/modules/joystick/sdl/Pad.h b/src/modules/joystick/sdl/Pad.h new file mode 100644 index 00000000..9e7b7cbd --- /dev/null +++ b/src/modules/joystick/sdl/Pad.h @@ -0,0 +1,79 @@ +#pragma once + +#include "joystick/Pad.h" + +#include +#include + +#include + +namespace eve::joystick::sdl { + +class Pad : public eve::joystick::Pad { +public: + explicit Pad(int id); + Pad(int id, int joyindex); + ~Pad() override; + + bool open(int deviceindex) override; + void close() override; + bool isConnected() const override; + + std::string getName() const override; + + int getAxisCount() const override; + int getButtonCount() const override; + int getHatCount() const override; + + float getAxis(int axisindex) const override; + std::vector getAxes() const override; + std::string getHat(int hatindex) const override; + + bool isDown(int button) const override; + bool isDown(const std::vector& buttons) const override; + + bool openGamepad(int deviceindex) override; + bool isGamepad() const override; + + float getGamepadAxis(const std::string& axis) const override; + bool isGamepadDown(const std::string& button) const override; + bool isGamepadDown(const std::vector& buttons) const override; + + std::string getGamepadMappingString() const override; + + void* getHandle() const override; + std::string getGUID() const override; + int getInstanceID() const override; + int getID() const override; + + void getDeviceInfo(int& vendorID, int& productID, int& productVersion) const override; + + bool isVibrationSupported() override; + bool setVibration(float left, float right, float duration = -1.0f) override; + bool setVibration() override; + void getVibration(float& left, float& right) override; + +private: + bool checkCreateHaptic(); + bool runVibrationEffect(); + + SDL_Joystick* joyhandle_ = nullptr; + SDL_GameController* controller_ = nullptr; + SDL_Haptic* haptic_ = nullptr; + + SDL_JoystickID instanceid_ = -1; + std::string guid_; + int id_ = 0; + std::string name_; + + struct Vibration { + float left = 0.0f; + float right = 0.0f; + SDL_HapticEffect effect{}; + Uint16 data[4]{}; + int id = -1; + Uint32 endtime = SDL_HAPTIC_INFINITY; + } vibration_; +}; + +} // namespace eve::joystick::sdl diff --git a/src/modules/keyboard/Keyboard.cpp b/src/modules/keyboard/Keyboard.cpp index 46a4d6e5..3cecb189 100644 --- a/src/modules/keyboard/Keyboard.cpp +++ b/src/modules/keyboard/Keyboard.cpp @@ -1,525 +1,31 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ +#include "keyboard/Keyboard.h" +#include "keyboard/sdl/Keyboard.h" -#include "common/config.h" +#include -#include "Keyboard.h" +namespace eve::keyboard { -namespace eve -{ -namespace keyboard -{ +Module_IMPL(Keyboard, new sdl::Keyboard()); -bool Keyboard::getConstant(const char *in, Key &out) -{ - return keys.find(in, out); +Keyboard::~Keyboard() {} + +void Keyboard::expose(ssq::Table& table) { + auto cls = table.addClass(name, Keyboard::create, false); + expose(cls); } -bool Keyboard::getConstant(Key in, const char *&out) -{ - return keys.find(in, out); +void Keyboard::expose(ssq::Class& cls) { + cls.addFunc("getName", &Keyboard::getName); + cls.addFunc("setKeyRepeat", &Keyboard::setKeyRepeat); + cls.addFunc("hasKeyRepeat", &Keyboard::hasKeyRepeat); + cls.addFunc("isDown", static_cast(&Keyboard::isDown)); + cls.addFunc("isScancodeDown", + static_cast(&Keyboard::isScancodeDown)); + cls.addFunc("getKeyFromScancode", &Keyboard::getKeyFromScancode); + cls.addFunc("getScancodeFromKey", &Keyboard::getScancodeFromKey); + cls.addFunc("setTextInput", static_cast(&Keyboard::setTextInput)); + cls.addFunc("hasTextInput", &Keyboard::hasTextInput); + cls.addFunc("hasScreenKeyboard", &Keyboard::hasScreenKeyboard); } -bool Keyboard::getConstant(const char *in, Scancode &out) -{ - return scancodes.find(in, out); -} - -bool Keyboard::getConstant(Scancode in, const char *&out) -{ - return scancodes.find(in, out); -} - -StringMap::Entry Keyboard::keyEntries[] = -{ - {"unknown", Keyboard::KEY_UNKNOWN}, - - {"return", Keyboard::KEY_RETURN}, - {"escape", Keyboard::KEY_ESCAPE}, - {"backspace", Keyboard::KEY_BACKSPACE}, - {"tab", Keyboard::KEY_TAB}, - {"space", Keyboard::KEY_SPACE}, - {"!", Keyboard::KEY_EXCLAIM}, - {"\"", Keyboard::KEY_QUOTEDBL}, - {"#", Keyboard::KEY_HASH}, - {"%", Keyboard::KEY_PERCENT}, - {"$", Keyboard::KEY_DOLLAR}, - {"&", Keyboard::KEY_AMPERSAND}, - {"'", Keyboard::KEY_QUOTE}, - {"(", Keyboard::KEY_LEFTPAREN}, - {")", Keyboard::KEY_RIGHTPAREN}, - {"*", Keyboard::KEY_ASTERISK}, - {"+", Keyboard::KEY_PLUS}, - {",", Keyboard::KEY_COMMA}, - {"-", Keyboard::KEY_MINUS}, - {".", Keyboard::KEY_PERIOD}, - {"/", Keyboard::KEY_SLASH}, - {"0", Keyboard::KEY_0}, - {"1", Keyboard::KEY_1}, - {"2", Keyboard::KEY_2}, - {"3", Keyboard::KEY_3}, - {"4", Keyboard::KEY_4}, - {"5", Keyboard::KEY_5}, - {"6", Keyboard::KEY_6}, - {"7", Keyboard::KEY_7}, - {"8", Keyboard::KEY_8}, - {"9", Keyboard::KEY_9}, - {":", Keyboard::KEY_COLON}, - {";", Keyboard::KEY_SEMICOLON}, - {"<", Keyboard::KEY_LESS}, - {"=", Keyboard::KEY_EQUALS}, - {">", Keyboard::KEY_GREATER}, - {"?", Keyboard::KEY_QUESTION}, - {"@", Keyboard::KEY_AT}, - - {"[", Keyboard::KEY_LEFTBRACKET}, - {"\\", Keyboard::KEY_BACKSLASH}, - {"]", Keyboard::KEY_RIGHTBRACKET}, - {"^", Keyboard::KEY_CARET}, - {"_", Keyboard::KEY_UNDERSCORE}, - {"`", Keyboard::KEY_BACKQUOTE}, - {"a", Keyboard::KEY_A}, - {"b", Keyboard::KEY_B}, - {"c", Keyboard::KEY_C}, - {"d", Keyboard::KEY_D}, - {"e", Keyboard::KEY_E}, - {"f", Keyboard::KEY_F}, - {"g", Keyboard::KEY_G}, - {"h", Keyboard::KEY_H}, - {"i", Keyboard::KEY_I}, - {"j", Keyboard::KEY_J}, - {"k", Keyboard::KEY_K}, - {"l", Keyboard::KEY_L}, - {"m", Keyboard::KEY_M}, - {"n", Keyboard::KEY_N}, - {"o", Keyboard::KEY_O}, - {"p", Keyboard::KEY_P}, - {"q", Keyboard::KEY_Q}, - {"r", Keyboard::KEY_R}, - {"s", Keyboard::KEY_S}, - {"t", Keyboard::KEY_T}, - {"u", Keyboard::KEY_U}, - {"v", Keyboard::KEY_V}, - {"w", Keyboard::KEY_W}, - {"x", Keyboard::KEY_X}, - {"y", Keyboard::KEY_Y}, - {"z", Keyboard::KEY_Z}, - - {"capslock", Keyboard::KEY_CAPSLOCK}, - - {"f1", Keyboard::KEY_F1}, - {"f2", Keyboard::KEY_F2}, - {"f3", Keyboard::KEY_F3}, - {"f4", Keyboard::KEY_F4}, - {"f5", Keyboard::KEY_F5}, - {"f6", Keyboard::KEY_F6}, - {"f7", Keyboard::KEY_F7}, - {"f8", Keyboard::KEY_F8}, - {"f9", Keyboard::KEY_F9}, - {"f10", Keyboard::KEY_F10}, - {"f11", Keyboard::KEY_F11}, - {"f12", Keyboard::KEY_F12}, - - {"printscreen", Keyboard::KEY_PRINTSCREEN}, - {"scrolllock", Keyboard::KEY_SCROLLLOCK}, - {"pause", Keyboard::KEY_PAUSE}, - {"insert", Keyboard::KEY_INSERT}, - {"home", Keyboard::KEY_HOME}, - {"pageup", Keyboard::KEY_PAGEUP}, - {"delete", Keyboard::KEY_DELETE}, - {"end", Keyboard::KEY_END}, - {"pagedown", Keyboard::KEY_PAGEDOWN}, - {"right", Keyboard::KEY_RIGHT}, - {"left", Keyboard::KEY_LEFT}, - {"down", Keyboard::KEY_DOWN}, - {"up", Keyboard::KEY_UP}, - - {"numlock", Keyboard::KEY_NUMLOCKCLEAR}, - {"kp/", Keyboard::KEY_KP_DIVIDE}, - {"kp*", Keyboard::KEY_KP_MULTIPLY}, - {"kp-", Keyboard::KEY_KP_MINUS}, - {"kp+", Keyboard::KEY_KP_PLUS}, - {"kpenter", Keyboard::KEY_KP_ENTER}, - {"kp0", Keyboard::KEY_KP_0}, - {"kp1", Keyboard::KEY_KP_1}, - {"kp2", Keyboard::KEY_KP_2}, - {"kp3", Keyboard::KEY_KP_3}, - {"kp4", Keyboard::KEY_KP_4}, - {"kp5", Keyboard::KEY_KP_5}, - {"kp6", Keyboard::KEY_KP_6}, - {"kp7", Keyboard::KEY_KP_7}, - {"kp8", Keyboard::KEY_KP_8}, - {"kp9", Keyboard::KEY_KP_9}, - {"kp.", Keyboard::KEY_KP_PERIOD}, - {"kp,", Keyboard::KEY_KP_COMMA}, - {"kp=", Keyboard::KEY_KP_EQUALS}, - - {"application", Keyboard::KEY_APPLICATION}, - {"power", Keyboard::KEY_POWER}, - {"f13", Keyboard::KEY_F13}, - {"f14", Keyboard::KEY_F14}, - {"f15", Keyboard::KEY_F15}, - {"f16", Keyboard::KEY_F16}, - {"f17", Keyboard::KEY_F17}, - {"f18", Keyboard::KEY_F18}, - {"f19", Keyboard::KEY_F19}, - {"f20", Keyboard::KEY_F20}, - {"f21", Keyboard::KEY_F21}, - {"f22", Keyboard::KEY_F22}, - {"f23", Keyboard::KEY_F23}, - {"f24", Keyboard::KEY_F24}, - {"execute", Keyboard::KEY_EXECUTE}, - {"help", Keyboard::KEY_HELP}, - {"menu", Keyboard::KEY_MENU}, - {"select", Keyboard::KEY_SELECT}, - {"stop", Keyboard::KEY_STOP}, - {"again", Keyboard::KEY_AGAIN}, - {"undo", Keyboard::KEY_UNDO}, - {"cut", Keyboard::KEY_CUT}, - {"copy", Keyboard::KEY_COPY}, - {"paste", Keyboard::KEY_PASTE}, - {"find", Keyboard::KEY_FIND}, - {"mute", Keyboard::KEY_MUTE}, - {"volumeup", Keyboard::KEY_VOLUMEUP}, - {"volumedown", Keyboard::KEY_VOLUMEDOWN}, - - {"alterase", Keyboard::KEY_ALTERASE}, - {"sysreq", Keyboard::KEY_SYSREQ}, - {"cancel", Keyboard::KEY_CANCEL}, - {"clear", Keyboard::KEY_CLEAR}, - {"prior", Keyboard::KEY_PRIOR}, - {"return2", Keyboard::KEY_RETURN2}, - {"separator", Keyboard::KEY_SEPARATOR}, - {"out", Keyboard::KEY_OUT}, - {"oper", Keyboard::KEY_OPER}, - {"clearagain", Keyboard::KEY_CLEARAGAIN}, - - {"thsousandsseparator", Keyboard::KEY_THOUSANDSSEPARATOR}, - {"decimalseparator", Keyboard::KEY_DECIMALSEPARATOR}, - {"currencyunit", Keyboard::KEY_CURRENCYUNIT}, - {"currencysubunit", Keyboard::KEY_CURRENCYSUBUNIT}, - - {"lctrl", Keyboard::KEY_LCTRL}, - {"lshift", Keyboard::KEY_LSHIFT}, - {"lalt", Keyboard::KEY_LALT}, - {"lgui", Keyboard::KEY_LGUI}, - {"rctrl", Keyboard::KEY_RCTRL}, - {"rshift", Keyboard::KEY_RSHIFT}, - {"ralt", Keyboard::KEY_RALT}, - {"rgui", Keyboard::KEY_RGUI}, - - {"mode", Keyboard::KEY_MODE}, - - {"audionext", Keyboard::KEY_AUDIONEXT}, - {"audioprev", Keyboard::KEY_AUDIOPREV}, - {"audiostop", Keyboard::KEY_AUDIOSTOP}, - {"audioplay", Keyboard::KEY_AUDIOPLAY}, - {"audiomute", Keyboard::KEY_AUDIOMUTE}, - {"mediaselect", Keyboard::KEY_MEDIASELECT}, - {"www", Keyboard::KEY_WWW}, - {"mail", Keyboard::KEY_MAIL}, - {"calculator", Keyboard::KEY_CALCULATOR}, - {"computer", Keyboard::KEY_COMPUTER}, - {"appsearch", Keyboard::KEY_APP_SEARCH}, - {"apphome", Keyboard::KEY_APP_HOME}, - {"appback", Keyboard::KEY_APP_BACK}, - {"appforward", Keyboard::KEY_APP_FORWARD}, - {"appstop", Keyboard::KEY_APP_STOP}, - {"apprefresh", Keyboard::KEY_APP_REFRESH}, - {"appbookmarks", Keyboard::KEY_APP_BOOKMARKS}, - - {"brightnessdown", Keyboard::KEY_BRIGHTNESSDOWN}, - {"brightnessup", Keyboard::KEY_BRIGHTNESSUP}, - {"displayswitch", Keyboard::KEY_DISPLAYSWITCH}, - {"kbdillumtoggle", Keyboard::KEY_KBDILLUMTOGGLE}, - {"kbdillumdown", Keyboard::KEY_KBDILLUMDOWN}, - {"kbdillumup", Keyboard::KEY_KBDILLUMUP}, - {"eject", Keyboard::KEY_EJECT}, - {"sleep", Keyboard::KEY_SLEEP}, -}; - -StringMap Keyboard::keys(Keyboard::keyEntries, sizeof(Keyboard::keyEntries)); - -StringMap::Entry Keyboard::scancodeEntries[] = -{ - {"unknown", SCANCODE_UNKNOWN}, - - {"a", SCANCODE_A}, - {"b", SCANCODE_B}, - {"c", SCANCODE_C}, - {"d", SCANCODE_D}, - {"e", SCANCODE_E}, - {"f", SCANCODE_F}, - {"g", SCANCODE_G}, - {"h", SCANCODE_H}, - {"i", SCANCODE_I}, - {"j", SCANCODE_J}, - {"k", SCANCODE_K}, - {"l", SCANCODE_L}, - {"m", SCANCODE_M}, - {"n", SCANCODE_N}, - {"o", SCANCODE_O}, - {"p", SCANCODE_P}, - {"q", SCANCODE_Q}, - {"r", SCANCODE_R}, - {"s", SCANCODE_S}, - {"t", SCANCODE_T}, - {"u", SCANCODE_U}, - {"v", SCANCODE_V}, - {"w", SCANCODE_W}, - {"x", SCANCODE_X}, - {"y", SCANCODE_Y}, - {"z", SCANCODE_Z}, - - {"1", SCANCODE_1}, - {"2", SCANCODE_2}, - {"3", SCANCODE_3}, - {"4", SCANCODE_4}, - {"5", SCANCODE_5}, - {"6", SCANCODE_6}, - {"7", SCANCODE_7}, - {"8", SCANCODE_8}, - {"9", SCANCODE_9}, - {"0", SCANCODE_0}, - - {"return", SCANCODE_RETURN}, - {"escape", SCANCODE_ESCAPE}, - {"backspace", SCANCODE_BACKSPACE}, - {"tab", SCANCODE_TAB}, - {"space", SCANCODE_SPACE}, - - {"-", SCANCODE_MINUS}, - {"=", SCANCODE_EQUALS}, - {"[", SCANCODE_LEFTBRACKET}, - {"]", SCANCODE_RIGHTBRACKET}, - {"\\", SCANCODE_BACKSLASH}, - {"nonus#", SCANCODE_NONUSHASH}, - {";", SCANCODE_SEMICOLON}, - {"'", SCANCODE_APOSTROPHE}, - {"`", SCANCODE_GRAVE}, - {",", SCANCODE_COMMA}, - {".", SCANCODE_PERIOD}, - {"/", SCANCODE_SLASH}, - - {"capslock", SCANCODE_CAPSLOCK}, - - {"f1", SCANCODE_F1}, - {"f2", SCANCODE_F2}, - {"f3", SCANCODE_F3}, - {"f4", SCANCODE_F4}, - {"f5", SCANCODE_F5}, - {"f6", SCANCODE_F6}, - {"f7", SCANCODE_F7}, - {"f8", SCANCODE_F8}, - {"f9", SCANCODE_F9}, - {"f10", SCANCODE_F10}, - {"f11", SCANCODE_F11}, - {"f12", SCANCODE_F12}, - - {"printscreen", SCANCODE_PRINTSCREEN}, - {"scrolllock", SCANCODE_SCROLLLOCK}, - {"pause", SCANCODE_PAUSE}, - {"insert", SCANCODE_INSERT}, - {"home", SCANCODE_HOME}, - {"pageup", SCANCODE_PAGEUP}, - {"delete", SCANCODE_DELETE}, - {"end", SCANCODE_END}, - {"pagedown", SCANCODE_PAGEDOWN}, - {"right", SCANCODE_RIGHT}, - {"left", SCANCODE_LEFT}, - {"down", SCANCODE_DOWN}, - {"up", SCANCODE_UP}, - - {"numlock", SCANCODE_NUMLOCKCLEAR}, - {"kp/", SCANCODE_KP_DIVIDE}, - {"kp*", SCANCODE_KP_MULTIPLY}, - {"kp-", SCANCODE_KP_MINUS}, - {"kp+", SCANCODE_KP_PLUS}, - {"kpenter", SCANCODE_KP_ENTER}, - {"kp1", SCANCODE_KP_1}, - {"kp2", SCANCODE_KP_2}, - {"kp3", SCANCODE_KP_3}, - {"kp4", SCANCODE_KP_4}, - {"kp5", SCANCODE_KP_5}, - {"kp6", SCANCODE_KP_6}, - {"kp7", SCANCODE_KP_7}, - {"kp8", SCANCODE_KP_8}, - {"kp9", SCANCODE_KP_9}, - {"kp0", SCANCODE_KP_0}, - {"kp.", SCANCODE_KP_PERIOD}, - - {"nonusbackslash", SCANCODE_NONUSBACKSLASH}, - {"application", SCANCODE_APPLICATION}, - {"power", SCANCODE_POWER}, - {"kp=", SCANCODE_KP_EQUALS}, - {"f13", SCANCODE_F13}, - {"f14", SCANCODE_F14}, - {"f15", SCANCODE_F15}, - {"f16", SCANCODE_F16}, - {"f17", SCANCODE_F17}, - {"f18", SCANCODE_F18}, - {"f19", SCANCODE_F19}, - {"f20", SCANCODE_F20}, - {"f21", SCANCODE_F21}, - {"f22", SCANCODE_F22}, - {"f23", SCANCODE_F23}, - {"f24", SCANCODE_F24}, - {"execute", SCANCODE_EXECUTE}, - {"help", SCANCODE_HELP}, - {"menu", SCANCODE_MENU}, - {"select", SCANCODE_SELECT}, - {"stop", SCANCODE_STOP}, - {"again", SCANCODE_AGAIN}, - {"undo", SCANCODE_UNDO}, - {"cut", SCANCODE_CUT}, - {"copy", SCANCODE_COPY}, - {"paste", SCANCODE_PASTE}, - {"find", SCANCODE_FIND}, - {"mute", SCANCODE_MUTE}, - {"volumeup", SCANCODE_VOLUMEUP}, - {"volumedown", SCANCODE_VOLUMEDOWN}, - {"kp,", SCANCODE_KP_COMMA}, - {"kp=400", SCANCODE_KP_EQUALSAS400}, - - {"international1", SCANCODE_INTERNATIONAL1}, - {"international2", SCANCODE_INTERNATIONAL2}, - {"international3", SCANCODE_INTERNATIONAL3}, - {"international4", SCANCODE_INTERNATIONAL4}, - {"international5", SCANCODE_INTERNATIONAL5}, - {"international6", SCANCODE_INTERNATIONAL6}, - {"international7", SCANCODE_INTERNATIONAL7}, - {"international8", SCANCODE_INTERNATIONAL8}, - {"international9", SCANCODE_INTERNATIONAL9}, - {"lang1", SCANCODE_LANG1}, - {"lang2", SCANCODE_LANG2}, - {"lang3", SCANCODE_LANG3}, - {"lang4", SCANCODE_LANG4}, - {"lang5", SCANCODE_LANG5}, - {"lang6", SCANCODE_LANG6}, - {"lang7", SCANCODE_LANG7}, - {"lang8", SCANCODE_LANG8}, - {"lang9", SCANCODE_LANG9}, - - {"alterase", SCANCODE_ALTERASE}, - {"sysreq", SCANCODE_SYSREQ}, - {"cancel", SCANCODE_CANCEL}, - {"clear", SCANCODE_CLEAR}, - {"prior", SCANCODE_PRIOR}, - {"return2", SCANCODE_RETURN2}, - {"separator", SCANCODE_SEPARATOR}, - {"out", SCANCODE_OUT}, - {"oper", SCANCODE_OPER}, - {"clearagain", SCANCODE_CLEARAGAIN}, - {"crsel", SCANCODE_CRSEL}, - {"exsel", SCANCODE_EXSEL}, - - {"kp00", SCANCODE_KP_00}, - {"kp000", SCANCODE_KP_000}, - {"thsousandsseparator", SCANCODE_THOUSANDSSEPARATOR}, - {"decimalseparator", SCANCODE_DECIMALSEPARATOR}, - {"currencyunit", SCANCODE_CURRENCYUNIT}, - {"currencysubunit", SCANCODE_CURRENCYSUBUNIT}, - {"kp(", SCANCODE_KP_LEFTPAREN}, - {"kp)", SCANCODE_KP_RIGHTPAREN}, - {"kp{", SCANCODE_KP_LEFTBRACE}, - {"kp}", SCANCODE_KP_RIGHTBRACE}, - {"kptab", SCANCODE_KP_TAB}, - {"kpbackspace", SCANCODE_KP_BACKSPACE}, - {"kpa", SCANCODE_KP_A}, - {"kpb", SCANCODE_KP_B}, - {"kpc", SCANCODE_KP_C}, - {"kpd", SCANCODE_KP_D}, - {"kpe", SCANCODE_KP_E}, - {"kpf", SCANCODE_KP_F}, - {"kpxor", SCANCODE_KP_XOR}, - {"kpower", SCANCODE_KP_POWER}, - {"kp%", SCANCODE_KP_PERCENT}, - {"kp<", SCANCODE_KP_LESS}, - {"kp>", SCANCODE_KP_GREATER}, - {"kp&", SCANCODE_KP_AMPERSAND}, - {"kp&&", SCANCODE_KP_DBLAMPERSAND}, - {"kp|", SCANCODE_KP_VERTICALBAR}, - {"kp||", SCANCODE_KP_DBLVERTICALBAR}, - {"kp:", SCANCODE_KP_COLON}, - {"kp#", SCANCODE_KP_HASH}, - {"kp ", SCANCODE_KP_SPACE}, - {"kp@", SCANCODE_KP_AT}, - {"kp!", SCANCODE_KP_EXCLAM}, - {"kpmemstore", SCANCODE_KP_MEMSTORE}, - {"kpmemrecall", SCANCODE_KP_MEMRECALL}, - {"kpmemclear", SCANCODE_KP_MEMCLEAR}, - {"kpmem+", SCANCODE_KP_MEMADD}, - {"kpmem-", SCANCODE_KP_MEMSUBTRACT}, - {"kpmem*", SCANCODE_KP_MEMMULTIPLY}, - {"kpmem/", SCANCODE_KP_MEMDIVIDE}, - {"kp+-", SCANCODE_KP_PLUSMINUS}, - {"kpclear", SCANCODE_KP_CLEAR}, - {"kpclearentry", SCANCODE_KP_CLEARENTRY}, - {"kpbinary", SCANCODE_KP_BINARY}, - {"kpoctal", SCANCODE_KP_OCTAL}, - {"kpdecimal", SCANCODE_KP_DECIMAL}, - {"kphex", SCANCODE_KP_HEXADECIMAL}, - - {"lctrl", SCANCODE_LCTRL}, - {"lshift", SCANCODE_LSHIFT}, - {"lalt", SCANCODE_LALT}, - {"lgui", SCANCODE_LGUI}, - {"rctrl", SCANCODE_RCTRL}, - {"rshift", SCANCODE_RSHIFT}, - {"ralt", SCANCODE_RALT}, - {"rgui", SCANCODE_RGUI}, - - {"mode", SCANCODE_MODE}, - - {"audionext", SCANCODE_AUDIONEXT}, - {"audioprev", SCANCODE_AUDIOPREV}, - {"audiostop", SCANCODE_AUDIOSTOP}, - {"audioplay", SCANCODE_AUDIOPLAY}, - {"audiomute", SCANCODE_AUDIOMUTE}, - {"mediaselect", SCANCODE_MEDIASELECT}, - {"www", SCANCODE_WWW}, - {"mail", SCANCODE_MAIL}, - {"calculator", SCANCODE_CALCULATOR}, - {"computer", SCANCODE_COMPUTER}, - {"acsearch", SCANCODE_AC_SEARCH}, - {"achome", SCANCODE_AC_HOME}, - {"acback", SCANCODE_AC_BACK}, - {"acforward", SCANCODE_AC_FORWARD}, - {"acstop", SCANCODE_AC_STOP}, - {"acrefresh", SCANCODE_AC_REFRESH}, - {"acbookmarks", SCANCODE_AC_BOOKMARKS}, - - {"brightnessdown", SCANCODE_BRIGHTNESSDOWN}, - {"brightnessup", SCANCODE_BRIGHTNESSUP}, - {"displayswitch", SCANCODE_DISPLAYSWITCH}, - {"kbdillumtoggle", SCANCODE_KBDILLUMTOGGLE}, - {"kbdillumdown", SCANCODE_KBDILLUMDOWN}, - {"kbdillumup", SCANCODE_KBDILLUMUP}, - {"eject", SCANCODE_EJECT}, - {"sleep", SCANCODE_SLEEP}, - - {"app1", SCANCODE_APP1}, - {"app2", SCANCODE_APP2}, -}; - -StringMap Keyboard::scancodes(Keyboard::scancodeEntries, sizeof(Keyboard::scancodeEntries)); - -} // keyboard -} // eve +} // namespace eve::keyboard diff --git a/src/modules/keyboard/Keyboard.h b/src/modules/keyboard/Keyboard.h index bbb158ec..79214982 100644 --- a/src/modules/keyboard/Keyboard.h +++ b/src/modules/keyboard/Keyboard.h @@ -1,566 +1,43 @@ #pragma once #include "common/Module.h" + +#include #include -namespace eve::keyboard -{ +namespace eve::keyboard { -class Keyboard : public Module -{ +/** + * Keyboard input. Key / scancode names follow SDL naming + * (SDL_GetKeyName / SDL_GetScancodeName), e.g. "A", "Return", "Left Ctrl". + */ +class Keyboard : public Module { public: + Module_REG(Keyboard); - /** - * Keyboard keys. They are dependent on the current layout of the keyboard. - **/ - enum Key - { - KEY_UNKNOWN, + virtual ~Keyboard(); - KEY_RETURN, - KEY_ESCAPE, - KEY_BACKSPACE, - KEY_TAB, - KEY_SPACE, - KEY_EXCLAIM, - KEY_QUOTEDBL, - KEY_HASH, - KEY_PERCENT, - KEY_DOLLAR, - KEY_AMPERSAND, - KEY_QUOTE, - KEY_LEFTPAREN, - KEY_RIGHTPAREN, - KEY_ASTERISK, - KEY_PLUS, - KEY_COMMA, - KEY_MINUS, - KEY_PERIOD, - KEY_SLASH, - KEY_0, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_COLON, - KEY_SEMICOLON, - KEY_LESS, - KEY_EQUALS, - KEY_GREATER, - KEY_QUESTION, - KEY_AT, + /** Whether held keys emit repeated keypressed events. */ + virtual void setKeyRepeat(bool enable) = 0; + virtual bool hasKeyRepeat() const = 0; - KEY_LEFTBRACKET, - KEY_BACKSLASH, - KEY_RIGHTBRACKET, - KEY_CARET, - KEY_UNDERSCORE, - KEY_BACKQUOTE, - KEY_A, - KEY_B, - KEY_C, - KEY_D, - KEY_E, - KEY_F, - KEY_G, - KEY_H, - KEY_I, - KEY_J, - KEY_K, - KEY_L, - KEY_M, - KEY_N, - KEY_O, - KEY_P, - KEY_Q, - KEY_R, - KEY_S, - KEY_T, - KEY_U, - KEY_V, - KEY_W, - KEY_X, - KEY_Y, - KEY_Z, + /** True if any of the named keys is currently down. */ + virtual bool isDown(const std::string& key) const = 0; + virtual bool isDown(const std::vector& keys) const = 0; - KEY_CAPSLOCK, + /** True if any of the named physical scancodes is currently down. */ + virtual bool isScancodeDown(const std::string& scancode) const = 0; + virtual bool isScancodeDown(const std::vector& scancodes) const = 0; - KEY_F1, - KEY_F2, - KEY_F3, - KEY_F4, - KEY_F5, - KEY_F6, - KEY_F7, - KEY_F8, - KEY_F9, - KEY_F10, - KEY_F11, - KEY_F12, - - KEY_PRINTSCREEN, - KEY_SCROLLLOCK, - KEY_PAUSE, - KEY_INSERT, - KEY_HOME, - KEY_PAGEUP, - KEY_DELETE, - KEY_END, - KEY_PAGEDOWN, - KEY_RIGHT, - KEY_LEFT, - KEY_DOWN, - KEY_UP, - - KEY_NUMLOCKCLEAR, - KEY_KP_DIVIDE, - KEY_KP_MULTIPLY, - KEY_KP_MINUS, - KEY_KP_PLUS, - KEY_KP_ENTER, - KEY_KP_1, - KEY_KP_2, - KEY_KP_3, - KEY_KP_4, - KEY_KP_5, - KEY_KP_6, - KEY_KP_7, - KEY_KP_8, - KEY_KP_9, - KEY_KP_0, - KEY_KP_PERIOD, - KEY_KP_COMMA, - KEY_KP_EQUALS, - - KEY_APPLICATION, - KEY_POWER, - KEY_F13, - KEY_F14, - KEY_F15, - KEY_F16, - KEY_F17, - KEY_F18, - KEY_F19, - KEY_F20, - KEY_F21, - KEY_F22, - KEY_F23, - KEY_F24, - KEY_EXECUTE, - KEY_HELP, - KEY_MENU, - KEY_SELECT, - KEY_STOP, - KEY_AGAIN, - KEY_UNDO, - KEY_CUT, - KEY_COPY, - KEY_PASTE, - KEY_FIND, - KEY_MUTE, - KEY_VOLUMEUP, - KEY_VOLUMEDOWN, - - KEY_ALTERASE, - KEY_SYSREQ, - KEY_CANCEL, - KEY_CLEAR, - KEY_PRIOR, - KEY_RETURN2, - KEY_SEPARATOR, - KEY_OUT, - KEY_OPER, - KEY_CLEARAGAIN, - - KEY_THOUSANDSSEPARATOR, - KEY_DECIMALSEPARATOR, - KEY_CURRENCYUNIT, - KEY_CURRENCYSUBUNIT, - - KEY_LCTRL, - KEY_LSHIFT, - KEY_LALT, - KEY_LGUI, - KEY_RCTRL, - KEY_RSHIFT, - KEY_RALT, - KEY_RGUI, - - KEY_MODE, - - KEY_AUDIONEXT, - KEY_AUDIOPREV, - KEY_AUDIOSTOP, - KEY_AUDIOPLAY, - KEY_AUDIOMUTE, - KEY_MEDIASELECT, - KEY_WWW, - KEY_MAIL, - KEY_CALCULATOR, - KEY_COMPUTER, - KEY_APP_SEARCH, - KEY_APP_HOME, - KEY_APP_BACK, - KEY_APP_FORWARD, - KEY_APP_STOP, - KEY_APP_REFRESH, - KEY_APP_BOOKMARKS, - - KEY_BRIGHTNESSDOWN, - KEY_BRIGHTNESSUP, - KEY_DISPLAYSWITCH, - KEY_KBDILLUMTOGGLE, - KEY_KBDILLUMDOWN, - KEY_KBDILLUMUP, - KEY_EJECT, - KEY_SLEEP, - - KEY_MAX_ENUM - }; - - /** - * Scancodes represent physical keys independent of the current layout. - * Their names may not match the names of the keys printed on the keyboard. - * Some of them are very esoteric... - **/ - enum Scancode - { - SCANCODE_UNKNOWN, - - SCANCODE_A, - SCANCODE_B, - SCANCODE_C, - SCANCODE_D, - SCANCODE_E, - SCANCODE_F, - SCANCODE_G, - SCANCODE_H, - SCANCODE_I, - SCANCODE_J, - SCANCODE_K, - SCANCODE_L, - SCANCODE_M, - SCANCODE_N, - SCANCODE_O, - SCANCODE_P, - SCANCODE_Q, - SCANCODE_R, - SCANCODE_S, - SCANCODE_T, - SCANCODE_U, - SCANCODE_V, - SCANCODE_W, - SCANCODE_X, - SCANCODE_Y, - SCANCODE_Z, - - SCANCODE_1, - SCANCODE_2, - SCANCODE_3, - SCANCODE_4, - SCANCODE_5, - SCANCODE_6, - SCANCODE_7, - SCANCODE_8, - SCANCODE_9, - SCANCODE_0, - - SCANCODE_RETURN, - SCANCODE_ESCAPE, - SCANCODE_BACKSPACE, - SCANCODE_TAB, - SCANCODE_SPACE, - - SCANCODE_MINUS, - SCANCODE_EQUALS, - SCANCODE_LEFTBRACKET, - SCANCODE_RIGHTBRACKET, - SCANCODE_BACKSLASH, - SCANCODE_NONUSHASH, - SCANCODE_SEMICOLON, - SCANCODE_APOSTROPHE, - SCANCODE_GRAVE, - SCANCODE_COMMA, - SCANCODE_PERIOD, - SCANCODE_SLASH, - - SCANCODE_CAPSLOCK, - - SCANCODE_F1, - SCANCODE_F2, - SCANCODE_F3, - SCANCODE_F4, - SCANCODE_F5, - SCANCODE_F6, - SCANCODE_F7, - SCANCODE_F8, - SCANCODE_F9, - SCANCODE_F10, - SCANCODE_F11, - SCANCODE_F12, - - SCANCODE_PRINTSCREEN, - SCANCODE_SCROLLLOCK, - SCANCODE_PAUSE, - SCANCODE_INSERT, - SCANCODE_HOME, - SCANCODE_PAGEUP, - SCANCODE_DELETE, - SCANCODE_END, - SCANCODE_PAGEDOWN, - SCANCODE_RIGHT, - SCANCODE_LEFT, - SCANCODE_DOWN, - SCANCODE_UP, - - SCANCODE_NUMLOCKCLEAR, - SCANCODE_KP_DIVIDE, - SCANCODE_KP_MULTIPLY, - SCANCODE_KP_MINUS, - SCANCODE_KP_PLUS, - SCANCODE_KP_ENTER, - SCANCODE_KP_1, - SCANCODE_KP_2, - SCANCODE_KP_3, - SCANCODE_KP_4, - SCANCODE_KP_5, - SCANCODE_KP_6, - SCANCODE_KP_7, - SCANCODE_KP_8, - SCANCODE_KP_9, - SCANCODE_KP_0, - SCANCODE_KP_PERIOD, - - SCANCODE_NONUSBACKSLASH, - SCANCODE_APPLICATION, - SCANCODE_POWER, - SCANCODE_KP_EQUALS, - SCANCODE_F13, - SCANCODE_F14, - SCANCODE_F15, - SCANCODE_F16, - SCANCODE_F17, - SCANCODE_F18, - SCANCODE_F19, - SCANCODE_F20, - SCANCODE_F21, - SCANCODE_F22, - SCANCODE_F23, - SCANCODE_F24, - SCANCODE_EXECUTE, - SCANCODE_HELP, - SCANCODE_MENU, - SCANCODE_SELECT, - SCANCODE_STOP, - SCANCODE_AGAIN, - SCANCODE_UNDO, - SCANCODE_CUT, - SCANCODE_COPY, - SCANCODE_PASTE, - SCANCODE_FIND, - SCANCODE_MUTE, - SCANCODE_VOLUMEUP, - SCANCODE_VOLUMEDOWN, - SCANCODE_KP_COMMA, - SCANCODE_KP_EQUALSAS400, - - SCANCODE_INTERNATIONAL1, - SCANCODE_INTERNATIONAL2, - SCANCODE_INTERNATIONAL3, - SCANCODE_INTERNATIONAL4, - SCANCODE_INTERNATIONAL5, - SCANCODE_INTERNATIONAL6, - SCANCODE_INTERNATIONAL7, - SCANCODE_INTERNATIONAL8, - SCANCODE_INTERNATIONAL9, - SCANCODE_LANG1, - SCANCODE_LANG2, - SCANCODE_LANG3, - SCANCODE_LANG4, - SCANCODE_LANG5, - SCANCODE_LANG6, - SCANCODE_LANG7, - SCANCODE_LANG8, - SCANCODE_LANG9, - - SCANCODE_ALTERASE, - SCANCODE_SYSREQ, - SCANCODE_CANCEL, - SCANCODE_CLEAR, - SCANCODE_PRIOR, - SCANCODE_RETURN2, - SCANCODE_SEPARATOR, - SCANCODE_OUT, - SCANCODE_OPER, - SCANCODE_CLEARAGAIN, - SCANCODE_CRSEL, - SCANCODE_EXSEL, - - SCANCODE_KP_00, - SCANCODE_KP_000, - SCANCODE_THOUSANDSSEPARATOR, - SCANCODE_DECIMALSEPARATOR, - SCANCODE_CURRENCYUNIT, - SCANCODE_CURRENCYSUBUNIT, - SCANCODE_KP_LEFTPAREN, - SCANCODE_KP_RIGHTPAREN, - SCANCODE_KP_LEFTBRACE, - SCANCODE_KP_RIGHTBRACE, - SCANCODE_KP_TAB, - SCANCODE_KP_BACKSPACE, - SCANCODE_KP_A, - SCANCODE_KP_B, - SCANCODE_KP_C, - SCANCODE_KP_D, - SCANCODE_KP_E, - SCANCODE_KP_F, - SCANCODE_KP_XOR, - SCANCODE_KP_POWER, - SCANCODE_KP_PERCENT, - SCANCODE_KP_LESS, - SCANCODE_KP_GREATER, - SCANCODE_KP_AMPERSAND, - SCANCODE_KP_DBLAMPERSAND, - SCANCODE_KP_VERTICALBAR, - SCANCODE_KP_DBLVERTICALBAR, - SCANCODE_KP_COLON, - SCANCODE_KP_HASH, - SCANCODE_KP_SPACE, - SCANCODE_KP_AT, - SCANCODE_KP_EXCLAM, - SCANCODE_KP_MEMSTORE, - SCANCODE_KP_MEMRECALL, - SCANCODE_KP_MEMCLEAR, - SCANCODE_KP_MEMADD, - SCANCODE_KP_MEMSUBTRACT, - SCANCODE_KP_MEMMULTIPLY, - SCANCODE_KP_MEMDIVIDE, - SCANCODE_KP_PLUSMINUS, - SCANCODE_KP_CLEAR, - SCANCODE_KP_CLEARENTRY, - SCANCODE_KP_BINARY, - SCANCODE_KP_OCTAL, - SCANCODE_KP_DECIMAL, - SCANCODE_KP_HEXADECIMAL, - - SCANCODE_LCTRL, - SCANCODE_LSHIFT, - SCANCODE_LALT, - SCANCODE_LGUI, - SCANCODE_RCTRL, - SCANCODE_RSHIFT, - SCANCODE_RALT, - SCANCODE_RGUI, - - SCANCODE_MODE, - - SCANCODE_AUDIONEXT, - SCANCODE_AUDIOPREV, - SCANCODE_AUDIOSTOP, - SCANCODE_AUDIOPLAY, - SCANCODE_AUDIOMUTE, - SCANCODE_MEDIASELECT, - SCANCODE_WWW, - SCANCODE_MAIL, - SCANCODE_CALCULATOR, - SCANCODE_COMPUTER, - SCANCODE_AC_SEARCH, - SCANCODE_AC_HOME, - SCANCODE_AC_BACK, - SCANCODE_AC_FORWARD, - SCANCODE_AC_STOP, - SCANCODE_AC_REFRESH, - SCANCODE_AC_BOOKMARKS, - - SCANCODE_BRIGHTNESSDOWN, - SCANCODE_BRIGHTNESSUP, - SCANCODE_DISPLAYSWITCH, - SCANCODE_KBDILLUMTOGGLE, - SCANCODE_KBDILLUMDOWN, - SCANCODE_KBDILLUMUP, - SCANCODE_EJECT, - SCANCODE_SLEEP, - - SCANCODE_APP1, - SCANCODE_APP2, - - SCANCODE_MAX_ENUM - }; - - virtual ~Keyboard() {} - - // Implements Module. - virtual ModuleType getModuleType() const { return M_KEYBOARD; } - - /** - * Sets whether repeat keypress events should be sent if a key is held down. - * Does not affect text input events. - * @param enable Whether to send repeat key press events. - **/ - virtual void setKeyRepeat(bool enable) = 0; - - /** - * Gets whether repeat keypress events will be sent if a key is held down. - **/ - virtual bool hasKeyRepeat() const = 0; - - /** - * Checks whether certain keys are pressed or not. - * @param keylist A list of key identifiers. - * @return Whether any of the specified keys are pressed. - **/ - virtual bool isDown(const std::vector &keylist) const = 0; - - /** - * Checks whether certain scancodes are pressed or not. - * @param scancodelist A list of scancodes. - * @return Whether any of the specified scancodes are pressed. - **/ - virtual bool isScancodeDown(const std::vector &scancodelist) const = 0; - - /** - * Gets the key corresponding to the specified scancode according to the - * current keyboard layout. - **/ - virtual Key getKeyFromScancode(Scancode scancode) const = 0; - - /** - * Gets the scancode corresponding to the specified key according to the - * current keyboard layout. - **/ - virtual Scancode getScancodeFromKey(Key key) const = 0; - - /** - * Sets whether text input events should be received. - * @param enable Whether to receive text input events. - **/ - virtual void setTextInput(bool enable) = 0; - - /** - * Sets whether text input events should be received, and specifies where - * on the screen the text will appear. This is used as a hint so on-screen - * keyboards don't cover the text area. - **/ - virtual void setTextInput(bool enable, double x, double y, double w, double h) = 0; - - /** - * Gets whether text input events are enabled. - **/ - virtual bool hasTextInput() const = 0; - - /** - * Gets whether the system will display an on-screen keyboard when text input - * events are enabled. - **/ - virtual bool hasScreenKeyboard() const = 0; - -}; - -} // eve::keyboard + /** Layout-aware key <-> physical scancode conversion (SDL names). */ + virtual std::string getKeyFromScancode(const std::string& scancode) const = 0; + virtual std::string getScancodeFromKey(const std::string& key) const = 0; + virtual void setTextInput(bool enable) = 0; + /** Hint where text appears so on-screen keyboards avoid covering it (pixel coords). */ + virtual void setTextInput(bool enable, double x, double y, double w, double h) = 0; + virtual bool hasTextInput() const = 0; + virtual bool hasScreenKeyboard() const = 0; +}; +} // namespace eve::keyboard diff --git a/src/modules/keyboard/sdl/Keyboard.cpp b/src/modules/keyboard/sdl/Keyboard.cpp index 1321014c..373308de 100644 --- a/src/modules/keyboard/sdl/Keyboard.cpp +++ b/src/modules/keyboard/sdl/Keyboard.cpp @@ -1,638 +1,106 @@ -/** - * Copyright (c) 2006-2021 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ +#include "keyboard/sdl/Keyboard.h" -#include "Keyboard.h" #include "window/Window.h" -namespace eve -{ -namespace keyboard -{ -namespace sdl -{ +#include +#include -Keyboard::Keyboard() - : key_repeat(false) -{ +namespace eve::keyboard::sdl { + +Keyboard::Keyboard() { + // Video subsystem owns the keyboard state / text input on most backends. + SDL_InitSubSystem(SDL_INIT_VIDEO); } -const char *Keyboard::getName() const -{ - return "love.keyboard.sdl"; +Keyboard::~Keyboard() { + SDL_QuitSubSystem(SDL_INIT_VIDEO); } -void Keyboard::setKeyRepeat(bool enable) -{ - key_repeat = enable; +void Keyboard::setKeyRepeat(bool enable) { + keyRepeat_ = enable; } -bool Keyboard::hasKeyRepeat() const -{ - return key_repeat; +bool Keyboard::hasKeyRepeat() const { + return keyRepeat_; } -bool Keyboard::isDown(const std::vector &keylist) const -{ - const Uint8 *state = SDL_GetKeyboardState(nullptr); - - for (Key key : keylist) - { - SDL_Scancode scancode = SDL_GetScancodeFromKey(keymap[key]); - - if (state[scancode]) - return true; - } - - return false; +bool Keyboard::isDown(const std::string& key) const { + return isDown(std::vector{key}); } -bool Keyboard::isScancodeDown(const std::vector &scancodelist) const -{ - const Uint8 *state = SDL_GetKeyboardState(nullptr); +bool Keyboard::isDown(const std::vector& keys) const { + const Uint8* state = SDL_GetKeyboardState(nullptr); + if (!state) return false; - for (Scancode scancode : scancodelist) - { - SDL_Scancode sdlcode = SDL_SCANCODE_UNKNOWN; - - if (scancodes.find(scancode, sdlcode) && state[sdlcode]) - return true; - } - - return false; + for (const auto& name : keys) { + SDL_Keycode key = SDL_GetKeyFromName(name.c_str()); + if (key == SDLK_UNKNOWN) continue; + SDL_Scancode scancode = SDL_GetScancodeFromKey(key); + if (scancode != SDL_SCANCODE_UNKNOWN && state[scancode]) return true; + } + return false; } -Keyboard::Key Keyboard::getKeyFromScancode(Scancode scancode) const -{ - SDL_Scancode sdlscancode = SDL_SCANCODE_UNKNOWN; - scancodes.find(scancode, sdlscancode); - - SDL_Keycode sdlkey = SDL_GetKeyFromScancode(sdlscancode); - - for (int i = 0; i < KEY_MAX_ENUM; i++) - { - if (keymap[i] == sdlkey) - return (Key) i; - } - - return KEY_UNKNOWN; +bool Keyboard::isScancodeDown(const std::string& scancode) const { + return isScancodeDown(std::vector{scancode}); } -Keyboard::Scancode Keyboard::getScancodeFromKey(Key key) const -{ - Scancode scancode = SCANCODE_UNKNOWN; +bool Keyboard::isScancodeDown(const std::vector& scancodes) const { + const Uint8* state = SDL_GetKeyboardState(nullptr); + if (!state) return false; - if (key != KEY_MAX_ENUM) - { - SDL_Keycode sdlkey = keymap[key]; - - SDL_Scancode sdlscancode = SDL_GetScancodeFromKey(sdlkey); - scancodes.find(sdlscancode, scancode); - } - - return scancode; + for (const auto& name : scancodes) { + SDL_Scancode scancode = SDL_GetScancodeFromName(name.c_str()); + if (scancode != SDL_SCANCODE_UNKNOWN && state[scancode]) return true; + } + return false; } -void Keyboard::setTextInput(bool enable) -{ - if (enable) - SDL_StartTextInput(); - else - SDL_StopTextInput(); +std::string Keyboard::getKeyFromScancode(const std::string& scancode) const { + SDL_Scancode sdlScancode = SDL_GetScancodeFromName(scancode.c_str()); + if (sdlScancode == SDL_SCANCODE_UNKNOWN) return {}; + SDL_Keycode key = SDL_GetKeyFromScancode(sdlScancode); + if (key == SDLK_UNKNOWN) return {}; + const char* name = SDL_GetKeyName(key); + return name ? name : std::string{}; } -void Keyboard::setTextInput(bool enable, double x, double y, double w, double h) -{ - // SDL_SetTextInputRect expects coordinates in window-space but setTextInput - // takes pixels, so we should convert. - auto window = Module::getInstance(M_WINDOW); - if (window) - { - window->DPIToWindowCoords(&x, &y); - window->DPIToWindowCoords(&w, &h); - } - - SDL_Rect rect = {(int) x, (int) y, (int) w, (int) h}; - SDL_SetTextInputRect(&rect); - - setTextInput(enable); +std::string Keyboard::getScancodeFromKey(const std::string& key) const { + SDL_Keycode sdlKey = SDL_GetKeyFromName(key.c_str()); + if (sdlKey == SDLK_UNKNOWN) return {}; + SDL_Scancode scancode = SDL_GetScancodeFromKey(sdlKey); + if (scancode == SDL_SCANCODE_UNKNOWN) return {}; + const char* name = SDL_GetScancodeName(scancode); + return name ? name : std::string{}; } -bool Keyboard::hasTextInput() const -{ - return SDL_IsTextInputActive() != SDL_FALSE; +void Keyboard::setTextInput(bool enable) { + if (enable) + SDL_StartTextInput(); + else + SDL_StopTextInput(); } -bool Keyboard::hasScreenKeyboard() const -{ - return SDL_HasScreenKeyboardSupport() != SDL_FALSE; +void Keyboard::setTextInput(bool enable, double x, double y, double w, double h) { + // SDL_SetTextInputRect expects window coords; API takes pixels. + auto* window = getModInst(window, Window); + if (window) { + window->DPIToWindowCoords(&x, &y); + window->DPIToWindowCoords(&w, &h); + } + + SDL_Rect rect = {static_cast(x), static_cast(y), static_cast(w), + static_cast(h)}; + SDL_SetTextInputRect(&rect); + setTextInput(enable); } -bool Keyboard::getConstant(Scancode in, SDL_Scancode &out) -{ - return scancodes.find(in, out); +bool Keyboard::hasTextInput() const { + return SDL_IsTextInputActive() != SDL_FALSE; } -bool Keyboard::getConstant(SDL_Scancode in, Scancode &out) -{ - return scancodes.find(in, out); +bool Keyboard::hasScreenKeyboard() const { + return SDL_HasScreenKeyboardSupport() != SDL_FALSE; } -const SDL_Keycode *Keyboard::createKeyMap() -{ - // Array must be static so its lifetime continues once the function returns. - static SDL_Keycode k[Keyboard::KEY_MAX_ENUM] = {SDLK_UNKNOWN}; - - k[Keyboard::KEY_UNKNOWN] = SDLK_UNKNOWN; - - k[Keyboard::KEY_RETURN] = SDLK_RETURN; - k[Keyboard::KEY_ESCAPE] = SDLK_ESCAPE; - k[Keyboard::KEY_BACKSPACE] = SDLK_BACKSPACE; - k[Keyboard::KEY_TAB] = SDLK_TAB; - k[Keyboard::KEY_SPACE] = SDLK_SPACE; - k[Keyboard::KEY_EXCLAIM] = SDLK_EXCLAIM; - k[Keyboard::KEY_QUOTEDBL] = SDLK_QUOTEDBL; - k[Keyboard::KEY_HASH] = SDLK_HASH; - k[Keyboard::KEY_PERCENT] = SDLK_PERCENT; - k[Keyboard::KEY_DOLLAR] = SDLK_DOLLAR; - k[Keyboard::KEY_AMPERSAND] = SDLK_AMPERSAND; - k[Keyboard::KEY_QUOTE] = SDLK_QUOTE; - k[Keyboard::KEY_LEFTPAREN] = SDLK_LEFTPAREN; - k[Keyboard::KEY_RIGHTPAREN] = SDLK_RIGHTPAREN; - k[Keyboard::KEY_ASTERISK] = SDLK_ASTERISK; - k[Keyboard::KEY_PLUS] = SDLK_PLUS; - k[Keyboard::KEY_COMMA] = SDLK_COMMA; - k[Keyboard::KEY_MINUS] = SDLK_MINUS; - k[Keyboard::KEY_PERIOD] = SDLK_PERIOD; - k[Keyboard::KEY_SLASH] = SDLK_SLASH; - k[Keyboard::KEY_0] = SDLK_0; - k[Keyboard::KEY_1] = SDLK_1; - k[Keyboard::KEY_2] = SDLK_2; - k[Keyboard::KEY_3] = SDLK_3; - k[Keyboard::KEY_4] = SDLK_4; - k[Keyboard::KEY_5] = SDLK_5; - k[Keyboard::KEY_6] = SDLK_6; - k[Keyboard::KEY_7] = SDLK_7; - k[Keyboard::KEY_8] = SDLK_8; - k[Keyboard::KEY_9] = SDLK_9; - k[Keyboard::KEY_COLON] = SDLK_COLON; - k[Keyboard::KEY_SEMICOLON] = SDLK_SEMICOLON; - k[Keyboard::KEY_LESS] = SDLK_LESS; - k[Keyboard::KEY_EQUALS] = SDLK_EQUALS; - k[Keyboard::KEY_GREATER] = SDLK_GREATER; - k[Keyboard::KEY_QUESTION] = SDLK_QUESTION; - k[Keyboard::KEY_AT] = SDLK_AT; - - k[Keyboard::KEY_LEFTBRACKET] = SDLK_LEFTBRACKET; - k[Keyboard::KEY_BACKSLASH] = SDLK_BACKSLASH; - k[Keyboard::KEY_RIGHTBRACKET] = SDLK_RIGHTBRACKET; - k[Keyboard::KEY_CARET] = SDLK_CARET; - k[Keyboard::KEY_UNDERSCORE] = SDLK_UNDERSCORE; - k[Keyboard::KEY_BACKQUOTE] = SDLK_BACKQUOTE; - k[Keyboard::KEY_A] = SDLK_a; - k[Keyboard::KEY_B] = SDLK_b; - k[Keyboard::KEY_C] = SDLK_c; - k[Keyboard::KEY_D] = SDLK_d; - k[Keyboard::KEY_E] = SDLK_e; - k[Keyboard::KEY_F] = SDLK_f; - k[Keyboard::KEY_G] = SDLK_g; - k[Keyboard::KEY_H] = SDLK_h; - k[Keyboard::KEY_I] = SDLK_i; - k[Keyboard::KEY_J] = SDLK_j; - k[Keyboard::KEY_K] = SDLK_k; - k[Keyboard::KEY_L] = SDLK_l; - k[Keyboard::KEY_M] = SDLK_m; - k[Keyboard::KEY_N] = SDLK_n; - k[Keyboard::KEY_O] = SDLK_o; - k[Keyboard::KEY_P] = SDLK_p; - k[Keyboard::KEY_Q] = SDLK_q; - k[Keyboard::KEY_R] = SDLK_r; - k[Keyboard::KEY_S] = SDLK_s; - k[Keyboard::KEY_T] = SDLK_t; - k[Keyboard::KEY_U] = SDLK_u; - k[Keyboard::KEY_V] = SDLK_v; - k[Keyboard::KEY_W] = SDLK_w; - k[Keyboard::KEY_X] = SDLK_x; - k[Keyboard::KEY_Y] = SDLK_y; - k[Keyboard::KEY_Z] = SDLK_z; - - k[Keyboard::KEY_CAPSLOCK] = SDLK_CAPSLOCK; - - k[Keyboard::KEY_F1] = SDLK_F1; - k[Keyboard::KEY_F2] = SDLK_F2; - k[Keyboard::KEY_F3] = SDLK_F3; - k[Keyboard::KEY_F4] = SDLK_F4; - k[Keyboard::KEY_F5] = SDLK_F5; - k[Keyboard::KEY_F6] = SDLK_F6; - k[Keyboard::KEY_F7] = SDLK_F7; - k[Keyboard::KEY_F8] = SDLK_F8; - k[Keyboard::KEY_F9] = SDLK_F9; - k[Keyboard::KEY_F10] = SDLK_F10; - k[Keyboard::KEY_F11] = SDLK_F11; - k[Keyboard::KEY_F12] = SDLK_F12; - - k[Keyboard::KEY_PRINTSCREEN] = SDLK_PRINTSCREEN; - k[Keyboard::KEY_SCROLLLOCK] = SDLK_SCROLLLOCK; - k[Keyboard::KEY_PAUSE] = SDLK_PAUSE; - k[Keyboard::KEY_INSERT] = SDLK_INSERT; - k[Keyboard::KEY_HOME] = SDLK_HOME; - k[Keyboard::KEY_PAGEUP] = SDLK_PAGEUP; - k[Keyboard::KEY_DELETE] = SDLK_DELETE; - k[Keyboard::KEY_END] = SDLK_END; - k[Keyboard::KEY_PAGEDOWN] = SDLK_PAGEDOWN; - k[Keyboard::KEY_RIGHT] = SDLK_RIGHT; - k[Keyboard::KEY_LEFT] = SDLK_LEFT; - k[Keyboard::KEY_DOWN] = SDLK_DOWN; - k[Keyboard::KEY_UP] = SDLK_UP; - - k[Keyboard::KEY_NUMLOCKCLEAR] = SDLK_NUMLOCKCLEAR; - k[Keyboard::KEY_KP_DIVIDE] = SDLK_KP_DIVIDE; - k[Keyboard::KEY_KP_MULTIPLY] = SDLK_KP_MULTIPLY; - k[Keyboard::KEY_KP_MINUS] = SDLK_KP_MINUS; - k[Keyboard::KEY_KP_PLUS] = SDLK_KP_PLUS; - k[Keyboard::KEY_KP_ENTER] = SDLK_KP_ENTER; - k[Keyboard::KEY_KP_0] = SDLK_KP_0; - k[Keyboard::KEY_KP_1] = SDLK_KP_1; - k[Keyboard::KEY_KP_2] = SDLK_KP_2; - k[Keyboard::KEY_KP_3] = SDLK_KP_3; - k[Keyboard::KEY_KP_4] = SDLK_KP_4; - k[Keyboard::KEY_KP_5] = SDLK_KP_5; - k[Keyboard::KEY_KP_6] = SDLK_KP_6; - k[Keyboard::KEY_KP_7] = SDLK_KP_7; - k[Keyboard::KEY_KP_8] = SDLK_KP_8; - k[Keyboard::KEY_KP_9] = SDLK_KP_9; - k[Keyboard::KEY_KP_PERIOD] = SDLK_KP_PERIOD; - k[Keyboard::KEY_KP_COMMA] = SDLK_KP_COMMA; - k[Keyboard::KEY_KP_EQUALS] = SDLK_KP_EQUALS; - - k[Keyboard::KEY_APPLICATION] = SDLK_APPLICATION; - k[Keyboard::KEY_POWER] = SDLK_POWER; - k[Keyboard::KEY_F13] = SDLK_F13; - k[Keyboard::KEY_F14] = SDLK_F14; - k[Keyboard::KEY_F15] = SDLK_F15; - k[Keyboard::KEY_F16] = SDLK_F16; - k[Keyboard::KEY_F17] = SDLK_F17; - k[Keyboard::KEY_F18] = SDLK_F18; - k[Keyboard::KEY_F19] = SDLK_F19; - k[Keyboard::KEY_F20] = SDLK_F20; - k[Keyboard::KEY_F21] = SDLK_F21; - k[Keyboard::KEY_F22] = SDLK_F22; - k[Keyboard::KEY_F23] = SDLK_F23; - k[Keyboard::KEY_F24] = SDLK_F24; - k[Keyboard::KEY_EXECUTE] = SDLK_EXECUTE; - k[Keyboard::KEY_HELP] = SDLK_HELP; - k[Keyboard::KEY_MENU] = SDLK_MENU; - k[Keyboard::KEY_SELECT] = SDLK_SELECT; - k[Keyboard::KEY_STOP] = SDLK_STOP; - k[Keyboard::KEY_AGAIN] = SDLK_AGAIN; - k[Keyboard::KEY_UNDO] = SDLK_UNDO; - k[Keyboard::KEY_CUT] = SDLK_CUT; - k[Keyboard::KEY_COPY] = SDLK_COPY; - k[Keyboard::KEY_PASTE] = SDLK_PASTE; - k[Keyboard::KEY_FIND] = SDLK_FIND; - k[Keyboard::KEY_MUTE] = SDLK_MUTE; - k[Keyboard::KEY_VOLUMEUP] = SDLK_VOLUMEUP; - k[Keyboard::KEY_VOLUMEDOWN] = SDLK_VOLUMEDOWN; - - k[Keyboard::KEY_ALTERASE] = SDLK_ALTERASE; - k[Keyboard::KEY_SYSREQ] = SDLK_SYSREQ; - k[Keyboard::KEY_CANCEL] = SDLK_CANCEL; - k[Keyboard::KEY_CLEAR] = SDLK_CLEAR; - k[Keyboard::KEY_PRIOR] = SDLK_PRIOR; - k[Keyboard::KEY_RETURN2] = SDLK_RETURN2; - k[Keyboard::KEY_SEPARATOR] = SDLK_SEPARATOR; - k[Keyboard::KEY_OUT] = SDLK_OUT; - k[Keyboard::KEY_OPER] = SDLK_OPER; - k[Keyboard::KEY_CLEARAGAIN] = SDLK_CLEARAGAIN; - - k[Keyboard::KEY_THOUSANDSSEPARATOR] = SDLK_THOUSANDSSEPARATOR; - k[Keyboard::KEY_DECIMALSEPARATOR] = SDLK_DECIMALSEPARATOR; - k[Keyboard::KEY_CURRENCYUNIT] = SDLK_CURRENCYUNIT; - k[Keyboard::KEY_CURRENCYSUBUNIT] = SDLK_CURRENCYSUBUNIT; - - k[Keyboard::KEY_LCTRL] = SDLK_LCTRL; - k[Keyboard::KEY_LSHIFT] = SDLK_LSHIFT; - k[Keyboard::KEY_LALT] = SDLK_LALT; - k[Keyboard::KEY_LGUI] = SDLK_LGUI; - k[Keyboard::KEY_RCTRL] = SDLK_RCTRL; - k[Keyboard::KEY_RSHIFT] = SDLK_RSHIFT; - k[Keyboard::KEY_RALT] = SDLK_RALT; - k[Keyboard::KEY_RGUI] = SDLK_RGUI; - - k[Keyboard::KEY_MODE] = SDLK_MODE; - - k[Keyboard::KEY_AUDIONEXT] = SDLK_AUDIONEXT; - k[Keyboard::KEY_AUDIOPREV] = SDLK_AUDIOPREV; - k[Keyboard::KEY_AUDIOSTOP] = SDLK_AUDIOSTOP; - k[Keyboard::KEY_AUDIOPLAY] = SDLK_AUDIOPLAY; - k[Keyboard::KEY_AUDIOMUTE] = SDLK_AUDIOMUTE; - k[Keyboard::KEY_MEDIASELECT] = SDLK_MEDIASELECT; - k[Keyboard::KEY_WWW] = SDLK_WWW; - k[Keyboard::KEY_MAIL] = SDLK_MAIL; - k[Keyboard::KEY_CALCULATOR] = SDLK_CALCULATOR; - k[Keyboard::KEY_COMPUTER] = SDLK_COMPUTER; - k[Keyboard::KEY_APP_SEARCH] = SDLK_AC_SEARCH; - k[Keyboard::KEY_APP_HOME] = SDLK_AC_HOME; - k[Keyboard::KEY_APP_BACK] = SDLK_AC_BACK; - k[Keyboard::KEY_APP_FORWARD] = SDLK_AC_FORWARD; - k[Keyboard::KEY_APP_STOP] = SDLK_AC_STOP; - k[Keyboard::KEY_APP_REFRESH] = SDLK_AC_REFRESH; - k[Keyboard::KEY_APP_BOOKMARKS] = SDLK_AC_BOOKMARKS; - - k[Keyboard::KEY_BRIGHTNESSDOWN] = SDLK_BRIGHTNESSDOWN; - k[Keyboard::KEY_BRIGHTNESSUP] = SDLK_BRIGHTNESSUP; - k[Keyboard::KEY_DISPLAYSWITCH] = SDLK_DISPLAYSWITCH; - k[Keyboard::KEY_KBDILLUMTOGGLE] = SDLK_KBDILLUMTOGGLE; - k[Keyboard::KEY_KBDILLUMDOWN] = SDLK_KBDILLUMDOWN; - k[Keyboard::KEY_KBDILLUMUP] = SDLK_KBDILLUMUP; - k[Keyboard::KEY_EJECT] = SDLK_EJECT; - k[Keyboard::KEY_SLEEP] = SDLK_SLEEP; - - return k; -} - -const SDL_Keycode *Keyboard::keymap = Keyboard::createKeyMap(); - -EnumMap::Entry Keyboard::scancodeEntries[] = -{ - {SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN}, - - {SCANCODE_A, SDL_SCANCODE_A}, - {SCANCODE_B, SDL_SCANCODE_B}, - {SCANCODE_C, SDL_SCANCODE_C}, - {SCANCODE_D, SDL_SCANCODE_D}, - {SCANCODE_E, SDL_SCANCODE_E}, - {SCANCODE_F, SDL_SCANCODE_F}, - {SCANCODE_G, SDL_SCANCODE_G}, - {SCANCODE_H, SDL_SCANCODE_H}, - {SCANCODE_I, SDL_SCANCODE_I}, - {SCANCODE_J, SDL_SCANCODE_J}, - {SCANCODE_K, SDL_SCANCODE_K}, - {SCANCODE_L, SDL_SCANCODE_L}, - {SCANCODE_M, SDL_SCANCODE_M}, - {SCANCODE_N, SDL_SCANCODE_N}, - {SCANCODE_O, SDL_SCANCODE_O}, - {SCANCODE_P, SDL_SCANCODE_P}, - {SCANCODE_Q, SDL_SCANCODE_Q}, - {SCANCODE_R, SDL_SCANCODE_R}, - {SCANCODE_S, SDL_SCANCODE_S}, - {SCANCODE_T, SDL_SCANCODE_T}, - {SCANCODE_U, SDL_SCANCODE_U}, - {SCANCODE_V, SDL_SCANCODE_V}, - {SCANCODE_W, SDL_SCANCODE_W}, - {SCANCODE_X, SDL_SCANCODE_X}, - {SCANCODE_Y, SDL_SCANCODE_Y}, - {SCANCODE_Z, SDL_SCANCODE_Z}, - - {SCANCODE_1, SDL_SCANCODE_1}, - {SCANCODE_2, SDL_SCANCODE_2}, - {SCANCODE_3, SDL_SCANCODE_3}, - {SCANCODE_4, SDL_SCANCODE_4}, - {SCANCODE_5, SDL_SCANCODE_5}, - {SCANCODE_6, SDL_SCANCODE_6}, - {SCANCODE_7, SDL_SCANCODE_7}, - {SCANCODE_8, SDL_SCANCODE_8}, - {SCANCODE_9, SDL_SCANCODE_9}, - {SCANCODE_0, SDL_SCANCODE_0}, - - {SCANCODE_RETURN, SDL_SCANCODE_RETURN}, - {SCANCODE_ESCAPE, SDL_SCANCODE_ESCAPE}, - {SCANCODE_BACKSPACE, SDL_SCANCODE_BACKSPACE}, - {SCANCODE_TAB, SDL_SCANCODE_TAB}, - {SCANCODE_SPACE, SDL_SCANCODE_SPACE}, - - {SCANCODE_MINUS, SDL_SCANCODE_MINUS}, - {SCANCODE_EQUALS, SDL_SCANCODE_EQUALS}, - {SCANCODE_LEFTBRACKET, SDL_SCANCODE_LEFTBRACKET}, - {SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RIGHTBRACKET}, - {SCANCODE_BACKSLASH, SDL_SCANCODE_BACKSLASH}, - {SCANCODE_NONUSHASH, SDL_SCANCODE_NONUSHASH}, - {SCANCODE_SEMICOLON, SDL_SCANCODE_SEMICOLON}, - {SCANCODE_APOSTROPHE, SDL_SCANCODE_APOSTROPHE}, - {SCANCODE_GRAVE, SDL_SCANCODE_GRAVE}, - {SCANCODE_COMMA, SDL_SCANCODE_COMMA}, - {SCANCODE_PERIOD, SDL_SCANCODE_PERIOD}, - {SCANCODE_SLASH, SDL_SCANCODE_SLASH}, - - {SCANCODE_CAPSLOCK, SDL_SCANCODE_CAPSLOCK}, - - {SCANCODE_F1, SDL_SCANCODE_F1}, - {SCANCODE_F2, SDL_SCANCODE_F2}, - {SCANCODE_F3, SDL_SCANCODE_F3}, - {SCANCODE_F4, SDL_SCANCODE_F4}, - {SCANCODE_F5, SDL_SCANCODE_F5}, - {SCANCODE_F6, SDL_SCANCODE_F6}, - {SCANCODE_F7, SDL_SCANCODE_F7}, - {SCANCODE_F8, SDL_SCANCODE_F8}, - {SCANCODE_F9, SDL_SCANCODE_F9}, - {SCANCODE_F10, SDL_SCANCODE_F10}, - {SCANCODE_F11, SDL_SCANCODE_F11}, - {SCANCODE_F12, SDL_SCANCODE_F12}, - - {SCANCODE_PRINTSCREEN, SDL_SCANCODE_PRINTSCREEN}, - {SCANCODE_SCROLLLOCK, SDL_SCANCODE_SCROLLLOCK}, - {SCANCODE_PAUSE, SDL_SCANCODE_PAUSE}, - {SCANCODE_INSERT, SDL_SCANCODE_INSERT}, - {SCANCODE_HOME, SDL_SCANCODE_HOME}, - {SCANCODE_PAGEUP, SDL_SCANCODE_PAGEUP}, - {SCANCODE_DELETE, SDL_SCANCODE_DELETE}, - {SCANCODE_END, SDL_SCANCODE_END}, - {SCANCODE_PAGEDOWN, SDL_SCANCODE_PAGEDOWN}, - {SCANCODE_RIGHT, SDL_SCANCODE_RIGHT}, - {SCANCODE_LEFT, SDL_SCANCODE_LEFT}, - {SCANCODE_DOWN, SDL_SCANCODE_DOWN}, - {SCANCODE_UP, SDL_SCANCODE_UP}, - - {SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_NUMLOCKCLEAR}, - {SCANCODE_KP_DIVIDE, SDL_SCANCODE_KP_DIVIDE}, - {SCANCODE_KP_MULTIPLY, SDL_SCANCODE_KP_MULTIPLY}, - {SCANCODE_KP_MINUS, SDL_SCANCODE_KP_MINUS}, - {SCANCODE_KP_PLUS, SDL_SCANCODE_KP_PLUS}, - {SCANCODE_KP_ENTER, SDL_SCANCODE_KP_ENTER}, - {SCANCODE_KP_1, SDL_SCANCODE_KP_1}, - {SCANCODE_KP_2, SDL_SCANCODE_KP_2}, - {SCANCODE_KP_3, SDL_SCANCODE_KP_3}, - {SCANCODE_KP_4, SDL_SCANCODE_KP_4}, - {SCANCODE_KP_5, SDL_SCANCODE_KP_5}, - {SCANCODE_KP_6, SDL_SCANCODE_KP_6}, - {SCANCODE_KP_7, SDL_SCANCODE_KP_7}, - {SCANCODE_KP_8, SDL_SCANCODE_KP_8}, - {SCANCODE_KP_9, SDL_SCANCODE_KP_9}, - {SCANCODE_KP_0, SDL_SCANCODE_KP_0}, - {SCANCODE_KP_PERIOD, SDL_SCANCODE_KP_PERIOD}, - - {SCANCODE_NONUSBACKSLASH, SDL_SCANCODE_NONUSBACKSLASH}, - {SCANCODE_APPLICATION, SDL_SCANCODE_APPLICATION}, - {SCANCODE_POWER, SDL_SCANCODE_POWER}, - {SCANCODE_KP_EQUALS, SDL_SCANCODE_KP_EQUALS}, - {SCANCODE_F13, SDL_SCANCODE_F13}, - {SCANCODE_F14, SDL_SCANCODE_F14}, - {SCANCODE_F15, SDL_SCANCODE_F15}, - {SCANCODE_F16, SDL_SCANCODE_F16}, - {SCANCODE_F17, SDL_SCANCODE_F17}, - {SCANCODE_F18, SDL_SCANCODE_F18}, - {SCANCODE_F19, SDL_SCANCODE_F19}, - {SCANCODE_F20, SDL_SCANCODE_F20}, - {SCANCODE_F21, SDL_SCANCODE_F21}, - {SCANCODE_F22, SDL_SCANCODE_F22}, - {SCANCODE_F23, SDL_SCANCODE_F23}, - {SCANCODE_F24, SDL_SCANCODE_F24}, - {SCANCODE_EXECUTE, SDL_SCANCODE_EXECUTE}, - {SCANCODE_HELP, SDL_SCANCODE_HELP}, - {SCANCODE_MENU, SDL_SCANCODE_MENU}, - {SCANCODE_SELECT, SDL_SCANCODE_SELECT}, - {SCANCODE_STOP, SDL_SCANCODE_STOP}, - {SCANCODE_AGAIN, SDL_SCANCODE_AGAIN}, - {SCANCODE_UNDO, SDL_SCANCODE_UNDO}, - {SCANCODE_CUT, SDL_SCANCODE_CUT}, - {SCANCODE_COPY, SDL_SCANCODE_COPY}, - {SCANCODE_PASTE, SDL_SCANCODE_PASTE}, - {SCANCODE_FIND, SDL_SCANCODE_FIND}, - {SCANCODE_MUTE, SDL_SCANCODE_MUTE}, - {SCANCODE_VOLUMEUP, SDL_SCANCODE_VOLUMEUP}, - {SCANCODE_VOLUMEDOWN, SDL_SCANCODE_VOLUMEDOWN}, - {SCANCODE_KP_COMMA, SDL_SCANCODE_KP_COMMA}, - {SCANCODE_KP_EQUALSAS400, SDL_SCANCODE_KP_EQUALSAS400}, - - {SCANCODE_INTERNATIONAL1, SDL_SCANCODE_INTERNATIONAL1}, - {SCANCODE_INTERNATIONAL2, SDL_SCANCODE_INTERNATIONAL2}, - {SCANCODE_INTERNATIONAL3, SDL_SCANCODE_INTERNATIONAL3}, - {SCANCODE_INTERNATIONAL4, SDL_SCANCODE_INTERNATIONAL4}, - {SCANCODE_INTERNATIONAL5, SDL_SCANCODE_INTERNATIONAL5}, - {SCANCODE_INTERNATIONAL6, SDL_SCANCODE_INTERNATIONAL6}, - {SCANCODE_INTERNATIONAL7, SDL_SCANCODE_INTERNATIONAL7}, - {SCANCODE_INTERNATIONAL8, SDL_SCANCODE_INTERNATIONAL8}, - {SCANCODE_INTERNATIONAL9, SDL_SCANCODE_INTERNATIONAL9}, - {SCANCODE_LANG1, SDL_SCANCODE_LANG1}, - {SCANCODE_LANG2, SDL_SCANCODE_LANG2}, - {SCANCODE_LANG3, SDL_SCANCODE_LANG3}, - {SCANCODE_LANG4, SDL_SCANCODE_LANG4}, - {SCANCODE_LANG5, SDL_SCANCODE_LANG5}, - {SCANCODE_LANG6, SDL_SCANCODE_LANG6}, - {SCANCODE_LANG7, SDL_SCANCODE_LANG7}, - {SCANCODE_LANG8, SDL_SCANCODE_LANG8}, - {SCANCODE_LANG9, SDL_SCANCODE_LANG9}, - - {SCANCODE_ALTERASE, SDL_SCANCODE_ALTERASE}, - {SCANCODE_SYSREQ, SDL_SCANCODE_SYSREQ}, - {SCANCODE_CANCEL, SDL_SCANCODE_CANCEL}, - {SCANCODE_CLEAR, SDL_SCANCODE_CLEAR}, - {SCANCODE_PRIOR, SDL_SCANCODE_PRIOR}, - {SCANCODE_RETURN2, SDL_SCANCODE_RETURN2}, - {SCANCODE_SEPARATOR, SDL_SCANCODE_SEPARATOR}, - {SCANCODE_OUT, SDL_SCANCODE_OUT}, - {SCANCODE_OPER, SDL_SCANCODE_OPER}, - {SCANCODE_CLEARAGAIN, SDL_SCANCODE_CLEARAGAIN}, - {SCANCODE_CRSEL, SDL_SCANCODE_CRSEL}, - {SCANCODE_EXSEL, SDL_SCANCODE_EXSEL}, - - {SCANCODE_KP_00, SDL_SCANCODE_KP_00}, - {SCANCODE_KP_000, SDL_SCANCODE_KP_000}, - {SCANCODE_THOUSANDSSEPARATOR, SDL_SCANCODE_THOUSANDSSEPARATOR}, - {SCANCODE_DECIMALSEPARATOR, SDL_SCANCODE_DECIMALSEPARATOR}, - {SCANCODE_CURRENCYUNIT, SDL_SCANCODE_CURRENCYUNIT}, - {SCANCODE_CURRENCYSUBUNIT, SDL_SCANCODE_CURRENCYSUBUNIT}, - {SCANCODE_KP_LEFTPAREN, SDL_SCANCODE_KP_LEFTPAREN}, - {SCANCODE_KP_RIGHTPAREN, SDL_SCANCODE_KP_RIGHTPAREN}, - {SCANCODE_KP_LEFTBRACE, SDL_SCANCODE_KP_LEFTBRACE}, - {SCANCODE_KP_RIGHTBRACE, SDL_SCANCODE_KP_RIGHTBRACE}, - {SCANCODE_KP_TAB, SDL_SCANCODE_KP_TAB}, - {SCANCODE_KP_BACKSPACE, SDL_SCANCODE_KP_BACKSPACE}, - {SCANCODE_KP_A, SDL_SCANCODE_KP_A}, - {SCANCODE_KP_B, SDL_SCANCODE_KP_B}, - {SCANCODE_KP_C, SDL_SCANCODE_KP_C}, - {SCANCODE_KP_D, SDL_SCANCODE_KP_D}, - {SCANCODE_KP_E, SDL_SCANCODE_KP_E}, - {SCANCODE_KP_F, SDL_SCANCODE_KP_F}, - {SCANCODE_KP_XOR, SDL_SCANCODE_KP_XOR}, - {SCANCODE_KP_POWER, SDL_SCANCODE_KP_POWER}, - {SCANCODE_KP_PERCENT, SDL_SCANCODE_KP_PERCENT}, - {SCANCODE_KP_LESS, SDL_SCANCODE_KP_LESS}, - {SCANCODE_KP_GREATER, SDL_SCANCODE_KP_GREATER}, - {SCANCODE_KP_AMPERSAND, SDL_SCANCODE_KP_AMPERSAND}, - {SCANCODE_KP_DBLAMPERSAND, SDL_SCANCODE_KP_DBLAMPERSAND}, - {SCANCODE_KP_VERTICALBAR, SDL_SCANCODE_KP_VERTICALBAR}, - {SCANCODE_KP_DBLVERTICALBAR, SDL_SCANCODE_KP_DBLVERTICALBAR}, - {SCANCODE_KP_COLON, SDL_SCANCODE_KP_COLON}, - {SCANCODE_KP_HASH, SDL_SCANCODE_KP_HASH}, - {SCANCODE_KP_SPACE, SDL_SCANCODE_KP_SPACE}, - {SCANCODE_KP_AT, SDL_SCANCODE_KP_AT}, - {SCANCODE_KP_EXCLAM, SDL_SCANCODE_KP_EXCLAM}, - {SCANCODE_KP_MEMSTORE, SDL_SCANCODE_KP_MEMSTORE}, - {SCANCODE_KP_MEMRECALL, SDL_SCANCODE_KP_MEMRECALL}, - {SCANCODE_KP_MEMCLEAR, SDL_SCANCODE_KP_MEMCLEAR}, - {SCANCODE_KP_MEMADD, SDL_SCANCODE_KP_MEMADD}, - {SCANCODE_KP_MEMSUBTRACT, SDL_SCANCODE_KP_MEMSUBTRACT}, - {SCANCODE_KP_MEMMULTIPLY, SDL_SCANCODE_KP_MEMMULTIPLY}, - {SCANCODE_KP_MEMDIVIDE, SDL_SCANCODE_KP_MEMDIVIDE}, - {SCANCODE_KP_PLUSMINUS, SDL_SCANCODE_KP_PLUSMINUS}, - {SCANCODE_KP_CLEAR, SDL_SCANCODE_KP_CLEAR}, - {SCANCODE_KP_CLEARENTRY, SDL_SCANCODE_KP_CLEARENTRY}, - {SCANCODE_KP_BINARY, SDL_SCANCODE_KP_BINARY}, - {SCANCODE_KP_OCTAL, SDL_SCANCODE_KP_OCTAL}, - {SCANCODE_KP_DECIMAL, SDL_SCANCODE_KP_DECIMAL}, - {SCANCODE_KP_HEXADECIMAL, SDL_SCANCODE_KP_HEXADECIMAL}, - - {SCANCODE_LCTRL, SDL_SCANCODE_LCTRL}, - {SCANCODE_LSHIFT, SDL_SCANCODE_LSHIFT}, - {SCANCODE_LALT, SDL_SCANCODE_LALT}, - {SCANCODE_LGUI, SDL_SCANCODE_LGUI}, - {SCANCODE_RCTRL, SDL_SCANCODE_RCTRL}, - {SCANCODE_RSHIFT, SDL_SCANCODE_RSHIFT}, - {SCANCODE_RALT, SDL_SCANCODE_RALT}, - {SCANCODE_RGUI, SDL_SCANCODE_RGUI}, - - {SCANCODE_MODE, SDL_SCANCODE_MODE}, - - {SCANCODE_AUDIONEXT, SDL_SCANCODE_AUDIONEXT}, - {SCANCODE_AUDIOPREV, SDL_SCANCODE_AUDIOPREV}, - {SCANCODE_AUDIOSTOP, SDL_SCANCODE_AUDIOSTOP}, - {SCANCODE_AUDIOPLAY, SDL_SCANCODE_AUDIOPLAY}, - {SCANCODE_AUDIOMUTE, SDL_SCANCODE_AUDIOMUTE}, - {SCANCODE_MEDIASELECT, SDL_SCANCODE_MEDIASELECT}, - {SCANCODE_WWW, SDL_SCANCODE_WWW}, - {SCANCODE_MAIL, SDL_SCANCODE_MAIL}, - {SCANCODE_CALCULATOR, SDL_SCANCODE_CALCULATOR}, - {SCANCODE_COMPUTER, SDL_SCANCODE_COMPUTER}, - {SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_SEARCH}, - {SCANCODE_AC_HOME, SDL_SCANCODE_AC_HOME}, - {SCANCODE_AC_BACK, SDL_SCANCODE_AC_BACK}, - {SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_FORWARD}, - {SCANCODE_AC_STOP, SDL_SCANCODE_AC_STOP}, - {SCANCODE_AC_REFRESH, SDL_SCANCODE_AC_REFRESH}, - {SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_BOOKMARKS}, - - {SCANCODE_BRIGHTNESSDOWN, SDL_SCANCODE_BRIGHTNESSDOWN}, - {SCANCODE_BRIGHTNESSUP, SDL_SCANCODE_BRIGHTNESSUP}, - {SCANCODE_DISPLAYSWITCH, SDL_SCANCODE_DISPLAYSWITCH}, - {SCANCODE_KBDILLUMTOGGLE, SDL_SCANCODE_KBDILLUMTOGGLE}, - {SCANCODE_KBDILLUMDOWN, SDL_SCANCODE_KBDILLUMDOWN}, - {SCANCODE_KBDILLUMUP, SDL_SCANCODE_KBDILLUMUP}, - {SCANCODE_EJECT, SDL_SCANCODE_EJECT}, - {SCANCODE_SLEEP, SDL_SCANCODE_SLEEP}, - - {SCANCODE_APP1, SDL_SCANCODE_APP1}, - {SCANCODE_APP2, SDL_SCANCODE_APP2}, -}; - -EnumMap Keyboard::scancodes(Keyboard::scancodeEntries, sizeof(Keyboard::scancodeEntries)); - -} // sdl -} // keyboard -} // eve +} // namespace eve::keyboard::sdl diff --git a/src/modules/keyboard/sdl/Keyboard.h b/src/modules/keyboard/sdl/Keyboard.h index 26d734e4..25cc82cd 100644 --- a/src/modules/keyboard/sdl/Keyboard.h +++ b/src/modules/keyboard/sdl/Keyboard.h @@ -2,51 +2,33 @@ #include "keyboard/Keyboard.h" -// SDL -#include +namespace eve::keyboard::sdl { -namespace eve::keyboard::sdl -{ - -class Keyboard : public love::keyboard::Keyboard -{ +class Keyboard : public eve::keyboard::Keyboard { public: + Keyboard(); + ~Keyboard() override; - Keyboard(); + void setKeyRepeat(bool enable) override; + bool hasKeyRepeat() const override; - // Implements Module. - const char *getName() const; + bool isDown(const std::string& key) const override; + bool isDown(const std::vector& keys) const override; - void setKeyRepeat(bool enable); - bool hasKeyRepeat() const; - bool isDown(const std::vector &keylist) const; - bool isScancodeDown(const std::vector &scancodelist) const; + bool isScancodeDown(const std::string& scancode) const override; + bool isScancodeDown(const std::vector& scancodes) const override; - Key getKeyFromScancode(Scancode scancode) const; - Scancode getScancodeFromKey(Key key) const; + std::string getKeyFromScancode(const std::string& scancode) const override; + std::string getScancodeFromKey(const std::string& key) const override; - void setTextInput(bool enable); - void setTextInput(bool enable, double x, double y, double w, double h); - bool hasTextInput() const; - bool hasScreenKeyboard() const; - - static bool getConstant(Scancode in, SDL_Scancode &out); - static bool getConstant(SDL_Scancode in, Scancode &out); + void setTextInput(bool enable) override; + void setTextInput(bool enable, double x, double y, double w, double h) override; + bool hasTextInput() const override; + bool hasScreenKeyboard() const override; private: + // Consumed by event::sdl::Event when converting SDL_KEYDOWN repeats. + bool keyRepeat_ = false; +}; - // Whether holding down a key triggers repeated key press events. - // The real implementation is in love::event::sdl::Event::Convert. - bool key_repeat; - - static const SDL_Keycode *createKeyMap(); - static const SDL_Keycode *keymap; - - static EnumMap::Entry scancodeEntries[]; - static EnumMap scancodes; - -}; // Keyboard - -} // - - +} // namespace eve::keyboard::sdl diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a23cc702..6ef8e172 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,6 +10,8 @@ file(GLOB all_test_cpp model.cpp mouse.cpp mouse_cpp.cpp + keyboard_cpp.cpp + joystick_cpp.cpp touch.cpp network.cpp RenderSystem.cpp @@ -109,7 +111,7 @@ else() endif() endif() -set(EVELIBS EVCmdLine EVScripts EVWindow EVFileSystem EVMouse EVTouch EVGraphics EVImage EVDevTools EVCommon EVEvent EVTimer EVNetwork EVData EVSound EVAudio) +set(EVELIBS EVCmdLine EVScripts EVWindow EVFileSystem EVMouse EVKeyboard EVJoystick EVTouch EVGraphics EVImage EVDevTools EVCommon EVEvent EVTimer EVNetwork EVData EVSound EVAudio) if(EVENGINE_PLATFORM_LIB) list(APPEND EVELIBS ${EVENGINE_PLATFORM_LIB}) diff --git a/test/joystick_cpp.cpp b/test/joystick_cpp.cpp new file mode 100644 index 00000000..39ae0d24 --- /dev/null +++ b/test/joystick_cpp.cpp @@ -0,0 +1,72 @@ +#include "zeroerr/assert.h" +#include "zeroerr/unittest.h" + +#include "joystick/Joystick.h" +#include "joystick/Pad.h" +#include "common/Exception.h" + +namespace { + +eve::joystick::Joystick* joystick() { + return eve::joystick::Joystick::create(); +} + +} // namespace + +TEST_CASE("joystick.create") { + auto* joy = joystick(); + REQUIRE(joy != nullptr); + CHECK(joy->getName() == "Joystick"); + CHECK(joy->getJoystickCount() >= 0); +} + +TEST_CASE("joystick.getJoystickOutOfRange") { + auto* joy = joystick(); + REQUIRE(joy != nullptr); + CHECK(joy->getJoystick(-1) == nullptr); + CHECK(joy->getJoystick(joy->getJoystickCount()) == nullptr); + CHECK(joy->getJoystickFromID(-99999) == nullptr); +} + +TEST_CASE("joystick.mappingRoundTrip") { + auto* joy = joystick(); + REQUIRE(joy != nullptr); + + // Empty is allowed; invalid non-empty should throw. + joy->loadGamepadMappings(""); + std::string saved = joy->saveGamepadMappings(); + // May be empty if no gamepads have been seen. + CHECK(saved.find("invalid") == std::string::npos); + + bool threw = false; + try { + joy->loadGamepadMappings("not-a-valid-mapping-line"); + } catch (const eve::Exception&) { + threw = true; + } + CHECK(threw); +} + +TEST_CASE("joystick.connectedPadsQueryable") { + auto* joy = joystick(); + REQUIRE(joy != nullptr); + + int n = joy->getJoystickCount(); + for (int i = 0; i < n; i++) { + auto* pad = joy->getJoystick(i); + REQUIRE(pad != nullptr); + CHECK(joy->getIndex(pad) == i); + CHECK(pad->getID() >= 0); + // Connected pads expose non-negative counts. + if (pad->isConnected()) { + CHECK(pad->getAxisCount() >= 0); + CHECK(pad->getButtonCount() >= 0); + CHECK(pad->getHatCount() >= 0); + CHECK(!pad->getGUID().empty()); + (void)pad->isDown(0); + (void)pad->isGamepadDown("a"); + (void)pad->getGamepadAxis("leftx"); + (void)pad->getHat(0); + } + } +} diff --git a/test/keyboard_cpp.cpp b/test/keyboard_cpp.cpp new file mode 100644 index 00000000..63bfde20 --- /dev/null +++ b/test/keyboard_cpp.cpp @@ -0,0 +1,75 @@ +#include "zeroerr/assert.h" +#include "zeroerr/unittest.h" + +#include "keyboard/Keyboard.h" +#include "window/Window.h" + +namespace { + +eve::keyboard::Keyboard* keyboard() { + return eve::keyboard::Keyboard::create(); +} + +eve::window::Window* ensureVideoWindow() { + auto* win = eve::window::Window::create(); + if (win == nullptr) return nullptr; + eve::window::WindowSettings settings; + settings.width = 320; + settings.height = 240; + settings.centered = true; + if (!win->setWindowSettings(settings)) return nullptr; + return win; +} + +} // namespace + +TEST_CASE("keyboard.create") { + auto* kb = keyboard(); + REQUIRE(kb != nullptr); + CHECK(kb->getName() == "Keyboard"); +} + +TEST_CASE("keyboard.keyRepeat") { + auto* kb = keyboard(); + REQUIRE(kb != nullptr); + kb->setKeyRepeat(false); + CHECK(!kb->hasKeyRepeat()); + kb->setKeyRepeat(true); + CHECK(kb->hasKeyRepeat()); +} + +TEST_CASE("keyboard.textInput") { + auto* win = ensureVideoWindow(); + if (win == nullptr) return; + + auto* kb = keyboard(); + REQUIRE(kb != nullptr); + + kb->setTextInput(false); + CHECK(!kb->hasTextInput()); + kb->setTextInput(true); + CHECK(kb->hasTextInput()); + kb->setTextInput(false); + CHECK(!kb->hasTextInput()); +} + +TEST_CASE("keyboard.keyScancodeRoundTrip") { + auto* win = ensureVideoWindow(); + if (win == nullptr) return; + + auto* kb = keyboard(); + REQUIRE(kb != nullptr); + + // SDL names; layout may remap but A should round-trip on QWERTY-like layouts. + std::string scancode = kb->getScancodeFromKey("A"); + CHECK(!scancode.empty()); + std::string key = kb->getKeyFromScancode(scancode); + CHECK(!key.empty()); +} + +TEST_CASE("keyboard.isDownUnknown") { + auto* kb = keyboard(); + REQUIRE(kb != nullptr); + CHECK(!kb->isDown("NotARealKey")); + CHECK(!kb->isScancodeDown("NotARealScancode")); +}