Browse Source
Merge pull request #2497 from wwylele/input-2
Merge pull request #2497 from wwylele/input-2
Refactor input emulation & add SDL gamepad supportnce_cpp
committed by
GitHub
40 changed files with 1244 additions and 574 deletions
-
1src/CMakeLists.txt
-
2src/citra/CMakeLists.txt
-
45src/citra/config.cpp
-
69src/citra/default_ini.h
-
21src/citra/emu_window/emu_window_sdl2.cpp
-
6src/citra/emu_window/emu_window_sdl2.h
-
2src/citra_qt/CMakeLists.txt
-
26src/citra_qt/bootmanager.cpp
-
6src/citra_qt/bootmanager.h
-
62src/citra_qt/config.cpp
-
5src/citra_qt/config.h
-
178src/citra_qt/configure_input.cpp
-
34src/citra_qt/configure_input.h
-
2src/common/CMakeLists.txt
-
1src/common/logging/backend.cpp
-
1src/common/logging/log.h
-
120src/common/param_package.cpp
-
40src/common/param_package.h
-
3src/core/CMakeLists.txt
-
26src/core/frontend/emu_window.cpp
-
54src/core/frontend/emu_window.h
-
110src/core/frontend/input.h
-
152src/core/frontend/key_map.cpp
-
93src/core/frontend/key_map.h
-
56src/core/hle/service/hid/hid.cpp
-
37src/core/hle/service/hid/hid.h
-
3src/core/settings.cpp
-
80src/core/settings.h
-
27src/input_common/CMakeLists.txt
-
58src/input_common/analog_from_button.cpp
-
31src/input_common/analog_from_button.h
-
82src/input_common/keyboard.cpp
-
45src/input_common/keyboard.h
-
63src/input_common/main.cpp
-
29src/input_common/main.h
-
202src/input_common/sdl/sdl.cpp
-
19src/input_common/sdl/sdl.h
-
1src/tests/CMakeLists.txt
-
25src/tests/common/param_package.cpp
-
1src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@ -0,0 +1,120 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <array>
|
||||
|
#include <vector>
|
||||
|
#include "common/logging/log.h"
|
||||
|
#include "common/param_package.h"
|
||||
|
#include "common/string_util.h"
|
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
constexpr char KEY_VALUE_SEPARATOR = ':'; |
||||
|
constexpr char PARAM_SEPARATOR = ','; |
||||
|
constexpr char ESCAPE_CHARACTER = '$'; |
||||
|
const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'}; |
||||
|
const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'}; |
||||
|
const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'}; |
||||
|
|
||||
|
ParamPackage::ParamPackage(const std::string& serialized) { |
||||
|
std::vector<std::string> pairs; |
||||
|
Common::SplitString(serialized, PARAM_SEPARATOR, pairs); |
||||
|
|
||||
|
for (const std::string& pair : pairs) { |
||||
|
std::vector<std::string> key_value; |
||||
|
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value); |
||||
|
if (key_value.size() != 2) { |
||||
|
LOG_ERROR(Common, "invalid key pair %s", pair.c_str()); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
for (std::string& part : key_value) { |
||||
|
part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR}); |
||||
|
part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR}); |
||||
|
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER}); |
||||
|
} |
||||
|
|
||||
|
Set(key_value[0], key_value[1]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : data(list) {} |
||||
|
|
||||
|
std::string ParamPackage::Serialize() const { |
||||
|
if (data.empty()) |
||||
|
return ""; |
||||
|
|
||||
|
std::string result; |
||||
|
|
||||
|
for (const auto& pair : data) { |
||||
|
std::array<std::string, 2> key_value{{pair.first, pair.second}}; |
||||
|
for (std::string& part : key_value) { |
||||
|
part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE); |
||||
|
part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE); |
||||
|
part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE); |
||||
|
} |
||||
|
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR; |
||||
|
} |
||||
|
|
||||
|
result.pop_back(); // discard the trailing PARAM_SEPARATOR
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { |
||||
|
auto pair = data.find(key); |
||||
|
if (pair == data.end()) { |
||||
|
LOG_DEBUG(Common, "key %s not found", key.c_str()); |
||||
|
return default_value; |
||||
|
} |
||||
|
|
||||
|
return pair->second; |
||||
|
} |
||||
|
|
||||
|
int ParamPackage::Get(const std::string& key, int default_value) const { |
||||
|
auto pair = data.find(key); |
||||
|
if (pair == data.end()) { |
||||
|
LOG_DEBUG(Common, "key %s not found", key.c_str()); |
||||
|
return default_value; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
return std::stoi(pair->second); |
||||
|
} catch (const std::logic_error&) { |
||||
|
LOG_ERROR(Common, "failed to convert %s to int", pair->second.c_str()); |
||||
|
return default_value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
float ParamPackage::Get(const std::string& key, float default_value) const { |
||||
|
auto pair = data.find(key); |
||||
|
if (pair == data.end()) { |
||||
|
LOG_DEBUG(Common, "key %s not found", key.c_str()); |
||||
|
return default_value; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
return std::stof(pair->second); |
||||
|
} catch (const std::logic_error&) { |
||||
|
LOG_ERROR(Common, "failed to convert %s to float", pair->second.c_str()); |
||||
|
return default_value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ParamPackage::Set(const std::string& key, const std::string& value) { |
||||
|
data[key] = value; |
||||
|
} |
||||
|
|
||||
|
void ParamPackage::Set(const std::string& key, int value) { |
||||
|
data[key] = std::to_string(value); |
||||
|
} |
||||
|
|
||||
|
void ParamPackage::Set(const std::string& key, float value) { |
||||
|
data[key] = std::to_string(value); |
||||
|
} |
||||
|
|
||||
|
bool ParamPackage::Has(const std::string& key) const { |
||||
|
return data.find(key) != data.end(); |
||||
|
} |
||||
|
|
||||
|
} // namespace Common
|
||||
@ -0,0 +1,40 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <initializer_list> |
||||
|
#include <string> |
||||
|
#include <unordered_map> |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
/// A string-based key-value container supporting serializing to and deserializing from a string |
||||
|
class ParamPackage { |
||||
|
public: |
||||
|
using DataType = std::unordered_map<std::string, std::string>; |
||||
|
|
||||
|
ParamPackage() = default; |
||||
|
explicit ParamPackage(const std::string& serialized); |
||||
|
ParamPackage(std::initializer_list<DataType::value_type> list); |
||||
|
ParamPackage(const ParamPackage& other) = default; |
||||
|
ParamPackage(ParamPackage&& other) = default; |
||||
|
|
||||
|
ParamPackage& operator=(const ParamPackage& other) = default; |
||||
|
ParamPackage& operator=(ParamPackage&& other) = default; |
||||
|
|
||||
|
std::string Serialize() const; |
||||
|
std::string Get(const std::string& key, const std::string& default_value) const; |
||||
|
int Get(const std::string& key, int default_value) const; |
||||
|
float Get(const std::string& key, float default_value) const; |
||||
|
void Set(const std::string& key, const std::string& value); |
||||
|
void Set(const std::string& key, int value); |
||||
|
void Set(const std::string& key, float value); |
||||
|
bool Has(const std::string& key) const; |
||||
|
|
||||
|
private: |
||||
|
DataType data; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Common |
||||
@ -0,0 +1,110 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <string> |
||||
|
#include <tuple> |
||||
|
#include <unordered_map> |
||||
|
#include <utility> |
||||
|
#include "common/logging/log.h" |
||||
|
#include "common/param_package.h" |
||||
|
|
||||
|
namespace Input { |
||||
|
|
||||
|
/// An abstract class template for an input device (a button, an analog input, etc.). |
||||
|
template <typename StatusType> |
||||
|
class InputDevice { |
||||
|
public: |
||||
|
virtual ~InputDevice() = default; |
||||
|
virtual StatusType GetStatus() const { |
||||
|
return {}; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/// An abstract class template for a factory that can create input devices. |
||||
|
template <typename InputDeviceType> |
||||
|
class Factory { |
||||
|
public: |
||||
|
virtual ~Factory() = default; |
||||
|
virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0; |
||||
|
}; |
||||
|
|
||||
|
namespace Impl { |
||||
|
|
||||
|
template <typename InputDeviceType> |
||||
|
using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>; |
||||
|
|
||||
|
template <typename InputDeviceType> |
||||
|
struct FactoryList { |
||||
|
static FactoryListType<InputDeviceType> list; |
||||
|
}; |
||||
|
|
||||
|
template <typename InputDeviceType> |
||||
|
FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list; |
||||
|
|
||||
|
} // namespace Impl |
||||
|
|
||||
|
/** |
||||
|
* Registers an input device factory. |
||||
|
* @tparam InputDeviceType the type of input devices the factory can create |
||||
|
* @param name the name of the factory. Will be used to match the "engine" parameter when creating |
||||
|
* a device |
||||
|
* @param factory the factory object to register |
||||
|
*/ |
||||
|
template <typename InputDeviceType> |
||||
|
void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) { |
||||
|
auto pair = std::make_pair(name, std::move(factory)); |
||||
|
if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) { |
||||
|
LOG_ERROR(Input, "Factory %s already registered", name.c_str()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Unregisters an input device factory. |
||||
|
* @tparam InputDeviceType the type of input devices the factory can create |
||||
|
* @param name the name of the factory to unregister |
||||
|
*/ |
||||
|
template <typename InputDeviceType> |
||||
|
void UnregisterFactory(const std::string& name) { |
||||
|
if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) { |
||||
|
LOG_ERROR(Input, "Factory %s not registered", name.c_str()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Create an input device from given paramters. |
||||
|
* @tparam InputDeviceType the type of input devices to create |
||||
|
* @param params a serialized ParamPackage string contains all parameters for creating the device |
||||
|
*/ |
||||
|
template <typename InputDeviceType> |
||||
|
std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) { |
||||
|
const Common::ParamPackage package(params); |
||||
|
const std::string engine = package.Get("engine", "null"); |
||||
|
const auto& factory_list = Impl::FactoryList<InputDeviceType>::list; |
||||
|
const auto pair = factory_list.find(engine); |
||||
|
if (pair == factory_list.end()) { |
||||
|
if (engine != "null") { |
||||
|
LOG_ERROR(Input, "Unknown engine name: %s", engine.c_str()); |
||||
|
} |
||||
|
return std::make_unique<InputDeviceType>(); |
||||
|
} |
||||
|
return pair->second->Create(package); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* A button device is an input device that returns bool as status. |
||||
|
* true for pressed; false for released. |
||||
|
*/ |
||||
|
using ButtonDevice = InputDevice<bool>; |
||||
|
|
||||
|
/** |
||||
|
* An analog device is an input device that returns a tuple of x and y coordinates as status. The |
||||
|
* coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up |
||||
|
* direction |
||||
|
*/ |
||||
|
using AnalogDevice = InputDevice<std::tuple<float, float>>; |
||||
|
|
||||
|
} // namespace Input |
||||
@ -1,152 +0,0 @@ |
|||||
// Copyright 2014 Citra Emulator Project
|
|
||||
// Licensed under GPLv2 or any later version
|
|
||||
// Refer to the license.txt file included.
|
|
||||
|
|
||||
#include <map>
|
|
||||
#include "core/frontend/emu_window.h"
|
|
||||
#include "core/frontend/key_map.h"
|
|
||||
|
|
||||
namespace KeyMap { |
|
||||
|
|
||||
// TODO (wwylele): currently we treat c-stick as four direction buttons
|
|
||||
// and map it directly to EmuWindow::ButtonPressed.
|
|
||||
// It should go the analog input way like circle pad does.
|
|
||||
const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{ |
|
||||
Service::HID::PAD_A, |
|
||||
Service::HID::PAD_B, |
|
||||
Service::HID::PAD_X, |
|
||||
Service::HID::PAD_Y, |
|
||||
Service::HID::PAD_L, |
|
||||
Service::HID::PAD_R, |
|
||||
Service::HID::PAD_ZL, |
|
||||
Service::HID::PAD_ZR, |
|
||||
Service::HID::PAD_START, |
|
||||
Service::HID::PAD_SELECT, |
|
||||
Service::HID::PAD_NONE, |
|
||||
Service::HID::PAD_UP, |
|
||||
Service::HID::PAD_DOWN, |
|
||||
Service::HID::PAD_LEFT, |
|
||||
Service::HID::PAD_RIGHT, |
|
||||
Service::HID::PAD_C_UP, |
|
||||
Service::HID::PAD_C_DOWN, |
|
||||
Service::HID::PAD_C_LEFT, |
|
||||
Service::HID::PAD_C_RIGHT, |
|
||||
|
|
||||
IndirectTarget::CirclePadUp, |
|
||||
IndirectTarget::CirclePadDown, |
|
||||
IndirectTarget::CirclePadLeft, |
|
||||
IndirectTarget::CirclePadRight, |
|
||||
IndirectTarget::CirclePadModifier, |
|
||||
}}; |
|
||||
|
|
||||
static std::map<HostDeviceKey, KeyTarget> key_map; |
|
||||
static int next_device_id = 0; |
|
||||
|
|
||||
static bool circle_pad_up = false; |
|
||||
static bool circle_pad_down = false; |
|
||||
static bool circle_pad_left = false; |
|
||||
static bool circle_pad_right = false; |
|
||||
static bool circle_pad_modifier = false; |
|
||||
|
|
||||
static void UpdateCirclePad(EmuWindow& emu_window) { |
|
||||
constexpr float SQRT_HALF = 0.707106781f; |
|
||||
int x = 0, y = 0; |
|
||||
|
|
||||
if (circle_pad_right) |
|
||||
++x; |
|
||||
if (circle_pad_left) |
|
||||
--x; |
|
||||
if (circle_pad_up) |
|
||||
++y; |
|
||||
if (circle_pad_down) |
|
||||
--y; |
|
||||
|
|
||||
float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f; |
|
||||
emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF), |
|
||||
y * modifier * (x == 0 ? 1.0f : SQRT_HALF)); |
|
||||
} |
|
||||
|
|
||||
int NewDeviceId() { |
|
||||
return next_device_id++; |
|
||||
} |
|
||||
|
|
||||
void SetKeyMapping(HostDeviceKey key, KeyTarget target) { |
|
||||
key_map[key] = target; |
|
||||
} |
|
||||
|
|
||||
void ClearKeyMapping(int device_id) { |
|
||||
auto iter = key_map.begin(); |
|
||||
while (iter != key_map.end()) { |
|
||||
if (iter->first.device_id == device_id) |
|
||||
key_map.erase(iter++); |
|
||||
else |
|
||||
++iter; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void PressKey(EmuWindow& emu_window, HostDeviceKey key) { |
|
||||
auto target = key_map.find(key); |
|
||||
if (target == key_map.end()) |
|
||||
return; |
|
||||
|
|
||||
if (target->second.direct) { |
|
||||
emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); |
|
||||
} else { |
|
||||
switch (target->second.target.indirect_target) { |
|
||||
case IndirectTarget::CirclePadUp: |
|
||||
circle_pad_up = true; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadDown: |
|
||||
circle_pad_down = true; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadLeft: |
|
||||
circle_pad_left = true; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadRight: |
|
||||
circle_pad_right = true; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadModifier: |
|
||||
circle_pad_modifier = true; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key) { |
|
||||
auto target = key_map.find(key); |
|
||||
if (target == key_map.end()) |
|
||||
return; |
|
||||
|
|
||||
if (target->second.direct) { |
|
||||
emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); |
|
||||
} else { |
|
||||
switch (target->second.target.indirect_target) { |
|
||||
case IndirectTarget::CirclePadUp: |
|
||||
circle_pad_up = false; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadDown: |
|
||||
circle_pad_down = false; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadLeft: |
|
||||
circle_pad_left = false; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadRight: |
|
||||
circle_pad_right = false; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
case IndirectTarget::CirclePadModifier: |
|
||||
circle_pad_modifier = false; |
|
||||
UpdateCirclePad(emu_window); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,93 +0,0 @@ |
|||||
// Copyright 2014 Citra Emulator Project |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <array> |
|
||||
#include <tuple> |
|
||||
#include "core/hle/service/hid/hid.h" |
|
||||
|
|
||||
class EmuWindow; |
|
||||
|
|
||||
namespace KeyMap { |
|
||||
|
|
||||
/** |
|
||||
* Represents key mapping targets that are not real 3DS buttons. |
|
||||
* They will be handled by KeyMap and translated to 3DS input. |
|
||||
*/ |
|
||||
enum class IndirectTarget { |
|
||||
CirclePadUp, |
|
||||
CirclePadDown, |
|
||||
CirclePadLeft, |
|
||||
CirclePadRight, |
|
||||
CirclePadModifier, |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Represents a key mapping target. It can be a PadState that represents real 3DS buttons, |
|
||||
* or an IndirectTarget. |
|
||||
*/ |
|
||||
struct KeyTarget { |
|
||||
bool direct; |
|
||||
union { |
|
||||
u32 direct_target_hex; |
|
||||
IndirectTarget indirect_target; |
|
||||
} target; |
|
||||
|
|
||||
KeyTarget() : direct(true) { |
|
||||
target.direct_target_hex = 0; |
|
||||
} |
|
||||
|
|
||||
KeyTarget(Service::HID::PadState pad) : direct(true) { |
|
||||
target.direct_target_hex = pad.hex; |
|
||||
} |
|
||||
|
|
||||
KeyTarget(IndirectTarget i) : direct(false) { |
|
||||
target.indirect_target = i; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
/** |
|
||||
* Represents a key for a specific host device. |
|
||||
*/ |
|
||||
struct HostDeviceKey { |
|
||||
int key_code; |
|
||||
int device_id; ///< Uniquely identifies a host device |
|
||||
|
|
||||
bool operator<(const HostDeviceKey& other) const { |
|
||||
return std::tie(key_code, device_id) < std::tie(other.key_code, other.device_id); |
|
||||
} |
|
||||
|
|
||||
bool operator==(const HostDeviceKey& other) const { |
|
||||
return std::tie(key_code, device_id) == std::tie(other.key_code, other.device_id); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets; |
|
||||
|
|
||||
/** |
|
||||
* Generates a new device id, which uniquely identifies a host device within KeyMap. |
|
||||
*/ |
|
||||
int NewDeviceId(); |
|
||||
|
|
||||
/** |
|
||||
* Maps a device-specific key to a target (a PadState or an IndirectTarget). |
|
||||
*/ |
|
||||
void SetKeyMapping(HostDeviceKey key, KeyTarget target); |
|
||||
|
|
||||
/** |
|
||||
* Clears all key mappings belonging to one device. |
|
||||
*/ |
|
||||
void ClearKeyMapping(int device_id); |
|
||||
|
|
||||
/** |
|
||||
* Maps a key press action and call the corresponding function in EmuWindow |
|
||||
*/ |
|
||||
void PressKey(EmuWindow& emu_window, HostDeviceKey key); |
|
||||
|
|
||||
/** |
|
||||
* Maps a key release action and call the corresponding function in EmuWindow |
|
||||
*/ |
|
||||
void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); |
|
||||
} |
|
||||
@ -0,0 +1,27 @@ |
|||||
|
set(SRCS |
||||
|
analog_from_button.cpp |
||||
|
keyboard.cpp |
||||
|
main.cpp |
||||
|
) |
||||
|
|
||||
|
set(HEADERS |
||||
|
analog_from_button.h |
||||
|
keyboard.h |
||||
|
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() |
||||
@ -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<Input::ButtonDevice>; |
||||
|
|
||||
|
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<float, float> 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<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { |
||||
|
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); |
||||
|
auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine)); |
||||
|
auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine)); |
||||
|
auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine)); |
||||
|
auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); |
||||
|
auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); |
||||
|
auto modifier_scale = params.Get("modifier_scale", 0.5f); |
||||
|
return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), |
||||
|
std::move(right), std::move(modifier), modifier_scale); |
||||
|
} |
||||
|
|
||||
|
} // namespace InputCommon
|
||||
@ -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 <memory> |
||||
|
#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<Input::AnalogDevice> { |
||||
|
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<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; |
||||
|
}; |
||||
|
|
||||
|
} // namespace InputCommon |
||||
@ -0,0 +1,82 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <atomic>
|
||||
|
#include <list>
|
||||
|
#include <mutex>
|
||||
|
#include "input_common/keyboard.h"
|
||||
|
|
||||
|
namespace InputCommon { |
||||
|
|
||||
|
class KeyButton final : public Input::ButtonDevice { |
||||
|
public: |
||||
|
explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) |
||||
|
: key_button_list(key_button_list_) {} |
||||
|
|
||||
|
~KeyButton(); |
||||
|
|
||||
|
bool GetStatus() const override { |
||||
|
return status.load(); |
||||
|
} |
||||
|
|
||||
|
friend class KeyButtonList; |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<KeyButtonList> key_button_list; |
||||
|
std::atomic<bool> status{false}; |
||||
|
}; |
||||
|
|
||||
|
struct KeyButtonPair { |
||||
|
int key_code; |
||||
|
KeyButton* key_button; |
||||
|
}; |
||||
|
|
||||
|
class KeyButtonList { |
||||
|
public: |
||||
|
void AddKeyButton(int key_code, KeyButton* key_button) { |
||||
|
std::lock_guard<std::mutex> guard(mutex); |
||||
|
list.push_back(KeyButtonPair{key_code, key_button}); |
||||
|
} |
||||
|
|
||||
|
void RemoveKeyButton(const KeyButton* key_button) { |
||||
|
std::lock_guard<std::mutex> 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<std::mutex> 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<KeyButtonPair> list; |
||||
|
}; |
||||
|
|
||||
|
Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {} |
||||
|
|
||||
|
KeyButton::~KeyButton() { |
||||
|
key_button_list->RemoveKeyButton(this); |
||||
|
} |
||||
|
|
||||
|
std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) { |
||||
|
int key_code = params.Get("code", 0); |
||||
|
std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(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
|
||||
@ -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 <memory> |
||||
|
#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<Input::ButtonDevice> { |
||||
|
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<Input::ButtonDevice> 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<KeyButtonList> key_button_list; |
||||
|
}; |
||||
|
|
||||
|
} // namespace InputCommon |
||||
@ -0,0 +1,63 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <memory>
|
||||
|
#include "common/param_package.h"
|
||||
|
#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 { |
||||
|
|
||||
|
static std::shared_ptr<Keyboard> keyboard; |
||||
|
|
||||
|
void Init() { |
||||
|
keyboard = std::make_shared<InputCommon::Keyboard>(); |
||||
|
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); |
||||
|
Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", |
||||
|
std::make_shared<InputCommon::AnalogFromButton>()); |
||||
|
#ifdef HAVE_SDL2
|
||||
|
SDL::Init(); |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
void Shutdown() { |
||||
|
Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); |
||||
|
keyboard.reset(); |
||||
|
Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); |
||||
|
|
||||
|
#ifdef HAVE_SDL2
|
||||
|
SDL::Shutdown(); |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
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(); |
||||
|
} |
||||
|
|
||||
|
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
|
||||
@ -0,0 +1,29 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <string> |
||||
|
|
||||
|
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); |
||||
|
|
||||
|
/// 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 |
||||
@ -0,0 +1,202 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <cmath>
|
||||
|
#include <memory>
|
||||
|
#include <string>
|
||||
|
#include <tuple>
|
||||
|
#include <unordered_map>
|
||||
|
#include <SDL.h>
|
||||
|
#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<int, std::weak_ptr<SDLJoystick>> joystick_list; |
||||
|
static std::shared_ptr<SDLButtonFactory> button_factory; |
||||
|
static std::shared_ptr<SDLAnalogFactory> 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<float, float> 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<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; |
||||
|
}; |
||||
|
|
||||
|
class SDLButton final : public Input::ButtonDevice { |
||||
|
public: |
||||
|
explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) |
||||
|
: joystick(joystick_), button(button_) {} |
||||
|
|
||||
|
bool GetStatus() const override { |
||||
|
return joystick->GetButton(button); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<SDLJoystick> joystick; |
||||
|
int button; |
||||
|
}; |
||||
|
|
||||
|
class SDLDirectionButton final : public Input::ButtonDevice { |
||||
|
public: |
||||
|
explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) |
||||
|
: joystick(joystick_), hat(hat_), direction(direction_) {} |
||||
|
|
||||
|
bool GetStatus() const override { |
||||
|
return joystick->GetHatDirection(hat, direction); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<SDLJoystick> joystick; |
||||
|
int hat; |
||||
|
Uint8 direction; |
||||
|
}; |
||||
|
|
||||
|
class SDLAnalog final : public Input::AnalogDevice { |
||||
|
public: |
||||
|
SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_) |
||||
|
: joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {} |
||||
|
|
||||
|
std::tuple<float, float> GetStatus() const override { |
||||
|
return joystick->GetAnalog(axis_x, axis_y); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<SDLJoystick> joystick; |
||||
|
int axis_x; |
||||
|
int axis_y; |
||||
|
}; |
||||
|
|
||||
|
static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) { |
||||
|
std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock(); |
||||
|
if (!joystick) { |
||||
|
joystick = std::make_shared<SDLJoystick>(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<Input::ButtonDevice> { |
||||
|
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<Input::ButtonDevice> 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<SDLDirectionButton>(GetJoystick(joystick_index), hat, |
||||
|
direction); |
||||
|
} |
||||
|
|
||||
|
const int button = params.Get("button", 0); |
||||
|
return std::make_unique<SDLButton>(GetJoystick(joystick_index), button); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/// An analog device factory that creates analog devices from SDL joystick
|
||||
|
class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> { |
||||
|
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<Input::AnalogDevice> 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<SDLAnalog>(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<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); |
||||
|
RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); |
||||
|
initialized = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Shutdown() { |
||||
|
if (initialized) { |
||||
|
using namespace Input; |
||||
|
UnregisterFactory<ButtonDevice>("sdl"); |
||||
|
UnregisterFactory<AnalogDevice>("sdl"); |
||||
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // namespace SDL
|
||||
|
} // namespace InputCommon
|
||||
@ -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 |
||||
@ -0,0 +1,25 @@ |
|||||
|
// Copyright 2017 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <catch.hpp>
|
||||
|
#include <math.h>
|
||||
|
#include "common/param_package.h"
|
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
TEST_CASE("ParamPackage", "[common]") { |
||||
|
ParamPackage original{ |
||||
|
{"abc", "xyz"}, {"def", "42"}, {"jkl", "$$:1:$2$,3"}, |
||||
|
}; |
||||
|
original.Set("ghi", 3.14f); |
||||
|
ParamPackage copy(original.Serialize()); |
||||
|
REQUIRE(copy.Get("abc", "") == "xyz"); |
||||
|
REQUIRE(copy.Get("def", 0) == 42); |
||||
|
REQUIRE(std::abs(copy.Get("ghi", 0.0f) - 3.14f) < 0.01f); |
||||
|
REQUIRE(copy.Get("jkl", "") == "$$:1:$2$,3"); |
||||
|
REQUIRE(copy.Get("mno", "uvw") == "uvw"); |
||||
|
REQUIRE(copy.Get("abc", 42) == 42); |
||||
|
} |
||||
|
|
||||
|
} // namespace Common
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue