It builds
This commit is contained in:
@ -358,18 +358,18 @@ int main(int argc, char** argv) {
|
||||
// Register frontend applets
|
||||
Frontend::RegisterDefaultApplets();
|
||||
|
||||
EmuWindow_SDL2::InitializeSDL2();
|
||||
InputCommon::InputSubsystem input_subsystem{};
|
||||
|
||||
const auto create_emu_window = [](bool fullscreen,
|
||||
const auto create_emu_window = [&](bool fullscreen,
|
||||
bool is_secondary) -> std::unique_ptr<EmuWindow_SDL2> {
|
||||
switch (Settings::values.graphics_api.GetValue()) {
|
||||
case Settings::GraphicsAPI::OpenGL:
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(fullscreen, is_secondary);
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen, is_secondary);
|
||||
case Settings::GraphicsAPI::Software:
|
||||
return std::make_unique<EmuWindow_SDL2_SW>(fullscreen, is_secondary);
|
||||
return std::make_unique<EmuWindow_SDL2_SW>(&input_subsystem, fullscreen, is_secondary);
|
||||
}
|
||||
LOG_ERROR(Frontend, "Invalid Graphics API, using OpenGL");
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(fullscreen, is_secondary);
|
||||
return std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen, is_secondary);
|
||||
};
|
||||
|
||||
const auto emu_window{create_emu_window(fullscreen, false)};
|
||||
@ -496,7 +496,6 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
Network::Shutdown();
|
||||
InputCommon::Shutdown();
|
||||
|
||||
system.Shutdown();
|
||||
|
||||
|
@ -97,7 +97,7 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<Type, range
|
||||
|
||||
void Config::ReadValues() {
|
||||
// Controls
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
/*for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.current_input_profile.buttons[i] =
|
||||
sdl2_config->GetString("Controls", Settings::NativeButton::mapping[i], default_param);
|
||||
@ -124,7 +124,7 @@ void Config::ReadValues() {
|
||||
"Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_ADDR);
|
||||
Settings::values.current_input_profile.udp_input_port =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Controls", "udp_input_port",
|
||||
InputCommon::CemuhookUDP::DEFAULT_PORT));
|
||||
InputCommon::CemuhookUDP::DEFAULT_PORT));*/
|
||||
|
||||
// Core
|
||||
ReadSetting("Core", Settings::values.use_cpu_jit);
|
||||
|
@ -2,131 +2,38 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include "citra/emu_window/emu_window_sdl2.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/core.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "network/network.h"
|
||||
|
||||
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
||||
InputCommon::GetMotionEmu()->Tilt(x, y);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||
if (button == SDL_BUTTON_LEFT) {
|
||||
if (state == SDL_PRESSED) {
|
||||
TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
|
||||
} else {
|
||||
TouchReleased();
|
||||
}
|
||||
} else if (button == SDL_BUTTON_RIGHT) {
|
||||
if (state == SDL_PRESSED) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(x, y);
|
||||
} else {
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
|
||||
int w, h;
|
||||
SDL_GetWindowSize(render_window, &w, &h);
|
||||
|
||||
touch_x *= w;
|
||||
touch_y *= h;
|
||||
|
||||
return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
|
||||
static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerDown(float x, float y) {
|
||||
// TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
|
||||
// This isn't critical because the best we can do when we have that is to average them, like the
|
||||
// 3DS does
|
||||
|
||||
const auto [px, py] = TouchToPixelPos(x, y);
|
||||
TouchPressed(px, py);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
|
||||
const auto [px, py] = TouchToPixelPos(x, y);
|
||||
TouchMoved(px, py);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
TouchReleased();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
if (state == SDL_PRESSED) {
|
||||
InputCommon::GetKeyboard()->PressKey(key);
|
||||
} else if (state == SDL_RELEASED) {
|
||||
InputCommon::GetKeyboard()->ReleaseKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2::IsOpen() const {
|
||||
return is_open;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::RequestClose() {
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnResize() {
|
||||
int width, height;
|
||||
SDL_GL_GetDrawableSize(render_window, &width, &height);
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::Fullscreen() {
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
|
||||
|
||||
// Try a different fullscreening method
|
||||
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
|
||||
|
||||
// Fallback algorithm: Maximise window.
|
||||
// Works on all systems (unless something is seriously wrong), so no fallback for this one.
|
||||
LOG_INFO(Frontend, "Falling back on a maximised window...");
|
||||
SDL_MaximizeWindow(render_window);
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(bool is_secondary) : EmuWindow(is_secondary) {}
|
||||
|
||||
EmuWindow_SDL2::~EmuWindow_SDL2() {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::InitializeSDL2() {
|
||||
EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, bool is_secondary)
|
||||
: EmuWindow(is_secondary), input_subsystem{input_subsystem_} {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
|
||||
LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}! Exiting...", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
InputCommon::Init();
|
||||
input_subsystem->Initialize();
|
||||
Network::Init();
|
||||
|
||||
SDL_SetMainReady();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2::~EmuWindow_SDL2() {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::PollEvents() {
|
||||
SDL_Event event;
|
||||
std::vector<SDL_Event> other_window_events;
|
||||
@ -170,10 +77,12 @@ void EmuWindow_SDL2::PollEvents() {
|
||||
}
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y);
|
||||
OnFingerDown(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchId));
|
||||
break;
|
||||
case SDL_FINGERMOTION:
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y);
|
||||
OnFingerMotion(event.tfinger.x, event.tfinger.y,
|
||||
static_cast<std::size_t>(event.tfinger.touchId));
|
||||
break;
|
||||
case SDL_FINGERUP:
|
||||
OnFingerUp();
|
||||
@ -195,6 +104,106 @@ void EmuWindow_SDL2::PollEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
|
||||
const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
|
||||
input_subsystem->GetMouse()->Move(x, y, 0, 0);
|
||||
input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
|
||||
input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
|
||||
}
|
||||
|
||||
InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
|
||||
switch (button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
return InputCommon::MouseButton::Left;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
return InputCommon::MouseButton::Right;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
return InputCommon::MouseButton::Wheel;
|
||||
case SDL_BUTTON_X1:
|
||||
return InputCommon::MouseButton::Backward;
|
||||
case SDL_BUTTON_X2:
|
||||
return InputCommon::MouseButton::Forward;
|
||||
default:
|
||||
return InputCommon::MouseButton::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
|
||||
int w, h;
|
||||
SDL_GetWindowSize(render_window, &w, &h);
|
||||
const float fx = static_cast<float>(touch_x) / w;
|
||||
const float fy = static_cast<float>(touch_y) / h;
|
||||
|
||||
return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
|
||||
const auto mouse_button = SDLButtonToMouseButton(button);
|
||||
if (state == SDL_PRESSED) {
|
||||
const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
|
||||
input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
|
||||
input_subsystem->GetMouse()->PressMouseButton(mouse_button);
|
||||
input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, mouse_button);
|
||||
} else {
|
||||
input_subsystem->GetMouse()->ReleaseButton(mouse_button);
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
if (state == SDL_PRESSED) {
|
||||
input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
|
||||
} else if (state == SDL_RELEASED) {
|
||||
input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key));
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2::IsOpen() const {
|
||||
return is_open;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::RequestClose() {
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnResize() {
|
||||
int width, height;
|
||||
SDL_GL_GetDrawableSize(render_window, &width, &height);
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::Fullscreen() {
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
|
||||
|
||||
// Try a different fullscreening method
|
||||
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
|
||||
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
|
||||
|
||||
// Fallback algorithm: Maximise window.
|
||||
// Works on all systems (unless something is seriously wrong), so no fallback for this one.
|
||||
LOG_INFO(Frontend, "Falling back on a maximised window...");
|
||||
SDL_MaximizeWindow(render_window);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
|
||||
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
@ -10,14 +10,16 @@
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
enum class MouseButton;
|
||||
} // namespace InputCommon
|
||||
|
||||
class EmuWindow_SDL2 : public Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(bool is_secondary);
|
||||
explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, bool is_secondary);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
/// Initializes SDL2
|
||||
static void InitializeSDL2();
|
||||
|
||||
/// Presents the most recent frame from the video backend
|
||||
virtual void Present() {}
|
||||
|
||||
@ -37,17 +39,20 @@ protected:
|
||||
/// Called by PollEvents when the mouse moves.
|
||||
void OnMouseMotion(s32 x, s32 y);
|
||||
|
||||
/// Converts a SDL mouse button into MouseInput mouse button
|
||||
InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
|
||||
|
||||
/// Translates pixel position to float position
|
||||
std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;
|
||||
|
||||
/// Called by PollEvents when a mouse button is pressed or released
|
||||
void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
|
||||
|
||||
/// Translates pixel position (0..1) to pixel positions
|
||||
std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
|
||||
|
||||
/// Called by PollEvents when a finger starts touching the touchscreen
|
||||
void OnFingerDown(float x, float y);
|
||||
void OnFingerDown(float x, float y, std::size_t id);
|
||||
|
||||
/// Called by PollEvents when a finger moves while touching the touchscreen
|
||||
void OnFingerMotion(float x, float y);
|
||||
void OnFingerMotion(float x, float y, std::size_t id);
|
||||
|
||||
/// Called by PollEvents when a finger stops touching the touchscreen
|
||||
void OnFingerUp();
|
||||
@ -78,4 +83,7 @@ protected:
|
||||
|
||||
/// Keeps track of how often to update the title bar during gameplay
|
||||
u32 last_time = 0;
|
||||
|
||||
/// Input subsystem to use with this window.
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
};
|
||||
|
@ -42,8 +42,8 @@ private:
|
||||
SDL_GLContext context;
|
||||
};
|
||||
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{is_secondary} {
|
||||
EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{input_subsystem, is_secondary} {
|
||||
// Initialize the window
|
||||
if (Settings::values.use_gles) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
|
@ -11,7 +11,7 @@ struct SDL_Window;
|
||||
|
||||
class EmuWindow_SDL2_GL : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_GL(bool fullscreen, bool is_secondary);
|
||||
explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2_GL();
|
||||
|
||||
void Present() override;
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
class DummyContext : public Frontend::GraphicsContext {};
|
||||
|
||||
EmuWindow_SDL2_SW::EmuWindow_SDL2_SW(bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{is_secondary} {
|
||||
EmuWindow_SDL2_SW::EmuWindow_SDL2_SW(InputCommon::InputSubsystem* input_subsystem, bool fullscreen, bool is_secondary)
|
||||
: EmuWindow_SDL2{input_subsystem, is_secondary} {
|
||||
std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
|
@ -12,7 +12,7 @@ struct SDL_Surface;
|
||||
|
||||
class EmuWindow_SDL2_SW : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_SW(bool fullscreen, bool is_secondary);
|
||||
explicit EmuWindow_SDL2_SW(InputCommon::InputSubsystem* input_subsystem, bool fullscreen, bool is_secondary);
|
||||
~EmuWindow_SDL2_SW();
|
||||
|
||||
void Present() override;
|
||||
|
@ -37,7 +37,7 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
|
||||
|
||||
using namespace InputCommon::CemuhookUDP;
|
||||
job = std::make_unique<CalibrationConfigurationJob>(
|
||||
host, port,
|
||||
host, port, pad_index,
|
||||
[this](CalibrationConfigurationJob::Status status) {
|
||||
QMetaObject::invokeMethod(this, [status, this] {
|
||||
QString text;
|
||||
@ -203,6 +203,7 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() {
|
||||
udp_test_in_progress = true;
|
||||
InputCommon::CemuhookUDP::TestCommunication(
|
||||
ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()),
|
||||
static_cast<u8>(ui->udp_pad_index->currentIndex()),
|
||||
[this] {
|
||||
LOG_INFO(Frontend, "UDP input test success");
|
||||
QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true));
|
||||
|
@ -101,6 +101,7 @@ add_library(citra_common STATIC
|
||||
scope_exit.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
settings_input.h
|
||||
slot_vector.h
|
||||
serialization/atomic.h
|
||||
serialization/boost_discrete_interval.hpp
|
||||
|
@ -39,6 +39,28 @@ enum class PollingError {
|
||||
Unknown,
|
||||
};
|
||||
|
||||
// Different results that can happen from a device request
|
||||
enum class DriverResult {
|
||||
Success,
|
||||
WrongReply,
|
||||
Timeout,
|
||||
UnsupportedControllerType,
|
||||
HandleInUse,
|
||||
ErrorReadingData,
|
||||
ErrorWritingData,
|
||||
NoDeviceDetected,
|
||||
InvalidHandle,
|
||||
NotSupported,
|
||||
Disabled,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
// Hint for amplification curve to be used
|
||||
enum class VibrationAmplificationType {
|
||||
Linear,
|
||||
Exponential,
|
||||
};
|
||||
|
||||
// Analog properties for calibration
|
||||
struct AnalogProperties {
|
||||
// Anything below this value will be detected as zero
|
||||
@ -119,6 +141,15 @@ struct TouchStatus {
|
||||
int id{};
|
||||
};
|
||||
|
||||
// HD rumble data
|
||||
struct VibrationStatus {
|
||||
f32 low_amplitude{};
|
||||
f32 low_frequency{};
|
||||
f32 high_amplitude{};
|
||||
f32 high_frequency{};
|
||||
VibrationAmplificationType type;
|
||||
};
|
||||
|
||||
// List of buttons to be passed to Qt that can be translated
|
||||
enum class ButtonNames {
|
||||
Undefined,
|
||||
|
@ -101,24 +101,6 @@ void Apply() {
|
||||
Core::DSP().SetSink(values.output_type.GetValue(), values.output_device.GetValue());
|
||||
Core::DSP().EnableStretching(values.enable_audio_stretching.GetValue());
|
||||
|
||||
auto hid = Service::HID::GetModule(system);
|
||||
if (hid) {
|
||||
hid->ReloadInputDevices();
|
||||
}
|
||||
|
||||
auto apt = Service::APT::GetModule(system);
|
||||
if (apt) {
|
||||
apt->GetAppletManager()->ReloadInputDevices();
|
||||
}
|
||||
|
||||
auto sm = system.ServiceManager();
|
||||
auto ir_user = sm.GetService<Service::IR::IR_USER>("ir:USER");
|
||||
if (ir_user)
|
||||
ir_user->ReloadInputDevices();
|
||||
auto ir_rst = sm.GetService<Service::IR::IR_RST>("ir:rst");
|
||||
if (ir_rst)
|
||||
ir_rst->ReloadInputDevices();
|
||||
|
||||
auto cam = Service::CAM::GetModule(system);
|
||||
if (cam) {
|
||||
cam->ReloadCameraDevices();
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "audio_core/input_details.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "core/hle/service/cam/cam_params.h"
|
||||
|
||||
namespace Settings {
|
||||
@ -53,8 +54,8 @@ enum class StereoRenderOption : u32 {
|
||||
CardboardVR = 5
|
||||
};
|
||||
|
||||
// Which eye to render when 3d is off. 800px wide mode could be added here in the future, when
|
||||
// implemented
|
||||
// Which eye to render when 3d is off. 800px wide mode
|
||||
// could be added here in the future, when implemented.
|
||||
enum class MonoRenderOption : u32 {
|
||||
LeftEye = 0,
|
||||
RightEye = 1,
|
||||
@ -75,109 +76,6 @@ enum class TextureFilter : u32 {
|
||||
xBRZ = 5,
|
||||
};
|
||||
|
||||
namespace NativeButton {
|
||||
|
||||
enum Values {
|
||||
A,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
DUp,
|
||||
DDown,
|
||||
DLeft,
|
||||
DRight,
|
||||
L,
|
||||
R,
|
||||
Start,
|
||||
Select,
|
||||
Debug,
|
||||
Gpio14,
|
||||
|
||||
ZL,
|
||||
ZR,
|
||||
|
||||
Home,
|
||||
|
||||
NumButtons,
|
||||
};
|
||||
|
||||
constexpr int BUTTON_HID_BEGIN = A;
|
||||
constexpr int BUTTON_IR_BEGIN = ZL;
|
||||
constexpr int BUTTON_NS_BEGIN = Home;
|
||||
|
||||
constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
|
||||
constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
|
||||
constexpr int BUTTON_NS_END = NumButtons;
|
||||
|
||||
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
|
||||
constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
|
||||
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
|
||||
|
||||
constexpr std::array<const char*, NumButtons> mapping = {{
|
||||
"button_a",
|
||||
"button_b",
|
||||
"button_x",
|
||||
"button_y",
|
||||
"button_up",
|
||||
"button_down",
|
||||
"button_left",
|
||||
"button_right",
|
||||
"button_l",
|
||||
"button_r",
|
||||
"button_start",
|
||||
"button_select",
|
||||
"button_debug",
|
||||
"button_gpio14",
|
||||
"button_zl",
|
||||
"button_zr",
|
||||
"button_home",
|
||||
}};
|
||||
} // namespace NativeButton
|
||||
|
||||
namespace NativeAnalog {
|
||||
enum Values {
|
||||
CirclePad,
|
||||
CStick,
|
||||
|
||||
NumAnalogs,
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, NumAnalogs> mapping = {{
|
||||
"circle_pad",
|
||||
"c_stick",
|
||||
}};
|
||||
} // namespace NativeAnalog
|
||||
|
||||
namespace NativeMotion {
|
||||
enum Values : int {
|
||||
MotionLeft,
|
||||
MotionRight,
|
||||
|
||||
NumMotions,
|
||||
};
|
||||
|
||||
constexpr int MOTION_HID_BEGIN = MotionLeft;
|
||||
constexpr int MOTION_HID_END = NumMotions;
|
||||
constexpr int NUM_MOTIONS_HID = NumMotions;
|
||||
|
||||
constexpr std::array<const char*, NumMotions> mapping = {{
|
||||
"motionleft",
|
||||
"motionright",
|
||||
}};
|
||||
} // namespace NativeMotion
|
||||
|
||||
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
|
||||
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
|
||||
using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
|
||||
|
||||
struct PlayerInput {
|
||||
ButtonsRaw buttons;
|
||||
AnalogsRaw analogs;
|
||||
MotionsRaw motions;
|
||||
|
||||
std::string profile_name;
|
||||
};
|
||||
|
||||
/** The Setting class is a simple resource manager. It defines a label and default value alongside
|
||||
* the actual value of the setting for simpler and less-error prone use with frontend
|
||||
* configurations. Specifying a default value and label is required. A minimum and maximum range can
|
||||
@ -440,11 +338,6 @@ private:
|
||||
Type custom{}; ///< The custom setting value
|
||||
};
|
||||
|
||||
struct TouchFromButtonMap {
|
||||
std::string name;
|
||||
std::vector<std::string> buttons;
|
||||
};
|
||||
|
||||
/// A special region value indicating that citra will automatically select a region
|
||||
/// value to fit the region lockout info of the game
|
||||
static constexpr s32 REGION_VALUE_AUTO_SELECT = -1;
|
||||
@ -461,6 +354,9 @@ struct Values {
|
||||
Setting<u16> udp_input_port{26760, "udp_input_port"};
|
||||
Setting<u8> udp_pad_index{0, "udp_pad_index"};
|
||||
|
||||
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
|
||||
Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
|
||||
|
||||
// Core
|
||||
Setting<bool> use_cpu_jit{true, "use_cpu_jit"};
|
||||
SwitchableSetting<s32, true> cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"};
|
||||
|
119
src/common/settings_input.h
Normal file
119
src/common/settings_input.h
Normal file
@ -0,0 +1,119 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Settings {
|
||||
namespace NativeButton {
|
||||
enum Values : int {
|
||||
A,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
DUp,
|
||||
DDown,
|
||||
DLeft,
|
||||
DRight,
|
||||
L,
|
||||
R,
|
||||
Start,
|
||||
Select,
|
||||
Debug,
|
||||
Gpio14,
|
||||
|
||||
ZL,
|
||||
ZR,
|
||||
|
||||
Home,
|
||||
|
||||
NumButtons,
|
||||
};
|
||||
|
||||
constexpr int BUTTON_HID_BEGIN = A;
|
||||
constexpr int BUTTON_IR_BEGIN = ZL;
|
||||
constexpr int BUTTON_NS_BEGIN = Home;
|
||||
|
||||
constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN;
|
||||
constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN;
|
||||
constexpr int BUTTON_NS_END = NumButtons;
|
||||
|
||||
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
|
||||
constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
|
||||
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
|
||||
|
||||
constexpr std::array<const char*, NumButtons> mapping = {{
|
||||
"button_a",
|
||||
"button_b",
|
||||
"button_x",
|
||||
"button_y",
|
||||
"button_up",
|
||||
"button_down",
|
||||
"button_left",
|
||||
"button_right",
|
||||
"button_l",
|
||||
"button_r",
|
||||
"button_start",
|
||||
"button_select",
|
||||
"button_debug",
|
||||
"button_gpio14",
|
||||
"button_zl",
|
||||
"button_zr",
|
||||
"button_home",
|
||||
}};
|
||||
} // namespace NativeButton
|
||||
|
||||
namespace NativeAnalog {
|
||||
enum Values : int {
|
||||
CirclePad,
|
||||
CStick,
|
||||
|
||||
NumAnalogs,
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, NumAnalogs> mapping = {{
|
||||
"circle_pad",
|
||||
"c_stick",
|
||||
}};
|
||||
} // namespace NativeAnalog
|
||||
|
||||
namespace NativeMotion {
|
||||
enum Values : int {
|
||||
MotionLeft,
|
||||
MotionRight,
|
||||
|
||||
NumMotions,
|
||||
};
|
||||
|
||||
constexpr int MOTION_HID_BEGIN = MotionLeft;
|
||||
constexpr int MOTION_HID_END = NumMotions;
|
||||
constexpr int NUM_MOTIONS_HID = NumMotions;
|
||||
|
||||
constexpr std::array<const char*, NumMotions> mapping = {{
|
||||
"motionleft",
|
||||
"motionright",
|
||||
}};
|
||||
} // namespace NativeMotion
|
||||
|
||||
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
|
||||
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
|
||||
using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
|
||||
|
||||
struct PlayerInput {
|
||||
ButtonsRaw buttons;
|
||||
AnalogsRaw analogs;
|
||||
MotionsRaw motions;
|
||||
|
||||
std::string profile_name;
|
||||
};
|
||||
|
||||
struct TouchFromButtonMap {
|
||||
std::string name;
|
||||
std::vector<std::string> buttons;
|
||||
};
|
||||
|
||||
} // namespace Settings
|
@ -113,10 +113,6 @@ add_library(citra_core STATIC
|
||||
gdbstub/gdbstub.h
|
||||
gdbstub/hio.cpp
|
||||
gdbstub/hio.h
|
||||
frontend/mic.cpp
|
||||
frontend/mic.h
|
||||
frontend/scope_acquire_context.cpp
|
||||
frontend/scope_acquire_context.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hid/emulated_console.cpp
|
||||
|
@ -19,38 +19,6 @@ EmuWindow::EmuWindow(bool is_secondary_) : is_secondary{is_secondary_} {}
|
||||
|
||||
EmuWindow::~EmuWindow() = default;
|
||||
|
||||
/**
|
||||
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
|
||||
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
|
||||
* @param framebuffer_x Framebuffer x-coordinate to check
|
||||
* @param framebuffer_y Framebuffer y-coordinate to check
|
||||
* @return True if the coordinates are within the touchpad, otherwise false
|
||||
*/
|
||||
static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x,
|
||||
unsigned framebuffer_y) {
|
||||
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) {
|
||||
return (framebuffer_y >= layout.bottom_screen.top &&
|
||||
framebuffer_y < layout.bottom_screen.bottom &&
|
||||
((framebuffer_x >= layout.bottom_screen.left / 2 &&
|
||||
framebuffer_x < layout.bottom_screen.right / 2) ||
|
||||
(framebuffer_x >= (layout.bottom_screen.left / 2) + (layout.width / 2) &&
|
||||
framebuffer_x < (layout.bottom_screen.right / 2) + (layout.width / 2))));
|
||||
} else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) {
|
||||
return (framebuffer_y >= layout.bottom_screen.top &&
|
||||
framebuffer_y < layout.bottom_screen.bottom &&
|
||||
((framebuffer_x >= layout.bottom_screen.left &&
|
||||
framebuffer_x < layout.bottom_screen.right) ||
|
||||
(framebuffer_x >= layout.cardboard.bottom_screen_right_eye + (layout.width / 2) &&
|
||||
framebuffer_x < layout.cardboard.bottom_screen_right_eye +
|
||||
layout.bottom_screen.GetWidth() + (layout.width / 2))));
|
||||
} else {
|
||||
return (framebuffer_y >= layout.bottom_screen.top &&
|
||||
framebuffer_y < layout.bottom_screen.bottom &&
|
||||
framebuffer_x >= layout.bottom_screen.left &&
|
||||
framebuffer_x < layout.bottom_screen.right);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<f32, f32> EmuWindow::MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const {
|
||||
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
|
||||
const float x = static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
|
||||
|
@ -134,7 +134,7 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
motion_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
[/*this, index*/](const Common::Input::CallbackStatus& callback) {
|
||||
// SetMotion(callback, index);
|
||||
},
|
||||
});
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
#include "core/hle/applets/applet.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/apt/applet_manager.h"
|
||||
@ -1230,33 +1231,25 @@ void AppletManager::CaptureFrameBuffers() {
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::LoadInputDevices() {
|
||||
home_button = Input::CreateDevice<Input::ButtonDevice>(
|
||||
Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]);
|
||||
}
|
||||
|
||||
void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) {
|
||||
if (is_device_reload_pending.exchange(false)) {
|
||||
LoadInputDevices();
|
||||
}
|
||||
|
||||
const bool state = home_button->GetStatus();
|
||||
//const auto& controller = hid_core.GetEmulatedController();
|
||||
//const bool state = home_button->GetStatus();
|
||||
// NOTE: We technically do support loading and jumping to home menu even if it isn't
|
||||
// initially registered. However since the home menu suspend is not bug-free, we don't
|
||||
// want normal users who didn't launch the home menu accidentally pressing the home
|
||||
// button binding and freezing their game, so for now, gate it to only environments
|
||||
// where the home menu was already loaded by the user (last condition).
|
||||
if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) {
|
||||
if (/*state &&*/ !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) {
|
||||
SendNotification(Notification::HomeButtonSingle);
|
||||
}
|
||||
last_home_button_state = state;
|
||||
//last_home_button_state = state;
|
||||
|
||||
// Reschedule recurrent event
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(
|
||||
system.CoreTiming().ScheduleEvent(
|
||||
usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event);
|
||||
}
|
||||
|
||||
AppletManager::AppletManager(Core::System& system) : system(system) {
|
||||
AppletManager::AppletManager(Core::System& system) : system(system), hid_core(system.HIDCore()) {
|
||||
lock = system.Kernel().CreateMutex(false, "APT_U:Lock");
|
||||
for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) {
|
||||
auto& slot_data = applet_slots[slot];
|
||||
@ -1283,8 +1276,4 @@ AppletManager::~AppletManager() {
|
||||
HLE::Applets::Shutdown();
|
||||
}
|
||||
|
||||
void AppletManager::ReloadInputDevices() {
|
||||
is_device_reload_pending.store(true);
|
||||
}
|
||||
|
||||
} // namespace Service::APT
|
||||
|
@ -23,6 +23,10 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::HID {
|
||||
class HIDCore;
|
||||
};
|
||||
|
||||
namespace Service::APT {
|
||||
|
||||
/// Signals used by APT functions
|
||||
@ -228,8 +232,6 @@ public:
|
||||
explicit AppletManager(Core::System& system);
|
||||
~AppletManager();
|
||||
|
||||
void ReloadInputDevices();
|
||||
|
||||
/**
|
||||
* Clears any existing parameter and places a new one. This function is currently only used by
|
||||
* HLE Applets and should be likely removed in the future
|
||||
@ -428,11 +430,10 @@ private:
|
||||
AppletSlot application_close_target = AppletSlot::Error;
|
||||
|
||||
Core::TimingEventType* home_button_update_event;
|
||||
std::atomic<bool> is_device_reload_pending{true};
|
||||
std::unique_ptr<Input::ButtonDevice> home_button;
|
||||
bool last_home_button_state = false;
|
||||
|
||||
Core::System& system;
|
||||
Core::HID::HIDCore& hid_core;
|
||||
|
||||
AppletSlotData* GetAppletSlot(AppletSlot slot) {
|
||||
return &applet_slots[static_cast<std::size_t>(slot)];
|
||||
@ -454,7 +455,6 @@ private:
|
||||
|
||||
void CaptureFrameBuffers();
|
||||
|
||||
void LoadInputDevices();
|
||||
void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
|
||||
|
||||
template <class Archive>
|
||||
@ -479,10 +479,6 @@ private:
|
||||
}
|
||||
ar& applet_slots;
|
||||
ar& library_applet_closing_command;
|
||||
|
||||
if (Archive::is_loading::value) {
|
||||
LoadInputDevices();
|
||||
}
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
@ -54,7 +54,8 @@ if (ENABLE_LIBUSB)
|
||||
drivers/gc_adapter.cpp
|
||||
drivers/gc_adapter.h
|
||||
)
|
||||
target_link_libraries(input_common PRIVATE libusb::usb)
|
||||
target_include_directories(input_common PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
|
||||
target_compile_definitions(input_common PRIVATE HAVE_LIBUSB)
|
||||
endif()
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace InputCommon {
|
||||
class LibUSBContext {
|
||||
public:
|
||||
explicit LibUSBContext() {
|
||||
init_result = libusb_init(&ctx);
|
||||
init_result = libusb_init_context(&ctx, nullptr, 0);
|
||||
}
|
||||
|
||||
~LibUSBContext() {
|
||||
@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Input::VibrationError GCAdapter::SetVibration(
|
||||
Common::Input::DriverResult GCAdapter::SetVibration(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
|
||||
const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
|
||||
const auto processed_amplitude =
|
||||
@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
|
||||
pads[identifier.port].rumble_amplitude = processed_amplitude;
|
||||
|
||||
if (!rumble_enabled) {
|
||||
return Common::Input::VibrationError::Disabled;
|
||||
return Common::Input::DriverResult::Disabled;
|
||||
}
|
||||
return Common::Input::VibrationError::None;
|
||||
return Common::Input::DriverResult::Success;
|
||||
}
|
||||
|
||||
bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
|
||||
@ -421,14 +421,14 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p
|
||||
{Settings::NativeButton::B, PadButton::ButtonB},
|
||||
{Settings::NativeButton::X, PadButton::ButtonX},
|
||||
{Settings::NativeButton::Y, PadButton::ButtonY},
|
||||
{Settings::NativeButton::Plus, PadButton::ButtonStart},
|
||||
{Settings::NativeButton::Start, PadButton::ButtonStart},
|
||||
{Settings::NativeButton::Select, PadButton::ButtonSelect},
|
||||
{Settings::NativeButton::DLeft, PadButton::ButtonLeft},
|
||||
{Settings::NativeButton::DUp, PadButton::ButtonUp},
|
||||
{Settings::NativeButton::DRight, PadButton::ButtonRight},
|
||||
{Settings::NativeButton::DDown, PadButton::ButtonDown},
|
||||
{Settings::NativeButton::SL, PadButton::TriggerL},
|
||||
{Settings::NativeButton::SR, PadButton::TriggerR},
|
||||
{Settings::NativeButton::R, PadButton::TriggerZ},
|
||||
{Settings::NativeButton::L, PadButton::TriggerL},
|
||||
{Settings::NativeButton::R, PadButton::TriggerR},
|
||||
};
|
||||
if (!params.Has("port")) {
|
||||
return {};
|
||||
@ -474,13 +474,13 @@ AnalogMapping GCAdapter::GetAnalogMappingForDevice(const Common::ParamPackage& p
|
||||
left_analog_params.Set("port", params.Get("port", 0));
|
||||
left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
|
||||
left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::CirclePad, std::move(left_analog_params));
|
||||
Common::ParamPackage right_analog_params;
|
||||
right_analog_params.Set("engine", GetEngineName());
|
||||
right_analog_params.Set("port", params.Get("port", 0));
|
||||
right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
|
||||
right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
|
||||
mapping.insert_or_assign(Settings::NativeAnalog::CStick, std::move(right_analog_params));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
explicit GCAdapter(std::string input_engine_);
|
||||
~GCAdapter() override;
|
||||
|
||||
Common::Input::VibrationError SetVibration(
|
||||
Common::Input::DriverResult SetVibration(
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
@ -53,6 +53,7 @@ private:
|
||||
ButtonX = 0x0400,
|
||||
ButtonY = 0x0800,
|
||||
ButtonStart = 0x1000,
|
||||
ButtonSelect = 0x2000,
|
||||
};
|
||||
|
||||
enum class PadAxes : u8 {
|
||||
|
@ -54,6 +54,21 @@ void Mouse::UpdateThread(std::stop_token stop_token) {
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::Move(int x, int y, int, int) {
|
||||
if (button_pressed) {
|
||||
const auto mouse_move = Common::MakeVec<int>(x, y) - mouse_origin;
|
||||
const float sensitivity = /*Settings::values.mouse_panning_sensitivity.GetValue()*/50 * 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);
|
||||
|
||||
last_motion_change = {
|
||||
static_cast<float>(-mouse_move.y) / 50.0f,
|
||||
static_cast<float>(-mouse_move.x) / 50.0f,
|
||||
last_motion_change.z,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::MouseMove(f32 touch_x, f32 touch_y) {
|
||||
SetAxis(real_mouse_identifier, mouse_axis_x, touch_x);
|
||||
SetAxis(real_mouse_identifier, mouse_axis_y, touch_y);
|
||||
|
@ -101,6 +101,7 @@ private:
|
||||
Common::Vec2<int> mouse_origin;
|
||||
Common::Vec2<int> last_mouse_position;
|
||||
Common::Vec2<float> last_mouse_change;
|
||||
Common::Vec3<float> last_motion_change;
|
||||
Common::Vec2<int> wheel_position;
|
||||
bool button_pressed;
|
||||
std::jthread update_thread;
|
||||
|
@ -25,9 +25,10 @@ class Socket {
|
||||
public:
|
||||
using clock = std::chrono::system_clock;
|
||||
|
||||
explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
|
||||
explicit Socket(const std::string& host, u16 port, u8 pad_index, SocketCallback callback_)
|
||||
: callback(std::move(callback_)), timer(io_service),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()) {
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(GenerateRandomClientId()),
|
||||
pad_index(pad_index) {
|
||||
boost::system::error_code ec{};
|
||||
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
|
||||
if (ec.value() != boost::system::errc::success) {
|
||||
@ -95,17 +96,13 @@ private:
|
||||
void HandleSend(const boost::system::error_code&) {
|
||||
boost::system::error_code _ignored{};
|
||||
// Send a request for getting port info for the pad
|
||||
const Request::PortInfo port_info{4, {0, 1, 2, 3}};
|
||||
const Request::PortInfo port_info{1, {pad_index, 0, 0, 0}};
|
||||
const auto port_message = Request::Create(port_info, client_id);
|
||||
std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
|
||||
socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
|
||||
|
||||
// Send a request for getting pad data for the pad
|
||||
const Request::PadData pad_data{
|
||||
Request::RegisterFlags::AllPads,
|
||||
0,
|
||||
EMPTY_MAC_ADDRESS,
|
||||
};
|
||||
const Request::PadData pad_data{Request::RegisterFlags::PadID, pad_index, EMPTY_MAC_ADDRESS};
|
||||
const auto pad_message = Request::Create(pad_data, client_id);
|
||||
std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
|
||||
socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
|
||||
@ -118,6 +115,7 @@ private:
|
||||
udp::socket socket;
|
||||
|
||||
const u32 client_id;
|
||||
const u8 pad_index;
|
||||
|
||||
static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
|
||||
static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
|
||||
@ -161,7 +159,7 @@ void UDPClient::ReloadSockets() {
|
||||
LOG_ERROR(Input, "Duplicated UDP servers found");
|
||||
return;
|
||||
}
|
||||
StartCommunication(0, host, udp_input_port);
|
||||
StartCommunication(0, host, udp_input_port, pad_index);
|
||||
}
|
||||
|
||||
std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const {
|
||||
@ -239,7 +237,7 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
|
||||
static_cast<int>(id == 0 ? PadButton::Touch1 : PadButton::Touch2);
|
||||
|
||||
// TODO: Use custom calibration per device
|
||||
/* const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
|
||||
const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
|
||||
const u16 min_x = static_cast<u16>(touch_param.Get("min_x", 100));
|
||||
const u16 min_y = static_cast<u16>(touch_param.Get("min_y", 50));
|
||||
const u16 max_x = static_cast<u16>(touch_param.Get("max_x", 1800));
|
||||
@ -260,7 +258,7 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
|
||||
}
|
||||
SetAxis(identifier, touch_axis_x_id, 0);
|
||||
SetAxis(identifier, touch_axis_y_id, 0);
|
||||
SetButton(identifier, touch_button_id, false);*/
|
||||
SetButton(identifier, touch_button_id, false);
|
||||
}
|
||||
|
||||
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX),
|
||||
@ -288,7 +286,7 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
|
||||
SetButton(identifier, static_cast<int>(PadButton::TouchHardPress), data.touch_hard_press != 0);
|
||||
}
|
||||
|
||||
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
|
||||
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index) {
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
||||
@ -297,7 +295,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host,
|
||||
clients[client].host = host;
|
||||
clients[client].port = port;
|
||||
clients[client].active = 0;
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, callback);
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, callback);
|
||||
clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
|
||||
for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
|
||||
const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
|
||||
@ -502,7 +500,7 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
void TestCommunication(const std::string& host, u16 port, u8 pad_index,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback) {
|
||||
std::thread([=] {
|
||||
@ -512,7 +510,7 @@ void TestCommunication(const std::string& host, u16 port,
|
||||
.port_info = [](Response::PortInfo) {},
|
||||
.pad_data = [&](Response::PadData) { success_event.Set(); },
|
||||
};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
Socket socket{host, port, pad_index, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
const bool result =
|
||||
success_event.WaitUntil(std::chrono::steady_clock::now() + std::chrono::seconds(10));
|
||||
@ -527,7 +525,7 @@ void TestCommunication(const std::string& host, u16 port,
|
||||
}
|
||||
|
||||
CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
const std::string& host, u16 port, std::function<void(Status)> status_callback,
|
||||
const std::string& host, u16 port, u8 pad_index, std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=, this] {
|
||||
@ -571,7 +569,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
Socket socket{host, port, pad_index, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
socket.Stop();
|
||||
|
@ -144,7 +144,7 @@ private:
|
||||
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);
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index);
|
||||
PadIdentifier GetPadIdentifier(std::size_t pad_index) const;
|
||||
Common::UUID GetHostUUID(const std::string& host) const;
|
||||
|
||||
@ -172,7 +172,7 @@ public:
|
||||
* @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,
|
||||
explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index,
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback);
|
||||
~CalibrationConfigurationJob();
|
||||
@ -182,7 +182,7 @@ private:
|
||||
Common::Event complete_event;
|
||||
};
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
void TestCommunication(const std::string& host, u16 port, u8 pad_index,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback);
|
||||
|
||||
|
@ -11,6 +11,14 @@ namespace InputCommon {
|
||||
|
||||
class Stick final : public Common::Input::InputDevice {
|
||||
public:
|
||||
// Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS
|
||||
// do not play nicely with the theoretical maximum range.
|
||||
// Using a value one lower from the maximum emulates real stick behavior.
|
||||
static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
|
||||
static constexpr float TAU = Common::PI * 2.0f;
|
||||
// Use wider angle to ease the transition.
|
||||
static constexpr float APERTURE = TAU * 0.15f;
|
||||
|
||||
using Button = std::unique_ptr<Common::Input::InputDevice>;
|
||||
|
||||
Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_,
|
||||
|
@ -84,7 +84,7 @@ enum RegisterFlags : u8 {
|
||||
struct Version {};
|
||||
/**
|
||||
* Requests the server to send information about what controllers are plugged into the ports
|
||||
* In yuzu's case, we only have one controller, so for simplicity's sake, we can just send a
|
||||
* In citra's case, we only have one controller, so for simplicity's sake, we can just send a
|
||||
* request explicitly for the first controller port and leave it at that. In the future it would be
|
||||
* nice to make this configurable
|
||||
*/
|
||||
|
@ -100,6 +100,18 @@ public:
|
||||
// Disable configuring mode for mapping
|
||||
void EndConfiguration();
|
||||
|
||||
// Sets rumble to a controller
|
||||
virtual Common::Input::DriverResult SetVibration(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
|
||||
return Common::Input::DriverResult::NotSupported;
|
||||
}
|
||||
|
||||
// Returns true if device supports vibrations
|
||||
virtual bool IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets polling mode to a controller
|
||||
virtual Common::Input::PollingError SetPollingMode(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
|
@ -4,14 +4,6 @@
|
||||
#include <memory>
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
<<<<<<< HEAD
|
||||
#include "input_common/analog_from_button.h"
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
#endif
|
||||
#include "input_common/keyboard.h"
|
||||
== == == =
|
||||
#include "input_common/drivers/keyboard.h"
|
||||
#include "input_common/drivers/mouse.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
@ -22,7 +14,6 @@
|
||||
#include "input_common/input_engine.h"
|
||||
#include "input_common/input_mapping.h"
|
||||
#include "input_common/input_poller.h"
|
||||
>>>>>>> 6e5fec9fe (add input common changes)
|
||||
#include "input_common/main.h"
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
@ -34,50 +25,18 @@
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
<<<<<<< HEAD
|
||||
#ifdef ENABLE_GCADAPTER std::shared_ptr < GCButtonFactory> gcbuttons;
|
||||
std::shared_ptr<GCAnalogFactory> gcanalog;
|
||||
std::shared_ptr<GCAdapter::Adapter> gcadapter;
|
||||
#endif
|
||||
static std::shared_ptr<Keyboard> keyboard;
|
||||
static std::shared_ptr<MotionEmu> motion_emu;
|
||||
static std::unique_ptr<CemuhookUDP::State> udp;
|
||||
static std::unique_ptr<SDL::State> sdl;
|
||||
|
||||
void Init() {
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
gcadapter = std::make_shared<GCAdapter::Adapter>();
|
||||
gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
|
||||
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
|
||||
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
|
||||
#endif
|
||||
keyboard = std::make_shared<Keyboard>();
|
||||
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
|
||||
std::make_shared<AnalogFromButton>());
|
||||
motion_emu = std::make_shared<MotionEmu>();
|
||||
Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
|
||||
Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
|
||||
std::make_shared<TouchFromButtonFactory>());
|
||||
== == == = struct InputSubsystem::Impl {
|
||||
struct InputSubsystem::Impl {
|
||||
template <typename Engine>
|
||||
void RegisterEngine(std::string name, std::shared_ptr<Engine>& engine) {
|
||||
MappingCallback mapping_callback{
|
||||
[this](const MappingData& data) { RegisterInput(data); }};
|
||||
MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }};
|
||||
|
||||
engine = std::make_shared<Engine>(name);
|
||||
engine->SetMappingCallback(mapping_callback);
|
||||
>>>>>>> 6e5fec9fe (add input common changes)
|
||||
|
||||
std::shared_ptr<InputFactory> input_factory =
|
||||
std::make_shared<InputFactory>(engine);
|
||||
std::shared_ptr<OutputFactory> output_factory =
|
||||
std::make_shared<OutputFactory>(engine);
|
||||
Common::Input::RegisterInputFactory(engine->GetEngineName(),
|
||||
std::move(input_factory));
|
||||
Common::Input::RegisterOutputFactory(engine->GetEngineName(),
|
||||
std::move(output_factory));
|
||||
std::shared_ptr<InputFactory> input_factory = std::make_shared<InputFactory>(engine);
|
||||
std::shared_ptr<OutputFactory> output_factory = std::make_shared<OutputFactory>(engine);
|
||||
Common::Input::RegisterInputFactory(engine->GetEngineName(), std::move(input_factory));
|
||||
Common::Input::RegisterOutputFactory(engine->GetEngineName(), std::move(output_factory));
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
@ -315,26 +274,8 @@
|
||||
impl->Initialize();
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
void Shutdown() {
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
|
||||
gcbuttons.reset();
|
||||
gcanalog.reset();
|
||||
#endif
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
|
||||
keyboard.reset();
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
|
||||
Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
|
||||
motion_emu.reset();
|
||||
Input::UnregisterFactory<Input::TouchDevice>("emu_window");
|
||||
Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
|
||||
sdl.reset();
|
||||
udp.reset();
|
||||
== == == = void InputSubsystem::Shutdown() {
|
||||
void InputSubsystem::Shutdown() {
|
||||
impl->Shutdown();
|
||||
>>>>>>> 6e5fec9fe (add input common changes)
|
||||
}
|
||||
|
||||
Keyboard* InputSubsystem::GetKeyboard() {
|
||||
@ -373,23 +314,19 @@
|
||||
return impl->GetInputDevices();
|
||||
}
|
||||
|
||||
AnalogMapping InputSubsystem::GetAnalogMappingForDevice(
|
||||
const Common::ParamPackage& device) const {
|
||||
AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetAnalogMappingForDevice(device);
|
||||
}
|
||||
|
||||
ButtonMapping InputSubsystem::GetButtonMappingForDevice(
|
||||
const Common::ParamPackage& device) const {
|
||||
ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetButtonMappingForDevice(device);
|
||||
}
|
||||
|
||||
MotionMapping InputSubsystem::GetMotionMappingForDevice(
|
||||
const Common::ParamPackage& device) const {
|
||||
MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
|
||||
return impl->GetMotionMappingForDevice(device);
|
||||
}
|
||||
|
||||
Common::Input::ButtonNames InputSubsystem::GetButtonName(
|
||||
const Common::ParamPackage& params) const {
|
||||
Common::Input::ButtonNames InputSubsystem::GetButtonName(const Common::ParamPackage& params) const {
|
||||
return impl->GetButtonName(params);
|
||||
}
|
||||
|
||||
@ -434,9 +371,8 @@
|
||||
return param.Serialize();
|
||||
}
|
||||
|
||||
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
|
||||
int key_right, int key_modifier,
|
||||
float modifier_scale) {
|
||||
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
|
||||
int key_modifier, float modifier_scale) {
|
||||
Common::ParamPackage circle_pad_param{
|
||||
{"engine", "analog_from_button"},
|
||||
{"up", GenerateKeyboardParam(key_up)},
|
||||
@ -448,74 +384,4 @@
|
||||
};
|
||||
return circle_pad_param.Serialize();
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
Common::ParamPackage GetControllerButtonBinds(const Common::ParamPackage& params,
|
||||
int button) {
|
||||
const auto native_button{static_cast<Settings::NativeButton::Values>(button)};
|
||||
const auto engine{params.Get("engine", "")};
|
||||
if (engine == "sdl") {
|
||||
return dynamic_cast<SDL::SDLState*>(sdl.get())
|
||||
->GetSDLControllerButtonBindByGUID(params.Get("guid", "0"),
|
||||
params.Get("port", 0), native_button);
|
||||
}
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
if (engine == "gcpad") {
|
||||
return gcbuttons->GetGcTo3DSMappedButton(params.Get("port", 0), native_button);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
Common::ParamPackage GetControllerAnalogBinds(const Common::ParamPackage& params,
|
||||
int analog) {
|
||||
const auto native_analog{static_cast<Settings::NativeAnalog::Values>(analog)};
|
||||
const auto engine{params.Get("engine", "")};
|
||||
if (engine == "sdl") {
|
||||
return dynamic_cast<SDL::SDLState*>(sdl.get())
|
||||
->GetSDLControllerAnalogBindByGUID(params.Get("guid", "0"),
|
||||
params.Get("port", 0), native_analog);
|
||||
}
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
if (engine == "gcpad") {
|
||||
return gcanalog->GetGcTo3DSMappedAnalog(params.Get("port", 0), native_analog);
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
void ReloadInputDevices() {
|
||||
if (!udp) {
|
||||
return;
|
||||
}
|
||||
udp->ReloadUDPClient();
|
||||
}
|
||||
|
||||
namespace Polling {
|
||||
|
||||
std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
|
||||
std::vector<std::unique_ptr<DevicePoller>> pollers;
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
pollers = sdl->GetPollers(type);
|
||||
#endif
|
||||
#ifdef ENABLE_GCADAPTER
|
||||
switch (type) {
|
||||
case DeviceType::Analog:
|
||||
pollers.push_back(std::make_unique<GCAnalogFactory>(*gcanalog));
|
||||
break;
|
||||
case DeviceType::Button:
|
||||
pollers.push_back(std::make_unique<GCButtonFactory>(*gcbuttons));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return pollers;
|
||||
}
|
||||
|
||||
} // namespace Polling
|
||||
== == == =
|
||||
>>>>>>> 6e5fec9fe (add input common changes)
|
||||
} // namespace InputCommon
|
||||
|
Reference in New Issue
Block a user