From 38e800f70d122051e12ac9f22e23d84b97fec424 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 21 Jan 2017 11:53:03 +0200 Subject: InputCommon: add Keyboard --- src/input_common/CMakeLists.txt | 15 ++++++++ src/input_common/keyboard.cpp | 82 +++++++++++++++++++++++++++++++++++++++++ src/input_common/keyboard.h | 45 ++++++++++++++++++++++ src/input_common/main.cpp | 35 ++++++++++++++++++ src/input_common/main.h | 25 +++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 src/input_common/CMakeLists.txt create mode 100644 src/input_common/keyboard.cpp create mode 100644 src/input_common/keyboard.h create mode 100644 src/input_common/main.cpp create mode 100644 src/input_common/main.h (limited to 'src/input_common') diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt new file mode 100644 index 000000000..ac1ad45a9 --- /dev/null +++ b/src/input_common/CMakeLists.txt @@ -0,0 +1,15 @@ +set(SRCS + keyboard.cpp + main.cpp + ) + +set(HEADERS + keyboard.h + main.h + ) + +create_directory_groups(${SRCS} ${HEADERS}) + +add_library(input_common STATIC ${SRCS} ${HEADERS}) +target_link_libraries(input_common common core) + diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp new file mode 100644 index 000000000..a8fc01f2e --- /dev/null +++ b/src/input_common/keyboard.cpp @@ -0,0 +1,82 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "input_common/keyboard.h" + +namespace InputCommon { + +class KeyButton final : public Input::ButtonDevice { +public: + explicit KeyButton(std::shared_ptr key_button_list_) + : key_button_list(key_button_list_) {} + + ~KeyButton(); + + bool GetStatus() const override { + return status.load(); + } + + friend class KeyButtonList; + +private: + std::shared_ptr key_button_list; + std::atomic status{false}; +}; + +struct KeyButtonPair { + int key_code; + KeyButton* key_button; +}; + +class KeyButtonList { +public: + void AddKeyButton(int key_code, KeyButton* key_button) { + std::lock_guard guard(mutex); + list.push_back(KeyButtonPair{key_code, key_button}); + } + + void RemoveKeyButton(const KeyButton* key_button) { + std::lock_guard guard(mutex); + list.remove_if( + [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; }); + } + + void ChangeKeyStatus(int key_code, bool pressed) { + std::lock_guard guard(mutex); + for (const KeyButtonPair& pair : list) { + if (pair.key_code == key_code) + pair.key_button->status.store(pressed); + } + } + +private: + std::mutex mutex; + std::list list; +}; + +Keyboard::Keyboard() : key_button_list{std::make_shared()} {} + +KeyButton::~KeyButton() { + key_button_list->RemoveKeyButton(this); +} + +std::unique_ptr Keyboard::Create(const Common::ParamPackage& params) { + int key_code = params.Get("code", 0); + std::unique_ptr button = std::make_unique(key_button_list); + key_button_list->AddKeyButton(key_code, button.get()); + return std::move(button); +} + +void Keyboard::PressKey(int key_code) { + key_button_list->ChangeKeyStatus(key_code, true); +} + +void Keyboard::ReleaseKey(int key_code) { + key_button_list->ChangeKeyStatus(key_code, false); +} + +} // namespace InputCommon diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h new file mode 100644 index 000000000..76359aa30 --- /dev/null +++ b/src/input_common/keyboard.h @@ -0,0 +1,45 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "core/frontend/input.h" + +namespace InputCommon { + +class KeyButtonList; + +/** + * A button device factory representing a keyboard. It receives keyboard events and forward them + * to all button devices it created. + */ +class Keyboard final : public Input::Factory { +public: + Keyboard(); + + /** + * Creates a button device from a keyboard key + * @param params contains parameters for creating the device: + * - "code": the code of the key to bind with the button + */ + std::unique_ptr Create(const Common::ParamPackage& params) override; + + /** + * Sets the status of all buttons bound with the key to pressed + * @param key_code the code of the key to press + */ + void PressKey(int key_code); + + /** + * Sets the status of all buttons bound with the key to released + * @param key_code the code of the key to release + */ + void ReleaseKey(int key_code); + +private: + std::shared_ptr key_button_list; +}; + +} // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp new file mode 100644 index 000000000..ff25220b4 --- /dev/null +++ b/src/input_common/main.cpp @@ -0,0 +1,35 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/param_package.h" +#include "input_common/keyboard.h" +#include "input_common/main.h" + +namespace InputCommon { + +static std::shared_ptr keyboard; + +void Init() { + keyboard = std::make_shared(); + Input::RegisterFactory("keyboard", keyboard); +} + +void Shutdown() { + Input::UnregisterFactory("keyboard"); + keyboard.reset(); +} + +Keyboard* GetKeyboard() { + return keyboard.get(); +} + +std::string GenerateKeyboardParam(int key_code) { + Common::ParamPackage param{ + {"engine", "keyboard"}, {"code", std::to_string(key_code)}, + }; + return param.Serialize(); +} + +} // namespace InputCommon diff --git a/src/input_common/main.h b/src/input_common/main.h new file mode 100644 index 000000000..a490dd829 --- /dev/null +++ b/src/input_common/main.h @@ -0,0 +1,25 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace InputCommon { + +/// Initializes and registers all built-in input device factories. +void Init(); + +/// Unresisters all build-in input device factories and shut them down. +void Shutdown(); + +class Keyboard; + +/// Gets the keyboard button device factory. +Keyboard* GetKeyboard(); + +/// Generates a serialized param package for creating a keyboard button device +std::string GenerateKeyboardParam(int key_code); + +} // namespace InputCommon -- cgit v1.2.3 From a6bd7917cbc06f9b8f5a7ae24e75db776dc1cd6a Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 21 Jan 2017 13:04:00 +0200 Subject: InputCommon: add AnalogFromButton --- src/input_common/CMakeLists.txt | 2 ++ src/input_common/analog_from_button.cpp | 58 +++++++++++++++++++++++++++++++++ src/input_common/analog_from_button.h | 31 ++++++++++++++++++ src/input_common/main.cpp | 18 ++++++++++ src/input_common/main.h | 4 +++ 5 files changed, 113 insertions(+) create mode 100755 src/input_common/analog_from_button.cpp create mode 100755 src/input_common/analog_from_button.h (limited to 'src/input_common') diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index ac1ad45a9..9f4422269 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -1,9 +1,11 @@ set(SRCS + analog_from_button.cpp keyboard.cpp main.cpp ) set(HEADERS + analog_from_button.h keyboard.h main.h ) diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp new file mode 100755 index 000000000..e1a260762 --- /dev/null +++ b/src/input_common/analog_from_button.cpp @@ -0,0 +1,58 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "input_common/analog_from_button.h" + +namespace InputCommon { + +class Analog final : public Input::AnalogDevice { +public: + using Button = std::unique_ptr; + + Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_, + float modifier_scale_) + : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), + right(std::move(right_)), modifier(std::move(modifier_)), + modifier_scale(modifier_scale_) {} + + std::tuple GetStatus() const override { + constexpr float SQRT_HALF = 0.707106781f; + int x = 0, y = 0; + + if (right->GetStatus()) + ++x; + if (left->GetStatus()) + --x; + if (up->GetStatus()) + ++y; + if (down->GetStatus()) + --y; + + float coef = modifier->GetStatus() ? modifier_scale : 1.0f; + return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF), + y * coef * (x == 0 ? 1.0f : SQRT_HALF)); + } + +private: + Button up; + Button down; + Button left; + Button right; + Button modifier; + float modifier_scale; +}; + +std::unique_ptr AnalogFromButton::Create(const Common::ParamPackage& params) { + const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); + auto up = Input::CreateDevice(params.Get("up", null_engine)); + auto down = Input::CreateDevice(params.Get("down", null_engine)); + auto left = Input::CreateDevice(params.Get("left", null_engine)); + auto right = Input::CreateDevice(params.Get("right", null_engine)); + auto modifier = Input::CreateDevice(params.Get("modifier", null_engine)); + auto modifier_scale = params.Get("modifier_scale", 0.5f); + return std::make_unique(std::move(up), std::move(down), std::move(left), + std::move(right), std::move(modifier), modifier_scale); +} + +} // namespace InputCommon diff --git a/src/input_common/analog_from_button.h b/src/input_common/analog_from_button.h new file mode 100755 index 000000000..bbd583dd9 --- /dev/null +++ b/src/input_common/analog_from_button.h @@ -0,0 +1,31 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "core/frontend/input.h" + +namespace InputCommon { + +/** + * An analog device factory that takes direction button devices and combines them into a analog + * device. + */ +class AnalogFromButton final : public Input::Factory { +public: + /** + * Creates an analog device from direction button devices + * @param params contains parameters for creating the device: + * - "up": a serialized ParamPackage for creating a button device for up direction + * - "down": a serialized ParamPackage for creating a button device for down direction + * - "left": a serialized ParamPackage for creating a button device for left direction + * - "right": a serialized ParamPackage for creating a button device for right direction + * - "modifier": a serialized ParamPackage for creating a button device as the modifier + * - "modifier_scale": a float for the multiplier the modifier gives to the position + */ + std::unique_ptr Create(const Common::ParamPackage& params) override; +}; + +} // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index ff25220b4..8455fdc17 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -4,6 +4,7 @@ #include #include "common/param_package.h" +#include "input_common/analog_from_button.h" #include "input_common/keyboard.h" #include "input_common/main.h" @@ -14,11 +15,14 @@ static std::shared_ptr keyboard; void Init() { keyboard = std::make_shared(); Input::RegisterFactory("keyboard", keyboard); + Input::RegisterFactory("analog_from_button", + std::make_shared()); } void Shutdown() { Input::UnregisterFactory("keyboard"); keyboard.reset(); + Input::UnregisterFactory("analog_from_button"); } Keyboard* GetKeyboard() { @@ -32,4 +36,18 @@ std::string GenerateKeyboardParam(int key_code) { return param.Serialize(); } +std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, + int key_modifier, float modifier_scale) { + Common::ParamPackage circle_pad_param{ + {"engine", "analog_from_button"}, + {"up", GenerateKeyboardParam(key_up)}, + {"down", GenerateKeyboardParam(key_down)}, + {"left", GenerateKeyboardParam(key_left)}, + {"right", GenerateKeyboardParam(key_right)}, + {"modifier", GenerateKeyboardParam(key_modifier)}, + {"modifier_scale", std::to_string(modifier_scale)}, + }; + return circle_pad_param.Serialize(); +} + } // namespace InputCommon diff --git a/src/input_common/main.h b/src/input_common/main.h index a490dd829..140bbd014 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -22,4 +22,8 @@ Keyboard* GetKeyboard(); /// Generates a serialized param package for creating a keyboard button device std::string GenerateKeyboardParam(int key_code); +/// Generates a serialized param package for creating an analog device taking input from keyboard +std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, + int key_modifier, float modifier_scale); + } // namespace InputCommon -- cgit v1.2.3 From 51b1c1f211bf8112eba845256bd52cbd36a5932a Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 21 Jan 2017 17:33:48 +0200 Subject: InputCommon: add SDL joystick support --- src/input_common/CMakeLists.txt | 10 ++ src/input_common/main.cpp | 10 ++ src/input_common/sdl/sdl.cpp | 202 ++++++++++++++++++++++++++++++++++++++++ src/input_common/sdl/sdl.h | 19 ++++ 4 files changed, 241 insertions(+) create mode 100644 src/input_common/sdl/sdl.cpp create mode 100644 src/input_common/sdl/sdl.h (limited to 'src/input_common') diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 9f4422269..cfe5caaa3 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -10,8 +10,18 @@ set(HEADERS main.h ) +if(SDL2_FOUND) + set(SRCS ${SRCS} sdl/sdl.cpp) + set(HEADERS ${HEADERS} sdl/sdl.h) + include_directories(${SDL2_INCLUDE_DIR}) +endif() + create_directory_groups(${SRCS} ${HEADERS}) add_library(input_common STATIC ${SRCS} ${HEADERS}) target_link_libraries(input_common common core) +if(SDL2_FOUND) + target_link_libraries(input_common ${SDL2_LIBRARY}) + set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) +endif() diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 8455fdc17..699f41e6b 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -7,6 +7,9 @@ #include "input_common/analog_from_button.h" #include "input_common/keyboard.h" #include "input_common/main.h" +#ifdef HAVE_SDL2 +#include "input_common/sdl/sdl.h" +#endif namespace InputCommon { @@ -17,12 +20,19 @@ void Init() { Input::RegisterFactory("keyboard", keyboard); Input::RegisterFactory("analog_from_button", std::make_shared()); +#ifdef HAVE_SDL2 + SDL::Init(); +#endif } void Shutdown() { Input::UnregisterFactory("keyboard"); keyboard.reset(); Input::UnregisterFactory("analog_from_button"); + +#ifdef HAVE_SDL2 + SDL::Shutdown(); +#endif } Keyboard* GetKeyboard() { diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp new file mode 100644 index 000000000..ae0206909 --- /dev/null +++ b/src/input_common/sdl/sdl.cpp @@ -0,0 +1,202 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include "common/math_util.h" +#include "input_common/sdl/sdl.h" + +namespace InputCommon { + +namespace SDL { + +class SDLJoystick; +class SDLButtonFactory; +class SDLAnalogFactory; +static std::unordered_map> joystick_list; +static std::shared_ptr button_factory; +static std::shared_ptr analog_factory; + +static bool initialized = false; + +class SDLJoystick { +public: + explicit SDLJoystick(int joystick_index) + : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { + if (!joystick) { + LOG_ERROR(Input, "failed to open joystick %d", joystick_index); + } + } + + bool GetButton(int button) const { + if (!joystick) + return {}; + SDL_JoystickUpdate(); + return SDL_JoystickGetButton(joystick.get(), button) == 1; + } + + std::tuple GetAnalog(int axis_x, int axis_y) const { + if (!joystick) + return {}; + SDL_JoystickUpdate(); + float x = SDL_JoystickGetAxis(joystick.get(), axis_x) / 32767.0f; + float y = SDL_JoystickGetAxis(joystick.get(), axis_y) / 32767.0f; + y = -y; // 3DS uses an y-axis inverse from SDL + + // Make sure the coordinates are in the unit circle, + // otherwise normalize it. + float r = x * x + y * y; + if (r > 1.0f) { + r = std::sqrt(r); + x /= r; + y /= r; + } + + return std::make_tuple(x, y); + } + + bool GetHatDirection(int hat, Uint8 direction) const { + return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; + } + +private: + std::unique_ptr joystick; +}; + +class SDLButton final : public Input::ButtonDevice { +public: + explicit SDLButton(std::shared_ptr joystick_, int button_) + : joystick(joystick_), button(button_) {} + + bool GetStatus() const override { + return joystick->GetButton(button); + } + +private: + std::shared_ptr joystick; + int button; +}; + +class SDLDirectionButton final : public Input::ButtonDevice { +public: + explicit SDLDirectionButton(std::shared_ptr joystick_, int hat_, Uint8 direction_) + : joystick(joystick_), hat(hat_), direction(direction_) {} + + bool GetStatus() const override { + return joystick->GetHatDirection(hat, direction); + } + +private: + std::shared_ptr joystick; + int hat; + Uint8 direction; +}; + +class SDLAnalog final : public Input::AnalogDevice { +public: + SDLAnalog(std::shared_ptr joystick_, int axis_x_, int axis_y_) + : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {} + + std::tuple GetStatus() const override { + return joystick->GetAnalog(axis_x, axis_y); + } + +private: + std::shared_ptr joystick; + int axis_x; + int axis_y; +}; + +static std::shared_ptr GetJoystick(int joystick_index) { + std::shared_ptr joystick = joystick_list[joystick_index].lock(); + if (!joystick) { + joystick = std::make_shared(joystick_index); + joystick_list[joystick_index] = joystick; + } + return joystick; +} + +/// A button device factory that creates button devices from SDL joystick +class SDLButtonFactory final : public Input::Factory { +public: + /** + * Creates a button device from a joystick button + * @param params contains parameters for creating the device: + * - "joystick": the index of the joystick to bind + * - "button"(optional): the index of the button to bind + * - "hat"(optional): the index of the hat to bind as direction buttons + * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", + * "down", "left" or "right" + */ + std::unique_ptr Create(const Common::ParamPackage& params) override { + const int joystick_index = params.Get("joystick", 0); + + if (params.Has("hat")) { + const int hat = params.Get("hat", 0); + const std::string direction_name = params.Get("direction", ""); + Uint8 direction; + if (direction_name == "up") { + direction = SDL_HAT_UP; + } else if (direction_name == "down") { + direction = SDL_HAT_DOWN; + } else if (direction_name == "left") { + direction = SDL_HAT_LEFT; + } else if (direction_name == "right") { + direction = SDL_HAT_RIGHT; + } else { + direction = 0; + } + return std::make_unique(GetJoystick(joystick_index), hat, + direction); + } + + const int button = params.Get("button", 0); + return std::make_unique(GetJoystick(joystick_index), button); + } +}; + +/// An analog device factory that creates analog devices from SDL joystick +class SDLAnalogFactory final : public Input::Factory { +public: + /** + * Creates analog device from joystick axes + * @param params contains parameters for creating the device: + * - "joystick": the index of the joystick to bind + * - "axis_x": the index of the axis to be bind as x-axis + * - "axis_y": the index of the axis to be bind as y-axis + */ + std::unique_ptr Create(const Common::ParamPackage& params) override { + const int joystick_index = params.Get("joystick", 0); + const int axis_x = params.Get("axis_x", 0); + const int axis_y = params.Get("axis_y", 1); + return std::make_unique(GetJoystick(joystick_index), axis_x, axis_y); + } +}; + +void Init() { + if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { + LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: %s", SDL_GetError()); + } else { + using namespace Input; + RegisterFactory("sdl", std::make_shared()); + RegisterFactory("sdl", std::make_shared()); + initialized = true; + } +} + +void Shutdown() { + if (initialized) { + using namespace Input; + UnregisterFactory("sdl"); + UnregisterFactory("sdl"); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + } +} + +} // namespace SDL +} // namespace InputCommon diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h new file mode 100644 index 000000000..3e72debcc --- /dev/null +++ b/src/input_common/sdl/sdl.h @@ -0,0 +1,19 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/frontend/input.h" + +namespace InputCommon { +namespace SDL { + +/// Initializes and registers SDL device factories +void Init(); + +/// Unresisters SDL device factories and shut them down. +void Shutdown(); + +} // namespace SDL +} // namespace InputCommon -- cgit v1.2.3