another try
This commit is contained in:
@@ -1,65 +1,65 @@
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_library(input_common STATIC
|
||||
drivers/camera.cpp
|
||||
drivers/camera.h
|
||||
drivers/gc_adapter.cpp
|
||||
drivers/gc_adapter.h
|
||||
drivers/keyboard.cpp
|
||||
drivers/keyboard.h
|
||||
drivers/mouse.cpp
|
||||
drivers/mouse.h
|
||||
drivers/sdl_driver.cpp
|
||||
drivers/sdl_driver.h
|
||||
drivers/tas_input.cpp
|
||||
drivers/tas_input.h
|
||||
drivers/touch_screen.cpp
|
||||
drivers/touch_screen.h
|
||||
drivers/udp_client.cpp
|
||||
drivers/udp_client.h
|
||||
drivers/virtual_amiibo.cpp
|
||||
drivers/virtual_amiibo.h
|
||||
helpers/stick_from_buttons.cpp
|
||||
helpers/stick_from_buttons.h
|
||||
helpers/touch_from_buttons.cpp
|
||||
helpers/touch_from_buttons.h
|
||||
helpers/udp_protocol.cpp
|
||||
helpers/udp_protocol.h
|
||||
input_engine.cpp
|
||||
input_engine.h
|
||||
input_mapping.cpp
|
||||
input_mapping.h
|
||||
input_poller.cpp
|
||||
input_poller.h
|
||||
main.cpp
|
||||
main.h
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(input_common PRIVATE
|
||||
/W4
|
||||
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(input_common PRIVATE
|
||||
-Werror=conversion
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
target_sources(input_common PRIVATE
|
||||
drivers/sdl_driver.cpp
|
||||
drivers/sdl_driver.h
|
||||
)
|
||||
target_link_libraries(input_common PRIVATE SDL2)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(input_common PRIVATE usb)
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
add_library(input_common STATIC
|
||||
drivers/camera.cpp
|
||||
drivers/camera.h
|
||||
drivers/gc_adapter.cpp
|
||||
drivers/gc_adapter.h
|
||||
drivers/keyboard.cpp
|
||||
drivers/keyboard.h
|
||||
drivers/mouse.cpp
|
||||
drivers/mouse.h
|
||||
drivers/sdl_driver.cpp
|
||||
drivers/sdl_driver.h
|
||||
drivers/tas_input.cpp
|
||||
drivers/tas_input.h
|
||||
drivers/touch_screen.cpp
|
||||
drivers/touch_screen.h
|
||||
drivers/udp_client.cpp
|
||||
drivers/udp_client.h
|
||||
drivers/virtual_amiibo.cpp
|
||||
drivers/virtual_amiibo.h
|
||||
helpers/stick_from_buttons.cpp
|
||||
helpers/stick_from_buttons.h
|
||||
helpers/touch_from_buttons.cpp
|
||||
helpers/touch_from_buttons.h
|
||||
helpers/udp_protocol.cpp
|
||||
helpers/udp_protocol.h
|
||||
input_engine.cpp
|
||||
input_engine.h
|
||||
input_mapping.cpp
|
||||
input_mapping.h
|
||||
input_poller.cpp
|
||||
input_poller.h
|
||||
main.cpp
|
||||
main.h
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(input_common PRIVATE
|
||||
/W4
|
||||
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||
)
|
||||
else()
|
||||
target_compile_options(input_common PRIVATE
|
||||
-Werror=conversion
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
target_sources(input_common PRIVATE
|
||||
drivers/sdl_driver.cpp
|
||||
drivers/sdl_driver.h
|
||||
)
|
||||
target_link_libraries(input_common PRIVATE SDL2)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(input_common PRIVATE usb)
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
@@ -1,82 +1,82 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/camera.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
}
|
||||
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
|
||||
const std::size_t desired_width = getImageWidth();
|
||||
const std::size_t desired_height = getImageHeight();
|
||||
status.data.resize(desired_width * desired_height);
|
||||
|
||||
// Resize image to desired format
|
||||
for (std::size_t y = 0; y < desired_height; y++) {
|
||||
for (std::size_t x = 0; x < desired_width; x++) {
|
||||
const std::size_t pixel_index = y * desired_width + x;
|
||||
const std::size_t old_x = width * x / desired_width;
|
||||
const std::size_t old_y = height * y / desired_height;
|
||||
const std::size_t data_pixel_index = old_y * width + old_x;
|
||||
status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
SetCamera(identifier, status);
|
||||
}
|
||||
|
||||
std::size_t Camera::getImageWidth() const {
|
||||
switch (status.format) {
|
||||
case Common::Input::CameraFormat::Size320x240:
|
||||
return 320;
|
||||
case Common::Input::CameraFormat::Size160x120:
|
||||
return 160;
|
||||
case Common::Input::CameraFormat::Size80x60:
|
||||
return 80;
|
||||
case Common::Input::CameraFormat::Size40x30:
|
||||
return 40;
|
||||
case Common::Input::CameraFormat::Size20x15:
|
||||
return 20;
|
||||
case Common::Input::CameraFormat::None:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t Camera::getImageHeight() const {
|
||||
switch (status.format) {
|
||||
case Common::Input::CameraFormat::Size320x240:
|
||||
return 240;
|
||||
case Common::Input::CameraFormat::Size160x120:
|
||||
return 120;
|
||||
case Common::Input::CameraFormat::Size80x60:
|
||||
return 60;
|
||||
case Common::Input::CameraFormat::Size40x30:
|
||||
return 30;
|
||||
case Common::Input::CameraFormat::Size20x15:
|
||||
return 15;
|
||||
case Common::Input::CameraFormat::None:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Input::CameraError Camera::SetCameraFormat(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_,
|
||||
const Common::Input::CameraFormat camera_format) {
|
||||
status.format = camera_format;
|
||||
return Common::Input::CameraError::None;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/camera.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
}
|
||||
|
||||
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
|
||||
const std::size_t desired_width = getImageWidth();
|
||||
const std::size_t desired_height = getImageHeight();
|
||||
status.data.resize(desired_width * desired_height);
|
||||
|
||||
// Resize image to desired format
|
||||
for (std::size_t y = 0; y < desired_height; y++) {
|
||||
for (std::size_t x = 0; x < desired_width; x++) {
|
||||
const std::size_t pixel_index = y * desired_width + x;
|
||||
const std::size_t old_x = width * x / desired_width;
|
||||
const std::size_t old_y = height * y / desired_height;
|
||||
const std::size_t data_pixel_index = old_y * width + old_x;
|
||||
status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
SetCamera(identifier, status);
|
||||
}
|
||||
|
||||
std::size_t Camera::getImageWidth() const {
|
||||
switch (status.format) {
|
||||
case Common::Input::CameraFormat::Size320x240:
|
||||
return 320;
|
||||
case Common::Input::CameraFormat::Size160x120:
|
||||
return 160;
|
||||
case Common::Input::CameraFormat::Size80x60:
|
||||
return 80;
|
||||
case Common::Input::CameraFormat::Size40x30:
|
||||
return 40;
|
||||
case Common::Input::CameraFormat::Size20x15:
|
||||
return 20;
|
||||
case Common::Input::CameraFormat::None:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t Camera::getImageHeight() const {
|
||||
switch (status.format) {
|
||||
case Common::Input::CameraFormat::Size320x240:
|
||||
return 240;
|
||||
case Common::Input::CameraFormat::Size160x120:
|
||||
return 120;
|
||||
case Common::Input::CameraFormat::Size80x60:
|
||||
return 60;
|
||||
case Common::Input::CameraFormat::Size40x30:
|
||||
return 30;
|
||||
case Common::Input::CameraFormat::Size20x15:
|
||||
return 15;
|
||||
case Common::Input::CameraFormat::None:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Input::CameraError Camera::SetCameraFormat(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_,
|
||||
const Common::Input::CameraFormat camera_format) {
|
||||
status.format = camera_format;
|
||||
return Common::Input::CameraError::None;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,29 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Camera final : public InputEngine {
|
||||
public:
|
||||
explicit Camera(std::string input_engine_);
|
||||
|
||||
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
|
||||
|
||||
std::size_t getImageWidth() const;
|
||||
std::size_t getImageHeight() const;
|
||||
|
||||
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
|
||||
Common::Input::CameraFormat camera_format) override;
|
||||
|
||||
Common::Input::CameraStatus status{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Camera final : public InputEngine {
|
||||
public:
|
||||
explicit Camera(std::string input_engine_);
|
||||
|
||||
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
|
||||
|
||||
std::size_t getImageWidth() const;
|
||||
std::size_t getImageHeight() const;
|
||||
|
||||
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
|
||||
Common::Input::CameraFormat camera_format) override;
|
||||
|
||||
Common::Input::CameraStatus status{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,137 +1,137 @@
|
||||
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <stop_token>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
struct libusb_context;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class LibUSBContext;
|
||||
class LibUSBDeviceHandle;
|
||||
|
||||
class GCAdapter : public InputEngine {
|
||||
public:
|
||||
explicit GCAdapter(std::string input_engine_);
|
||||
~GCAdapter() override;
|
||||
|
||||
Common::Input::VibrationError SetVibration(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
enum class PadButton {
|
||||
Undefined = 0x0000,
|
||||
ButtonLeft = 0x0001,
|
||||
ButtonRight = 0x0002,
|
||||
ButtonDown = 0x0004,
|
||||
ButtonUp = 0x0008,
|
||||
TriggerZ = 0x0010,
|
||||
TriggerR = 0x0020,
|
||||
TriggerL = 0x0040,
|
||||
ButtonA = 0x0100,
|
||||
ButtonB = 0x0200,
|
||||
ButtonX = 0x0400,
|
||||
ButtonY = 0x0800,
|
||||
ButtonStart = 0x1000,
|
||||
};
|
||||
|
||||
enum class PadAxes : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
TriggerLeft,
|
||||
TriggerRight,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
enum class ControllerTypes {
|
||||
None,
|
||||
Wired,
|
||||
Wireless,
|
||||
};
|
||||
|
||||
struct GCController {
|
||||
ControllerTypes type = ControllerTypes::None;
|
||||
PadIdentifier identifier{};
|
||||
bool enable_vibration = false;
|
||||
u8 rumble_amplitude{};
|
||||
std::array<u8, 6> axis_origin{};
|
||||
u8 reset_origin_counter{};
|
||||
};
|
||||
|
||||
using AdapterPayload = std::array<u8, 37>;
|
||||
|
||||
void UpdatePadType(std::size_t port, ControllerTypes pad_type);
|
||||
void UpdateControllers(const AdapterPayload& adapter_payload);
|
||||
void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
|
||||
void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
|
||||
|
||||
void AdapterInputThread(std::stop_token stop_token);
|
||||
|
||||
void AdapterScanThread(std::stop_token stop_token);
|
||||
|
||||
bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
|
||||
|
||||
/// For use in initialization, querying devices to find the adapter
|
||||
bool Setup();
|
||||
|
||||
/// Returns true if we successfully gain access to GC Adapter
|
||||
bool CheckDeviceAccess();
|
||||
|
||||
/// Captures GC Adapter endpoint address
|
||||
/// Returns true if the endpoint was set correctly
|
||||
bool GetGCEndpoint(libusb_device* device);
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(std::size_t port) const;
|
||||
|
||||
/// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
void UpdateVibrations();
|
||||
|
||||
/// Updates vibration state of all controllers
|
||||
void SendVibrations();
|
||||
|
||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||
|
||||
std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
|
||||
std::array<GCController, 4> pads;
|
||||
|
||||
std::jthread adapter_input_thread;
|
||||
std::jthread adapter_scan_thread;
|
||||
bool restart_scan_thread{};
|
||||
|
||||
std::unique_ptr<LibUSBContext> libusb_ctx;
|
||||
|
||||
u8 input_endpoint{0};
|
||||
u8 output_endpoint{0};
|
||||
u8 input_error_counter{0};
|
||||
u8 output_error_counter{0};
|
||||
int vibration_counter{0};
|
||||
|
||||
bool rumble_enabled{true};
|
||||
bool vibration_changed{true};
|
||||
};
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <stop_token>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
struct libusb_context;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class LibUSBContext;
|
||||
class LibUSBDeviceHandle;
|
||||
|
||||
class GCAdapter : public InputEngine {
|
||||
public:
|
||||
explicit GCAdapter(std::string input_engine_);
|
||||
~GCAdapter() override;
|
||||
|
||||
Common::Input::VibrationError SetVibration(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
enum class PadButton {
|
||||
Undefined = 0x0000,
|
||||
ButtonLeft = 0x0001,
|
||||
ButtonRight = 0x0002,
|
||||
ButtonDown = 0x0004,
|
||||
ButtonUp = 0x0008,
|
||||
TriggerZ = 0x0010,
|
||||
TriggerR = 0x0020,
|
||||
TriggerL = 0x0040,
|
||||
ButtonA = 0x0100,
|
||||
ButtonB = 0x0200,
|
||||
ButtonX = 0x0400,
|
||||
ButtonY = 0x0800,
|
||||
ButtonStart = 0x1000,
|
||||
};
|
||||
|
||||
enum class PadAxes : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
TriggerLeft,
|
||||
TriggerRight,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
enum class ControllerTypes {
|
||||
None,
|
||||
Wired,
|
||||
Wireless,
|
||||
};
|
||||
|
||||
struct GCController {
|
||||
ControllerTypes type = ControllerTypes::None;
|
||||
PadIdentifier identifier{};
|
||||
bool enable_vibration = false;
|
||||
u8 rumble_amplitude{};
|
||||
std::array<u8, 6> axis_origin{};
|
||||
u8 reset_origin_counter{};
|
||||
};
|
||||
|
||||
using AdapterPayload = std::array<u8, 37>;
|
||||
|
||||
void UpdatePadType(std::size_t port, ControllerTypes pad_type);
|
||||
void UpdateControllers(const AdapterPayload& adapter_payload);
|
||||
void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
|
||||
void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
|
||||
|
||||
void AdapterInputThread(std::stop_token stop_token);
|
||||
|
||||
void AdapterScanThread(std::stop_token stop_token);
|
||||
|
||||
bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
|
||||
|
||||
/// For use in initialization, querying devices to find the adapter
|
||||
bool Setup();
|
||||
|
||||
/// Returns true if we successfully gain access to GC Adapter
|
||||
bool CheckDeviceAccess();
|
||||
|
||||
/// Captures GC Adapter endpoint address
|
||||
/// Returns true if the endpoint was set correctly
|
||||
bool GetGCEndpoint(libusb_device* device);
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(std::size_t port) const;
|
||||
|
||||
/// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
void UpdateVibrations();
|
||||
|
||||
/// Updates vibration state of all controllers
|
||||
void SendVibrations();
|
||||
|
||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||
|
||||
std::unique_ptr<LibUSBDeviceHandle> usb_adapter_handle;
|
||||
std::array<GCController, 4> pads;
|
||||
|
||||
std::jthread adapter_input_thread;
|
||||
std::jthread adapter_scan_thread;
|
||||
bool restart_scan_thread{};
|
||||
|
||||
std::unique_ptr<LibUSBContext> libusb_ctx;
|
||||
|
||||
u8 input_endpoint{0};
|
||||
u8 output_endpoint{0};
|
||||
u8 input_error_counter{0};
|
||||
u8 output_error_counter{0};
|
||||
int vibration_counter{0};
|
||||
|
||||
bool rumble_enabled{true};
|
||||
bool vibration_changed{true};
|
||||
};
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,111 +1,111 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
constexpr PadIdentifier key_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
constexpr PadIdentifier keyboard_key_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 1,
|
||||
.pad = 0,
|
||||
};
|
||||
constexpr PadIdentifier keyboard_modifier_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 1,
|
||||
.pad = 1,
|
||||
};
|
||||
|
||||
Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
// Keyboard is broken into 3 diferent sets:
|
||||
// key: Unfiltered intended for controllers.
|
||||
// keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
|
||||
// keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard
|
||||
// emulation.
|
||||
PreSetController(key_identifier);
|
||||
PreSetController(keyboard_key_identifier);
|
||||
PreSetController(keyboard_modifier_identifier);
|
||||
}
|
||||
|
||||
void Keyboard::PressKey(int key_code) {
|
||||
SetButton(key_identifier, key_code, true);
|
||||
}
|
||||
|
||||
void Keyboard::ReleaseKey(int key_code) {
|
||||
SetButton(key_identifier, key_code, false);
|
||||
}
|
||||
|
||||
void Keyboard::PressKeyboardKey(int key_index) {
|
||||
if (key_index == Settings::NativeKeyboard::None) {
|
||||
return;
|
||||
}
|
||||
SetButton(keyboard_key_identifier, key_index, true);
|
||||
}
|
||||
|
||||
void Keyboard::ReleaseKeyboardKey(int key_index) {
|
||||
if (key_index == Settings::NativeKeyboard::None) {
|
||||
return;
|
||||
}
|
||||
SetButton(keyboard_key_identifier, key_index, false);
|
||||
}
|
||||
|
||||
void Keyboard::SetKeyboardModifiers(int key_modifiers) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
bool key_value = ((key_modifiers >> i) & 0x1) != 0;
|
||||
SetButton(keyboard_modifier_identifier, i, key_value);
|
||||
// Use the modifier to press the key button equivalent
|
||||
switch (i) {
|
||||
case Settings::NativeKeyboard::LeftControl:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftControlKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::LeftShift:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftShiftKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::LeftAlt:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftAltKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::LeftMeta:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftMetaKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightControl:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightControlKey,
|
||||
key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightShift:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightShiftKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightAlt:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightAltKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightMeta:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightMetaKey, key_value);
|
||||
break;
|
||||
default:
|
||||
// Other modifier keys should be pressed with PressKey since they stay enabled until
|
||||
// next press
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::ReleaseAllKeys() {
|
||||
ResetButtonState();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Keyboard::GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
devices.emplace_back(Common::ParamPackage{
|
||||
{"engine", GetEngineName()},
|
||||
{"display", "Keyboard Only"},
|
||||
});
|
||||
return devices;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
constexpr PadIdentifier key_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
constexpr PadIdentifier keyboard_key_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 1,
|
||||
.pad = 0,
|
||||
};
|
||||
constexpr PadIdentifier keyboard_modifier_identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 1,
|
||||
.pad = 1,
|
||||
};
|
||||
|
||||
Keyboard::Keyboard(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
// Keyboard is broken into 3 diferent sets:
|
||||
// key: Unfiltered intended for controllers.
|
||||
// keyboard_key: Allows only Settings::NativeKeyboard::Keys intended for keyboard emulation.
|
||||
// keyboard_modifier: Allows only Settings::NativeKeyboard::Modifiers intended for keyboard
|
||||
// emulation.
|
||||
PreSetController(key_identifier);
|
||||
PreSetController(keyboard_key_identifier);
|
||||
PreSetController(keyboard_modifier_identifier);
|
||||
}
|
||||
|
||||
void Keyboard::PressKey(int key_code) {
|
||||
SetButton(key_identifier, key_code, true);
|
||||
}
|
||||
|
||||
void Keyboard::ReleaseKey(int key_code) {
|
||||
SetButton(key_identifier, key_code, false);
|
||||
}
|
||||
|
||||
void Keyboard::PressKeyboardKey(int key_index) {
|
||||
if (key_index == Settings::NativeKeyboard::None) {
|
||||
return;
|
||||
}
|
||||
SetButton(keyboard_key_identifier, key_index, true);
|
||||
}
|
||||
|
||||
void Keyboard::ReleaseKeyboardKey(int key_index) {
|
||||
if (key_index == Settings::NativeKeyboard::None) {
|
||||
return;
|
||||
}
|
||||
SetButton(keyboard_key_identifier, key_index, false);
|
||||
}
|
||||
|
||||
void Keyboard::SetKeyboardModifiers(int key_modifiers) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
bool key_value = ((key_modifiers >> i) & 0x1) != 0;
|
||||
SetButton(keyboard_modifier_identifier, i, key_value);
|
||||
// Use the modifier to press the key button equivalent
|
||||
switch (i) {
|
||||
case Settings::NativeKeyboard::LeftControl:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftControlKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::LeftShift:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftShiftKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::LeftAlt:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftAltKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::LeftMeta:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::LeftMetaKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightControl:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightControlKey,
|
||||
key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightShift:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightShiftKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightAlt:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightAltKey, key_value);
|
||||
break;
|
||||
case Settings::NativeKeyboard::RightMeta:
|
||||
SetButton(keyboard_key_identifier, Settings::NativeKeyboard::RightMetaKey, key_value);
|
||||
break;
|
||||
default:
|
||||
// Other modifier keys should be pressed with PressKey since they stay enabled until
|
||||
// next press
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::ReleaseAllKeys() {
|
||||
ResetButtonState();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Keyboard::GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
devices.emplace_back(Common::ParamPackage{
|
||||
{"engine", GetEngineName()},
|
||||
{"display", "Keyboard Only"},
|
||||
});
|
||||
return devices;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,55 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Keyboard final : public InputEngine {
|
||||
public:
|
||||
explicit Keyboard(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Sets the status of the keyboard key to pressed
|
||||
* @param key_index index of the key to press
|
||||
*/
|
||||
void PressKeyboardKey(int key_index);
|
||||
|
||||
/**
|
||||
* Sets the status of the keyboard key to released
|
||||
* @param key_index index of the key to release
|
||||
*/
|
||||
void ReleaseKeyboardKey(int key_index);
|
||||
|
||||
/**
|
||||
* Sets the status of all keyboard modifier keys
|
||||
* @param key_modifiers the code of the key to release
|
||||
*/
|
||||
void SetKeyboardModifiers(int key_modifiers);
|
||||
|
||||
/// Sets all keys to the non pressed state
|
||||
void ReleaseAllKeys();
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Keyboard final : public InputEngine {
|
||||
public:
|
||||
explicit Keyboard(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Sets the status of the keyboard key to pressed
|
||||
* @param key_index index of the key to press
|
||||
*/
|
||||
void PressKeyboardKey(int key_index);
|
||||
|
||||
/**
|
||||
* Sets the status of the keyboard key to released
|
||||
* @param key_index index of the key to release
|
||||
*/
|
||||
void ReleaseKeyboardKey(int key_index);
|
||||
|
||||
/**
|
||||
* Sets the status of all keyboard modifier keys
|
||||
* @param key_modifiers the code of the key to release
|
||||
*/
|
||||
void SetKeyboardModifiers(int key_modifiers);
|
||||
|
||||
/// Sets all keys to the non pressed state
|
||||
void ReleaseAllKeys();
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,215 +1,215 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr int mouse_axis_x = 0;
|
||||
constexpr int mouse_axis_y = 1;
|
||||
constexpr int wheel_axis_x = 2;
|
||||
constexpr int wheel_axis_y = 3;
|
||||
constexpr int motion_wheel_y = 4;
|
||||
constexpr int touch_axis_x = 10;
|
||||
constexpr int touch_axis_y = 11;
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, mouse_axis_x);
|
||||
PreSetAxis(identifier, mouse_axis_y);
|
||||
PreSetAxis(identifier, wheel_axis_x);
|
||||
PreSetAxis(identifier, wheel_axis_y);
|
||||
PreSetAxis(identifier, motion_wheel_y);
|
||||
PreSetAxis(identifier, touch_axis_x);
|
||||
PreSetAxis(identifier, touch_axis_y);
|
||||
update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
|
||||
}
|
||||
|
||||
void Mouse::UpdateThread(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Mouse");
|
||||
constexpr int update_time = 10;
|
||||
while (!stop_token.stop_requested()) {
|
||||
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
||||
// Slow movement by 4%
|
||||
last_mouse_change *= 0.96f;
|
||||
const float sensitivity =
|
||||
Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f;
|
||||
SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity);
|
||||
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity);
|
||||
}
|
||||
|
||||
SetAxis(identifier, motion_wheel_y, 0.0f);
|
||||
|
||||
if (mouse_panning_timout++ > 20) {
|
||||
StopPanning();
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) {
|
||||
// If native mouse is enabled just set the screen coordinates
|
||||
if (Settings::values.mouse_enabled) {
|
||||
SetAxis(identifier, mouse_axis_x, touch_x);
|
||||
SetAxis(identifier, mouse_axis_y, touch_y);
|
||||
return;
|
||||
}
|
||||
|
||||
SetAxis(identifier, touch_axis_x, touch_x);
|
||||
SetAxis(identifier, touch_axis_y, touch_y);
|
||||
|
||||
if (Settings::values.mouse_panning) {
|
||||
auto mouse_change =
|
||||
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
|
||||
mouse_panning_timout = 0;
|
||||
|
||||
const auto move_distance = mouse_change.Length();
|
||||
if (move_distance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make slow movements at least 3 units on lenght
|
||||
if (move_distance < 3.0f) {
|
||||
// Normalize value
|
||||
mouse_change /= move_distance;
|
||||
mouse_change *= 3.0f;
|
||||
}
|
||||
|
||||
// Average mouse movements
|
||||
last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f);
|
||||
|
||||
const auto last_move_distance = last_mouse_change.Length();
|
||||
|
||||
// Make fast movements clamp to 8 units on lenght
|
||||
if (last_move_distance > 8.0f) {
|
||||
// Normalize value
|
||||
last_mouse_change /= last_move_distance;
|
||||
last_mouse_change *= 8.0f;
|
||||
}
|
||||
|
||||
// Ignore average if it's less than 1 unit and use current movement value
|
||||
if (last_move_distance < 1.0f) {
|
||||
last_mouse_change = mouse_change / mouse_change.Length();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (button_pressed) {
|
||||
const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
|
||||
const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f;
|
||||
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity);
|
||||
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) {
|
||||
SetAxis(identifier, touch_axis_x, touch_x);
|
||||
SetAxis(identifier, touch_axis_y, touch_y);
|
||||
SetButton(identifier, static_cast<int>(button), true);
|
||||
// Set initial analog parameters
|
||||
mouse_origin = {x, y};
|
||||
last_mouse_position = {x, y};
|
||||
button_pressed = true;
|
||||
}
|
||||
|
||||
void Mouse::ReleaseButton(MouseButton button) {
|
||||
SetButton(identifier, static_cast<int>(button), false);
|
||||
|
||||
if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
||||
SetAxis(identifier, mouse_axis_x, 0);
|
||||
SetAxis(identifier, mouse_axis_y, 0);
|
||||
}
|
||||
button_pressed = false;
|
||||
}
|
||||
|
||||
void Mouse::MouseWheelChange(int x, int y) {
|
||||
wheel_position.x += x;
|
||||
wheel_position.y += y;
|
||||
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
|
||||
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
|
||||
SetAxis(identifier, motion_wheel_y, static_cast<f32>(y) / 100.0f);
|
||||
}
|
||||
|
||||
void Mouse::ReleaseAllButtons() {
|
||||
ResetButtonState();
|
||||
button_pressed = false;
|
||||
}
|
||||
|
||||
void Mouse::StopPanning() {
|
||||
last_mouse_change = {};
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
devices.emplace_back(Common::ParamPackage{
|
||||
{"engine", GetEngineName()},
|
||||
{"display", "Keyboard/Mouse"},
|
||||
});
|
||||
return devices;
|
||||
}
|
||||
|
||||
AnalogMapping Mouse::GetAnalogMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
// Only overwrite different buttons from default
|
||||
AnalogMapping mapping = {};
|
||||
Common::ParamPackage right_analog_params;
|
||||
right_analog_params.Set("engine", GetEngineName());
|
||||
right_analog_params.Set("axis_x", 0);
|
||||
right_analog_params.Set("axis_y", 1);
|
||||
right_analog_params.Set("threshold", 0.5f);
|
||||
right_analog_params.Set("range", 1.0f);
|
||||
right_analog_params.Set("deadzone", 0.0f);
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames Mouse::GetUIButtonName(const Common::ParamPackage& params) const {
|
||||
const auto button = static_cast<MouseButton>(params.Get("button", 0));
|
||||
switch (button) {
|
||||
case MouseButton::Left:
|
||||
return Common::Input::ButtonNames::ButtonLeft;
|
||||
case MouseButton::Right:
|
||||
return Common::Input::ButtonNames::ButtonRight;
|
||||
case MouseButton::Wheel:
|
||||
return Common::Input::ButtonNames::ButtonMouseWheel;
|
||||
case MouseButton::Backward:
|
||||
return Common::Input::ButtonNames::ButtonBackward;
|
||||
case MouseButton::Forward:
|
||||
return Common::Input::ButtonNames::ButtonForward;
|
||||
case MouseButton::Task:
|
||||
return Common::Input::ButtonNames::ButtonTask;
|
||||
case MouseButton::Extra:
|
||||
return Common::Input::ButtonNames::ButtonExtra;
|
||||
case MouseButton::Undefined:
|
||||
default:
|
||||
return Common::Input::ButtonNames::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const {
|
||||
if (params.Has("button")) {
|
||||
return GetUIButtonName(params);
|
||||
}
|
||||
if (params.Has("axis")) {
|
||||
return Common::Input::ButtonNames::Value;
|
||||
}
|
||||
if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
|
||||
return Common::Input::ButtonNames::Engine;
|
||||
}
|
||||
|
||||
return Common::Input::ButtonNames::Invalid;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr int mouse_axis_x = 0;
|
||||
constexpr int mouse_axis_y = 1;
|
||||
constexpr int wheel_axis_x = 2;
|
||||
constexpr int wheel_axis_y = 3;
|
||||
constexpr int motion_wheel_y = 4;
|
||||
constexpr int touch_axis_x = 10;
|
||||
constexpr int touch_axis_y = 11;
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
PreSetAxis(identifier, mouse_axis_x);
|
||||
PreSetAxis(identifier, mouse_axis_y);
|
||||
PreSetAxis(identifier, wheel_axis_x);
|
||||
PreSetAxis(identifier, wheel_axis_y);
|
||||
PreSetAxis(identifier, motion_wheel_y);
|
||||
PreSetAxis(identifier, touch_axis_x);
|
||||
PreSetAxis(identifier, touch_axis_y);
|
||||
update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
|
||||
}
|
||||
|
||||
void Mouse::UpdateThread(std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Mouse");
|
||||
constexpr int update_time = 10;
|
||||
while (!stop_token.stop_requested()) {
|
||||
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
||||
// Slow movement by 4%
|
||||
last_mouse_change *= 0.96f;
|
||||
const float sensitivity =
|
||||
Settings::values.mouse_panning_sensitivity.GetValue() * 0.022f;
|
||||
SetAxis(identifier, mouse_axis_x, last_mouse_change.x * sensitivity);
|
||||
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y * sensitivity);
|
||||
}
|
||||
|
||||
SetAxis(identifier, motion_wheel_y, 0.0f);
|
||||
|
||||
if (mouse_panning_timout++ > 20) {
|
||||
StopPanning();
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y) {
|
||||
// If native mouse is enabled just set the screen coordinates
|
||||
if (Settings::values.mouse_enabled) {
|
||||
SetAxis(identifier, mouse_axis_x, touch_x);
|
||||
SetAxis(identifier, mouse_axis_y, touch_y);
|
||||
return;
|
||||
}
|
||||
|
||||
SetAxis(identifier, touch_axis_x, touch_x);
|
||||
SetAxis(identifier, touch_axis_y, touch_y);
|
||||
|
||||
if (Settings::values.mouse_panning) {
|
||||
auto mouse_change =
|
||||
(Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y)).Cast<float>();
|
||||
mouse_panning_timout = 0;
|
||||
|
||||
const auto move_distance = mouse_change.Length();
|
||||
if (move_distance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make slow movements at least 3 units on lenght
|
||||
if (move_distance < 3.0f) {
|
||||
// Normalize value
|
||||
mouse_change /= move_distance;
|
||||
mouse_change *= 3.0f;
|
||||
}
|
||||
|
||||
// Average mouse movements
|
||||
last_mouse_change = (last_mouse_change * 0.91f) + (mouse_change * 0.09f);
|
||||
|
||||
const auto last_move_distance = last_mouse_change.Length();
|
||||
|
||||
// Make fast movements clamp to 8 units on lenght
|
||||
if (last_move_distance > 8.0f) {
|
||||
// Normalize value
|
||||
last_mouse_change /= last_move_distance;
|
||||
last_mouse_change *= 8.0f;
|
||||
}
|
||||
|
||||
// Ignore average if it's less than 1 unit and use current movement value
|
||||
if (last_move_distance < 1.0f) {
|
||||
last_mouse_change = mouse_change / mouse_change.Length();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (button_pressed) {
|
||||
const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
|
||||
const float sensitivity = Settings::values.mouse_panning_sensitivity.GetValue() * 0.0012f;
|
||||
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * sensitivity);
|
||||
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * sensitivity);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button) {
|
||||
SetAxis(identifier, touch_axis_x, touch_x);
|
||||
SetAxis(identifier, touch_axis_y, touch_y);
|
||||
SetButton(identifier, static_cast<int>(button), true);
|
||||
// Set initial analog parameters
|
||||
mouse_origin = {x, y};
|
||||
last_mouse_position = {x, y};
|
||||
button_pressed = true;
|
||||
}
|
||||
|
||||
void Mouse::ReleaseButton(MouseButton button) {
|
||||
SetButton(identifier, static_cast<int>(button), false);
|
||||
|
||||
if (!Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
||||
SetAxis(identifier, mouse_axis_x, 0);
|
||||
SetAxis(identifier, mouse_axis_y, 0);
|
||||
}
|
||||
button_pressed = false;
|
||||
}
|
||||
|
||||
void Mouse::MouseWheelChange(int x, int y) {
|
||||
wheel_position.x += x;
|
||||
wheel_position.y += y;
|
||||
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
|
||||
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
|
||||
SetAxis(identifier, motion_wheel_y, static_cast<f32>(y) / 100.0f);
|
||||
}
|
||||
|
||||
void Mouse::ReleaseAllButtons() {
|
||||
ResetButtonState();
|
||||
button_pressed = false;
|
||||
}
|
||||
|
||||
void Mouse::StopPanning() {
|
||||
last_mouse_change = {};
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Mouse::GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
devices.emplace_back(Common::ParamPackage{
|
||||
{"engine", GetEngineName()},
|
||||
{"display", "Keyboard/Mouse"},
|
||||
});
|
||||
return devices;
|
||||
}
|
||||
|
||||
AnalogMapping Mouse::GetAnalogMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
// Only overwrite different buttons from default
|
||||
AnalogMapping mapping = {};
|
||||
Common::ParamPackage right_analog_params;
|
||||
right_analog_params.Set("engine", GetEngineName());
|
||||
right_analog_params.Set("axis_x", 0);
|
||||
right_analog_params.Set("axis_y", 1);
|
||||
right_analog_params.Set("threshold", 0.5f);
|
||||
right_analog_params.Set("range", 1.0f);
|
||||
right_analog_params.Set("deadzone", 0.0f);
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames Mouse::GetUIButtonName(const Common::ParamPackage& params) const {
|
||||
const auto button = static_cast<MouseButton>(params.Get("button", 0));
|
||||
switch (button) {
|
||||
case MouseButton::Left:
|
||||
return Common::Input::ButtonNames::ButtonLeft;
|
||||
case MouseButton::Right:
|
||||
return Common::Input::ButtonNames::ButtonRight;
|
||||
case MouseButton::Wheel:
|
||||
return Common::Input::ButtonNames::ButtonMouseWheel;
|
||||
case MouseButton::Backward:
|
||||
return Common::Input::ButtonNames::ButtonBackward;
|
||||
case MouseButton::Forward:
|
||||
return Common::Input::ButtonNames::ButtonForward;
|
||||
case MouseButton::Task:
|
||||
return Common::Input::ButtonNames::ButtonTask;
|
||||
case MouseButton::Extra:
|
||||
return Common::Input::ButtonNames::ButtonExtra;
|
||||
case MouseButton::Undefined:
|
||||
default:
|
||||
return Common::Input::ButtonNames::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames Mouse::GetUIName(const Common::ParamPackage& params) const {
|
||||
if (params.Has("button")) {
|
||||
return GetUIButtonName(params);
|
||||
}
|
||||
if (params.Has("axis")) {
|
||||
return Common::Input::ButtonNames::Value;
|
||||
}
|
||||
if (params.Has("axis_x") && params.Has("axis_y") && params.Has("axis_z")) {
|
||||
return Common::Input::ButtonNames::Engine;
|
||||
}
|
||||
|
||||
return Common::Input::ButtonNames::Invalid;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,82 +1,82 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
|
||||
#include "common/vector_math.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
enum class MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Wheel,
|
||||
Backward,
|
||||
Forward,
|
||||
Task,
|
||||
Extra,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Mouse final : public InputEngine {
|
||||
public:
|
||||
explicit Mouse(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
* @param center_x the x-coordinate of the middle of the screen
|
||||
* @param center_y the y-coordinate of the middle of the screen
|
||||
*/
|
||||
void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
* @param key_code the code of the key to press
|
||||
*/
|
||||
void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to released
|
||||
* @param key_code the code of the key to release
|
||||
*/
|
||||
void ReleaseButton(MouseButton button);
|
||||
|
||||
/**
|
||||
* Sets the status of the mouse wheel
|
||||
* @param x delta movement in the x direction
|
||||
* @param y delta movement in the y direction
|
||||
*/
|
||||
void MouseWheelChange(int x, int y);
|
||||
|
||||
void ReleaseAllButtons();
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
private:
|
||||
void UpdateThread(std::stop_token stop_token);
|
||||
void StopPanning();
|
||||
|
||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||
|
||||
Common::Vec2<int> mouse_origin;
|
||||
Common::Vec2<int> last_mouse_position;
|
||||
Common::Vec2<float> last_mouse_change;
|
||||
Common::Vec2<int> wheel_position;
|
||||
bool button_pressed;
|
||||
int mouse_panning_timout{};
|
||||
std::jthread update_thread;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
|
||||
#include "common/vector_math.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
enum class MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Wheel,
|
||||
Backward,
|
||||
Forward,
|
||||
Task,
|
||||
Extra,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class Mouse final : public InputEngine {
|
||||
public:
|
||||
explicit Mouse(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
* @param center_x the x-coordinate of the middle of the screen
|
||||
* @param center_y the y-coordinate of the middle of the screen
|
||||
*/
|
||||
void MouseMove(int x, int y, f32 touch_x, f32 touch_y, int center_x, int center_y);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
* @param key_code the code of the key to press
|
||||
*/
|
||||
void PressButton(int x, int y, f32 touch_x, f32 touch_y, MouseButton button);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to released
|
||||
* @param key_code the code of the key to release
|
||||
*/
|
||||
void ReleaseButton(MouseButton button);
|
||||
|
||||
/**
|
||||
* Sets the status of the mouse wheel
|
||||
* @param x delta movement in the x direction
|
||||
* @param y delta movement in the y direction
|
||||
*/
|
||||
void MouseWheelChange(int x, int y);
|
||||
|
||||
void ReleaseAllButtons();
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
private:
|
||||
void UpdateThread(std::stop_token stop_token);
|
||||
void StopPanning();
|
||||
|
||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||
|
||||
Common::Vec2<int> mouse_origin;
|
||||
Common::Vec2<int> last_mouse_position;
|
||||
Common::Vec2<float> last_mouse_change;
|
||||
Common::Vec2<int> wheel_position;
|
||||
bool button_pressed;
|
||||
int mouse_panning_timout{};
|
||||
std::jthread update_thread;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,134 +1,134 @@
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
union SDL_Event;
|
||||
using SDL_GameController = struct _SDL_GameController;
|
||||
using SDL_Joystick = struct _SDL_Joystick;
|
||||
using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
using ButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>;
|
||||
using ZButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
|
||||
|
||||
class SDLDriver : public InputEngine {
|
||||
public:
|
||||
/// Initializes and registers SDL device factories
|
||||
explicit SDLDriver(std::string input_engine_);
|
||||
|
||||
/// Unregisters SDL device factories and shut them down.
|
||||
~SDLDriver() override;
|
||||
|
||||
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
||||
void HandleGameControllerEvent(const SDL_Event& event);
|
||||
|
||||
/// Get the nth joystick with the corresponding GUID
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
|
||||
* tie it to a SDLJoystick with the same guid and that port
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port);
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
|
||||
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
std::string GetHatButtonName(u8 direction_value) const override;
|
||||
u8 GetHatButtonId(const std::string& direction_name) const override;
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::Input::VibrationError SetVibration(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
|
||||
private:
|
||||
struct VibrationRequest {
|
||||
PadIdentifier identifier;
|
||||
Common::Input::VibrationStatus vibration;
|
||||
};
|
||||
|
||||
void InitJoystick(int joystick_index);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
/// Needs to be called before SDL_QuitSubSystem.
|
||||
void CloseJoysticks();
|
||||
|
||||
/// Takes all vibrations from the queue and sends the command to the controller
|
||||
void SendVibrations();
|
||||
|
||||
Common::ParamPackage BuildAnalogParamPackageForButton(int port, const Common::UUID& guid,
|
||||
s32 axis, float value = 0.1f) const;
|
||||
Common::ParamPackage BuildButtonParamPackageForButton(int port, const Common::UUID& guid,
|
||||
s32 button) const;
|
||||
|
||||
Common::ParamPackage BuildHatParamPackageForButton(int port, const Common::UUID& guid, s32 hat,
|
||||
u8 value) const;
|
||||
|
||||
Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const;
|
||||
|
||||
Common::ParamPackage BuildParamPackageForBinding(
|
||||
int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const;
|
||||
|
||||
Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
|
||||
int axis_y, float offset_x,
|
||||
float offset_y) const;
|
||||
|
||||
/// Returns the default button bindings list for generic controllers
|
||||
ButtonBindings GetDefaultButtonBinding() const;
|
||||
|
||||
/// Returns the default button bindings list for nintendo controllers
|
||||
ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
|
||||
|
||||
/// Returns the button mappings from a single controller
|
||||
ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
|
||||
const ButtonBindings& switch_to_sdl_button,
|
||||
const ZButtonBindings& switch_to_sdl_axis) const;
|
||||
|
||||
/// Returns the button mappings from two different controllers
|
||||
ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
|
||||
const std::shared_ptr<SDLJoystick>& joystick2,
|
||||
const ButtonBindings& switch_to_sdl_button,
|
||||
const ZButtonBindings& switch_to_sdl_axis) const;
|
||||
|
||||
/// Returns true if the button is on the left joycon
|
||||
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
|
||||
|
||||
/// Queue of vibration request to controllers
|
||||
Common::SPSCQueue<VibrationRequest> vibration_queue;
|
||||
|
||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||
std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
std::mutex joystick_map_mutex;
|
||||
|
||||
bool start_thread = false;
|
||||
std::atomic<bool> initialized = false;
|
||||
|
||||
std::thread poll_thread;
|
||||
std::thread vibration_thread;
|
||||
};
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
union SDL_Event;
|
||||
using SDL_GameController = struct _SDL_GameController;
|
||||
using SDL_Joystick = struct _SDL_Joystick;
|
||||
using SDL_JoystickID = s32;
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class SDLJoystick;
|
||||
|
||||
using ButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>;
|
||||
using ZButtonBindings =
|
||||
std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
|
||||
|
||||
class SDLDriver : public InputEngine {
|
||||
public:
|
||||
/// Initializes and registers SDL device factories
|
||||
explicit SDLDriver(std::string input_engine_);
|
||||
|
||||
/// Unregisters SDL device factories and shut them down.
|
||||
~SDLDriver() override;
|
||||
|
||||
/// Handle SDL_Events for joysticks from SDL_PollEvent
|
||||
void HandleGameControllerEvent(const SDL_Event& event);
|
||||
|
||||
/// Get the nth joystick with the corresponding GUID
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickBySDLID(SDL_JoystickID sdl_id);
|
||||
|
||||
/**
|
||||
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
|
||||
* tie it to a SDLJoystick with the same guid and that port
|
||||
*/
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port);
|
||||
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
|
||||
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
std::string GetHatButtonName(u8 direction_value) const override;
|
||||
u8 GetHatButtonId(const std::string& direction_name) const override;
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::Input::VibrationError SetVibration(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
|
||||
private:
|
||||
struct VibrationRequest {
|
||||
PadIdentifier identifier;
|
||||
Common::Input::VibrationStatus vibration;
|
||||
};
|
||||
|
||||
void InitJoystick(int joystick_index);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
/// Needs to be called before SDL_QuitSubSystem.
|
||||
void CloseJoysticks();
|
||||
|
||||
/// Takes all vibrations from the queue and sends the command to the controller
|
||||
void SendVibrations();
|
||||
|
||||
Common::ParamPackage BuildAnalogParamPackageForButton(int port, const Common::UUID& guid,
|
||||
s32 axis, float value = 0.1f) const;
|
||||
Common::ParamPackage BuildButtonParamPackageForButton(int port, const Common::UUID& guid,
|
||||
s32 button) const;
|
||||
|
||||
Common::ParamPackage BuildHatParamPackageForButton(int port, const Common::UUID& guid, s32 hat,
|
||||
u8 value) const;
|
||||
|
||||
Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const;
|
||||
|
||||
Common::ParamPackage BuildParamPackageForBinding(
|
||||
int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const;
|
||||
|
||||
Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
|
||||
int axis_y, float offset_x,
|
||||
float offset_y) const;
|
||||
|
||||
/// Returns the default button bindings list for generic controllers
|
||||
ButtonBindings GetDefaultButtonBinding() const;
|
||||
|
||||
/// Returns the default button bindings list for nintendo controllers
|
||||
ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const;
|
||||
|
||||
/// Returns the button mappings from a single controller
|
||||
ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
|
||||
const ButtonBindings& switch_to_sdl_button,
|
||||
const ZButtonBindings& switch_to_sdl_axis) const;
|
||||
|
||||
/// Returns the button mappings from two different controllers
|
||||
ButtonMapping GetDualControllerMapping(const std::shared_ptr<SDLJoystick>& joystick,
|
||||
const std::shared_ptr<SDLJoystick>& joystick2,
|
||||
const ButtonBindings& switch_to_sdl_button,
|
||||
const ZButtonBindings& switch_to_sdl_axis) const;
|
||||
|
||||
/// Returns true if the button is on the left joycon
|
||||
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
|
||||
|
||||
/// Queue of vibration request to controllers
|
||||
Common::SPSCQueue<VibrationRequest> vibration_queue;
|
||||
|
||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||
std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
std::mutex joystick_map_mutex;
|
||||
|
||||
bool start_thread = false;
|
||||
std::atomic<bool> initialized = false;
|
||||
|
||||
std::thread poll_thread;
|
||||
std::thread vibration_thread;
|
||||
};
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,337 +1,337 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
|
||||
enum class Tas::TasAxis : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
// Supported keywords and buttons from a TAS file
|
||||
constexpr std::array<std::pair<std::string_view, TasButton>, 18> text_to_tas_button = {
|
||||
std::pair{"KEY_A", TasButton::BUTTON_A},
|
||||
{"KEY_B", TasButton::BUTTON_B},
|
||||
{"KEY_X", TasButton::BUTTON_X},
|
||||
{"KEY_Y", TasButton::BUTTON_Y},
|
||||
{"KEY_LSTICK", TasButton::STICK_L},
|
||||
{"KEY_RSTICK", TasButton::STICK_R},
|
||||
{"KEY_L", TasButton::TRIGGER_L},
|
||||
{"KEY_R", TasButton::TRIGGER_R},
|
||||
{"KEY_PLUS", TasButton::BUTTON_PLUS},
|
||||
{"KEY_MINUS", TasButton::BUTTON_MINUS},
|
||||
{"KEY_DLEFT", TasButton::BUTTON_LEFT},
|
||||
{"KEY_DUP", TasButton::BUTTON_UP},
|
||||
{"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
|
||||
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
|
||||
{"KEY_SL", TasButton::BUTTON_SL},
|
||||
{"KEY_SR", TasButton::BUTTON_SR},
|
||||
// These buttons are disabled to avoid TAS input from activating hotkeys
|
||||
// {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
|
||||
// {"KEY_HOME", TasButton::BUTTON_HOME},
|
||||
{"KEY_ZL", TasButton::TRIGGER_ZL},
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
Tas::Tas(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) {
|
||||
PadIdentifier identifier{
|
||||
.guid = Common::UUID{},
|
||||
.port = player_index,
|
||||
.pad = 0,
|
||||
};
|
||||
PreSetController(identifier);
|
||||
}
|
||||
ClearInput();
|
||||
if (!Settings::values.tas_enable) {
|
||||
needs_reset = true;
|
||||
return;
|
||||
}
|
||||
LoadTasFiles();
|
||||
}
|
||||
|
||||
Tas::~Tas() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Tas::LoadTasFiles() {
|
||||
script_length = 0;
|
||||
for (size_t i = 0; i < commands.size(); i++) {
|
||||
LoadTasFile(i, 0);
|
||||
if (commands[i].size() > script_length) {
|
||||
script_length = commands[i].size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
commands[player_index].clear();
|
||||
|
||||
std::string file = Common::FS::ReadStringFromFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
|
||||
fmt::format("script{}-{}.txt", file_index, player_index + 1),
|
||||
Common::FS::FileType::BinaryFile);
|
||||
std::istringstream command_line(file);
|
||||
std::string line;
|
||||
int frame_no = 0;
|
||||
while (std::getline(command_line, line, '\n')) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ' ')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_list.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto num_frames = std::stoi(seg_list[0]);
|
||||
while (frame_no < num_frames) {
|
||||
commands[player_index].emplace_back();
|
||||
frame_no++;
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_ERROR(Input, "Invalid argument: '{}' at command {}", seg_list[0], frame_no);
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_ERROR(Input, "Out of range: '{}' at command {}", seg_list[0], frame_no);
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
.buttons = ReadCommandButtons(seg_list[1]),
|
||||
.l_axis = ReadCommandAxis(seg_list[2]),
|
||||
.r_axis = ReadCommandAxis(seg_list[3]),
|
||||
};
|
||||
commands[player_index].push_back(command);
|
||||
frame_no++;
|
||||
}
|
||||
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
|
||||
}
|
||||
|
||||
void Tas::WriteTasFile(std::u8string_view file_name) {
|
||||
std::string output_text;
|
||||
for (size_t frame = 0; frame < record_commands.size(); frame++) {
|
||||
const TASCommand& line = record_commands[frame];
|
||||
output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
|
||||
WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
|
||||
}
|
||||
|
||||
const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name;
|
||||
const auto bytes_written =
|
||||
Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text);
|
||||
if (bytes_written == output_text.size()) {
|
||||
LOG_INFO(Input, "TAS file written to file!");
|
||||
} else {
|
||||
LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
|
||||
output_text.size());
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
|
||||
last_input = {
|
||||
.buttons = buttons,
|
||||
.l_axis = left_axis,
|
||||
.r_axis = right_axis,
|
||||
};
|
||||
}
|
||||
|
||||
std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
|
||||
TasState state;
|
||||
if (is_recording) {
|
||||
return {TasState::Recording, 0, record_commands.size()};
|
||||
}
|
||||
|
||||
if (is_running) {
|
||||
state = TasState::Running;
|
||||
} else {
|
||||
state = TasState::Stopped;
|
||||
}
|
||||
|
||||
return {state, current_command, script_length};
|
||||
}
|
||||
|
||||
void Tas::UpdateThread() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
if (is_running) {
|
||||
Stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_recording) {
|
||||
record_commands.push_back(last_input);
|
||||
}
|
||||
if (needs_reset) {
|
||||
current_command = 0;
|
||||
needs_reset = false;
|
||||
LoadTasFiles();
|
||||
LOG_DEBUG(Input, "tas_reset done");
|
||||
}
|
||||
|
||||
if (!is_running) {
|
||||
ClearInput();
|
||||
return;
|
||||
}
|
||||
if (current_command < script_length) {
|
||||
LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
|
||||
const size_t frame = current_command++;
|
||||
for (size_t player_index = 0; player_index < commands.size(); player_index++) {
|
||||
TASCommand command{};
|
||||
if (frame < commands[player_index].size()) {
|
||||
command = commands[player_index][frame];
|
||||
}
|
||||
|
||||
PadIdentifier identifier{
|
||||
.guid = Common::UUID{},
|
||||
.port = player_index,
|
||||
.pad = 0,
|
||||
};
|
||||
for (std::size_t i = 0; i < sizeof(command.buttons) * 8; ++i) {
|
||||
const bool button_status = (command.buttons & (1LLU << i)) != 0;
|
||||
const int button = static_cast<int>(i);
|
||||
SetButton(identifier, button, button_status);
|
||||
}
|
||||
SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y);
|
||||
SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y);
|
||||
}
|
||||
} else {
|
||||
is_running = Settings::values.tas_loop.GetValue();
|
||||
LoadTasFiles();
|
||||
current_command = 0;
|
||||
ClearInput();
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::ClearInput() {
|
||||
ResetButtonState();
|
||||
ResetAnalogState();
|
||||
}
|
||||
|
||||
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ';')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_list.size() < 2) {
|
||||
LOG_ERROR(Input, "Invalid axis data: '{}'", line);
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const float x = std::stof(seg_list.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seg_list.at(1)) / 32767.0f;
|
||||
return {x, y};
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_ERROR(Input, "Invalid argument: '{}'", line);
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_ERROR(Input, "Out of range: '{}'", line);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
u64 Tas::ReadCommandButtons(const std::string& line) const {
|
||||
std::istringstream button_text(line);
|
||||
std::string button_line;
|
||||
u64 buttons = 0;
|
||||
while (std::getline(button_text, button_line, ';')) {
|
||||
for (const auto& [text, tas_button] : text_to_tas_button) {
|
||||
if (text == button_line) {
|
||||
buttons |= static_cast<u64>(tas_button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandButtons(u64 buttons) const {
|
||||
std::string returns;
|
||||
for (const auto& [text_button, tas_button] : text_to_tas_button) {
|
||||
if ((buttons & static_cast<u64>(tas_button)) != 0) {
|
||||
returns += fmt::format("{};", text_button);
|
||||
}
|
||||
}
|
||||
return returns.empty() ? "NONE" : returns;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandAxis(TasAnalog analog) const {
|
||||
return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
|
||||
}
|
||||
|
||||
void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) {
|
||||
SetAxis(identifier, static_cast<int>(axis), value);
|
||||
}
|
||||
|
||||
void Tas::StartStop() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
if (is_running) {
|
||||
Stop();
|
||||
} else {
|
||||
is_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::Stop() {
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
void Tas::Reset() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
needs_reset = true;
|
||||
}
|
||||
|
||||
bool Tas::Record() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return true;
|
||||
}
|
||||
is_recording = !is_recording;
|
||||
return is_recording;
|
||||
}
|
||||
|
||||
void Tas::SaveRecording(bool overwrite_file) {
|
||||
if (is_recording) {
|
||||
return;
|
||||
}
|
||||
if (record_commands.empty()) {
|
||||
return;
|
||||
}
|
||||
WriteTasFile(u8"record.txt");
|
||||
if (overwrite_file) {
|
||||
WriteTasFile(u8"script0-1.txt");
|
||||
}
|
||||
needs_reset = true;
|
||||
record_commands.clear();
|
||||
}
|
||||
|
||||
} // namespace InputCommon::TasInput
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
|
||||
enum class Tas::TasAxis : u8 {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
// Supported keywords and buttons from a TAS file
|
||||
constexpr std::array<std::pair<std::string_view, TasButton>, 18> text_to_tas_button = {
|
||||
std::pair{"KEY_A", TasButton::BUTTON_A},
|
||||
{"KEY_B", TasButton::BUTTON_B},
|
||||
{"KEY_X", TasButton::BUTTON_X},
|
||||
{"KEY_Y", TasButton::BUTTON_Y},
|
||||
{"KEY_LSTICK", TasButton::STICK_L},
|
||||
{"KEY_RSTICK", TasButton::STICK_R},
|
||||
{"KEY_L", TasButton::TRIGGER_L},
|
||||
{"KEY_R", TasButton::TRIGGER_R},
|
||||
{"KEY_PLUS", TasButton::BUTTON_PLUS},
|
||||
{"KEY_MINUS", TasButton::BUTTON_MINUS},
|
||||
{"KEY_DLEFT", TasButton::BUTTON_LEFT},
|
||||
{"KEY_DUP", TasButton::BUTTON_UP},
|
||||
{"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
|
||||
{"KEY_DDOWN", TasButton::BUTTON_DOWN},
|
||||
{"KEY_SL", TasButton::BUTTON_SL},
|
||||
{"KEY_SR", TasButton::BUTTON_SR},
|
||||
// These buttons are disabled to avoid TAS input from activating hotkeys
|
||||
// {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
|
||||
// {"KEY_HOME", TasButton::BUTTON_HOME},
|
||||
{"KEY_ZL", TasButton::TRIGGER_ZL},
|
||||
{"KEY_ZR", TasButton::TRIGGER_ZR},
|
||||
};
|
||||
|
||||
Tas::Tas(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
for (size_t player_index = 0; player_index < PLAYER_NUMBER; player_index++) {
|
||||
PadIdentifier identifier{
|
||||
.guid = Common::UUID{},
|
||||
.port = player_index,
|
||||
.pad = 0,
|
||||
};
|
||||
PreSetController(identifier);
|
||||
}
|
||||
ClearInput();
|
||||
if (!Settings::values.tas_enable) {
|
||||
needs_reset = true;
|
||||
return;
|
||||
}
|
||||
LoadTasFiles();
|
||||
}
|
||||
|
||||
Tas::~Tas() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Tas::LoadTasFiles() {
|
||||
script_length = 0;
|
||||
for (size_t i = 0; i < commands.size(); i++) {
|
||||
LoadTasFile(i, 0);
|
||||
if (commands[i].size() > script_length) {
|
||||
script_length = commands[i].size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::LoadTasFile(size_t player_index, size_t file_index) {
|
||||
commands[player_index].clear();
|
||||
|
||||
std::string file = Common::FS::ReadStringFromFile(
|
||||
Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) /
|
||||
fmt::format("script{}-{}.txt", file_index, player_index + 1),
|
||||
Common::FS::FileType::BinaryFile);
|
||||
std::istringstream command_line(file);
|
||||
std::string line;
|
||||
int frame_no = 0;
|
||||
while (std::getline(command_line, line, '\n')) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ' ')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_list.size() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto num_frames = std::stoi(seg_list[0]);
|
||||
while (frame_no < num_frames) {
|
||||
commands[player_index].emplace_back();
|
||||
frame_no++;
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_ERROR(Input, "Invalid argument: '{}' at command {}", seg_list[0], frame_no);
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_ERROR(Input, "Out of range: '{}' at command {}", seg_list[0], frame_no);
|
||||
}
|
||||
|
||||
TASCommand command = {
|
||||
.buttons = ReadCommandButtons(seg_list[1]),
|
||||
.l_axis = ReadCommandAxis(seg_list[2]),
|
||||
.r_axis = ReadCommandAxis(seg_list[3]),
|
||||
};
|
||||
commands[player_index].push_back(command);
|
||||
frame_no++;
|
||||
}
|
||||
LOG_INFO(Input, "TAS file loaded! {} frames", frame_no);
|
||||
}
|
||||
|
||||
void Tas::WriteTasFile(std::u8string_view file_name) {
|
||||
std::string output_text;
|
||||
for (size_t frame = 0; frame < record_commands.size(); frame++) {
|
||||
const TASCommand& line = record_commands[frame];
|
||||
output_text += fmt::format("{} {} {} {}\n", frame, WriteCommandButtons(line.buttons),
|
||||
WriteCommandAxis(line.l_axis), WriteCommandAxis(line.r_axis));
|
||||
}
|
||||
|
||||
const auto tas_file_name = Common::FS::GetYuzuPath(Common::FS::YuzuPath::TASDir) / file_name;
|
||||
const auto bytes_written =
|
||||
Common::FS::WriteStringToFile(tas_file_name, Common::FS::FileType::TextFile, output_text);
|
||||
if (bytes_written == output_text.size()) {
|
||||
LOG_INFO(Input, "TAS file written to file!");
|
||||
} else {
|
||||
LOG_ERROR(Input, "Writing the TAS-file has failed! {} / {} bytes written", bytes_written,
|
||||
output_text.size());
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
|
||||
last_input = {
|
||||
.buttons = buttons,
|
||||
.l_axis = left_axis,
|
||||
.r_axis = right_axis,
|
||||
};
|
||||
}
|
||||
|
||||
std::tuple<TasState, size_t, size_t> Tas::GetStatus() const {
|
||||
TasState state;
|
||||
if (is_recording) {
|
||||
return {TasState::Recording, 0, record_commands.size()};
|
||||
}
|
||||
|
||||
if (is_running) {
|
||||
state = TasState::Running;
|
||||
} else {
|
||||
state = TasState::Stopped;
|
||||
}
|
||||
|
||||
return {state, current_command, script_length};
|
||||
}
|
||||
|
||||
void Tas::UpdateThread() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
if (is_running) {
|
||||
Stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_recording) {
|
||||
record_commands.push_back(last_input);
|
||||
}
|
||||
if (needs_reset) {
|
||||
current_command = 0;
|
||||
needs_reset = false;
|
||||
LoadTasFiles();
|
||||
LOG_DEBUG(Input, "tas_reset done");
|
||||
}
|
||||
|
||||
if (!is_running) {
|
||||
ClearInput();
|
||||
return;
|
||||
}
|
||||
if (current_command < script_length) {
|
||||
LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
|
||||
const size_t frame = current_command++;
|
||||
for (size_t player_index = 0; player_index < commands.size(); player_index++) {
|
||||
TASCommand command{};
|
||||
if (frame < commands[player_index].size()) {
|
||||
command = commands[player_index][frame];
|
||||
}
|
||||
|
||||
PadIdentifier identifier{
|
||||
.guid = Common::UUID{},
|
||||
.port = player_index,
|
||||
.pad = 0,
|
||||
};
|
||||
for (std::size_t i = 0; i < sizeof(command.buttons) * 8; ++i) {
|
||||
const bool button_status = (command.buttons & (1LLU << i)) != 0;
|
||||
const int button = static_cast<int>(i);
|
||||
SetButton(identifier, button, button_status);
|
||||
}
|
||||
SetTasAxis(identifier, TasAxis::StickX, command.l_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::StickY, command.l_axis.y);
|
||||
SetTasAxis(identifier, TasAxis::SubstickX, command.r_axis.x);
|
||||
SetTasAxis(identifier, TasAxis::SubstickY, command.r_axis.y);
|
||||
}
|
||||
} else {
|
||||
is_running = Settings::values.tas_loop.GetValue();
|
||||
LoadTasFiles();
|
||||
current_command = 0;
|
||||
ClearInput();
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::ClearInput() {
|
||||
ResetButtonState();
|
||||
ResetAnalogState();
|
||||
}
|
||||
|
||||
TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
|
||||
std::vector<std::string> seg_list;
|
||||
{
|
||||
std::istringstream line_stream(line);
|
||||
std::string segment;
|
||||
while (std::getline(line_stream, segment, ';')) {
|
||||
seg_list.push_back(std::move(segment));
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_list.size() < 2) {
|
||||
LOG_ERROR(Input, "Invalid axis data: '{}'", line);
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const float x = std::stof(seg_list.at(0)) / 32767.0f;
|
||||
const float y = std::stof(seg_list.at(1)) / 32767.0f;
|
||||
return {x, y};
|
||||
} catch (const std::invalid_argument&) {
|
||||
LOG_ERROR(Input, "Invalid argument: '{}'", line);
|
||||
} catch (const std::out_of_range&) {
|
||||
LOG_ERROR(Input, "Out of range: '{}'", line);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
u64 Tas::ReadCommandButtons(const std::string& line) const {
|
||||
std::istringstream button_text(line);
|
||||
std::string button_line;
|
||||
u64 buttons = 0;
|
||||
while (std::getline(button_text, button_line, ';')) {
|
||||
for (const auto& [text, tas_button] : text_to_tas_button) {
|
||||
if (text == button_line) {
|
||||
buttons |= static_cast<u64>(tas_button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandButtons(u64 buttons) const {
|
||||
std::string returns;
|
||||
for (const auto& [text_button, tas_button] : text_to_tas_button) {
|
||||
if ((buttons & static_cast<u64>(tas_button)) != 0) {
|
||||
returns += fmt::format("{};", text_button);
|
||||
}
|
||||
}
|
||||
return returns.empty() ? "NONE" : returns;
|
||||
}
|
||||
|
||||
std::string Tas::WriteCommandAxis(TasAnalog analog) const {
|
||||
return fmt::format("{};{}", analog.x * 32767, analog.y * 32767);
|
||||
}
|
||||
|
||||
void Tas::SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value) {
|
||||
SetAxis(identifier, static_cast<int>(axis), value);
|
||||
}
|
||||
|
||||
void Tas::StartStop() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
if (is_running) {
|
||||
Stop();
|
||||
} else {
|
||||
is_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Tas::Stop() {
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
void Tas::Reset() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return;
|
||||
}
|
||||
needs_reset = true;
|
||||
}
|
||||
|
||||
bool Tas::Record() {
|
||||
if (!Settings::values.tas_enable) {
|
||||
return true;
|
||||
}
|
||||
is_recording = !is_recording;
|
||||
return is_recording;
|
||||
}
|
||||
|
||||
void Tas::SaveRecording(bool overwrite_file) {
|
||||
if (is_recording) {
|
||||
return;
|
||||
}
|
||||
if (record_commands.empty()) {
|
||||
return;
|
||||
}
|
||||
WriteTasFile(u8"record.txt");
|
||||
if (overwrite_file) {
|
||||
WriteTasFile(u8"script0-1.txt");
|
||||
}
|
||||
needs_reset = true;
|
||||
record_commands.clear();
|
||||
}
|
||||
|
||||
} // namespace InputCommon::TasInput
|
||||
|
@@ -1,200 +1,200 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
/*
|
||||
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
|
||||
Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
|
||||
for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
|
||||
|
||||
A script file has the same format as TAS-nx uses, so final files will look like this:
|
||||
|
||||
1 KEY_B 0;0 0;0
|
||||
6 KEY_ZL 0;0 0;0
|
||||
41 KEY_ZL;KEY_Y 0;0 0;0
|
||||
43 KEY_X;KEY_A 32767;0 0;0
|
||||
44 KEY_A 32767;0 0;0
|
||||
45 KEY_A 32767;0 0;0
|
||||
46 KEY_A 32767;0 0;0
|
||||
47 KEY_A 32767;0 0;0
|
||||
|
||||
After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
|
||||
CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
|
||||
has. Playback can be started or stopped using CTRL+F5.
|
||||
|
||||
However, for playback to actually work, the correct input device has to be selected: In the Controls
|
||||
menu, select TAS from the device list for the controller that the script should be played on.
|
||||
|
||||
Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
|
||||
connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
|
||||
again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
|
||||
record.txt.
|
||||
|
||||
For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
|
||||
P1).
|
||||
*/
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
|
||||
constexpr size_t PLAYER_NUMBER = 10;
|
||||
|
||||
enum class TasButton : u64 {
|
||||
BUTTON_A = 1U << 0,
|
||||
BUTTON_B = 1U << 1,
|
||||
BUTTON_X = 1U << 2,
|
||||
BUTTON_Y = 1U << 3,
|
||||
STICK_L = 1U << 4,
|
||||
STICK_R = 1U << 5,
|
||||
TRIGGER_L = 1U << 6,
|
||||
TRIGGER_R = 1U << 7,
|
||||
TRIGGER_ZL = 1U << 8,
|
||||
TRIGGER_ZR = 1U << 9,
|
||||
BUTTON_PLUS = 1U << 10,
|
||||
BUTTON_MINUS = 1U << 11,
|
||||
BUTTON_LEFT = 1U << 12,
|
||||
BUTTON_UP = 1U << 13,
|
||||
BUTTON_RIGHT = 1U << 14,
|
||||
BUTTON_DOWN = 1U << 15,
|
||||
BUTTON_SL = 1U << 16,
|
||||
BUTTON_SR = 1U << 17,
|
||||
BUTTON_HOME = 1U << 18,
|
||||
BUTTON_CAPTURE = 1U << 19,
|
||||
};
|
||||
|
||||
struct TasAnalog {
|
||||
float x{};
|
||||
float y{};
|
||||
};
|
||||
|
||||
enum class TasState {
|
||||
Running,
|
||||
Recording,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
class Tas final : public InputEngine {
|
||||
public:
|
||||
explicit Tas(std::string input_engine_);
|
||||
~Tas() override;
|
||||
|
||||
/**
|
||||
* Changes the input status that will be stored in each frame
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @param left_axis Value of the left axis
|
||||
* @param right_axis Value of the right axis
|
||||
*/
|
||||
void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
|
||||
|
||||
// Main loop that records or executes input
|
||||
void UpdateThread();
|
||||
|
||||
// Sets the flag to start or stop the TAS command execution and swaps controllers profiles
|
||||
void StartStop();
|
||||
|
||||
// Stop the TAS and reverts any controller profile
|
||||
void Stop();
|
||||
|
||||
// Sets the flag to reload the file and start from the beginning in the next update
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Sets the flag to enable or disable recording of inputs
|
||||
* @returns true if the current recording status is enabled
|
||||
*/
|
||||
bool Record();
|
||||
|
||||
/**
|
||||
* Saves contents of record_commands on a file
|
||||
* @param overwrite_file Indicates if player 1 should be overwritten
|
||||
*/
|
||||
void SaveRecording(bool overwrite_file);
|
||||
|
||||
/**
|
||||
* Returns the current status values of TAS playback/recording
|
||||
* @returns A Tuple of
|
||||
* TasState indicating the current state out of Running ;
|
||||
* Current playback progress ;
|
||||
* Total length of script file currently loaded or being recorded
|
||||
*/
|
||||
std::tuple<TasState, size_t, size_t> GetStatus() const;
|
||||
|
||||
private:
|
||||
enum class TasAxis : u8;
|
||||
|
||||
struct TASCommand {
|
||||
u64 buttons{};
|
||||
TasAnalog l_axis{};
|
||||
TasAnalog r_axis{};
|
||||
};
|
||||
|
||||
/// Loads TAS files from all players
|
||||
void LoadTasFiles();
|
||||
|
||||
/**
|
||||
* Loads TAS file from the specified player
|
||||
* @param player_index Player number to save the script
|
||||
* @param file_index Script number of the file
|
||||
*/
|
||||
void LoadTasFile(size_t player_index, size_t file_index);
|
||||
|
||||
/**
|
||||
* Writes a TAS file from the recorded commands
|
||||
* @param file_name Name of the file to be written
|
||||
*/
|
||||
void WriteTasFile(std::u8string_view file_name);
|
||||
|
||||
/**
|
||||
* Parses a string containing the axis values. X and Y have a range from -32767 to 32767
|
||||
* @param line String containing axis values with the following format "x;y"
|
||||
* @returns A TAS analog object with axis values with range from -1.0 to 1.0
|
||||
*/
|
||||
TasAnalog ReadCommandAxis(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Parses a string containing the button values. Each button is represented by it's text format
|
||||
* specified in text_to_tas_button array
|
||||
* @param line string containing button name with the following format "a;b;c;d..."
|
||||
* @returns A u64 with each bit representing the status of a button
|
||||
*/
|
||||
u64 ReadCommandButtons(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Reset state of all players
|
||||
*/
|
||||
void ClearInput();
|
||||
|
||||
/**
|
||||
* Converts an u64 containing the button status into the text equivalent
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @returns A string with the name of the buttons to be written to the file
|
||||
*/
|
||||
std::string WriteCommandButtons(u64 buttons) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @param analog Value of the axis
|
||||
* @returns A string with the value of the axis to be written to the file
|
||||
*/
|
||||
std::string WriteCommandAxis(TasAnalog analog) const;
|
||||
|
||||
/// Sets an axis for a particular pad to the given value.
|
||||
void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
|
||||
|
||||
size_t script_length{0};
|
||||
bool is_recording{false};
|
||||
bool is_running{false};
|
||||
bool needs_reset{false};
|
||||
std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
|
||||
std::vector<TASCommand> record_commands{};
|
||||
size_t current_command{0};
|
||||
TASCommand last_input{}; // only used for recording
|
||||
};
|
||||
} // namespace InputCommon::TasInput
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
/*
|
||||
To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
|
||||
Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
|
||||
for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
|
||||
|
||||
A script file has the same format as TAS-nx uses, so final files will look like this:
|
||||
|
||||
1 KEY_B 0;0 0;0
|
||||
6 KEY_ZL 0;0 0;0
|
||||
41 KEY_ZL;KEY_Y 0;0 0;0
|
||||
43 KEY_X;KEY_A 32767;0 0;0
|
||||
44 KEY_A 32767;0 0;0
|
||||
45 KEY_A 32767;0 0;0
|
||||
46 KEY_A 32767;0 0;0
|
||||
47 KEY_A 32767;0 0;0
|
||||
|
||||
After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey
|
||||
CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file
|
||||
has. Playback can be started or stopped using CTRL+F5.
|
||||
|
||||
However, for playback to actually work, the correct input device has to be selected: In the Controls
|
||||
menu, select TAS from the device list for the controller that the script should be played on.
|
||||
|
||||
Recording a new script file is really simple: Just make sure that the proper device (not TAS) is
|
||||
connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke
|
||||
again (CTRL+F7). The new script will be saved at the location previously selected, as the filename
|
||||
record.txt.
|
||||
|
||||
For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller
|
||||
P1).
|
||||
*/
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
|
||||
constexpr size_t PLAYER_NUMBER = 10;
|
||||
|
||||
enum class TasButton : u64 {
|
||||
BUTTON_A = 1U << 0,
|
||||
BUTTON_B = 1U << 1,
|
||||
BUTTON_X = 1U << 2,
|
||||
BUTTON_Y = 1U << 3,
|
||||
STICK_L = 1U << 4,
|
||||
STICK_R = 1U << 5,
|
||||
TRIGGER_L = 1U << 6,
|
||||
TRIGGER_R = 1U << 7,
|
||||
TRIGGER_ZL = 1U << 8,
|
||||
TRIGGER_ZR = 1U << 9,
|
||||
BUTTON_PLUS = 1U << 10,
|
||||
BUTTON_MINUS = 1U << 11,
|
||||
BUTTON_LEFT = 1U << 12,
|
||||
BUTTON_UP = 1U << 13,
|
||||
BUTTON_RIGHT = 1U << 14,
|
||||
BUTTON_DOWN = 1U << 15,
|
||||
BUTTON_SL = 1U << 16,
|
||||
BUTTON_SR = 1U << 17,
|
||||
BUTTON_HOME = 1U << 18,
|
||||
BUTTON_CAPTURE = 1U << 19,
|
||||
};
|
||||
|
||||
struct TasAnalog {
|
||||
float x{};
|
||||
float y{};
|
||||
};
|
||||
|
||||
enum class TasState {
|
||||
Running,
|
||||
Recording,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
class Tas final : public InputEngine {
|
||||
public:
|
||||
explicit Tas(std::string input_engine_);
|
||||
~Tas() override;
|
||||
|
||||
/**
|
||||
* Changes the input status that will be stored in each frame
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @param left_axis Value of the left axis
|
||||
* @param right_axis Value of the right axis
|
||||
*/
|
||||
void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
|
||||
|
||||
// Main loop that records or executes input
|
||||
void UpdateThread();
|
||||
|
||||
// Sets the flag to start or stop the TAS command execution and swaps controllers profiles
|
||||
void StartStop();
|
||||
|
||||
// Stop the TAS and reverts any controller profile
|
||||
void Stop();
|
||||
|
||||
// Sets the flag to reload the file and start from the beginning in the next update
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* Sets the flag to enable or disable recording of inputs
|
||||
* @returns true if the current recording status is enabled
|
||||
*/
|
||||
bool Record();
|
||||
|
||||
/**
|
||||
* Saves contents of record_commands on a file
|
||||
* @param overwrite_file Indicates if player 1 should be overwritten
|
||||
*/
|
||||
void SaveRecording(bool overwrite_file);
|
||||
|
||||
/**
|
||||
* Returns the current status values of TAS playback/recording
|
||||
* @returns A Tuple of
|
||||
* TasState indicating the current state out of Running ;
|
||||
* Current playback progress ;
|
||||
* Total length of script file currently loaded or being recorded
|
||||
*/
|
||||
std::tuple<TasState, size_t, size_t> GetStatus() const;
|
||||
|
||||
private:
|
||||
enum class TasAxis : u8;
|
||||
|
||||
struct TASCommand {
|
||||
u64 buttons{};
|
||||
TasAnalog l_axis{};
|
||||
TasAnalog r_axis{};
|
||||
};
|
||||
|
||||
/// Loads TAS files from all players
|
||||
void LoadTasFiles();
|
||||
|
||||
/**
|
||||
* Loads TAS file from the specified player
|
||||
* @param player_index Player number to save the script
|
||||
* @param file_index Script number of the file
|
||||
*/
|
||||
void LoadTasFile(size_t player_index, size_t file_index);
|
||||
|
||||
/**
|
||||
* Writes a TAS file from the recorded commands
|
||||
* @param file_name Name of the file to be written
|
||||
*/
|
||||
void WriteTasFile(std::u8string_view file_name);
|
||||
|
||||
/**
|
||||
* Parses a string containing the axis values. X and Y have a range from -32767 to 32767
|
||||
* @param line String containing axis values with the following format "x;y"
|
||||
* @returns A TAS analog object with axis values with range from -1.0 to 1.0
|
||||
*/
|
||||
TasAnalog ReadCommandAxis(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Parses a string containing the button values. Each button is represented by it's text format
|
||||
* specified in text_to_tas_button array
|
||||
* @param line string containing button name with the following format "a;b;c;d..."
|
||||
* @returns A u64 with each bit representing the status of a button
|
||||
*/
|
||||
u64 ReadCommandButtons(const std::string& line) const;
|
||||
|
||||
/**
|
||||
* Reset state of all players
|
||||
*/
|
||||
void ClearInput();
|
||||
|
||||
/**
|
||||
* Converts an u64 containing the button status into the text equivalent
|
||||
* @param buttons Bitfield with the status of the buttons
|
||||
* @returns A string with the name of the buttons to be written to the file
|
||||
*/
|
||||
std::string WriteCommandButtons(u64 buttons) const;
|
||||
|
||||
/**
|
||||
* Converts an TAS analog object containing the axis status into the text equivalent
|
||||
* @param analog Value of the axis
|
||||
* @returns A string with the value of the axis to be written to the file
|
||||
*/
|
||||
std::string WriteCommandAxis(TasAnalog analog) const;
|
||||
|
||||
/// Sets an axis for a particular pad to the given value.
|
||||
void SetTasAxis(const PadIdentifier& identifier, TasAxis axis, f32 value);
|
||||
|
||||
size_t script_length{0};
|
||||
bool is_recording{false};
|
||||
bool is_running{false};
|
||||
bool needs_reset{false};
|
||||
std::array<std::vector<TASCommand>, PLAYER_NUMBER> commands{};
|
||||
std::vector<TASCommand> record_commands{};
|
||||
size_t current_command{0};
|
||||
TASCommand last_input{}; // only used for recording
|
||||
};
|
||||
} // namespace InputCommon::TasInput
|
||||
|
@@ -1,107 +1,107 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
// Touch doesn't exist handle it as a new one
|
||||
TouchPressed(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_active = true;
|
||||
SetButton(identifier, static_cast<int>(i), true);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
|
||||
if (GetIndexFromFingerId(finger_id)) {
|
||||
// Touch already exist. Just update the data
|
||||
TouchMoved(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
const auto index = GetNextFreeIndex();
|
||||
if (!index) {
|
||||
// No free entries. Ignore input
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = true;
|
||||
fingers[i].finger_id = finger_id;
|
||||
TouchMoved(x, y, finger_id);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchReleased(std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = false;
|
||||
SetButton(identifier, static_cast<int>(i), false);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
const auto& finger = fingers[index];
|
||||
if (!finger.is_enabled) {
|
||||
continue;
|
||||
}
|
||||
if (finger.finger_id == finger_id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
if (!fingers[index].is_enabled) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void TouchScreen::ClearActiveFlag() {
|
||||
for (auto& finger : fingers) {
|
||||
finger.is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseInactiveTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (!finger.is_active) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseAllTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (finger.is_enabled) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
// Touch doesn't exist handle it as a new one
|
||||
TouchPressed(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_active = true;
|
||||
SetButton(identifier, static_cast<int>(i), true);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
|
||||
if (GetIndexFromFingerId(finger_id)) {
|
||||
// Touch already exist. Just update the data
|
||||
TouchMoved(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
const auto index = GetNextFreeIndex();
|
||||
if (!index) {
|
||||
// No free entries. Ignore input
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = true;
|
||||
fingers[i].finger_id = finger_id;
|
||||
TouchMoved(x, y, finger_id);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchReleased(std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = false;
|
||||
SetButton(identifier, static_cast<int>(i), false);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
const auto& finger = fingers[index];
|
||||
if (!finger.is_enabled) {
|
||||
continue;
|
||||
}
|
||||
if (finger.finger_id == finger_id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
if (!fingers[index].is_enabled) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void TouchScreen::ClearActiveFlag() {
|
||||
for (auto& finger : fingers) {
|
||||
finger.is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseInactiveTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (!finger.is_active) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseAllTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (finger.is_enabled) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,67 +1,67 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A touch device factory representing a touch screen. It receives touch events and forward them
|
||||
* to all touch devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that touch has moved and marks this touch point as active
|
||||
* @param x new horizontal position
|
||||
* @param y new vertical position
|
||||
* @param finger_id of the touch point to be updated
|
||||
*/
|
||||
void TouchMoved(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Signals and creates a new touch point with this finger id
|
||||
* @param x starting horizontal position
|
||||
* @param y starting vertical position
|
||||
* @param finger_id to be assigned to the new touch point
|
||||
*/
|
||||
void TouchPressed(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Signals and resets the touch point related to the this finger id
|
||||
* @param finger_id to be released
|
||||
*/
|
||||
void TouchReleased(std::size_t finger_id);
|
||||
|
||||
/// Resets the active flag for each touch point
|
||||
void ClearActiveFlag();
|
||||
|
||||
/// Releases all touch that haven't been marked as active
|
||||
void ReleaseInactiveTouch();
|
||||
|
||||
/// Resets all inputs to their initial value
|
||||
void ReleaseAllTouch();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGER_COUNT = 16;
|
||||
|
||||
struct TouchStatus {
|
||||
std::size_t finger_id{};
|
||||
bool is_enabled{};
|
||||
bool is_active{};
|
||||
};
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A touch device factory representing a touch screen. It receives touch events and forward them
|
||||
* to all touch devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that touch has moved and marks this touch point as active
|
||||
* @param x new horizontal position
|
||||
* @param y new vertical position
|
||||
* @param finger_id of the touch point to be updated
|
||||
*/
|
||||
void TouchMoved(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Signals and creates a new touch point with this finger id
|
||||
* @param x starting horizontal position
|
||||
* @param y starting vertical position
|
||||
* @param finger_id to be assigned to the new touch point
|
||||
*/
|
||||
void TouchPressed(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Signals and resets the touch point related to the this finger id
|
||||
* @param finger_id to be released
|
||||
*/
|
||||
void TouchReleased(std::size_t finger_id);
|
||||
|
||||
/// Resets the active flag for each touch point
|
||||
void ClearActiveFlag();
|
||||
|
||||
/// Releases all touch that haven't been marked as active
|
||||
void ReleaseInactiveTouch();
|
||||
|
||||
/// Resets all inputs to their initial value
|
||||
void ReleaseAllTouch();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGER_COUNT = 16;
|
||||
|
||||
struct TouchStatus {
|
||||
std::size_t finger_id{};
|
||||
bool is_enabled{};
|
||||
bool is_active{};
|
||||
};
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,192 +1,192 @@
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
class Socket;
|
||||
|
||||
namespace Response {
|
||||
enum class Battery : u8;
|
||||
struct PadData;
|
||||
struct PortInfo;
|
||||
struct TouchPad;
|
||||
struct Version;
|
||||
} // namespace Response
|
||||
|
||||
enum class PadTouch {
|
||||
Click,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct UDPPadStatus {
|
||||
std::string host{"127.0.0.1"};
|
||||
u16 port{26760};
|
||||
std::size_t pad_index{};
|
||||
};
|
||||
|
||||
struct DeviceStatus {
|
||||
std::mutex update_mutex;
|
||||
|
||||
// calibration data for scaling the device's touch area to 3ds
|
||||
struct CalibrationData {
|
||||
u16 min_x{};
|
||||
u16 min_y{};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
};
|
||||
std::optional<CalibrationData> touch_calibration;
|
||||
};
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class UDPClient final : public InputEngine {
|
||||
public:
|
||||
explicit UDPClient(std::string input_engine_);
|
||||
~UDPClient() override;
|
||||
|
||||
void ReloadSockets();
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
enum class PadButton {
|
||||
Undefined = 0x0000,
|
||||
Share = 0x0001,
|
||||
L3 = 0x0002,
|
||||
R3 = 0x0004,
|
||||
Options = 0x0008,
|
||||
Up = 0x0010,
|
||||
Right = 0x0020,
|
||||
Down = 0x0040,
|
||||
Left = 0x0080,
|
||||
L2 = 0x0100,
|
||||
R2 = 0x0200,
|
||||
L1 = 0x0400,
|
||||
R1 = 0x0800,
|
||||
Triangle = 0x1000,
|
||||
Circle = 0x2000,
|
||||
Cross = 0x4000,
|
||||
Square = 0x8000,
|
||||
Touch1 = 0x10000,
|
||||
Touch2 = 0x20000,
|
||||
Home = 0x40000,
|
||||
TouchHardPress = 0x80000,
|
||||
};
|
||||
|
||||
enum class PadAxes : u8 {
|
||||
LeftStickX,
|
||||
LeftStickY,
|
||||
RightStickX,
|
||||
RightStickY,
|
||||
AnalogLeft,
|
||||
AnalogDown,
|
||||
AnalogRight,
|
||||
AnalogUp,
|
||||
AnalogSquare,
|
||||
AnalogCross,
|
||||
AnalogCircle,
|
||||
AnalogTriangle,
|
||||
AnalogR1,
|
||||
AnalogL1,
|
||||
AnalogR2,
|
||||
AnalogL3,
|
||||
AnalogR3,
|
||||
Touch1X,
|
||||
Touch1Y,
|
||||
Touch2X,
|
||||
Touch2Y,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct PadData {
|
||||
std::size_t pad_index{};
|
||||
bool connected{};
|
||||
DeviceStatus status;
|
||||
u64 packet_sequence{};
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update;
|
||||
};
|
||||
|
||||
struct ClientConnection {
|
||||
ClientConnection();
|
||||
~ClientConnection();
|
||||
Common::UUID uuid{"00000000-0000-0000-0000-00007F000001"};
|
||||
std::string host{"127.0.0.1"};
|
||||
u16 port{26760};
|
||||
s8 active{-1};
|
||||
std::unique_ptr<Socket> socket;
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
// Translates configuration to client number
|
||||
std::size_t GetClientNumber(std::string_view host, u16 port) const;
|
||||
|
||||
// Translates UDP battery level to input engine battery level
|
||||
Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const;
|
||||
|
||||
void OnVersion(Response::Version);
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
void OnPadData(Response::PadData, std::size_t client);
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port);
|
||||
PadIdentifier GetPadIdentifier(std::size_t pad_index) const;
|
||||
Common::UUID GetHostUUID(const std::string& host) const;
|
||||
|
||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||
|
||||
// Allocate clients for 8 udp servers
|
||||
static constexpr std::size_t MAX_UDP_CLIENTS = 8;
|
||||
static constexpr std::size_t PADS_PER_CLIENT = 4;
|
||||
std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{};
|
||||
std::array<ClientConnection, MAX_UDP_CLIENTS> clients{};
|
||||
};
|
||||
|
||||
/// An async job allowing configuration of the touchpad calibration.
|
||||
class CalibrationConfigurationJob {
|
||||
public:
|
||||
enum class Status {
|
||||
Initialized,
|
||||
Ready,
|
||||
Stage1Completed,
|
||||
Completed,
|
||||
};
|
||||
/**
|
||||
* Constructs and starts the job with the specified parameter.
|
||||
*
|
||||
* @param status_callback Callback for job status updates
|
||||
* @param data_callback Called when calibration data is ready
|
||||
*/
|
||||
explicit CalibrationConfigurationJob(const std::string& host, u16 port,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback);
|
||||
~CalibrationConfigurationJob();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
Common::Event complete_event;
|
||||
};
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback);
|
||||
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
class Socket;
|
||||
|
||||
namespace Response {
|
||||
enum class Battery : u8;
|
||||
struct PadData;
|
||||
struct PortInfo;
|
||||
struct TouchPad;
|
||||
struct Version;
|
||||
} // namespace Response
|
||||
|
||||
enum class PadTouch {
|
||||
Click,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct UDPPadStatus {
|
||||
std::string host{"127.0.0.1"};
|
||||
u16 port{26760};
|
||||
std::size_t pad_index{};
|
||||
};
|
||||
|
||||
struct DeviceStatus {
|
||||
std::mutex update_mutex;
|
||||
|
||||
// calibration data for scaling the device's touch area to 3ds
|
||||
struct CalibrationData {
|
||||
u16 min_x{};
|
||||
u16 min_y{};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
};
|
||||
std::optional<CalibrationData> touch_calibration;
|
||||
};
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class UDPClient final : public InputEngine {
|
||||
public:
|
||||
explicit UDPClient(std::string input_engine_);
|
||||
~UDPClient() override;
|
||||
|
||||
void ReloadSockets();
|
||||
|
||||
/// Used for automapping features
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
|
||||
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
enum class PadButton {
|
||||
Undefined = 0x0000,
|
||||
Share = 0x0001,
|
||||
L3 = 0x0002,
|
||||
R3 = 0x0004,
|
||||
Options = 0x0008,
|
||||
Up = 0x0010,
|
||||
Right = 0x0020,
|
||||
Down = 0x0040,
|
||||
Left = 0x0080,
|
||||
L2 = 0x0100,
|
||||
R2 = 0x0200,
|
||||
L1 = 0x0400,
|
||||
R1 = 0x0800,
|
||||
Triangle = 0x1000,
|
||||
Circle = 0x2000,
|
||||
Cross = 0x4000,
|
||||
Square = 0x8000,
|
||||
Touch1 = 0x10000,
|
||||
Touch2 = 0x20000,
|
||||
Home = 0x40000,
|
||||
TouchHardPress = 0x80000,
|
||||
};
|
||||
|
||||
enum class PadAxes : u8 {
|
||||
LeftStickX,
|
||||
LeftStickY,
|
||||
RightStickX,
|
||||
RightStickY,
|
||||
AnalogLeft,
|
||||
AnalogDown,
|
||||
AnalogRight,
|
||||
AnalogUp,
|
||||
AnalogSquare,
|
||||
AnalogCross,
|
||||
AnalogCircle,
|
||||
AnalogTriangle,
|
||||
AnalogR1,
|
||||
AnalogL1,
|
||||
AnalogR2,
|
||||
AnalogL3,
|
||||
AnalogR3,
|
||||
Touch1X,
|
||||
Touch1Y,
|
||||
Touch2X,
|
||||
Touch2Y,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct PadData {
|
||||
std::size_t pad_index{};
|
||||
bool connected{};
|
||||
DeviceStatus status;
|
||||
u64 packet_sequence{};
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update;
|
||||
};
|
||||
|
||||
struct ClientConnection {
|
||||
ClientConnection();
|
||||
~ClientConnection();
|
||||
Common::UUID uuid{"00000000-0000-0000-0000-00007F000001"};
|
||||
std::string host{"127.0.0.1"};
|
||||
u16 port{26760};
|
||||
s8 active{-1};
|
||||
std::unique_ptr<Socket> socket;
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
// Translates configuration to client number
|
||||
std::size_t GetClientNumber(std::string_view host, u16 port) const;
|
||||
|
||||
// Translates UDP battery level to input engine battery level
|
||||
Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const;
|
||||
|
||||
void OnVersion(Response::Version);
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
void OnPadData(Response::PadData, std::size_t client);
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port);
|
||||
PadIdentifier GetPadIdentifier(std::size_t pad_index) const;
|
||||
Common::UUID GetHostUUID(const std::string& host) const;
|
||||
|
||||
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||
|
||||
// Allocate clients for 8 udp servers
|
||||
static constexpr std::size_t MAX_UDP_CLIENTS = 8;
|
||||
static constexpr std::size_t PADS_PER_CLIENT = 4;
|
||||
std::array<PadData, MAX_UDP_CLIENTS * PADS_PER_CLIENT> pads{};
|
||||
std::array<ClientConnection, MAX_UDP_CLIENTS> clients{};
|
||||
};
|
||||
|
||||
/// An async job allowing configuration of the touchpad calibration.
|
||||
class CalibrationConfigurationJob {
|
||||
public:
|
||||
enum class Status {
|
||||
Initialized,
|
||||
Ready,
|
||||
Stage1Completed,
|
||||
Completed,
|
||||
};
|
||||
/**
|
||||
* Constructs and starts the job with the specified parameter.
|
||||
*
|
||||
* @param status_callback Callback for job status updates
|
||||
* @param data_callback Called when calibration data is ready
|
||||
*/
|
||||
explicit CalibrationConfigurationJob(const std::string& host, u16 port,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback);
|
||||
~CalibrationConfigurationJob();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
Common::Event complete_event;
|
||||
};
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback);
|
||||
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
|
@@ -1,101 +1,101 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
|
||||
|
||||
VirtualAmiibo::~VirtualAmiibo() = default;
|
||||
|
||||
Common::Input::PollingError VirtualAmiibo::SetPollingMode(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_,
|
||||
const Common::Input::PollingMode polling_mode_) {
|
||||
polling_mode = polling_mode_;
|
||||
|
||||
if (polling_mode == Common::Input::PollingMode::NFC) {
|
||||
if (state == State::Initialized) {
|
||||
state = State::WaitingForAmiibo;
|
||||
}
|
||||
} else {
|
||||
if (state == State::AmiiboIsOpen) {
|
||||
CloseAmiibo();
|
||||
}
|
||||
}
|
||||
|
||||
return Common::Input::PollingError::None;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::SupportsNfc(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_) const {
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
|
||||
const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
|
||||
if (!amiibo_file.IsOpen()) {
|
||||
LOG_ERROR(Core, "Amiibo is already on use");
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
if (!amiibo_file.Write(data)) {
|
||||
LOG_ERROR(Service_NFP, "Error writting to file");
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
|
||||
const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
|
||||
if (state != State::WaitingForAmiibo) {
|
||||
return Info::WrongDeviceState;
|
||||
}
|
||||
|
||||
if (!amiibo_file.IsOpen()) {
|
||||
return Info::UnableToLoad;
|
||||
}
|
||||
|
||||
amiibo_data.resize(amiibo_size);
|
||||
|
||||
if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
|
||||
return Info::NotAnAmiibo;
|
||||
}
|
||||
|
||||
file_path = filename;
|
||||
state = State::AmiiboIsOpen;
|
||||
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
|
||||
: State::Initialized;
|
||||
SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}});
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr PadIdentifier identifier = {
|
||||
.guid = Common::UUID{},
|
||||
.port = 0,
|
||||
.pad = 0,
|
||||
};
|
||||
|
||||
VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
|
||||
|
||||
VirtualAmiibo::~VirtualAmiibo() = default;
|
||||
|
||||
Common::Input::PollingError VirtualAmiibo::SetPollingMode(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_,
|
||||
const Common::Input::PollingMode polling_mode_) {
|
||||
polling_mode = polling_mode_;
|
||||
|
||||
if (polling_mode == Common::Input::PollingMode::NFC) {
|
||||
if (state == State::Initialized) {
|
||||
state = State::WaitingForAmiibo;
|
||||
}
|
||||
} else {
|
||||
if (state == State::AmiiboIsOpen) {
|
||||
CloseAmiibo();
|
||||
}
|
||||
}
|
||||
|
||||
return Common::Input::PollingError::None;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::SupportsNfc(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_) const {
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
|
||||
const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
|
||||
if (!amiibo_file.IsOpen()) {
|
||||
LOG_ERROR(Core, "Amiibo is already on use");
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
if (!amiibo_file.Write(data)) {
|
||||
LOG_ERROR(Service_NFP, "Error writting to file");
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
|
||||
const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
|
||||
if (state != State::WaitingForAmiibo) {
|
||||
return Info::WrongDeviceState;
|
||||
}
|
||||
|
||||
if (!amiibo_file.IsOpen()) {
|
||||
return Info::UnableToLoad;
|
||||
}
|
||||
|
||||
amiibo_data.resize(amiibo_size);
|
||||
|
||||
if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
|
||||
return Info::NotAnAmiibo;
|
||||
}
|
||||
|
||||
file_path = filename;
|
||||
state = State::AmiiboIsOpen;
|
||||
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
|
||||
: State::Initialized;
|
||||
SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}});
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,61 +1,61 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace Common::FS {
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class VirtualAmiibo final : public InputEngine {
|
||||
public:
|
||||
enum class State {
|
||||
Initialized,
|
||||
WaitingForAmiibo,
|
||||
AmiiboIsOpen,
|
||||
};
|
||||
|
||||
enum class Info {
|
||||
Success,
|
||||
UnableToLoad,
|
||||
NotAnAmiibo,
|
||||
WrongDeviceState,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
explicit VirtualAmiibo(std::string input_engine_);
|
||||
~VirtualAmiibo() override;
|
||||
|
||||
// Sets polling mode to a controller
|
||||
Common::Input::PollingError SetPollingMode(
|
||||
const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
|
||||
|
||||
Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
|
||||
|
||||
Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
|
||||
const std::vector<u8>& data) override;
|
||||
|
||||
State GetCurrentState() const;
|
||||
|
||||
Info LoadAmiibo(const std::string& amiibo_file);
|
||||
Info CloseAmiibo();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t amiibo_size = 0x21C;
|
||||
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
|
||||
|
||||
std::string file_path{};
|
||||
State state{State::Initialized};
|
||||
std::vector<u8> amiibo_data;
|
||||
Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
|
||||
};
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace Common::FS {
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class VirtualAmiibo final : public InputEngine {
|
||||
public:
|
||||
enum class State {
|
||||
Initialized,
|
||||
WaitingForAmiibo,
|
||||
AmiiboIsOpen,
|
||||
};
|
||||
|
||||
enum class Info {
|
||||
Success,
|
||||
UnableToLoad,
|
||||
NotAnAmiibo,
|
||||
WrongDeviceState,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
explicit VirtualAmiibo(std::string input_engine_);
|
||||
~VirtualAmiibo() override;
|
||||
|
||||
// Sets polling mode to a controller
|
||||
Common::Input::PollingError SetPollingMode(
|
||||
const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
|
||||
|
||||
Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
|
||||
|
||||
Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
|
||||
const std::vector<u8>& data) override;
|
||||
|
||||
State GetCurrentState() const;
|
||||
|
||||
Info LoadAmiibo(const std::string& amiibo_file);
|
||||
Info CloseAmiibo();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t amiibo_size = 0x21C;
|
||||
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
|
||||
|
||||
std::string file_path{};
|
||||
State state{State::Initialized};
|
||||
std::vector<u8> amiibo_data;
|
||||
Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
|
||||
};
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,338 +1,338 @@
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include "common/math_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/helpers/stick_from_buttons.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class Stick final : public Common::Input::InputDevice {
|
||||
public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
|
||||
Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
|
||||
float modifier_scale_, float modifier_angle_)
|
||||
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
|
||||
right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
|
||||
modifier_angle(modifier_angle_) {
|
||||
up->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateUpButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
down->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateDownButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
left->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateLeftButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
right->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateRightButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
modifier->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateModButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
last_x_axis_value = 0.0f;
|
||||
last_y_axis_value = 0.0f;
|
||||
}
|
||||
|
||||
bool IsAngleGreater(float old_angle, float new_angle) const {
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
// Use wider angle to ease the transition.
|
||||
constexpr float aperture = TAU * 0.15f;
|
||||
const float top_limit = new_angle + aperture;
|
||||
return (old_angle > new_angle && old_angle <= top_limit) ||
|
||||
(old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
|
||||
}
|
||||
|
||||
bool IsAngleSmaller(float old_angle, float new_angle) const {
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
// Use wider angle to ease the transition.
|
||||
constexpr float aperture = TAU * 0.15f;
|
||||
const float bottom_limit = new_angle - aperture;
|
||||
return (old_angle >= bottom_limit && old_angle < new_angle) ||
|
||||
(old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
|
||||
}
|
||||
|
||||
float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
float new_angle = angle;
|
||||
|
||||
auto time_difference = static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
|
||||
time_difference /= 1000.0f * 1000.0f;
|
||||
if (time_difference > 0.5f) {
|
||||
time_difference = 0.5f;
|
||||
}
|
||||
|
||||
if (IsAngleGreater(new_angle, goal_angle)) {
|
||||
new_angle -= modifier_angle * time_difference;
|
||||
if (new_angle < 0) {
|
||||
new_angle += TAU;
|
||||
}
|
||||
if (!IsAngleGreater(new_angle, goal_angle)) {
|
||||
return goal_angle;
|
||||
}
|
||||
} else if (IsAngleSmaller(new_angle, goal_angle)) {
|
||||
new_angle += modifier_angle * time_difference;
|
||||
if (new_angle >= TAU) {
|
||||
new_angle -= TAU;
|
||||
}
|
||||
if (!IsAngleSmaller(new_angle, goal_angle)) {
|
||||
return goal_angle;
|
||||
}
|
||||
} else {
|
||||
return goal_angle;
|
||||
}
|
||||
return new_angle;
|
||||
}
|
||||
|
||||
void SetGoalAngle(bool r, bool l, bool u, bool d) {
|
||||
// Move to the right
|
||||
if (r && !u && !d) {
|
||||
goal_angle = 0.0f;
|
||||
}
|
||||
|
||||
// Move to the upper right
|
||||
if (r && u && !d) {
|
||||
goal_angle = Common::PI * 0.25f;
|
||||
}
|
||||
|
||||
// Move up
|
||||
if (u && !l && !r) {
|
||||
goal_angle = Common::PI * 0.5f;
|
||||
}
|
||||
|
||||
// Move to the upper left
|
||||
if (l && u && !d) {
|
||||
goal_angle = Common::PI * 0.75f;
|
||||
}
|
||||
|
||||
// Move to the left
|
||||
if (l && !u && !d) {
|
||||
goal_angle = Common::PI;
|
||||
}
|
||||
|
||||
// Move to the bottom left
|
||||
if (l && !u && d) {
|
||||
goal_angle = Common::PI * 1.25f;
|
||||
}
|
||||
|
||||
// Move down
|
||||
if (d && !l && !r) {
|
||||
goal_angle = Common::PI * 1.5f;
|
||||
}
|
||||
|
||||
// Move to the bottom right
|
||||
if (r && !u && d) {
|
||||
goal_angle = Common::PI * 1.75f;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
up_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
down_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
left_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
right_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
const auto& new_status = button_callback.button_status;
|
||||
const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value;
|
||||
modifier_status.toggle = new_status.toggle;
|
||||
|
||||
// Update button status with current
|
||||
if (!modifier_status.toggle) {
|
||||
modifier_status.locked = false;
|
||||
if (modifier_status.value != new_button_value) {
|
||||
modifier_status.value = new_button_value;
|
||||
}
|
||||
} else {
|
||||
// Toggle button and lock status
|
||||
if (new_button_value && !modifier_status.locked) {
|
||||
modifier_status.locked = true;
|
||||
modifier_status.value = !modifier_status.value;
|
||||
}
|
||||
|
||||
// Unlock button ready for next press
|
||||
if (!new_button_value && modifier_status.locked) {
|
||||
modifier_status.locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateStatus() {
|
||||
const float coef = modifier_status.value ? modifier_scale : 1.0f;
|
||||
|
||||
bool r = right_status;
|
||||
bool l = left_status;
|
||||
bool u = up_status;
|
||||
bool d = down_status;
|
||||
|
||||
// Eliminate contradictory movements
|
||||
if (r && l) {
|
||||
r = false;
|
||||
l = false;
|
||||
}
|
||||
if (u && d) {
|
||||
u = false;
|
||||
d = false;
|
||||
}
|
||||
|
||||
// Move if a key is pressed
|
||||
if (r || l || u || d) {
|
||||
amplitude = coef;
|
||||
} else {
|
||||
amplitude = 0;
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto time_difference = static_cast<u64>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
|
||||
|
||||
if (time_difference < 10) {
|
||||
// Disable analog mode if inputs are too fast
|
||||
SetGoalAngle(r, l, u, d);
|
||||
angle = goal_angle;
|
||||
} else {
|
||||
angle = GetAngle(now);
|
||||
SetGoalAngle(r, l, u, d);
|
||||
}
|
||||
|
||||
last_update = now;
|
||||
Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Stick,
|
||||
.stick_status = GetStatus(),
|
||||
};
|
||||
last_x_axis_value = status.stick_status.x.raw_value;
|
||||
last_y_axis_value = status.stick_status.y.raw_value;
|
||||
TriggerOnChange(status);
|
||||
}
|
||||
|
||||
void ForceUpdate() override {
|
||||
up->ForceUpdate();
|
||||
down->ForceUpdate();
|
||||
left->ForceUpdate();
|
||||
right->ForceUpdate();
|
||||
modifier->ForceUpdate();
|
||||
}
|
||||
|
||||
void SoftUpdate() override {
|
||||
Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Stick,
|
||||
.stick_status = GetStatus(),
|
||||
};
|
||||
if (last_x_axis_value == status.stick_status.x.raw_value &&
|
||||
last_y_axis_value == status.stick_status.y.raw_value) {
|
||||
return;
|
||||
}
|
||||
last_x_axis_value = status.stick_status.x.raw_value;
|
||||
last_y_axis_value = status.stick_status.y.raw_value;
|
||||
TriggerOnChange(status);
|
||||
}
|
||||
|
||||
Common::Input::StickStatus GetStatus() const {
|
||||
Common::Input::StickStatus status{};
|
||||
status.x.properties = properties;
|
||||
status.y.properties = properties;
|
||||
if (Settings::values.emulate_analog_keyboard) {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
float angle_ = GetAngle(now);
|
||||
status.x.raw_value = std::cos(angle_) * amplitude;
|
||||
status.y.raw_value = std::sin(angle_) * amplitude;
|
||||
return status;
|
||||
}
|
||||
constexpr float SQRT_HALF = 0.707106781f;
|
||||
int x = 0, y = 0;
|
||||
if (right_status) {
|
||||
++x;
|
||||
}
|
||||
if (left_status) {
|
||||
--x;
|
||||
}
|
||||
if (up_status) {
|
||||
++y;
|
||||
}
|
||||
if (down_status) {
|
||||
--y;
|
||||
}
|
||||
const float coef = modifier_status.value ? modifier_scale : 1.0f;
|
||||
status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
|
||||
status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
Button up;
|
||||
Button down;
|
||||
Button left;
|
||||
Button right;
|
||||
Button modifier;
|
||||
float modifier_scale{};
|
||||
float modifier_angle{};
|
||||
float angle{};
|
||||
float goal_angle{};
|
||||
float amplitude{};
|
||||
bool up_status{};
|
||||
bool down_status{};
|
||||
bool left_status{};
|
||||
bool right_status{};
|
||||
float last_x_axis_value{};
|
||||
float last_y_axis_value{};
|
||||
Common::Input::ButtonStatus modifier_status{};
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update;
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
|
||||
const Common::ParamPackage& params) {
|
||||
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
|
||||
auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("up", null_engine));
|
||||
auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("down", null_engine));
|
||||
auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("left", null_engine));
|
||||
auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("right", null_engine));
|
||||
auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("modifier", null_engine));
|
||||
auto modifier_scale = params.Get("modifier_scale", 0.5f);
|
||||
auto modifier_angle = params.Get("modifier_angle", 5.5f);
|
||||
return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
|
||||
std::move(right), std::move(modifier), modifier_scale,
|
||||
modifier_angle);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include "common/math_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "input_common/helpers/stick_from_buttons.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class Stick final : public Common::Input::InputDevice {
|
||||
public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
|
||||
Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
|
||||
float modifier_scale_, float modifier_angle_)
|
||||
: up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
|
||||
right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
|
||||
modifier_angle(modifier_angle_) {
|
||||
up->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateUpButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
down->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateDownButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
left->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateLeftButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
right->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateRightButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
modifier->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateModButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
last_x_axis_value = 0.0f;
|
||||
last_y_axis_value = 0.0f;
|
||||
}
|
||||
|
||||
bool IsAngleGreater(float old_angle, float new_angle) const {
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
// Use wider angle to ease the transition.
|
||||
constexpr float aperture = TAU * 0.15f;
|
||||
const float top_limit = new_angle + aperture;
|
||||
return (old_angle > new_angle && old_angle <= top_limit) ||
|
||||
(old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
|
||||
}
|
||||
|
||||
bool IsAngleSmaller(float old_angle, float new_angle) const {
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
// Use wider angle to ease the transition.
|
||||
constexpr float aperture = TAU * 0.15f;
|
||||
const float bottom_limit = new_angle - aperture;
|
||||
return (old_angle >= bottom_limit && old_angle < new_angle) ||
|
||||
(old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
|
||||
}
|
||||
|
||||
float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
|
||||
constexpr float TAU = Common::PI * 2.0f;
|
||||
float new_angle = angle;
|
||||
|
||||
auto time_difference = static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
|
||||
time_difference /= 1000.0f * 1000.0f;
|
||||
if (time_difference > 0.5f) {
|
||||
time_difference = 0.5f;
|
||||
}
|
||||
|
||||
if (IsAngleGreater(new_angle, goal_angle)) {
|
||||
new_angle -= modifier_angle * time_difference;
|
||||
if (new_angle < 0) {
|
||||
new_angle += TAU;
|
||||
}
|
||||
if (!IsAngleGreater(new_angle, goal_angle)) {
|
||||
return goal_angle;
|
||||
}
|
||||
} else if (IsAngleSmaller(new_angle, goal_angle)) {
|
||||
new_angle += modifier_angle * time_difference;
|
||||
if (new_angle >= TAU) {
|
||||
new_angle -= TAU;
|
||||
}
|
||||
if (!IsAngleSmaller(new_angle, goal_angle)) {
|
||||
return goal_angle;
|
||||
}
|
||||
} else {
|
||||
return goal_angle;
|
||||
}
|
||||
return new_angle;
|
||||
}
|
||||
|
||||
void SetGoalAngle(bool r, bool l, bool u, bool d) {
|
||||
// Move to the right
|
||||
if (r && !u && !d) {
|
||||
goal_angle = 0.0f;
|
||||
}
|
||||
|
||||
// Move to the upper right
|
||||
if (r && u && !d) {
|
||||
goal_angle = Common::PI * 0.25f;
|
||||
}
|
||||
|
||||
// Move up
|
||||
if (u && !l && !r) {
|
||||
goal_angle = Common::PI * 0.5f;
|
||||
}
|
||||
|
||||
// Move to the upper left
|
||||
if (l && u && !d) {
|
||||
goal_angle = Common::PI * 0.75f;
|
||||
}
|
||||
|
||||
// Move to the left
|
||||
if (l && !u && !d) {
|
||||
goal_angle = Common::PI;
|
||||
}
|
||||
|
||||
// Move to the bottom left
|
||||
if (l && !u && d) {
|
||||
goal_angle = Common::PI * 1.25f;
|
||||
}
|
||||
|
||||
// Move down
|
||||
if (d && !l && !r) {
|
||||
goal_angle = Common::PI * 1.5f;
|
||||
}
|
||||
|
||||
// Move to the bottom right
|
||||
if (r && !u && d) {
|
||||
goal_angle = Common::PI * 1.75f;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateUpButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
up_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateDownButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
down_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateLeftButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
left_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateRightButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
right_status = button_callback.button_status.value;
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
const auto& new_status = button_callback.button_status;
|
||||
const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value;
|
||||
modifier_status.toggle = new_status.toggle;
|
||||
|
||||
// Update button status with current
|
||||
if (!modifier_status.toggle) {
|
||||
modifier_status.locked = false;
|
||||
if (modifier_status.value != new_button_value) {
|
||||
modifier_status.value = new_button_value;
|
||||
}
|
||||
} else {
|
||||
// Toggle button and lock status
|
||||
if (new_button_value && !modifier_status.locked) {
|
||||
modifier_status.locked = true;
|
||||
modifier_status.value = !modifier_status.value;
|
||||
}
|
||||
|
||||
// Unlock button ready for next press
|
||||
if (!new_button_value && modifier_status.locked) {
|
||||
modifier_status.locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
void UpdateStatus() {
|
||||
const float coef = modifier_status.value ? modifier_scale : 1.0f;
|
||||
|
||||
bool r = right_status;
|
||||
bool l = left_status;
|
||||
bool u = up_status;
|
||||
bool d = down_status;
|
||||
|
||||
// Eliminate contradictory movements
|
||||
if (r && l) {
|
||||
r = false;
|
||||
l = false;
|
||||
}
|
||||
if (u && d) {
|
||||
u = false;
|
||||
d = false;
|
||||
}
|
||||
|
||||
// Move if a key is pressed
|
||||
if (r || l || u || d) {
|
||||
amplitude = coef;
|
||||
} else {
|
||||
amplitude = 0;
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto time_difference = static_cast<u64>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
|
||||
|
||||
if (time_difference < 10) {
|
||||
// Disable analog mode if inputs are too fast
|
||||
SetGoalAngle(r, l, u, d);
|
||||
angle = goal_angle;
|
||||
} else {
|
||||
angle = GetAngle(now);
|
||||
SetGoalAngle(r, l, u, d);
|
||||
}
|
||||
|
||||
last_update = now;
|
||||
Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Stick,
|
||||
.stick_status = GetStatus(),
|
||||
};
|
||||
last_x_axis_value = status.stick_status.x.raw_value;
|
||||
last_y_axis_value = status.stick_status.y.raw_value;
|
||||
TriggerOnChange(status);
|
||||
}
|
||||
|
||||
void ForceUpdate() override {
|
||||
up->ForceUpdate();
|
||||
down->ForceUpdate();
|
||||
left->ForceUpdate();
|
||||
right->ForceUpdate();
|
||||
modifier->ForceUpdate();
|
||||
}
|
||||
|
||||
void SoftUpdate() override {
|
||||
Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Stick,
|
||||
.stick_status = GetStatus(),
|
||||
};
|
||||
if (last_x_axis_value == status.stick_status.x.raw_value &&
|
||||
last_y_axis_value == status.stick_status.y.raw_value) {
|
||||
return;
|
||||
}
|
||||
last_x_axis_value = status.stick_status.x.raw_value;
|
||||
last_y_axis_value = status.stick_status.y.raw_value;
|
||||
TriggerOnChange(status);
|
||||
}
|
||||
|
||||
Common::Input::StickStatus GetStatus() const {
|
||||
Common::Input::StickStatus status{};
|
||||
status.x.properties = properties;
|
||||
status.y.properties = properties;
|
||||
if (Settings::values.emulate_analog_keyboard) {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
float angle_ = GetAngle(now);
|
||||
status.x.raw_value = std::cos(angle_) * amplitude;
|
||||
status.y.raw_value = std::sin(angle_) * amplitude;
|
||||
return status;
|
||||
}
|
||||
constexpr float SQRT_HALF = 0.707106781f;
|
||||
int x = 0, y = 0;
|
||||
if (right_status) {
|
||||
++x;
|
||||
}
|
||||
if (left_status) {
|
||||
--x;
|
||||
}
|
||||
if (up_status) {
|
||||
++y;
|
||||
}
|
||||
if (down_status) {
|
||||
--y;
|
||||
}
|
||||
const float coef = modifier_status.value ? modifier_scale : 1.0f;
|
||||
status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
|
||||
status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
Button up;
|
||||
Button down;
|
||||
Button left;
|
||||
Button right;
|
||||
Button modifier;
|
||||
float modifier_scale{};
|
||||
float modifier_angle{};
|
||||
float angle{};
|
||||
float goal_angle{};
|
||||
float amplitude{};
|
||||
bool up_status{};
|
||||
bool down_status{};
|
||||
bool left_status{};
|
||||
bool right_status{};
|
||||
float last_x_axis_value{};
|
||||
float last_y_axis_value{};
|
||||
Common::Input::ButtonStatus modifier_status{};
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_update;
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> StickFromButton::Create(
|
||||
const Common::ParamPackage& params) {
|
||||
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
|
||||
auto up = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("up", null_engine));
|
||||
auto down = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("down", null_engine));
|
||||
auto left = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("left", null_engine));
|
||||
auto right = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("right", null_engine));
|
||||
auto modifier = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("modifier", null_engine));
|
||||
auto modifier_scale = params.Get("modifier_scale", 0.5f);
|
||||
auto modifier_angle = params.Get("modifier_angle", 5.5f);
|
||||
return std::make_unique<Stick>(std::move(up), std::move(down), std::move(left),
|
||||
std::move(right), std::move(modifier), modifier_scale,
|
||||
modifier_angle);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,29 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/input.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* An analog device factory that takes direction button devices and combines them into a analog
|
||||
* device.
|
||||
*/
|
||||
class StickFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
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<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/input.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* An analog device factory that takes direction button devices and combines them into a analog
|
||||
* device.
|
||||
*/
|
||||
class StickFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
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<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,82 +1,82 @@
|
||||
// SPDX-FileCopyrightText: 2020 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/settings.h"
|
||||
#include "input_common/helpers/touch_from_buttons.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class TouchFromButtonDevice final : public Common::Input::InputDevice {
|
||||
public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
|
||||
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
|
||||
last_button_value = false;
|
||||
button->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
button->ForceUpdate();
|
||||
}
|
||||
|
||||
void ForceUpdate() override {
|
||||
button->ForceUpdate();
|
||||
}
|
||||
|
||||
Common::Input::TouchStatus GetStatus(bool pressed) const {
|
||||
const Common::Input::ButtonStatus button_status{
|
||||
.value = pressed,
|
||||
};
|
||||
Common::Input::TouchStatus status{
|
||||
.pressed = button_status,
|
||||
.x = {},
|
||||
.y = {},
|
||||
.id = touch_id,
|
||||
};
|
||||
status.x.properties = properties;
|
||||
status.y.properties = properties;
|
||||
|
||||
if (!pressed) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status.x.raw_value = x;
|
||||
status.y.raw_value = y;
|
||||
return status;
|
||||
}
|
||||
|
||||
void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Touch,
|
||||
.touch_status = GetStatus(button_callback.button_status.value),
|
||||
};
|
||||
if (last_button_value != button_callback.button_status.value) {
|
||||
last_button_value = button_callback.button_status.value;
|
||||
TriggerOnChange(status);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Button button;
|
||||
bool last_button_value;
|
||||
const int touch_id;
|
||||
const float x;
|
||||
const float y;
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
|
||||
const Common::ParamPackage& params) {
|
||||
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
|
||||
auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("button", null_engine));
|
||||
const auto touch_id = params.Get("touch_id", 0);
|
||||
const float x = params.Get("x", 0.0f) / 1280.0f;
|
||||
const float y = params.Get("y", 0.0f) / 720.0f;
|
||||
return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: 2020 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/settings.h"
|
||||
#include "input_common/helpers/touch_from_buttons.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class TouchFromButtonDevice final : public Common::Input::InputDevice {
|
||||
public:
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
|
||||
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
|
||||
last_button_value = false;
|
||||
button->SetCallback({
|
||||
.on_change =
|
||||
[this](const Common::Input::CallbackStatus& callback_) {
|
||||
UpdateButtonStatus(callback_);
|
||||
},
|
||||
});
|
||||
button->ForceUpdate();
|
||||
}
|
||||
|
||||
void ForceUpdate() override {
|
||||
button->ForceUpdate();
|
||||
}
|
||||
|
||||
Common::Input::TouchStatus GetStatus(bool pressed) const {
|
||||
const Common::Input::ButtonStatus button_status{
|
||||
.value = pressed,
|
||||
};
|
||||
Common::Input::TouchStatus status{
|
||||
.pressed = button_status,
|
||||
.x = {},
|
||||
.y = {},
|
||||
.id = touch_id,
|
||||
};
|
||||
status.x.properties = properties;
|
||||
status.y.properties = properties;
|
||||
|
||||
if (!pressed) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status.x.raw_value = x;
|
||||
status.y.raw_value = y;
|
||||
return status;
|
||||
}
|
||||
|
||||
void UpdateButtonStatus(const Common::Input::CallbackStatus& button_callback) {
|
||||
const Common::Input::CallbackStatus status{
|
||||
.type = Common::Input::InputType::Touch,
|
||||
.touch_status = GetStatus(button_callback.button_status.value),
|
||||
};
|
||||
if (last_button_value != button_callback.button_status.value) {
|
||||
last_button_value = button_callback.button_status.value;
|
||||
TriggerOnChange(status);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Button button;
|
||||
bool last_button_value;
|
||||
const int touch_id;
|
||||
const float x;
|
||||
const float y;
|
||||
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
|
||||
};
|
||||
|
||||
std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
|
||||
const Common::ParamPackage& params) {
|
||||
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
|
||||
auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
|
||||
params.Get("button", null_engine));
|
||||
const auto touch_id = params.Get("touch_id", 0);
|
||||
const float x = params.Get("x", 0.0f) / 1280.0f;
|
||||
const float y = params.Get("y", 0.0f) / 720.0f;
|
||||
return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,21 +1,21 @@
|
||||
// SPDX-FileCopyrightText: 2020 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/input.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A touch device factory that takes a list of button devices and combines them into a touch device.
|
||||
*/
|
||||
class TouchFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
public:
|
||||
/**
|
||||
* Creates a touch device from a list of button devices
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: 2020 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/input.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A touch device factory that takes a list of button devices and combines them into a touch device.
|
||||
*/
|
||||
class TouchFromButton final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
public:
|
||||
/**
|
||||
* Creates a touch device from a list of button devices
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,77 +1,77 @@
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
static constexpr std::size_t GetSizeOfResponseType(Type t) {
|
||||
switch (t) {
|
||||
case Type::Version:
|
||||
return sizeof(Response::Version);
|
||||
case Type::PortInfo:
|
||||
return sizeof(Response::PortInfo);
|
||||
case Type::PadData:
|
||||
return sizeof(Response::PadData);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace Response {
|
||||
|
||||
/**
|
||||
* Returns Type if the packet is valid, else none
|
||||
*
|
||||
* Note: Modifies the buffer to zero out the crc (since thats the easiest way to check without
|
||||
* copying the buffer)
|
||||
*/
|
||||
std::optional<Type> Validate(u8* data, std::size_t size) {
|
||||
if (size < sizeof(Header)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Header header{};
|
||||
std::memcpy(&header, data, sizeof(Header));
|
||||
if (header.magic != SERVER_MAGIC) {
|
||||
LOG_ERROR(Input, "UDP Packet has an unexpected magic value");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (header.protocol_version != PROTOCOL_VERSION) {
|
||||
LOG_ERROR(Input, "UDP Packet protocol mismatch");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (header.type < Type::Version || header.type > Type::PadData) {
|
||||
LOG_ERROR(Input, "UDP Packet is an unknown type");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Packet size must equal sizeof(Header) + sizeof(Data)
|
||||
// and also verify that the packet info mentions the correct size. Since the spec includes the
|
||||
// type of the packet as part of the data, we need to include it in size calculations here
|
||||
// ie: payload_length == sizeof(T) + sizeof(Type)
|
||||
const std::size_t data_len = GetSizeOfResponseType(header.type);
|
||||
if (header.payload_length != data_len + sizeof(Type) || size < data_len + sizeof(Header)) {
|
||||
LOG_ERROR(
|
||||
Input,
|
||||
"UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}",
|
||||
size, header.payload_length, data_len + sizeof(Type));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const u32 crc32 = header.crc;
|
||||
boost::crc_32_type result;
|
||||
// zero out the crc in the buffer and then run the crc against it
|
||||
std::memset(&data[offsetof(Header, crc)], 0, sizeof(u32_le));
|
||||
|
||||
result.process_bytes(data, data_len + sizeof(Header));
|
||||
if (crc32 != result.checksum()) {
|
||||
LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc));
|
||||
return std::nullopt;
|
||||
}
|
||||
return header.type;
|
||||
}
|
||||
} // namespace Response
|
||||
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
static constexpr std::size_t GetSizeOfResponseType(Type t) {
|
||||
switch (t) {
|
||||
case Type::Version:
|
||||
return sizeof(Response::Version);
|
||||
case Type::PortInfo:
|
||||
return sizeof(Response::PortInfo);
|
||||
case Type::PadData:
|
||||
return sizeof(Response::PadData);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace Response {
|
||||
|
||||
/**
|
||||
* Returns Type if the packet is valid, else none
|
||||
*
|
||||
* Note: Modifies the buffer to zero out the crc (since thats the easiest way to check without
|
||||
* copying the buffer)
|
||||
*/
|
||||
std::optional<Type> Validate(u8* data, std::size_t size) {
|
||||
if (size < sizeof(Header)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Header header{};
|
||||
std::memcpy(&header, data, sizeof(Header));
|
||||
if (header.magic != SERVER_MAGIC) {
|
||||
LOG_ERROR(Input, "UDP Packet has an unexpected magic value");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (header.protocol_version != PROTOCOL_VERSION) {
|
||||
LOG_ERROR(Input, "UDP Packet protocol mismatch");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (header.type < Type::Version || header.type > Type::PadData) {
|
||||
LOG_ERROR(Input, "UDP Packet is an unknown type");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Packet size must equal sizeof(Header) + sizeof(Data)
|
||||
// and also verify that the packet info mentions the correct size. Since the spec includes the
|
||||
// type of the packet as part of the data, we need to include it in size calculations here
|
||||
// ie: payload_length == sizeof(T) + sizeof(Type)
|
||||
const std::size_t data_len = GetSizeOfResponseType(header.type);
|
||||
if (header.payload_length != data_len + sizeof(Type) || size < data_len + sizeof(Header)) {
|
||||
LOG_ERROR(
|
||||
Input,
|
||||
"UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}",
|
||||
size, header.payload_length, data_len + sizeof(Type));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const u32 crc32 = header.crc;
|
||||
boost::crc_32_type result;
|
||||
// zero out the crc in the buffer and then run the crc against it
|
||||
std::memset(&data[offsetof(Header, crc)], 0, sizeof(u32_le));
|
||||
|
||||
result.process_bytes(data, data_len + sizeof(Header));
|
||||
if (crc32 != result.checksum()) {
|
||||
LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc));
|
||||
return std::nullopt;
|
||||
}
|
||||
return header.type;
|
||||
}
|
||||
} // namespace Response
|
||||
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
|
@@ -1,302 +1,302 @@
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
|
||||
#endif
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
constexpr std::size_t MAX_PACKET_SIZE = 100;
|
||||
constexpr u16 PROTOCOL_VERSION = 1001;
|
||||
constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE)
|
||||
constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE)
|
||||
|
||||
enum class Type : u32 {
|
||||
Version = 0x00100000,
|
||||
PortInfo = 0x00100001,
|
||||
PadData = 0x00100002,
|
||||
};
|
||||
|
||||
struct Header {
|
||||
u32_le magic{};
|
||||
u16_le protocol_version{};
|
||||
u16_le payload_length{};
|
||||
u32_le crc{};
|
||||
u32_le id{};
|
||||
///> In the protocol, the type of the packet is not part of the header, but its convenient to
|
||||
///> include in the header so the callee doesn't have to duplicate the type twice when building
|
||||
///> the data
|
||||
Type type{};
|
||||
};
|
||||
static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable");
|
||||
|
||||
using MacAddress = std::array<u8, 6>;
|
||||
constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
template <typename T>
|
||||
struct Message {
|
||||
Header header{};
|
||||
T data;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
template <typename T>
|
||||
constexpr Type GetMessageType();
|
||||
|
||||
template <typename T>
|
||||
Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
|
||||
boost::crc_32_type crc;
|
||||
Header header{
|
||||
magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
|
||||
};
|
||||
Message<T> message{header, data};
|
||||
crc.process_bytes(&message, sizeof(Message<T>));
|
||||
message.header.crc = crc.checksum();
|
||||
return message;
|
||||
}
|
||||
|
||||
namespace Request {
|
||||
|
||||
enum RegisterFlags : u8 {
|
||||
AllPads,
|
||||
PadID,
|
||||
PadMACAdddress,
|
||||
};
|
||||
|
||||
struct Version {};
|
||||
/**
|
||||
* Requests the server to send information about what controllers are plugged into the ports
|
||||
* In yuzu's case, we only have one controller, so for simplicity's sake, we can just send a
|
||||
* request explicitly for the first controller port and leave it at that. In the future it would be
|
||||
* nice to make this configurable
|
||||
*/
|
||||
constexpr u32 MAX_PORTS = 4;
|
||||
struct PortInfo {
|
||||
u32_le pad_count{}; ///> Number of ports to request data for
|
||||
std::array<u8, MAX_PORTS> port;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<PortInfo>,
|
||||
"UDP Request PortInfo is not trivially copyable");
|
||||
|
||||
/**
|
||||
* Request the latest pad information from the server. If the server hasn't received this message
|
||||
* from the client in a reasonable time frame, the server will stop sending updates. The default
|
||||
* timeout seems to be 5 seconds.
|
||||
*/
|
||||
struct PadData {
|
||||
/// Determines which method will be used as a look up for the controller
|
||||
RegisterFlags flags{};
|
||||
/// Index of the port of the controller to retrieve data about
|
||||
u8 port_id{};
|
||||
/// Mac address of the controller to retrieve data about
|
||||
MacAddress mac;
|
||||
};
|
||||
static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<PadData>,
|
||||
"UDP Request PadData is not trivially copyable");
|
||||
|
||||
/**
|
||||
* Creates a message with the proper header data that can be sent to the server.
|
||||
* @param data Request body to send
|
||||
* @param client_id ID of the udp client (usually not checked on the server)
|
||||
*/
|
||||
template <typename T>
|
||||
Message<T> Create(const T data, const u32 client_id = 0) {
|
||||
return CreateMessage(CLIENT_MAGIC, data, client_id);
|
||||
}
|
||||
} // namespace Request
|
||||
|
||||
namespace Response {
|
||||
|
||||
enum class ConnectionType : u8 {
|
||||
None,
|
||||
Usb,
|
||||
Bluetooth,
|
||||
};
|
||||
|
||||
enum class State : u8 {
|
||||
Disconnected,
|
||||
Reserved,
|
||||
Connected,
|
||||
};
|
||||
|
||||
enum class Model : u8 {
|
||||
None,
|
||||
PartialGyro,
|
||||
FullGyro,
|
||||
Generic,
|
||||
};
|
||||
|
||||
enum class Battery : u8 {
|
||||
None = 0x00,
|
||||
Dying = 0x01,
|
||||
Low = 0x02,
|
||||
Medium = 0x03,
|
||||
High = 0x04,
|
||||
Full = 0x05,
|
||||
Charging = 0xEE,
|
||||
Charged = 0xEF,
|
||||
};
|
||||
|
||||
struct Version {
|
||||
u16_le version{};
|
||||
};
|
||||
static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Version>,
|
||||
"UDP Response Version is not trivially copyable");
|
||||
|
||||
struct PortInfo {
|
||||
u8 id{};
|
||||
State state{};
|
||||
Model model{};
|
||||
ConnectionType connection_type{};
|
||||
MacAddress mac;
|
||||
Battery battery{};
|
||||
u8 is_pad_active{};
|
||||
};
|
||||
static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<PortInfo>,
|
||||
"UDP Response PortInfo is not trivially copyable");
|
||||
|
||||
struct TouchPad {
|
||||
u8 is_active{};
|
||||
u8 id{};
|
||||
u16_le x{};
|
||||
u16_le y{};
|
||||
};
|
||||
static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PadData {
|
||||
PortInfo info{};
|
||||
u32_le packet_counter{};
|
||||
|
||||
u16_le digital_button{};
|
||||
// The following union isn't trivially copyable but we don't use this input anyway.
|
||||
// union DigitalButton {
|
||||
// u16_le button;
|
||||
// BitField<0, 1, u16> button_1; // Share
|
||||
// BitField<1, 1, u16> button_2; // L3
|
||||
// BitField<2, 1, u16> button_3; // R3
|
||||
// BitField<3, 1, u16> button_4; // Options
|
||||
// BitField<4, 1, u16> button_5; // Up
|
||||
// BitField<5, 1, u16> button_6; // Right
|
||||
// BitField<6, 1, u16> button_7; // Down
|
||||
// BitField<7, 1, u16> button_8; // Left
|
||||
// BitField<8, 1, u16> button_9; // L2
|
||||
// BitField<9, 1, u16> button_10; // R2
|
||||
// BitField<10, 1, u16> button_11; // L1
|
||||
// BitField<11, 1, u16> button_12; // R1
|
||||
// BitField<12, 1, u16> button_13; // Triangle
|
||||
// BitField<13, 1, u16> button_14; // Circle
|
||||
// BitField<14, 1, u16> button_15; // Cross
|
||||
// BitField<15, 1, u16> button_16; // Square
|
||||
// } digital_button;
|
||||
|
||||
u8 home;
|
||||
/// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
|
||||
u8 touch_hard_press{};
|
||||
u8 left_stick_x{};
|
||||
u8 left_stick_y{};
|
||||
u8 right_stick_x{};
|
||||
u8 right_stick_y{};
|
||||
|
||||
struct AnalogButton {
|
||||
u8 button_dpad_left_analog{};
|
||||
u8 button_dpad_down_analog{};
|
||||
u8 button_dpad_right_analog{};
|
||||
u8 button_dpad_up_analog{};
|
||||
u8 button_square_analog{};
|
||||
u8 button_cross_analog{};
|
||||
u8 button_circle_analog{};
|
||||
u8 button_triangle_analog{};
|
||||
u8 button_r1_analog{};
|
||||
u8 button_l1_analog{};
|
||||
u8 trigger_r2{};
|
||||
u8 trigger_l2{};
|
||||
} analog_button;
|
||||
|
||||
std::array<TouchPad, 2> touch;
|
||||
|
||||
u64_le motion_timestamp;
|
||||
|
||||
struct Accelerometer {
|
||||
float x{};
|
||||
float y{};
|
||||
float z{};
|
||||
} accel;
|
||||
|
||||
struct Gyroscope {
|
||||
float pitch{};
|
||||
float yaw{};
|
||||
float roll{};
|
||||
} gyro;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
|
||||
static_assert(std::is_trivially_copyable_v<PadData>,
|
||||
"UDP Response PadData is not trivially copyable");
|
||||
|
||||
static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
|
||||
"UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
|
||||
|
||||
static_assert(sizeof(PadData::AnalogButton) == 12,
|
||||
"UDP Response AnalogButton struct has wrong size ");
|
||||
static_assert(sizeof(PadData::Accelerometer) == 12,
|
||||
"UDP Response Accelerometer struct has wrong size ");
|
||||
static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
|
||||
|
||||
/**
|
||||
* Create a Response Message from the data
|
||||
* @param data array of bytes sent from the server
|
||||
* @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
|
||||
* copy the data into the appropriate struct for that Type
|
||||
*/
|
||||
std::optional<Type> Validate(u8* data, std::size_t size);
|
||||
|
||||
} // namespace Response
|
||||
|
||||
template <>
|
||||
constexpr Type GetMessageType<Request::Version>() {
|
||||
return Type::Version;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Request::PortInfo>() {
|
||||
return Type::PortInfo;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Request::PadData>() {
|
||||
return Type::PadData;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Response::Version>() {
|
||||
return Type::Version;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Response::PortInfo>() {
|
||||
return Type::PortInfo;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Response::PadData>() {
|
||||
return Type::PadData;
|
||||
}
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
|
||||
#endif
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
constexpr std::size_t MAX_PACKET_SIZE = 100;
|
||||
constexpr u16 PROTOCOL_VERSION = 1001;
|
||||
constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE)
|
||||
constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE)
|
||||
|
||||
enum class Type : u32 {
|
||||
Version = 0x00100000,
|
||||
PortInfo = 0x00100001,
|
||||
PadData = 0x00100002,
|
||||
};
|
||||
|
||||
struct Header {
|
||||
u32_le magic{};
|
||||
u16_le protocol_version{};
|
||||
u16_le payload_length{};
|
||||
u32_le crc{};
|
||||
u32_le id{};
|
||||
///> In the protocol, the type of the packet is not part of the header, but its convenient to
|
||||
///> include in the header so the callee doesn't have to duplicate the type twice when building
|
||||
///> the data
|
||||
Type type{};
|
||||
};
|
||||
static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable");
|
||||
|
||||
using MacAddress = std::array<u8, 6>;
|
||||
constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
template <typename T>
|
||||
struct Message {
|
||||
Header header{};
|
||||
T data;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
template <typename T>
|
||||
constexpr Type GetMessageType();
|
||||
|
||||
template <typename T>
|
||||
Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
|
||||
boost::crc_32_type crc;
|
||||
Header header{
|
||||
magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
|
||||
};
|
||||
Message<T> message{header, data};
|
||||
crc.process_bytes(&message, sizeof(Message<T>));
|
||||
message.header.crc = crc.checksum();
|
||||
return message;
|
||||
}
|
||||
|
||||
namespace Request {
|
||||
|
||||
enum RegisterFlags : u8 {
|
||||
AllPads,
|
||||
PadID,
|
||||
PadMACAdddress,
|
||||
};
|
||||
|
||||
struct Version {};
|
||||
/**
|
||||
* Requests the server to send information about what controllers are plugged into the ports
|
||||
* In yuzu's case, we only have one controller, so for simplicity's sake, we can just send a
|
||||
* request explicitly for the first controller port and leave it at that. In the future it would be
|
||||
* nice to make this configurable
|
||||
*/
|
||||
constexpr u32 MAX_PORTS = 4;
|
||||
struct PortInfo {
|
||||
u32_le pad_count{}; ///> Number of ports to request data for
|
||||
std::array<u8, MAX_PORTS> port;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<PortInfo>,
|
||||
"UDP Request PortInfo is not trivially copyable");
|
||||
|
||||
/**
|
||||
* Request the latest pad information from the server. If the server hasn't received this message
|
||||
* from the client in a reasonable time frame, the server will stop sending updates. The default
|
||||
* timeout seems to be 5 seconds.
|
||||
*/
|
||||
struct PadData {
|
||||
/// Determines which method will be used as a look up for the controller
|
||||
RegisterFlags flags{};
|
||||
/// Index of the port of the controller to retrieve data about
|
||||
u8 port_id{};
|
||||
/// Mac address of the controller to retrieve data about
|
||||
MacAddress mac;
|
||||
};
|
||||
static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<PadData>,
|
||||
"UDP Request PadData is not trivially copyable");
|
||||
|
||||
/**
|
||||
* Creates a message with the proper header data that can be sent to the server.
|
||||
* @param data Request body to send
|
||||
* @param client_id ID of the udp client (usually not checked on the server)
|
||||
*/
|
||||
template <typename T>
|
||||
Message<T> Create(const T data, const u32 client_id = 0) {
|
||||
return CreateMessage(CLIENT_MAGIC, data, client_id);
|
||||
}
|
||||
} // namespace Request
|
||||
|
||||
namespace Response {
|
||||
|
||||
enum class ConnectionType : u8 {
|
||||
None,
|
||||
Usb,
|
||||
Bluetooth,
|
||||
};
|
||||
|
||||
enum class State : u8 {
|
||||
Disconnected,
|
||||
Reserved,
|
||||
Connected,
|
||||
};
|
||||
|
||||
enum class Model : u8 {
|
||||
None,
|
||||
PartialGyro,
|
||||
FullGyro,
|
||||
Generic,
|
||||
};
|
||||
|
||||
enum class Battery : u8 {
|
||||
None = 0x00,
|
||||
Dying = 0x01,
|
||||
Low = 0x02,
|
||||
Medium = 0x03,
|
||||
High = 0x04,
|
||||
Full = 0x05,
|
||||
Charging = 0xEE,
|
||||
Charged = 0xEF,
|
||||
};
|
||||
|
||||
struct Version {
|
||||
u16_le version{};
|
||||
};
|
||||
static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Version>,
|
||||
"UDP Response Version is not trivially copyable");
|
||||
|
||||
struct PortInfo {
|
||||
u8 id{};
|
||||
State state{};
|
||||
Model model{};
|
||||
ConnectionType connection_type{};
|
||||
MacAddress mac;
|
||||
Battery battery{};
|
||||
u8 is_pad_active{};
|
||||
};
|
||||
static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<PortInfo>,
|
||||
"UDP Response PortInfo is not trivially copyable");
|
||||
|
||||
struct TouchPad {
|
||||
u8 is_active{};
|
||||
u8 id{};
|
||||
u16_le x{};
|
||||
u16_le y{};
|
||||
};
|
||||
static_assert(sizeof(TouchPad) == 6, "UDP Response TouchPad struct has wrong size ");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PadData {
|
||||
PortInfo info{};
|
||||
u32_le packet_counter{};
|
||||
|
||||
u16_le digital_button{};
|
||||
// The following union isn't trivially copyable but we don't use this input anyway.
|
||||
// union DigitalButton {
|
||||
// u16_le button;
|
||||
// BitField<0, 1, u16> button_1; // Share
|
||||
// BitField<1, 1, u16> button_2; // L3
|
||||
// BitField<2, 1, u16> button_3; // R3
|
||||
// BitField<3, 1, u16> button_4; // Options
|
||||
// BitField<4, 1, u16> button_5; // Up
|
||||
// BitField<5, 1, u16> button_6; // Right
|
||||
// BitField<6, 1, u16> button_7; // Down
|
||||
// BitField<7, 1, u16> button_8; // Left
|
||||
// BitField<8, 1, u16> button_9; // L2
|
||||
// BitField<9, 1, u16> button_10; // R2
|
||||
// BitField<10, 1, u16> button_11; // L1
|
||||
// BitField<11, 1, u16> button_12; // R1
|
||||
// BitField<12, 1, u16> button_13; // Triangle
|
||||
// BitField<13, 1, u16> button_14; // Circle
|
||||
// BitField<14, 1, u16> button_15; // Cross
|
||||
// BitField<15, 1, u16> button_16; // Square
|
||||
// } digital_button;
|
||||
|
||||
u8 home;
|
||||
/// If the device supports a "click" on the touchpad, this will change to 1 when a click happens
|
||||
u8 touch_hard_press{};
|
||||
u8 left_stick_x{};
|
||||
u8 left_stick_y{};
|
||||
u8 right_stick_x{};
|
||||
u8 right_stick_y{};
|
||||
|
||||
struct AnalogButton {
|
||||
u8 button_dpad_left_analog{};
|
||||
u8 button_dpad_down_analog{};
|
||||
u8 button_dpad_right_analog{};
|
||||
u8 button_dpad_up_analog{};
|
||||
u8 button_square_analog{};
|
||||
u8 button_cross_analog{};
|
||||
u8 button_circle_analog{};
|
||||
u8 button_triangle_analog{};
|
||||
u8 button_r1_analog{};
|
||||
u8 button_l1_analog{};
|
||||
u8 trigger_r2{};
|
||||
u8 trigger_l2{};
|
||||
} analog_button;
|
||||
|
||||
std::array<TouchPad, 2> touch;
|
||||
|
||||
u64_le motion_timestamp;
|
||||
|
||||
struct Accelerometer {
|
||||
float x{};
|
||||
float y{};
|
||||
float z{};
|
||||
} accel;
|
||||
|
||||
struct Gyroscope {
|
||||
float pitch{};
|
||||
float yaw{};
|
||||
float roll{};
|
||||
} gyro;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size ");
|
||||
static_assert(std::is_trivially_copyable_v<PadData>,
|
||||
"UDP Response PadData is not trivially copyable");
|
||||
|
||||
static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE,
|
||||
"UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>");
|
||||
|
||||
static_assert(sizeof(PadData::AnalogButton) == 12,
|
||||
"UDP Response AnalogButton struct has wrong size ");
|
||||
static_assert(sizeof(PadData::Accelerometer) == 12,
|
||||
"UDP Response Accelerometer struct has wrong size ");
|
||||
static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size ");
|
||||
|
||||
/**
|
||||
* Create a Response Message from the data
|
||||
* @param data array of bytes sent from the server
|
||||
* @return boost::none if it failed to parse or Type if it succeeded. The client can then safely
|
||||
* copy the data into the appropriate struct for that Type
|
||||
*/
|
||||
std::optional<Type> Validate(u8* data, std::size_t size);
|
||||
|
||||
} // namespace Response
|
||||
|
||||
template <>
|
||||
constexpr Type GetMessageType<Request::Version>() {
|
||||
return Type::Version;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Request::PortInfo>() {
|
||||
return Type::PortInfo;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Request::PadData>() {
|
||||
return Type::PadData;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Response::Version>() {
|
||||
return Type::Version;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Response::PortInfo>() {
|
||||
return Type::PortInfo;
|
||||
}
|
||||
template <>
|
||||
constexpr Type GetMessageType<Response::PadData>() {
|
||||
return Type::PadData;
|
||||
}
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
|
@@ -1,443 +1,443 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
void InputEngine::PreSetController(const PadIdentifier& identifier) {
|
||||
std::scoped_lock lock{mutex};
|
||||
controller_list.try_emplace(identifier);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.buttons.try_emplace(button, false);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.hat_buttons.try_emplace(button, u8{0});
|
||||
}
|
||||
|
||||
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.axes.try_emplace(axis, 0.0f);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.motions.try_emplace(motion);
|
||||
}
|
||||
|
||||
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.buttons.insert_or_assign(button, value);
|
||||
}
|
||||
}
|
||||
TriggerOnButtonChange(identifier, button, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.hat_buttons.insert_or_assign(button, value);
|
||||
}
|
||||
}
|
||||
TriggerOnHatButtonChange(identifier, button, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.axes.insert_or_assign(axis, value);
|
||||
}
|
||||
}
|
||||
TriggerOnAxisChange(identifier, axis, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.battery = value;
|
||||
}
|
||||
}
|
||||
TriggerOnBatteryChange(identifier, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.motions.insert_or_assign(motion, value);
|
||||
}
|
||||
}
|
||||
TriggerOnMotionChange(identifier, motion, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetCamera(const PadIdentifier& identifier,
|
||||
const Common::Input::CameraStatus& value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.camera = value;
|
||||
}
|
||||
}
|
||||
TriggerOnCameraChange(identifier, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.nfc = value;
|
||||
}
|
||||
}
|
||||
TriggerOnNfcChange(identifier, value);
|
||||
}
|
||||
|
||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto button_iter = controller.buttons.find(button);
|
||||
if (button_iter == controller.buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid button {}", button);
|
||||
return false;
|
||||
}
|
||||
return button_iter->second;
|
||||
}
|
||||
|
||||
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto hat_iter = controller.hat_buttons.find(button);
|
||||
if (hat_iter == controller.hat_buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid hat button {}", button);
|
||||
return false;
|
||||
}
|
||||
return (hat_iter->second & direction) != 0;
|
||||
}
|
||||
|
||||
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return 0.0f;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto axis_iter = controller.axes.find(axis);
|
||||
if (axis_iter == controller.axes.cend()) {
|
||||
LOG_ERROR(Input, "Invalid axis {}", axis);
|
||||
return 0.0f;
|
||||
}
|
||||
return axis_iter->second;
|
||||
}
|
||||
|
||||
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.battery;
|
||||
}
|
||||
|
||||
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.motions.at(motion);
|
||||
}
|
||||
|
||||
Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.camera;
|
||||
}
|
||||
|
||||
Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.nfc;
|
||||
}
|
||||
|
||||
void InputEngine::ResetButtonState() {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& button : controller.second.buttons) {
|
||||
SetButton(controller.first, button.first, false);
|
||||
}
|
||||
for (const auto& button : controller.second.hat_buttons) {
|
||||
SetHatButton(controller.first, button.first, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::ResetAnalogState() {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& axis : controller.second.axes) {
|
||||
SetAxis(controller.first, axis.first, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
PreSetButton(identifier, button);
|
||||
if (value == GetButton(identifier, button)) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::Button,
|
||||
.index = button,
|
||||
.button_value = value,
|
||||
});
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
for (std::size_t index = 1; index < 0xff; index <<= 1) {
|
||||
bool button_value = (value & index) != 0;
|
||||
if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) {
|
||||
continue;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::HatButton,
|
||||
.index = button,
|
||||
.hat_name = GetHatButtonName(static_cast<u8>(index)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::Analog,
|
||||
.index = axis,
|
||||
.axis_value = value,
|
||||
});
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] Common::Input::BatteryLevel value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
bool is_active = false;
|
||||
if (std::abs(value.accel_x) > 1.5f || std::abs(value.accel_y) > 1.5f ||
|
||||
std::abs(value.accel_z) > 1.5f) {
|
||||
is_active = true;
|
||||
}
|
||||
if (std::abs(value.gyro_x) > 0.6f || std::abs(value.gyro_y) > 0.6f ||
|
||||
std::abs(value.gyro_z) > 0.6f) {
|
||||
is_active = true;
|
||||
}
|
||||
if (!is_active) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::Motion,
|
||||
.index = motion,
|
||||
.motion_value = value,
|
||||
});
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::CameraStatus& value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::NfcStatus& value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||
const PadIdentifier& identifier, EngineInputType type,
|
||||
int index) const {
|
||||
if (input_identifier.type != type) {
|
||||
return false;
|
||||
}
|
||||
if (input_identifier.index != index) {
|
||||
return false;
|
||||
}
|
||||
if (input_identifier.identifier != identifier) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputEngine::BeginConfiguration() {
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void InputEngine::EndConfiguration() {
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
const std::string& InputEngine::GetEngineName() const {
|
||||
return input_engine;
|
||||
}
|
||||
|
||||
int InputEngine::SetCallback(InputIdentifier input_identifier) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
void InputEngine::SetMappingCallback(MappingCallback callback) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
mapping_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void InputEngine::DeleteCallback(int key) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
const auto& iterator = callback_list.find(key);
|
||||
if (iterator == callback_list.end()) {
|
||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||
return;
|
||||
}
|
||||
callback_list.erase(iterator);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
void InputEngine::PreSetController(const PadIdentifier& identifier) {
|
||||
std::scoped_lock lock{mutex};
|
||||
controller_list.try_emplace(identifier);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.buttons.try_emplace(button, false);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.hat_buttons.try_emplace(button, u8{0});
|
||||
}
|
||||
|
||||
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.axes.try_emplace(axis, 0.0f);
|
||||
}
|
||||
|
||||
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
controller.motions.try_emplace(motion);
|
||||
}
|
||||
|
||||
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.buttons.insert_or_assign(button, value);
|
||||
}
|
||||
}
|
||||
TriggerOnButtonChange(identifier, button, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.hat_buttons.insert_or_assign(button, value);
|
||||
}
|
||||
}
|
||||
TriggerOnHatButtonChange(identifier, button, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.axes.insert_or_assign(axis, value);
|
||||
}
|
||||
}
|
||||
TriggerOnAxisChange(identifier, axis, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.battery = value;
|
||||
}
|
||||
}
|
||||
TriggerOnBatteryChange(identifier, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.motions.insert_or_assign(motion, value);
|
||||
}
|
||||
}
|
||||
TriggerOnMotionChange(identifier, motion, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetCamera(const PadIdentifier& identifier,
|
||||
const Common::Input::CameraStatus& value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.camera = value;
|
||||
}
|
||||
}
|
||||
TriggerOnCameraChange(identifier, value);
|
||||
}
|
||||
|
||||
void InputEngine::SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value) {
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
ControllerData& controller = controller_list.at(identifier);
|
||||
if (!configuring) {
|
||||
controller.nfc = value;
|
||||
}
|
||||
}
|
||||
TriggerOnNfcChange(identifier, value);
|
||||
}
|
||||
|
||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto button_iter = controller.buttons.find(button);
|
||||
if (button_iter == controller.buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid button {}", button);
|
||||
return false;
|
||||
}
|
||||
return button_iter->second;
|
||||
}
|
||||
|
||||
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return false;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto hat_iter = controller.hat_buttons.find(button);
|
||||
if (hat_iter == controller.hat_buttons.cend()) {
|
||||
LOG_ERROR(Input, "Invalid hat button {}", button);
|
||||
return false;
|
||||
}
|
||||
return (hat_iter->second & direction) != 0;
|
||||
}
|
||||
|
||||
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return 0.0f;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
const auto axis_iter = controller.axes.find(axis);
|
||||
if (axis_iter == controller.axes.cend()) {
|
||||
LOG_ERROR(Input, "Invalid axis {}", axis);
|
||||
return 0.0f;
|
||||
}
|
||||
return axis_iter->second;
|
||||
}
|
||||
|
||||
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.battery;
|
||||
}
|
||||
|
||||
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.motions.at(motion);
|
||||
}
|
||||
|
||||
Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.camera;
|
||||
}
|
||||
|
||||
Common::Input::NfcStatus InputEngine::GetNfc(const PadIdentifier& identifier) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto controller_iter = controller_list.find(identifier);
|
||||
if (controller_iter == controller_list.cend()) {
|
||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||
identifier.pad, identifier.port);
|
||||
return {};
|
||||
}
|
||||
const ControllerData& controller = controller_iter->second;
|
||||
return controller.nfc;
|
||||
}
|
||||
|
||||
void InputEngine::ResetButtonState() {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& button : controller.second.buttons) {
|
||||
SetButton(controller.first, button.first, false);
|
||||
}
|
||||
for (const auto& button : controller.second.hat_buttons) {
|
||||
SetHatButton(controller.first, button.first, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::ResetAnalogState() {
|
||||
for (const auto& controller : controller_list) {
|
||||
for (const auto& axis : controller.second.axes) {
|
||||
SetAxis(controller.first, axis.first, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
PreSetButton(identifier, button);
|
||||
if (value == GetButton(identifier, button)) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::Button,
|
||||
.index = button,
|
||||
.button_value = value,
|
||||
});
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
for (std::size_t index = 1; index < 0xff; index <<= 1) {
|
||||
bool button_value = (value & index) != 0;
|
||||
if (button_value == GetHatButton(identifier, button, static_cast<u8>(index))) {
|
||||
continue;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::HatButton,
|
||||
.index = button,
|
||||
.hat_name = GetHatButtonName(static_cast<u8>(index)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
if (std::abs(value - GetAxis(identifier, axis)) < 0.5f) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::Analog,
|
||||
.index = axis,
|
||||
.axis_value = value,
|
||||
});
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] Common::Input::BatteryLevel value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
if (!configuring || !mapping_callback.on_data) {
|
||||
return;
|
||||
}
|
||||
bool is_active = false;
|
||||
if (std::abs(value.accel_x) > 1.5f || std::abs(value.accel_y) > 1.5f ||
|
||||
std::abs(value.accel_z) > 1.5f) {
|
||||
is_active = true;
|
||||
}
|
||||
if (std::abs(value.gyro_x) > 0.6f || std::abs(value.gyro_y) > 0.6f ||
|
||||
std::abs(value.gyro_z) > 0.6f) {
|
||||
is_active = true;
|
||||
}
|
||||
if (!is_active) {
|
||||
return;
|
||||
}
|
||||
mapping_callback.on_data(MappingData{
|
||||
.engine = GetEngineName(),
|
||||
.pad = identifier,
|
||||
.type = EngineInputType::Motion,
|
||||
.index = motion,
|
||||
.motion_value = value,
|
||||
});
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::CameraStatus& value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::TriggerOnNfcChange(const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::NfcStatus& value) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InputIdentifier& poller = poller_pair.second;
|
||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Nfc, 0)) {
|
||||
continue;
|
||||
}
|
||||
if (poller.callback.on_change) {
|
||||
poller.callback.on_change();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||
const PadIdentifier& identifier, EngineInputType type,
|
||||
int index) const {
|
||||
if (input_identifier.type != type) {
|
||||
return false;
|
||||
}
|
||||
if (input_identifier.index != index) {
|
||||
return false;
|
||||
}
|
||||
if (input_identifier.identifier != identifier) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputEngine::BeginConfiguration() {
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void InputEngine::EndConfiguration() {
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
const std::string& InputEngine::GetEngineName() const {
|
||||
return input_engine;
|
||||
}
|
||||
|
||||
int InputEngine::SetCallback(InputIdentifier input_identifier) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
void InputEngine::SetMappingCallback(MappingCallback callback) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
mapping_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void InputEngine::DeleteCallback(int key) {
|
||||
std::scoped_lock lock{mutex_callback};
|
||||
const auto& iterator = callback_list.find(key);
|
||||
if (iterator == callback_list.end()) {
|
||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||
return;
|
||||
}
|
||||
callback_list.erase(iterator);
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,258 +1,258 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/uuid.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
// Pad Identifier of data source
|
||||
struct PadIdentifier {
|
||||
Common::UUID guid{};
|
||||
std::size_t port{};
|
||||
std::size_t pad{};
|
||||
|
||||
friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
|
||||
};
|
||||
|
||||
// Basic motion data containing data from the sensors and a timestamp in microseconds
|
||||
struct BasicMotion {
|
||||
float gyro_x{};
|
||||
float gyro_y{};
|
||||
float gyro_z{};
|
||||
float accel_x{};
|
||||
float accel_y{};
|
||||
float accel_z{};
|
||||
u64 delta_timestamp{};
|
||||
};
|
||||
|
||||
// Types of input that are stored in the engine
|
||||
enum class EngineInputType {
|
||||
None,
|
||||
Analog,
|
||||
Battery,
|
||||
Button,
|
||||
Camera,
|
||||
HatButton,
|
||||
Motion,
|
||||
Nfc,
|
||||
};
|
||||
|
||||
namespace std {
|
||||
// Hash used to create lists from PadIdentifier data
|
||||
template <>
|
||||
struct hash<PadIdentifier> {
|
||||
size_t operator()(const PadIdentifier& pad_id) const noexcept {
|
||||
u64 hash_value = pad_id.guid.Hash();
|
||||
hash_value ^= (static_cast<u64>(pad_id.port) << 32);
|
||||
hash_value ^= static_cast<u64>(pad_id.pad);
|
||||
return static_cast<size_t>(hash_value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
// Data from the engine and device needed for creating a ParamPackage
|
||||
struct MappingData {
|
||||
std::string engine{};
|
||||
PadIdentifier pad{};
|
||||
EngineInputType type{};
|
||||
int index{};
|
||||
bool button_value{};
|
||||
std::string hat_name{};
|
||||
f32 axis_value{};
|
||||
BasicMotion motion_value{};
|
||||
};
|
||||
|
||||
// Triggered if data changed on the controller
|
||||
struct UpdateCallback {
|
||||
std::function<void()> on_change;
|
||||
};
|
||||
|
||||
// Triggered if data changed on the controller and the engine is on configuring mode
|
||||
struct MappingCallback {
|
||||
std::function<void(const MappingData&)> on_data;
|
||||
};
|
||||
|
||||
// Input Identifier of data source
|
||||
struct InputIdentifier {
|
||||
PadIdentifier identifier;
|
||||
EngineInputType type;
|
||||
int index;
|
||||
UpdateCallback callback;
|
||||
};
|
||||
|
||||
class InputEngine {
|
||||
public:
|
||||
explicit InputEngine(std::string input_engine_) : input_engine{std::move(input_engine_)} {}
|
||||
|
||||
virtual ~InputEngine() = default;
|
||||
|
||||
// Enable configuring mode for mapping
|
||||
void BeginConfiguration();
|
||||
|
||||
// Disable configuring mode for mapping
|
||||
void EndConfiguration();
|
||||
|
||||
// Sets a led pattern for a controller
|
||||
virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::LedStatus& led_status) {}
|
||||
|
||||
// Sets rumble to a controller
|
||||
virtual Common::Input::VibrationError SetVibration(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
|
||||
return Common::Input::VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
// Returns true if device supports vibrations
|
||||
virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets polling mode to a controller
|
||||
virtual Common::Input::PollingError SetPollingMode(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::PollingMode polling_mode) {
|
||||
return Common::Input::PollingError::NotSupported;
|
||||
}
|
||||
|
||||
// Sets camera format to a controller
|
||||
virtual Common::Input::CameraError SetCameraFormat(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] Common::Input::CameraFormat camera_format) {
|
||||
return Common::Input::CameraError::NotSupported;
|
||||
}
|
||||
|
||||
// Request nfc data from a controller
|
||||
virtual Common::Input::NfcState SupportsNfc(
|
||||
[[maybe_unused]] const PadIdentifier& identifier) const {
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
}
|
||||
|
||||
// Writes data to an nfc tag
|
||||
virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const std::vector<u8>& data) {
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
}
|
||||
|
||||
// Returns the engine name
|
||||
[[nodiscard]] const std::string& GetEngineName() const;
|
||||
|
||||
/// Used for automapping features
|
||||
virtual std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the button mappings for the given device
|
||||
virtual ButtonMapping GetButtonMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the analog mappings for the given device
|
||||
virtual AnalogMapping GetAnalogMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the motion mappings for the given device
|
||||
virtual MotionMapping GetMotionMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the name of the given input.
|
||||
virtual Common::Input::ButtonNames GetUIName(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) const {
|
||||
return Common::Input::ButtonNames::Engine;
|
||||
}
|
||||
|
||||
/// Retrieves the index number of the given hat button direction
|
||||
virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns true if axis of a stick aren't mapped in the correct direction
|
||||
virtual bool IsStickInverted([[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void PreSetController(const PadIdentifier& identifier);
|
||||
void PreSetButton(const PadIdentifier& identifier, int button);
|
||||
void PreSetHatButton(const PadIdentifier& identifier, int button);
|
||||
void PreSetAxis(const PadIdentifier& identifier, int axis);
|
||||
void PreSetMotion(const PadIdentifier& identifier, int motion);
|
||||
void ResetButtonState();
|
||||
void ResetAnalogState();
|
||||
|
||||
bool GetButton(const PadIdentifier& identifier, int button) const;
|
||||
bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
|
||||
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
||||
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
||||
Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
|
||||
Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
|
||||
|
||||
int SetCallback(InputIdentifier input_identifier);
|
||||
void SetMappingCallback(MappingCallback callback);
|
||||
void DeleteCallback(int key);
|
||||
|
||||
protected:
|
||||
void SetButton(const PadIdentifier& identifier, int button, bool value);
|
||||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
|
||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||
void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
|
||||
void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
|
||||
|
||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private:
|
||||
struct ControllerData {
|
||||
std::unordered_map<int, bool> buttons;
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
Common::Input::BatteryLevel battery{};
|
||||
Common::Input::CameraStatus camera{};
|
||||
Common::Input::NfcStatus nfc{};
|
||||
};
|
||||
|
||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value);
|
||||
void TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||
const Common::Input::CameraStatus& value);
|
||||
void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
|
||||
|
||||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||
const PadIdentifier& identifier, EngineInputType type,
|
||||
int index) const;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex mutex_callback;
|
||||
bool configuring{false};
|
||||
const std::string input_engine;
|
||||
int last_callback_key = 0;
|
||||
std::unordered_map<PadIdentifier, ControllerData> controller_list;
|
||||
std::unordered_map<int, InputIdentifier> callback_list;
|
||||
MappingCallback mapping_callback;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/uuid.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
// Pad Identifier of data source
|
||||
struct PadIdentifier {
|
||||
Common::UUID guid{};
|
||||
std::size_t port{};
|
||||
std::size_t pad{};
|
||||
|
||||
friend constexpr bool operator==(const PadIdentifier&, const PadIdentifier&) = default;
|
||||
};
|
||||
|
||||
// Basic motion data containing data from the sensors and a timestamp in microseconds
|
||||
struct BasicMotion {
|
||||
float gyro_x{};
|
||||
float gyro_y{};
|
||||
float gyro_z{};
|
||||
float accel_x{};
|
||||
float accel_y{};
|
||||
float accel_z{};
|
||||
u64 delta_timestamp{};
|
||||
};
|
||||
|
||||
// Types of input that are stored in the engine
|
||||
enum class EngineInputType {
|
||||
None,
|
||||
Analog,
|
||||
Battery,
|
||||
Button,
|
||||
Camera,
|
||||
HatButton,
|
||||
Motion,
|
||||
Nfc,
|
||||
};
|
||||
|
||||
namespace std {
|
||||
// Hash used to create lists from PadIdentifier data
|
||||
template <>
|
||||
struct hash<PadIdentifier> {
|
||||
size_t operator()(const PadIdentifier& pad_id) const noexcept {
|
||||
u64 hash_value = pad_id.guid.Hash();
|
||||
hash_value ^= (static_cast<u64>(pad_id.port) << 32);
|
||||
hash_value ^= static_cast<u64>(pad_id.pad);
|
||||
return static_cast<size_t>(hash_value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
// Data from the engine and device needed for creating a ParamPackage
|
||||
struct MappingData {
|
||||
std::string engine{};
|
||||
PadIdentifier pad{};
|
||||
EngineInputType type{};
|
||||
int index{};
|
||||
bool button_value{};
|
||||
std::string hat_name{};
|
||||
f32 axis_value{};
|
||||
BasicMotion motion_value{};
|
||||
};
|
||||
|
||||
// Triggered if data changed on the controller
|
||||
struct UpdateCallback {
|
||||
std::function<void()> on_change;
|
||||
};
|
||||
|
||||
// Triggered if data changed on the controller and the engine is on configuring mode
|
||||
struct MappingCallback {
|
||||
std::function<void(const MappingData&)> on_data;
|
||||
};
|
||||
|
||||
// Input Identifier of data source
|
||||
struct InputIdentifier {
|
||||
PadIdentifier identifier;
|
||||
EngineInputType type;
|
||||
int index;
|
||||
UpdateCallback callback;
|
||||
};
|
||||
|
||||
class InputEngine {
|
||||
public:
|
||||
explicit InputEngine(std::string input_engine_) : input_engine{std::move(input_engine_)} {}
|
||||
|
||||
virtual ~InputEngine() = default;
|
||||
|
||||
// Enable configuring mode for mapping
|
||||
void BeginConfiguration();
|
||||
|
||||
// Disable configuring mode for mapping
|
||||
void EndConfiguration();
|
||||
|
||||
// Sets a led pattern for a controller
|
||||
virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::LedStatus& led_status) {}
|
||||
|
||||
// Sets rumble to a controller
|
||||
virtual Common::Input::VibrationError SetVibration(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
|
||||
return Common::Input::VibrationError::NotSupported;
|
||||
}
|
||||
|
||||
// Returns true if device supports vibrations
|
||||
virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets polling mode to a controller
|
||||
virtual Common::Input::PollingError SetPollingMode(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::PollingMode polling_mode) {
|
||||
return Common::Input::PollingError::NotSupported;
|
||||
}
|
||||
|
||||
// Sets camera format to a controller
|
||||
virtual Common::Input::CameraError SetCameraFormat(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] Common::Input::CameraFormat camera_format) {
|
||||
return Common::Input::CameraError::NotSupported;
|
||||
}
|
||||
|
||||
// Request nfc data from a controller
|
||||
virtual Common::Input::NfcState SupportsNfc(
|
||||
[[maybe_unused]] const PadIdentifier& identifier) const {
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
}
|
||||
|
||||
// Writes data to an nfc tag
|
||||
virtual Common::Input::NfcState WriteNfcData([[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const std::vector<u8>& data) {
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
}
|
||||
|
||||
// Returns the engine name
|
||||
[[nodiscard]] const std::string& GetEngineName() const;
|
||||
|
||||
/// Used for automapping features
|
||||
virtual std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the button mappings for the given device
|
||||
virtual ButtonMapping GetButtonMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the analog mappings for the given device
|
||||
virtual AnalogMapping GetAnalogMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the motion mappings for the given device
|
||||
virtual MotionMapping GetMotionMappingForDevice(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Retrieves the name of the given input.
|
||||
virtual Common::Input::ButtonNames GetUIName(
|
||||
[[maybe_unused]] const Common::ParamPackage& params) const {
|
||||
return Common::Input::ButtonNames::Engine;
|
||||
}
|
||||
|
||||
/// Retrieves the index number of the given hat button direction
|
||||
virtual u8 GetHatButtonId([[maybe_unused]] const std::string& direction_name) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns true if axis of a stick aren't mapped in the correct direction
|
||||
virtual bool IsStickInverted([[maybe_unused]] const Common::ParamPackage& params) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void PreSetController(const PadIdentifier& identifier);
|
||||
void PreSetButton(const PadIdentifier& identifier, int button);
|
||||
void PreSetHatButton(const PadIdentifier& identifier, int button);
|
||||
void PreSetAxis(const PadIdentifier& identifier, int axis);
|
||||
void PreSetMotion(const PadIdentifier& identifier, int motion);
|
||||
void ResetButtonState();
|
||||
void ResetAnalogState();
|
||||
|
||||
bool GetButton(const PadIdentifier& identifier, int button) const;
|
||||
bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
|
||||
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
||||
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
||||
Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
|
||||
Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
|
||||
|
||||
int SetCallback(InputIdentifier input_identifier);
|
||||
void SetMappingCallback(MappingCallback callback);
|
||||
void DeleteCallback(int key);
|
||||
|
||||
protected:
|
||||
void SetButton(const PadIdentifier& identifier, int button, bool value);
|
||||
void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
|
||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||
void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
|
||||
void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
|
||||
|
||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private:
|
||||
struct ControllerData {
|
||||
std::unordered_map<int, bool> buttons;
|
||||
std::unordered_map<int, u8> hat_buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, BasicMotion> motions;
|
||||
Common::Input::BatteryLevel battery{};
|
||||
Common::Input::CameraStatus camera{};
|
||||
Common::Input::NfcStatus nfc{};
|
||||
};
|
||||
|
||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||
void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
|
||||
void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
|
||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||
const BasicMotion& value);
|
||||
void TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||
const Common::Input::CameraStatus& value);
|
||||
void TriggerOnNfcChange(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
|
||||
|
||||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||
const PadIdentifier& identifier, EngineInputType type,
|
||||
int index) const;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex mutex_callback;
|
||||
bool configuring{false};
|
||||
const std::string input_engine;
|
||||
int last_callback_key = 0;
|
||||
std::unordered_map<PadIdentifier, ControllerData> controller_list;
|
||||
std::unordered_map<int, InputIdentifier> callback_list;
|
||||
MappingCallback mapping_callback;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,218 +1,218 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/input_mapping.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
MappingFactory::MappingFactory() = default;
|
||||
|
||||
void MappingFactory::BeginMapping(Polling::InputType type) {
|
||||
is_enabled = true;
|
||||
input_type = type;
|
||||
input_queue.Clear();
|
||||
first_axis = -1;
|
||||
second_axis = -1;
|
||||
}
|
||||
|
||||
Common::ParamPackage MappingFactory::GetNextInput() {
|
||||
Common::ParamPackage input;
|
||||
input_queue.Pop(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterInput(const MappingData& data) {
|
||||
if (!is_enabled) {
|
||||
return;
|
||||
}
|
||||
if (!IsDriverValid(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (input_type) {
|
||||
case Polling::InputType::Button:
|
||||
RegisterButton(data);
|
||||
return;
|
||||
case Polling::InputType::Stick:
|
||||
RegisterStick(data);
|
||||
return;
|
||||
case Polling::InputType::Motion:
|
||||
RegisterMotion(data);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MappingFactory::StopMapping() {
|
||||
is_enabled = false;
|
||||
input_type = Polling::InputType::None;
|
||||
input_queue.Clear();
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterButton(const MappingData& data) {
|
||||
Common::ParamPackage new_input;
|
||||
new_input.Set("engine", data.engine);
|
||||
if (data.pad.guid.IsValid()) {
|
||||
new_input.Set("guid", data.pad.guid.RawString());
|
||||
}
|
||||
new_input.Set("port", static_cast<int>(data.pad.port));
|
||||
new_input.Set("pad", static_cast<int>(data.pad.pad));
|
||||
|
||||
switch (data.type) {
|
||||
case EngineInputType::Button:
|
||||
// Workaround for old compatibility
|
||||
if (data.engine == "keyboard") {
|
||||
new_input.Set("code", data.index);
|
||||
break;
|
||||
}
|
||||
new_input.Set("button", data.index);
|
||||
break;
|
||||
case EngineInputType::HatButton:
|
||||
new_input.Set("hat", data.index);
|
||||
new_input.Set("direction", data.hat_name);
|
||||
break;
|
||||
case EngineInputType::Analog:
|
||||
// Ignore mouse axis when mapping buttons
|
||||
if (data.engine == "mouse") {
|
||||
return;
|
||||
}
|
||||
new_input.Set("axis", data.index);
|
||||
new_input.Set("threshold", 0.5f);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
input_queue.Push(new_input);
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterStick(const MappingData& data) {
|
||||
Common::ParamPackage new_input;
|
||||
new_input.Set("engine", data.engine);
|
||||
if (data.pad.guid.IsValid()) {
|
||||
new_input.Set("guid", data.pad.guid.RawString());
|
||||
}
|
||||
new_input.Set("port", static_cast<int>(data.pad.port));
|
||||
new_input.Set("pad", static_cast<int>(data.pad.pad));
|
||||
|
||||
// If engine is mouse map the mouse position as a joystick
|
||||
if (data.engine == "mouse") {
|
||||
new_input.Set("axis_x", 0);
|
||||
new_input.Set("axis_y", 1);
|
||||
new_input.Set("threshold", 0.5f);
|
||||
new_input.Set("range", 1.0f);
|
||||
new_input.Set("deadzone", 0.0f);
|
||||
input_queue.Push(new_input);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case EngineInputType::Button:
|
||||
case EngineInputType::HatButton:
|
||||
RegisterButton(data);
|
||||
return;
|
||||
case EngineInputType::Analog:
|
||||
if (first_axis == data.index) {
|
||||
return;
|
||||
}
|
||||
if (first_axis == -1) {
|
||||
first_axis = data.index;
|
||||
return;
|
||||
}
|
||||
new_input.Set("axis_x", first_axis);
|
||||
new_input.Set("axis_y", data.index);
|
||||
new_input.Set("threshold", 0.5f);
|
||||
new_input.Set("range", 0.95f);
|
||||
new_input.Set("deadzone", 0.15f);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
input_queue.Push(new_input);
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterMotion(const MappingData& data) {
|
||||
Common::ParamPackage new_input;
|
||||
new_input.Set("engine", data.engine);
|
||||
if (data.pad.guid.IsValid()) {
|
||||
new_input.Set("guid", data.pad.guid.RawString());
|
||||
}
|
||||
new_input.Set("port", static_cast<int>(data.pad.port));
|
||||
new_input.Set("pad", static_cast<int>(data.pad.pad));
|
||||
|
||||
// If engine is mouse map the mouse position as 3 axis motion
|
||||
if (data.engine == "mouse") {
|
||||
new_input.Set("axis_x", 1);
|
||||
new_input.Set("invert_x", "-");
|
||||
new_input.Set("axis_y", 0);
|
||||
new_input.Set("axis_z", 4);
|
||||
new_input.Set("range", 1.0f);
|
||||
new_input.Set("deadzone", 0.0f);
|
||||
input_queue.Push(new_input);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case EngineInputType::Button:
|
||||
case EngineInputType::HatButton:
|
||||
RegisterButton(data);
|
||||
return;
|
||||
case EngineInputType::Analog:
|
||||
if (first_axis == data.index) {
|
||||
return;
|
||||
}
|
||||
if (second_axis == data.index) {
|
||||
return;
|
||||
}
|
||||
if (first_axis == -1) {
|
||||
first_axis = data.index;
|
||||
return;
|
||||
}
|
||||
if (second_axis == -1) {
|
||||
second_axis = data.index;
|
||||
return;
|
||||
}
|
||||
new_input.Set("axis_x", first_axis);
|
||||
new_input.Set("axis_y", second_axis);
|
||||
new_input.Set("axis_z", data.index);
|
||||
new_input.Set("range", 1.0f);
|
||||
new_input.Set("deadzone", 0.20f);
|
||||
break;
|
||||
case EngineInputType::Motion:
|
||||
new_input.Set("motion", data.index);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
input_queue.Push(new_input);
|
||||
}
|
||||
|
||||
bool MappingFactory::IsDriverValid(const MappingData& data) const {
|
||||
// Only port 0 can be mapped on the keyboard
|
||||
if (data.engine == "keyboard" && data.pad.port != 0) {
|
||||
return false;
|
||||
}
|
||||
// To prevent mapping with two devices we disable any UDP except motion
|
||||
if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" &&
|
||||
data.type != EngineInputType::Motion) {
|
||||
return false;
|
||||
}
|
||||
// The following drivers don't need to be mapped
|
||||
if (data.engine == "tas") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "touch") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "touch_from_button") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "analog_from_button") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/input_mapping.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
MappingFactory::MappingFactory() = default;
|
||||
|
||||
void MappingFactory::BeginMapping(Polling::InputType type) {
|
||||
is_enabled = true;
|
||||
input_type = type;
|
||||
input_queue.Clear();
|
||||
first_axis = -1;
|
||||
second_axis = -1;
|
||||
}
|
||||
|
||||
Common::ParamPackage MappingFactory::GetNextInput() {
|
||||
Common::ParamPackage input;
|
||||
input_queue.Pop(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterInput(const MappingData& data) {
|
||||
if (!is_enabled) {
|
||||
return;
|
||||
}
|
||||
if (!IsDriverValid(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (input_type) {
|
||||
case Polling::InputType::Button:
|
||||
RegisterButton(data);
|
||||
return;
|
||||
case Polling::InputType::Stick:
|
||||
RegisterStick(data);
|
||||
return;
|
||||
case Polling::InputType::Motion:
|
||||
RegisterMotion(data);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MappingFactory::StopMapping() {
|
||||
is_enabled = false;
|
||||
input_type = Polling::InputType::None;
|
||||
input_queue.Clear();
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterButton(const MappingData& data) {
|
||||
Common::ParamPackage new_input;
|
||||
new_input.Set("engine", data.engine);
|
||||
if (data.pad.guid.IsValid()) {
|
||||
new_input.Set("guid", data.pad.guid.RawString());
|
||||
}
|
||||
new_input.Set("port", static_cast<int>(data.pad.port));
|
||||
new_input.Set("pad", static_cast<int>(data.pad.pad));
|
||||
|
||||
switch (data.type) {
|
||||
case EngineInputType::Button:
|
||||
// Workaround for old compatibility
|
||||
if (data.engine == "keyboard") {
|
||||
new_input.Set("code", data.index);
|
||||
break;
|
||||
}
|
||||
new_input.Set("button", data.index);
|
||||
break;
|
||||
case EngineInputType::HatButton:
|
||||
new_input.Set("hat", data.index);
|
||||
new_input.Set("direction", data.hat_name);
|
||||
break;
|
||||
case EngineInputType::Analog:
|
||||
// Ignore mouse axis when mapping buttons
|
||||
if (data.engine == "mouse") {
|
||||
return;
|
||||
}
|
||||
new_input.Set("axis", data.index);
|
||||
new_input.Set("threshold", 0.5f);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
input_queue.Push(new_input);
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterStick(const MappingData& data) {
|
||||
Common::ParamPackage new_input;
|
||||
new_input.Set("engine", data.engine);
|
||||
if (data.pad.guid.IsValid()) {
|
||||
new_input.Set("guid", data.pad.guid.RawString());
|
||||
}
|
||||
new_input.Set("port", static_cast<int>(data.pad.port));
|
||||
new_input.Set("pad", static_cast<int>(data.pad.pad));
|
||||
|
||||
// If engine is mouse map the mouse position as a joystick
|
||||
if (data.engine == "mouse") {
|
||||
new_input.Set("axis_x", 0);
|
||||
new_input.Set("axis_y", 1);
|
||||
new_input.Set("threshold", 0.5f);
|
||||
new_input.Set("range", 1.0f);
|
||||
new_input.Set("deadzone", 0.0f);
|
||||
input_queue.Push(new_input);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case EngineInputType::Button:
|
||||
case EngineInputType::HatButton:
|
||||
RegisterButton(data);
|
||||
return;
|
||||
case EngineInputType::Analog:
|
||||
if (first_axis == data.index) {
|
||||
return;
|
||||
}
|
||||
if (first_axis == -1) {
|
||||
first_axis = data.index;
|
||||
return;
|
||||
}
|
||||
new_input.Set("axis_x", first_axis);
|
||||
new_input.Set("axis_y", data.index);
|
||||
new_input.Set("threshold", 0.5f);
|
||||
new_input.Set("range", 0.95f);
|
||||
new_input.Set("deadzone", 0.15f);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
input_queue.Push(new_input);
|
||||
}
|
||||
|
||||
void MappingFactory::RegisterMotion(const MappingData& data) {
|
||||
Common::ParamPackage new_input;
|
||||
new_input.Set("engine", data.engine);
|
||||
if (data.pad.guid.IsValid()) {
|
||||
new_input.Set("guid", data.pad.guid.RawString());
|
||||
}
|
||||
new_input.Set("port", static_cast<int>(data.pad.port));
|
||||
new_input.Set("pad", static_cast<int>(data.pad.pad));
|
||||
|
||||
// If engine is mouse map the mouse position as 3 axis motion
|
||||
if (data.engine == "mouse") {
|
||||
new_input.Set("axis_x", 1);
|
||||
new_input.Set("invert_x", "-");
|
||||
new_input.Set("axis_y", 0);
|
||||
new_input.Set("axis_z", 4);
|
||||
new_input.Set("range", 1.0f);
|
||||
new_input.Set("deadzone", 0.0f);
|
||||
input_queue.Push(new_input);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case EngineInputType::Button:
|
||||
case EngineInputType::HatButton:
|
||||
RegisterButton(data);
|
||||
return;
|
||||
case EngineInputType::Analog:
|
||||
if (first_axis == data.index) {
|
||||
return;
|
||||
}
|
||||
if (second_axis == data.index) {
|
||||
return;
|
||||
}
|
||||
if (first_axis == -1) {
|
||||
first_axis = data.index;
|
||||
return;
|
||||
}
|
||||
if (second_axis == -1) {
|
||||
second_axis = data.index;
|
||||
return;
|
||||
}
|
||||
new_input.Set("axis_x", first_axis);
|
||||
new_input.Set("axis_y", second_axis);
|
||||
new_input.Set("axis_z", data.index);
|
||||
new_input.Set("range", 1.0f);
|
||||
new_input.Set("deadzone", 0.20f);
|
||||
break;
|
||||
case EngineInputType::Motion:
|
||||
new_input.Set("motion", data.index);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
input_queue.Push(new_input);
|
||||
}
|
||||
|
||||
bool MappingFactory::IsDriverValid(const MappingData& data) const {
|
||||
// Only port 0 can be mapped on the keyboard
|
||||
if (data.engine == "keyboard" && data.pad.port != 0) {
|
||||
return false;
|
||||
}
|
||||
// To prevent mapping with two devices we disable any UDP except motion
|
||||
if (!Settings::values.enable_udp_controller && data.engine == "cemuhookudp" &&
|
||||
data.type != EngineInputType::Motion) {
|
||||
return false;
|
||||
}
|
||||
// The following drivers don't need to be mapped
|
||||
if (data.engine == "tas") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "touch") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "touch_from_button") {
|
||||
return false;
|
||||
}
|
||||
if (data.engine == "analog_from_button") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,88 +1,88 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
namespace InputCommon::Polling {
|
||||
enum class InputType;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class InputEngine;
|
||||
struct MappingData;
|
||||
|
||||
class MappingFactory {
|
||||
public:
|
||||
MappingFactory();
|
||||
|
||||
/**
|
||||
* Resets all variables to begin the mapping process
|
||||
* @param type type of input desired to be returned
|
||||
*/
|
||||
void BeginMapping(Polling::InputType type);
|
||||
|
||||
/// Returns an input event with mapping information from the input_queue
|
||||
[[nodiscard]] Common::ParamPackage GetNextInput();
|
||||
|
||||
/**
|
||||
* Registers mapping input data from the driver
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterInput(const MappingData& data);
|
||||
|
||||
/// Stop polling from all backends
|
||||
void StopMapping();
|
||||
|
||||
private:
|
||||
/**
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button: Creates a basic button ParamPackage
|
||||
* - HatButton: Creates a basic hat button ParamPackage
|
||||
* - Analog: Creates a basic analog ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterButton(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterStick(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first two axis and on the third axis creates a basic Motion
|
||||
* ParamPackage
|
||||
* - Motion: Creates a basic Motion ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterMotion(const MappingData& data);
|
||||
|
||||
/**
|
||||
* Returns true if driver can be mapped
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
bool IsDriverValid(const MappingData& data) const;
|
||||
|
||||
Common::SPSCQueue<Common::ParamPackage> input_queue;
|
||||
Polling::InputType input_type{Polling::InputType::None};
|
||||
bool is_enabled{};
|
||||
int first_axis = -1;
|
||||
int second_axis = -1;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
namespace InputCommon::Polling {
|
||||
enum class InputType;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class InputEngine;
|
||||
struct MappingData;
|
||||
|
||||
class MappingFactory {
|
||||
public:
|
||||
MappingFactory();
|
||||
|
||||
/**
|
||||
* Resets all variables to begin the mapping process
|
||||
* @param type type of input desired to be returned
|
||||
*/
|
||||
void BeginMapping(Polling::InputType type);
|
||||
|
||||
/// Returns an input event with mapping information from the input_queue
|
||||
[[nodiscard]] Common::ParamPackage GetNextInput();
|
||||
|
||||
/**
|
||||
* Registers mapping input data from the driver
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterInput(const MappingData& data);
|
||||
|
||||
/// Stop polling from all backends
|
||||
void StopMapping();
|
||||
|
||||
private:
|
||||
/**
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button: Creates a basic button ParamPackage
|
||||
* - HatButton: Creates a basic hat button ParamPackage
|
||||
* - Analog: Creates a basic analog ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterButton(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first axis and on the second axis creates a basic stick ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterStick(const MappingData& data);
|
||||
|
||||
/**
|
||||
* If provided data satisfies the requirements it will push an element to the input_queue
|
||||
* Supported input:
|
||||
* - Button, HatButton: Pass the data to RegisterButton
|
||||
* - Analog: Stores the first two axis and on the third axis creates a basic Motion
|
||||
* ParamPackage
|
||||
* - Motion: Creates a basic Motion ParamPackage
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
void RegisterMotion(const MappingData& data);
|
||||
|
||||
/**
|
||||
* Returns true if driver can be mapped
|
||||
* @param data A struct containing all the information needed to create a proper
|
||||
* ParamPackage
|
||||
*/
|
||||
bool IsDriverValid(const MappingData& data) const;
|
||||
|
||||
Common::SPSCQueue<Common::ParamPackage> input_queue;
|
||||
Polling::InputType input_type{Polling::InputType::None};
|
||||
bool is_enabled{};
|
||||
int first_axis = -1;
|
||||
int second_axis = -1;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,237 +1,237 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Input {
|
||||
class InputDevice;
|
||||
|
||||
template <typename InputDevice>
|
||||
class Factory;
|
||||
}; // namespace Input
|
||||
|
||||
namespace InputCommon {
|
||||
class InputEngine;
|
||||
|
||||
class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
|
||||
public:
|
||||
explicit OutputFactory(std::shared_ptr<InputEngine> input_engine_);
|
||||
|
||||
/**
|
||||
* Creates an output device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid" text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique output device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::OutputDevice> Create(
|
||||
const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<InputEngine> input_engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* An Input factory. It receives input events and forward them to all input devices it created.
|
||||
*/
|
||||
class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
public:
|
||||
explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);
|
||||
|
||||
/**
|
||||
* Creates an input device from the parameters given. Identifies the type of input to be
|
||||
* returned if it contains the following parameters:
|
||||
* - button: Contains "button" or "code"
|
||||
* - hat_button: Contains "hat"
|
||||
* - analog: Contains "axis"
|
||||
* - trigger: Contains "button" and "axis"
|
||||
* - stick: Contains "axis_x" and "axis_y"
|
||||
* - motion: Contains "axis_x", "axis_y" and "axis_z"
|
||||
* - motion: Contains "motion"
|
||||
* - touch: Contains "button", "axis_x" and "axis_y"
|
||||
* - battery: Contains "battery"
|
||||
* - output: Contains "output"
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "hat": similar as "button" but it's a group of hat buttons from SDL
|
||||
* - "axis": the axis number of the axis to bind with the input
|
||||
* - "motion": the motion number of the motion to bind with the input
|
||||
* - "axis_x": same as axis but specifying horizontal direction
|
||||
* - "axis_y": same as axis but specifying vertical direction
|
||||
* - "axis_z": same as axis but specifying forward direction
|
||||
* - "battery": Only used as a placeholder to set the input type
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Creates a button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a hat button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a stick device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates an analog device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a trigger device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a touch device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a battery device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a motion device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "axis_z": the controller forward axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "offset_z": the amount of offset in the z axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "invert_z": inverts the sign of the forward axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
||||
|
||||
/**
|
||||
* Creates a camera device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a nfc device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateNfcDevice(const Common::ParamPackage& params);
|
||||
|
||||
std::shared_ptr<InputEngine> input_engine;
|
||||
};
|
||||
} // namespace InputCommon
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Input {
|
||||
class InputDevice;
|
||||
|
||||
template <typename InputDevice>
|
||||
class Factory;
|
||||
}; // namespace Input
|
||||
|
||||
namespace InputCommon {
|
||||
class InputEngine;
|
||||
|
||||
class OutputFactory final : public Common::Input::Factory<Common::Input::OutputDevice> {
|
||||
public:
|
||||
explicit OutputFactory(std::shared_ptr<InputEngine> input_engine_);
|
||||
|
||||
/**
|
||||
* Creates an output device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid" text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique output device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::OutputDevice> Create(
|
||||
const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<InputEngine> input_engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* An Input factory. It receives input events and forward them to all input devices it created.
|
||||
*/
|
||||
class InputFactory final : public Common::Input::Factory<Common::Input::InputDevice> {
|
||||
public:
|
||||
explicit InputFactory(std::shared_ptr<InputEngine> input_engine_);
|
||||
|
||||
/**
|
||||
* Creates an input device from the parameters given. Identifies the type of input to be
|
||||
* returned if it contains the following parameters:
|
||||
* - button: Contains "button" or "code"
|
||||
* - hat_button: Contains "hat"
|
||||
* - analog: Contains "axis"
|
||||
* - trigger: Contains "button" and "axis"
|
||||
* - stick: Contains "axis_x" and "axis_y"
|
||||
* - motion: Contains "axis_x", "axis_y" and "axis_z"
|
||||
* - motion: Contains "motion"
|
||||
* - touch: Contains "button", "axis_x" and "axis_y"
|
||||
* - battery: Contains "battery"
|
||||
* - output: Contains "output"
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "hat": similar as "button" but it's a group of hat buttons from SDL
|
||||
* - "axis": the axis number of the axis to bind with the input
|
||||
* - "motion": the motion number of the motion to bind with the input
|
||||
* - "axis_x": same as axis but specifying horizontal direction
|
||||
* - "axis_y": same as axis but specifying vertical direction
|
||||
* - "axis_z": same as axis but specifying forward direction
|
||||
* - "battery": Only used as a placeholder to set the input type
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Creates a button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the keyboard key to bind with the input
|
||||
* - "button": same as "code" but for controller buttons
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a hat button device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateHatButtonDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a stick device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateStickDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates an analog device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateAnalogDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a trigger device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis": the controller axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset": the amount of offset in the axis
|
||||
* - "invert": inverts the sign of the axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTriggerDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a touch device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "button": the controller hat id to bind with the input
|
||||
* - "direction": the direction id to be detected
|
||||
* - "toggle": press once to enable, press again to disable
|
||||
* - "inverted": inverts the output of the button
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "threshold": the minimum required value to considered pressed
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateTouchDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a battery device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateBatteryDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a motion device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "axis_x": the controller horizontal axis id to bind with the input
|
||||
* - "axis_y": the controller vertical axis id to bind with the input
|
||||
* - "axis_z": the controller forward axis id to bind with the input
|
||||
* - "deadzone": the minimum required value to be detected
|
||||
* - "range": the maximum value required to reach 100%
|
||||
* - "offset_x": the amount of offset in the x axis
|
||||
* - "offset_y": the amount of offset in the y axis
|
||||
* - "offset_z": the amount of offset in the z axis
|
||||
* - "invert_x": inverts the sign of the horizontal axis
|
||||
* - "invert_y": inverts the sign of the vertical axis
|
||||
* - "invert_z": inverts the sign of the forward axis
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
||||
|
||||
/**
|
||||
* Creates a camera device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
|
||||
const Common::ParamPackage& params);
|
||||
|
||||
/**
|
||||
* Creates a nfc device from the parameters given.
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "guid": text string for identifying controllers
|
||||
* - "port": port of the connected device
|
||||
* - "pad": slot of the connected controller
|
||||
* @returns a unique input device with the parameters specified
|
||||
*/
|
||||
std::unique_ptr<Common::Input::InputDevice> CreateNfcDevice(const Common::ParamPackage& params);
|
||||
|
||||
std::shared_ptr<InputEngine> input_engine;
|
||||
};
|
||||
} // namespace InputCommon
|
||||
|
@@ -1,496 +1,496 @@
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/camera.h"
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
#include "input_common/helpers/stick_from_buttons.h"
|
||||
#include "input_common/helpers/touch_from_buttons.h"
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/input_mapping.h"
|
||||
#include "input_common/input_poller.h"
|
||||
#include "input_common/main.h"
|
||||
#ifdef HAVE_SDL2
|
||||
#include "input_common/drivers/sdl_driver.h"
|
||||
#endif
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
struct InputSubsystem::Impl {
|
||||
void Initialize() {
|
||||
mapping_factory = std::make_shared<MappingFactory>();
|
||||
MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
|
||||
|
||||
keyboard = std::make_shared<Keyboard>("keyboard");
|
||||
keyboard->SetMappingCallback(mapping_callback);
|
||||
keyboard_factory = std::make_shared<InputFactory>(keyboard);
|
||||
keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(),
|
||||
keyboard_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName(),
|
||||
keyboard_output_factory);
|
||||
|
||||
mouse = std::make_shared<Mouse>("mouse");
|
||||
mouse->SetMappingCallback(mapping_callback);
|
||||
mouse_factory = std::make_shared<InputFactory>(mouse);
|
||||
mouse_output_factory = std::make_shared<OutputFactory>(mouse);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(),
|
||||
mouse_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(),
|
||||
mouse_output_factory);
|
||||
|
||||
touch_screen = std::make_shared<TouchScreen>("touch");
|
||||
touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(),
|
||||
touch_screen_factory);
|
||||
|
||||
gcadapter = std::make_shared<GCAdapter>("gcpad");
|
||||
gcadapter->SetMappingCallback(mapping_callback);
|
||||
gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
|
||||
gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(),
|
||||
gcadapter_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(),
|
||||
gcadapter_output_factory);
|
||||
|
||||
udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
|
||||
udp_client->SetMappingCallback(mapping_callback);
|
||||
udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
|
||||
udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(),
|
||||
udp_client_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(),
|
||||
udp_client_output_factory);
|
||||
|
||||
tas_input = std::make_shared<TasInput::Tas>("tas");
|
||||
tas_input->SetMappingCallback(mapping_callback);
|
||||
tas_input_factory = std::make_shared<InputFactory>(tas_input);
|
||||
tas_output_factory = std::make_shared<OutputFactory>(tas_input);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(),
|
||||
tas_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
|
||||
tas_output_factory);
|
||||
|
||||
camera = std::make_shared<Camera>("camera");
|
||||
camera->SetMappingCallback(mapping_callback);
|
||||
camera_input_factory = std::make_shared<InputFactory>(camera);
|
||||
camera_output_factory = std::make_shared<OutputFactory>(camera);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
|
||||
camera_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
|
||||
camera_output_factory);
|
||||
|
||||
virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
|
||||
virtual_amiibo->SetMappingCallback(mapping_callback);
|
||||
virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
|
||||
virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(virtual_amiibo->GetEngineName(),
|
||||
virtual_amiibo_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(virtual_amiibo->GetEngineName(),
|
||||
virtual_amiibo_output_factory);
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
sdl = std::make_shared<SDLDriver>("sdl");
|
||||
sdl->SetMappingCallback(mapping_callback);
|
||||
sdl_input_factory = std::make_shared<InputFactory>(sdl);
|
||||
sdl_output_factory = std::make_shared<OutputFactory>(sdl);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(),
|
||||
sdl_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
|
||||
sdl_output_factory);
|
||||
#endif
|
||||
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(
|
||||
"touch_from_button", std::make_shared<TouchFromButton>());
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(
|
||||
"analog_from_button", std::make_shared<StickFromButton>());
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName());
|
||||
keyboard.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName());
|
||||
mouse.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName());
|
||||
touch_screen.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName());
|
||||
gcadapter.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName());
|
||||
udp_client.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName());
|
||||
tas_input.reset();
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
|
||||
sdl.reset();
|
||||
#endif
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices = {
|
||||
Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
|
||||
};
|
||||
|
||||
auto keyboard_devices = keyboard->GetInputDevices();
|
||||
devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
|
||||
auto mouse_devices = mouse->GetInputDevices();
|
||||
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
auto udp_devices = udp_client->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
#ifdef HAVE_SDL2
|
||||
auto sdl_devices = sdl->GetInputDevices();
|
||||
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
||||
#endif
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MotionMapping GetMotionMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetMotionMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetMotionMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return Common::Input::ButtonNames::Undefined;
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->GetUIName(params);
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetUIName(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetUIName(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetUIName(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetUIName(params);
|
||||
}
|
||||
#endif
|
||||
return Common::Input::ButtonNames::Invalid;
|
||||
}
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) {
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->IsStickInverted(params);
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->IsStickInverted(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->IsStickInverted(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->IsStickInverted(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->IsStickInverted(params);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsController(const Common::ParamPackage& params) {
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void BeginConfiguration() {
|
||||
keyboard->BeginConfiguration();
|
||||
mouse->BeginConfiguration();
|
||||
gcadapter->BeginConfiguration();
|
||||
udp_client->BeginConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
sdl->BeginConfiguration();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EndConfiguration() {
|
||||
keyboard->EndConfiguration();
|
||||
mouse->EndConfiguration();
|
||||
gcadapter->EndConfiguration();
|
||||
udp_client->EndConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
sdl->EndConfiguration();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RegisterInput(const MappingData& data) {
|
||||
mapping_factory->RegisterInput(data);
|
||||
}
|
||||
|
||||
std::shared_ptr<MappingFactory> mapping_factory;
|
||||
|
||||
std::shared_ptr<Keyboard> keyboard;
|
||||
std::shared_ptr<Mouse> mouse;
|
||||
std::shared_ptr<GCAdapter> gcadapter;
|
||||
std::shared_ptr<TouchScreen> touch_screen;
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||
std::shared_ptr<Camera> camera;
|
||||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
|
||||
std::shared_ptr<InputFactory> keyboard_factory;
|
||||
std::shared_ptr<InputFactory> mouse_factory;
|
||||
std::shared_ptr<InputFactory> gcadapter_input_factory;
|
||||
std::shared_ptr<InputFactory> touch_screen_factory;
|
||||
std::shared_ptr<InputFactory> udp_client_input_factory;
|
||||
std::shared_ptr<InputFactory> tas_input_factory;
|
||||
std::shared_ptr<InputFactory> camera_input_factory;
|
||||
std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
|
||||
|
||||
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
||||
std::shared_ptr<OutputFactory> mouse_output_factory;
|
||||
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
||||
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
||||
std::shared_ptr<OutputFactory> tas_output_factory;
|
||||
std::shared_ptr<OutputFactory> camera_output_factory;
|
||||
std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
std::shared_ptr<SDLDriver> sdl;
|
||||
std::shared_ptr<InputFactory> sdl_input_factory;
|
||||
std::shared_ptr<OutputFactory> sdl_output_factory;
|
||||
#endif
|
||||
};
|
||||
|
||||
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
|
||||
|
||||
InputSubsystem::~InputSubsystem() = default;
|
||||
|
||||
void InputSubsystem::Initialize() {
|
||||
impl->Initialize();
|
||||
}
|
||||
|
||||
void InputSubsystem::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
Keyboard* InputSubsystem::GetKeyboard() {
|
||||
return impl->keyboard.get();
|
||||
}
|
||||
|
||||
const Keyboard* InputSubsystem::GetKeyboard() const {
|
||||
return impl->keyboard.get();
|
||||
}
|
||||
|
||||
Mouse* InputSubsystem::GetMouse() {
|
||||
return impl->mouse.get();
|
||||
}
|
||||
|
||||
const Mouse* InputSubsystem::GetMouse() const {
|
||||
return impl->mouse.get();
|
||||
}
|
||||
|
||||
TouchScreen* InputSubsystem::GetTouchScreen() {
|
||||
return impl->touch_screen.get();
|
||||
}
|
||||
|
||||
const TouchScreen* InputSubsystem::GetTouchScreen() const {
|
||||
return impl->touch_screen.get();
|
||||
}
|
||||
|
||||
TasInput::Tas* InputSubsystem::GetTas() {
|
||||
return impl->tas_input.get();
|
||||
}
|
||||
|
||||
const TasInput::Tas* InputSubsystem::GetTas() const {
|
||||
return impl->tas_input.get();
|
||||
}
|
||||
|
||||
Camera* InputSubsystem::GetCamera() {
|
||||
return impl->camera.get();
|
||||
}
|
||||
|
||||
const Camera* InputSubsystem::GetCamera() const {
|
||||
return impl->camera.get();
|
||||
}
|
||||
|
||||
VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
|
||||
return impl->virtual_amiibo.get();
|
||||
}
|
||||
|
||||
const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
|
||||
return impl->virtual_amiibo.get();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
||||
return impl->GetInputDevices();
|
||||
}
|
||||
|
||||
AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetAnalogMappingForDevice(device);
|
||||
}
|
||||
|
||||
ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetButtonMappingForDevice(device);
|
||||
}
|
||||
|
||||
MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetMotionMappingForDevice(device);
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
|
||||
return impl->GetButtonName(params);
|
||||
}
|
||||
|
||||
bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
|
||||
return impl->IsController(params);
|
||||
}
|
||||
|
||||
bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
|
||||
if (params.Has("axis_x") && params.Has("axis_y")) {
|
||||
return impl->IsStickInverted(params);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
impl->udp_client.get()->ReloadSockets();
|
||||
}
|
||||
|
||||
void InputSubsystem::BeginMapping(Polling::InputType type) {
|
||||
impl->BeginConfiguration();
|
||||
impl->mapping_factory->BeginMapping(type);
|
||||
}
|
||||
|
||||
Common::ParamPackage InputSubsystem::GetNextInput() const {
|
||||
return impl->mapping_factory->GetNextInput();
|
||||
}
|
||||
|
||||
void InputSubsystem::StopMapping() const {
|
||||
impl->EndConfiguration();
|
||||
impl->mapping_factory->StopMapping();
|
||||
}
|
||||
|
||||
std::string GenerateKeyboardParam(int key_code) {
|
||||
Common::ParamPackage param;
|
||||
param.Set("engine", "keyboard");
|
||||
param.Set("code", key_code);
|
||||
param.Set("toggle", false);
|
||||
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
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <memory>
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/drivers/camera.h"
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
#include "input_common/helpers/stick_from_buttons.h"
|
||||
#include "input_common/helpers/touch_from_buttons.h"
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/input_mapping.h"
|
||||
#include "input_common/input_poller.h"
|
||||
#include "input_common/main.h"
|
||||
#ifdef HAVE_SDL2
|
||||
#include "input_common/drivers/sdl_driver.h"
|
||||
#endif
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
struct InputSubsystem::Impl {
|
||||
void Initialize() {
|
||||
mapping_factory = std::make_shared<MappingFactory>();
|
||||
MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
|
||||
|
||||
keyboard = std::make_shared<Keyboard>("keyboard");
|
||||
keyboard->SetMappingCallback(mapping_callback);
|
||||
keyboard_factory = std::make_shared<InputFactory>(keyboard);
|
||||
keyboard_output_factory = std::make_shared<OutputFactory>(keyboard);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName(),
|
||||
keyboard_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName(),
|
||||
keyboard_output_factory);
|
||||
|
||||
mouse = std::make_shared<Mouse>("mouse");
|
||||
mouse->SetMappingCallback(mapping_callback);
|
||||
mouse_factory = std::make_shared<InputFactory>(mouse);
|
||||
mouse_output_factory = std::make_shared<OutputFactory>(mouse);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(mouse->GetEngineName(),
|
||||
mouse_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName(),
|
||||
mouse_output_factory);
|
||||
|
||||
touch_screen = std::make_shared<TouchScreen>("touch");
|
||||
touch_screen_factory = std::make_shared<InputFactory>(touch_screen);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName(),
|
||||
touch_screen_factory);
|
||||
|
||||
gcadapter = std::make_shared<GCAdapter>("gcpad");
|
||||
gcadapter->SetMappingCallback(mapping_callback);
|
||||
gcadapter_input_factory = std::make_shared<InputFactory>(gcadapter);
|
||||
gcadapter_output_factory = std::make_shared<OutputFactory>(gcadapter);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName(),
|
||||
gcadapter_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName(),
|
||||
gcadapter_output_factory);
|
||||
|
||||
udp_client = std::make_shared<CemuhookUDP::UDPClient>("cemuhookudp");
|
||||
udp_client->SetMappingCallback(mapping_callback);
|
||||
udp_client_input_factory = std::make_shared<InputFactory>(udp_client);
|
||||
udp_client_output_factory = std::make_shared<OutputFactory>(udp_client);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName(),
|
||||
udp_client_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName(),
|
||||
udp_client_output_factory);
|
||||
|
||||
tas_input = std::make_shared<TasInput::Tas>("tas");
|
||||
tas_input->SetMappingCallback(mapping_callback);
|
||||
tas_input_factory = std::make_shared<InputFactory>(tas_input);
|
||||
tas_output_factory = std::make_shared<OutputFactory>(tas_input);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName(),
|
||||
tas_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
|
||||
tas_output_factory);
|
||||
|
||||
camera = std::make_shared<Camera>("camera");
|
||||
camera->SetMappingCallback(mapping_callback);
|
||||
camera_input_factory = std::make_shared<InputFactory>(camera);
|
||||
camera_output_factory = std::make_shared<OutputFactory>(camera);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
|
||||
camera_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
|
||||
camera_output_factory);
|
||||
|
||||
virtual_amiibo = std::make_shared<VirtualAmiibo>("virtual_amiibo");
|
||||
virtual_amiibo->SetMappingCallback(mapping_callback);
|
||||
virtual_amiibo_input_factory = std::make_shared<InputFactory>(virtual_amiibo);
|
||||
virtual_amiibo_output_factory = std::make_shared<OutputFactory>(virtual_amiibo);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(virtual_amiibo->GetEngineName(),
|
||||
virtual_amiibo_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(virtual_amiibo->GetEngineName(),
|
||||
virtual_amiibo_output_factory);
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
sdl = std::make_shared<SDLDriver>("sdl");
|
||||
sdl->SetMappingCallback(mapping_callback);
|
||||
sdl_input_factory = std::make_shared<InputFactory>(sdl);
|
||||
sdl_output_factory = std::make_shared<OutputFactory>(sdl);
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(sdl->GetEngineName(),
|
||||
sdl_input_factory);
|
||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
|
||||
sdl_output_factory);
|
||||
#endif
|
||||
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(
|
||||
"touch_from_button", std::make_shared<TouchFromButton>());
|
||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(
|
||||
"analog_from_button", std::make_shared<StickFromButton>());
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(keyboard->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(keyboard->GetEngineName());
|
||||
keyboard.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(mouse->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(mouse->GetEngineName());
|
||||
mouse.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(touch_screen->GetEngineName());
|
||||
touch_screen.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(gcadapter->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(gcadapter->GetEngineName());
|
||||
gcadapter.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(udp_client->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(udp_client->GetEngineName());
|
||||
udp_client.reset();
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(tas_input->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName());
|
||||
tas_input.reset();
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
|
||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
|
||||
sdl.reset();
|
||||
#endif
|
||||
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
|
||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>("analog_from_button");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices = {
|
||||
Common::ParamPackage{{"display", "Any"}, {"engine", "any"}},
|
||||
};
|
||||
|
||||
auto keyboard_devices = keyboard->GetInputDevices();
|
||||
devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
|
||||
auto mouse_devices = mouse->GetInputDevices();
|
||||
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
auto udp_devices = udp_client->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
#ifdef HAVE_SDL2
|
||||
auto sdl_devices = sdl->GetInputDevices();
|
||||
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
||||
#endif
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MotionMapping GetMotionMappingForDevice(
|
||||
const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return {};
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetMotionMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetMotionMappingForDevice(params);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames GetButtonName(const Common::ParamPackage& params) const {
|
||||
if (!params.Has("engine") || params.Get("engine", "") == "any") {
|
||||
return Common::Input::ButtonNames::Undefined;
|
||||
}
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->GetUIName(params);
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->GetUIName(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->GetUIName(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->GetUIName(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->GetUIName(params);
|
||||
}
|
||||
#endif
|
||||
return Common::Input::ButtonNames::Invalid;
|
||||
}
|
||||
|
||||
bool IsStickInverted(const Common::ParamPackage& params) {
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return mouse->IsStickInverted(params);
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter->IsStickInverted(params);
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return udp_client->IsStickInverted(params);
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return tas_input->IsStickInverted(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return sdl->IsStickInverted(params);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsController(const Common::ParamPackage& params) {
|
||||
const std::string engine = params.Get("engine", "");
|
||||
if (engine == mouse->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
if (engine == udp_client->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
if (engine == tas_input->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (engine == sdl->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void BeginConfiguration() {
|
||||
keyboard->BeginConfiguration();
|
||||
mouse->BeginConfiguration();
|
||||
gcadapter->BeginConfiguration();
|
||||
udp_client->BeginConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
sdl->BeginConfiguration();
|
||||
#endif
|
||||
}
|
||||
|
||||
void EndConfiguration() {
|
||||
keyboard->EndConfiguration();
|
||||
mouse->EndConfiguration();
|
||||
gcadapter->EndConfiguration();
|
||||
udp_client->EndConfiguration();
|
||||
#ifdef HAVE_SDL2
|
||||
sdl->EndConfiguration();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RegisterInput(const MappingData& data) {
|
||||
mapping_factory->RegisterInput(data);
|
||||
}
|
||||
|
||||
std::shared_ptr<MappingFactory> mapping_factory;
|
||||
|
||||
std::shared_ptr<Keyboard> keyboard;
|
||||
std::shared_ptr<Mouse> mouse;
|
||||
std::shared_ptr<GCAdapter> gcadapter;
|
||||
std::shared_ptr<TouchScreen> touch_screen;
|
||||
std::shared_ptr<TasInput::Tas> tas_input;
|
||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||
std::shared_ptr<Camera> camera;
|
||||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
|
||||
std::shared_ptr<InputFactory> keyboard_factory;
|
||||
std::shared_ptr<InputFactory> mouse_factory;
|
||||
std::shared_ptr<InputFactory> gcadapter_input_factory;
|
||||
std::shared_ptr<InputFactory> touch_screen_factory;
|
||||
std::shared_ptr<InputFactory> udp_client_input_factory;
|
||||
std::shared_ptr<InputFactory> tas_input_factory;
|
||||
std::shared_ptr<InputFactory> camera_input_factory;
|
||||
std::shared_ptr<InputFactory> virtual_amiibo_input_factory;
|
||||
|
||||
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
||||
std::shared_ptr<OutputFactory> mouse_output_factory;
|
||||
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
||||
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
||||
std::shared_ptr<OutputFactory> tas_output_factory;
|
||||
std::shared_ptr<OutputFactory> camera_output_factory;
|
||||
std::shared_ptr<OutputFactory> virtual_amiibo_output_factory;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
std::shared_ptr<SDLDriver> sdl;
|
||||
std::shared_ptr<InputFactory> sdl_input_factory;
|
||||
std::shared_ptr<OutputFactory> sdl_output_factory;
|
||||
#endif
|
||||
};
|
||||
|
||||
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
|
||||
|
||||
InputSubsystem::~InputSubsystem() = default;
|
||||
|
||||
void InputSubsystem::Initialize() {
|
||||
impl->Initialize();
|
||||
}
|
||||
|
||||
void InputSubsystem::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
Keyboard* InputSubsystem::GetKeyboard() {
|
||||
return impl->keyboard.get();
|
||||
}
|
||||
|
||||
const Keyboard* InputSubsystem::GetKeyboard() const {
|
||||
return impl->keyboard.get();
|
||||
}
|
||||
|
||||
Mouse* InputSubsystem::GetMouse() {
|
||||
return impl->mouse.get();
|
||||
}
|
||||
|
||||
const Mouse* InputSubsystem::GetMouse() const {
|
||||
return impl->mouse.get();
|
||||
}
|
||||
|
||||
TouchScreen* InputSubsystem::GetTouchScreen() {
|
||||
return impl->touch_screen.get();
|
||||
}
|
||||
|
||||
const TouchScreen* InputSubsystem::GetTouchScreen() const {
|
||||
return impl->touch_screen.get();
|
||||
}
|
||||
|
||||
TasInput::Tas* InputSubsystem::GetTas() {
|
||||
return impl->tas_input.get();
|
||||
}
|
||||
|
||||
const TasInput::Tas* InputSubsystem::GetTas() const {
|
||||
return impl->tas_input.get();
|
||||
}
|
||||
|
||||
Camera* InputSubsystem::GetCamera() {
|
||||
return impl->camera.get();
|
||||
}
|
||||
|
||||
const Camera* InputSubsystem::GetCamera() const {
|
||||
return impl->camera.get();
|
||||
}
|
||||
|
||||
VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() {
|
||||
return impl->virtual_amiibo.get();
|
||||
}
|
||||
|
||||
const VirtualAmiibo* InputSubsystem::GetVirtualAmiibo() const {
|
||||
return impl->virtual_amiibo.get();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
||||
return impl->GetInputDevices();
|
||||
}
|
||||
|
||||
AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetAnalogMappingForDevice(device);
|
||||
}
|
||||
|
||||
ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetButtonMappingForDevice(device);
|
||||
}
|
||||
|
||||
MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetMotionMappingForDevice(device);
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
|
||||
return impl->GetButtonName(params);
|
||||
}
|
||||
|
||||
bool InputSubsystem::IsController(const Common::ParamPackage& params) const {
|
||||
return impl->IsController(params);
|
||||
}
|
||||
|
||||
bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const {
|
||||
if (params.Has("axis_x") && params.Has("axis_y")) {
|
||||
return impl->IsStickInverted(params);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
impl->udp_client.get()->ReloadSockets();
|
||||
}
|
||||
|
||||
void InputSubsystem::BeginMapping(Polling::InputType type) {
|
||||
impl->BeginConfiguration();
|
||||
impl->mapping_factory->BeginMapping(type);
|
||||
}
|
||||
|
||||
Common::ParamPackage InputSubsystem::GetNextInput() const {
|
||||
return impl->mapping_factory->GetNextInput();
|
||||
}
|
||||
|
||||
void InputSubsystem::StopMapping() const {
|
||||
impl->EndConfiguration();
|
||||
impl->mapping_factory->StopMapping();
|
||||
}
|
||||
|
||||
std::string GenerateKeyboardParam(int key_code) {
|
||||
Common::ParamPackage param;
|
||||
param.Set("engine", "keyboard");
|
||||
param.Set("code", key_code);
|
||||
param.Set("toggle", false);
|
||||
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
|
||||
|
@@ -1,161 +1,161 @@
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
class ParamPackage;
|
||||
}
|
||||
|
||||
namespace Common::Input {
|
||||
enum class ButtonNames;
|
||||
}
|
||||
|
||||
namespace Settings::NativeAnalog {
|
||||
enum Values : int;
|
||||
}
|
||||
|
||||
namespace Settings::NativeButton {
|
||||
enum Values : int;
|
||||
}
|
||||
|
||||
namespace Settings::NativeMotion {
|
||||
enum Values : int;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class Camera;
|
||||
class Keyboard;
|
||||
class Mouse;
|
||||
class TouchScreen;
|
||||
class VirtualAmiibo;
|
||||
struct MappingData;
|
||||
} // namespace InputCommon
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
class Tas;
|
||||
} // namespace InputCommon::TasInput
|
||||
|
||||
namespace InputCommon {
|
||||
namespace Polling {
|
||||
/// Type of input desired for mapping purposes
|
||||
enum class InputType { None, Button, Stick, Motion, Touch };
|
||||
} // namespace Polling
|
||||
|
||||
/**
|
||||
* Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
|
||||
* mapping for the device.
|
||||
*/
|
||||
using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
|
||||
using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
|
||||
using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
|
||||
|
||||
class InputSubsystem {
|
||||
public:
|
||||
explicit InputSubsystem();
|
||||
~InputSubsystem();
|
||||
|
||||
InputSubsystem(const InputSubsystem&) = delete;
|
||||
InputSubsystem& operator=(const InputSubsystem&) = delete;
|
||||
|
||||
InputSubsystem(InputSubsystem&&) = delete;
|
||||
InputSubsystem& operator=(InputSubsystem&&) = delete;
|
||||
|
||||
/// Initializes and registers all built-in input device factories.
|
||||
void Initialize();
|
||||
|
||||
/// Unregisters all built-in input device factories and shuts them down.
|
||||
void Shutdown();
|
||||
|
||||
/// Retrieves the underlying keyboard device.
|
||||
[[nodiscard]] Keyboard* GetKeyboard();
|
||||
|
||||
/// Retrieves the underlying keyboard device.
|
||||
[[nodiscard]] const Keyboard* GetKeyboard() const;
|
||||
|
||||
/// Retrieves the underlying mouse device.
|
||||
[[nodiscard]] Mouse* GetMouse();
|
||||
|
||||
/// Retrieves the underlying mouse device.
|
||||
[[nodiscard]] const Mouse* GetMouse() const;
|
||||
|
||||
/// Retrieves the underlying touch screen device.
|
||||
[[nodiscard]] TouchScreen* GetTouchScreen();
|
||||
|
||||
/// Retrieves the underlying touch screen device.
|
||||
[[nodiscard]] const TouchScreen* GetTouchScreen() const;
|
||||
|
||||
/// Retrieves the underlying tas input device.
|
||||
[[nodiscard]] TasInput::Tas* GetTas();
|
||||
|
||||
/// Retrieves the underlying tas input device.
|
||||
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
||||
|
||||
/// Retrieves the underlying camera input device.
|
||||
[[nodiscard]] Camera* GetCamera();
|
||||
|
||||
/// Retrieves the underlying camera input device.
|
||||
[[nodiscard]] const Camera* GetCamera() const;
|
||||
|
||||
/// Retrieves the underlying virtual amiibo input device.
|
||||
[[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();
|
||||
|
||||
/// Retrieves the underlying virtual amiibo input device.
|
||||
[[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
|
||||
|
||||
/**
|
||||
* Returns all available input devices that this Factory can create a new device with.
|
||||
* Each returned ParamPackage should have a `display` field used for display, a `engine` field
|
||||
* for backends to determine if this backend is meant to service the request and any other
|
||||
* information needed to identify this in the backend later.
|
||||
*/
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
|
||||
|
||||
/// Retrieves the analog mappings for the given device.
|
||||
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Retrieves the button mappings for the given device.
|
||||
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Retrieves the motion mappings for the given device.
|
||||
[[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Returns an enum contaning the name to be displayed from the input engine.
|
||||
[[nodiscard]] Common::Input::ButtonNames GetButtonName(
|
||||
const Common::ParamPackage& params) const;
|
||||
|
||||
/// Returns true if device is a controller.
|
||||
[[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
|
||||
|
||||
/// Returns true if axis of a stick aren't mapped in the correct direction
|
||||
[[nodiscard]] bool IsStickInverted(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Reloads the input devices.
|
||||
void ReloadInputDevices();
|
||||
|
||||
/// Start polling from all backends for a desired input type.
|
||||
void BeginMapping(Polling::InputType type);
|
||||
|
||||
/// Returns an input event with mapping information.
|
||||
[[nodiscard]] Common::ParamPackage GetNextInput() const;
|
||||
|
||||
/// Stop polling from all backends.
|
||||
void StopMapping() const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
/// 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
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
class ParamPackage;
|
||||
}
|
||||
|
||||
namespace Common::Input {
|
||||
enum class ButtonNames;
|
||||
}
|
||||
|
||||
namespace Settings::NativeAnalog {
|
||||
enum Values : int;
|
||||
}
|
||||
|
||||
namespace Settings::NativeButton {
|
||||
enum Values : int;
|
||||
}
|
||||
|
||||
namespace Settings::NativeMotion {
|
||||
enum Values : int;
|
||||
}
|
||||
|
||||
namespace InputCommon {
|
||||
class Camera;
|
||||
class Keyboard;
|
||||
class Mouse;
|
||||
class TouchScreen;
|
||||
class VirtualAmiibo;
|
||||
struct MappingData;
|
||||
} // namespace InputCommon
|
||||
|
||||
namespace InputCommon::TasInput {
|
||||
class Tas;
|
||||
} // namespace InputCommon::TasInput
|
||||
|
||||
namespace InputCommon {
|
||||
namespace Polling {
|
||||
/// Type of input desired for mapping purposes
|
||||
enum class InputType { None, Button, Stick, Motion, Touch };
|
||||
} // namespace Polling
|
||||
|
||||
/**
|
||||
* Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
|
||||
* mapping for the device.
|
||||
*/
|
||||
using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
|
||||
using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
|
||||
using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
|
||||
|
||||
class InputSubsystem {
|
||||
public:
|
||||
explicit InputSubsystem();
|
||||
~InputSubsystem();
|
||||
|
||||
InputSubsystem(const InputSubsystem&) = delete;
|
||||
InputSubsystem& operator=(const InputSubsystem&) = delete;
|
||||
|
||||
InputSubsystem(InputSubsystem&&) = delete;
|
||||
InputSubsystem& operator=(InputSubsystem&&) = delete;
|
||||
|
||||
/// Initializes and registers all built-in input device factories.
|
||||
void Initialize();
|
||||
|
||||
/// Unregisters all built-in input device factories and shuts them down.
|
||||
void Shutdown();
|
||||
|
||||
/// Retrieves the underlying keyboard device.
|
||||
[[nodiscard]] Keyboard* GetKeyboard();
|
||||
|
||||
/// Retrieves the underlying keyboard device.
|
||||
[[nodiscard]] const Keyboard* GetKeyboard() const;
|
||||
|
||||
/// Retrieves the underlying mouse device.
|
||||
[[nodiscard]] Mouse* GetMouse();
|
||||
|
||||
/// Retrieves the underlying mouse device.
|
||||
[[nodiscard]] const Mouse* GetMouse() const;
|
||||
|
||||
/// Retrieves the underlying touch screen device.
|
||||
[[nodiscard]] TouchScreen* GetTouchScreen();
|
||||
|
||||
/// Retrieves the underlying touch screen device.
|
||||
[[nodiscard]] const TouchScreen* GetTouchScreen() const;
|
||||
|
||||
/// Retrieves the underlying tas input device.
|
||||
[[nodiscard]] TasInput::Tas* GetTas();
|
||||
|
||||
/// Retrieves the underlying tas input device.
|
||||
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
||||
|
||||
/// Retrieves the underlying camera input device.
|
||||
[[nodiscard]] Camera* GetCamera();
|
||||
|
||||
/// Retrieves the underlying camera input device.
|
||||
[[nodiscard]] const Camera* GetCamera() const;
|
||||
|
||||
/// Retrieves the underlying virtual amiibo input device.
|
||||
[[nodiscard]] VirtualAmiibo* GetVirtualAmiibo();
|
||||
|
||||
/// Retrieves the underlying virtual amiibo input device.
|
||||
[[nodiscard]] const VirtualAmiibo* GetVirtualAmiibo() const;
|
||||
|
||||
/**
|
||||
* Returns all available input devices that this Factory can create a new device with.
|
||||
* Each returned ParamPackage should have a `display` field used for display, a `engine` field
|
||||
* for backends to determine if this backend is meant to service the request and any other
|
||||
* information needed to identify this in the backend later.
|
||||
*/
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
|
||||
|
||||
/// Retrieves the analog mappings for the given device.
|
||||
[[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Retrieves the button mappings for the given device.
|
||||
[[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Retrieves the motion mappings for the given device.
|
||||
[[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Returns an enum contaning the name to be displayed from the input engine.
|
||||
[[nodiscard]] Common::Input::ButtonNames GetButtonName(
|
||||
const Common::ParamPackage& params) const;
|
||||
|
||||
/// Returns true if device is a controller.
|
||||
[[nodiscard]] bool IsController(const Common::ParamPackage& params) const;
|
||||
|
||||
/// Returns true if axis of a stick aren't mapped in the correct direction
|
||||
[[nodiscard]] bool IsStickInverted(const Common::ParamPackage& device) const;
|
||||
|
||||
/// Reloads the input devices.
|
||||
void ReloadInputDevices();
|
||||
|
||||
/// Start polling from all backends for a desired input type.
|
||||
void BeginMapping(Polling::InputType type);
|
||||
|
||||
/// Returns an input event with mapping information.
|
||||
[[nodiscard]] Common::ParamPackage GetNextInput() const;
|
||||
|
||||
/// Stop polling from all backends.
|
||||
void StopMapping() const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
/// 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
|
||||
|
Reference in New Issue
Block a user