another try
This commit is contained in:
@@ -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
|
||||
|
Reference in New Issue
Block a user