From a6bd7917cbc06f9b8f5a7ae24e75db776dc1cd6a Mon Sep 17 00:00:00 2001
From: wwylele <wwylele@gmail.com>
Date: Sat, 21 Jan 2017 13:04:00 +0200
Subject: [PATCH] InputCommon: add AnalogFromButton

---
 src/citra/config.cpp                    | 20 +++++++++
 src/citra_qt/config.cpp                 | 27 ++++++++++++
 src/citra_qt/config.h                   |  2 +
 src/input_common/CMakeLists.txt         |  2 +
 src/input_common/analog_from_button.cpp | 58 +++++++++++++++++++++++++
 src/input_common/analog_from_button.h   | 31 +++++++++++++
 src/input_common/main.cpp               | 18 ++++++++
 src/input_common/main.h                 |  4 ++
 8 files changed, 162 insertions(+)
 create mode 100755 src/input_common/analog_from_button.cpp
 create mode 100755 src/input_common/analog_from_button.h

diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 818824596e..ef1229912d 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -8,6 +8,7 @@
 #include "citra/default_ini.h"
 #include "common/file_util.h"
 #include "common/logging/log.h"
+#include "common/param_package.h"
 #include "config.h"
 #include "core/settings.h"
 #include "input_common/main.h"
@@ -44,6 +45,15 @@ static const std::array<int, Settings::NativeButton::NumButtons> default_buttons
     SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
 };
 
+static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{
+    {
+        SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
+    },
+    {
+        SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, SDL_SCANCODE_D,
+    },
+}};
+
 void Config::ReadValues() {
     // Controls
     for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
@@ -54,6 +64,16 @@ void Config::ReadValues() {
             Settings::values.buttons[i] = default_param;
     }
 
+    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+        std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+            default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+            default_analogs[i][3], default_analogs[i][4], 0.5f);
+        Settings::values.analogs[i] =
+            sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
+        if (Settings::values.analogs[i].empty())
+            Settings::values.analogs[i] = default_param;
+    }
+
     // Core
     Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
 
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 5855c7105d..6ccfa15779 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -22,6 +22,15 @@ const std::array<int, Settings::NativeButton::NumButtons> Config::default_button
     Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
 };
 
+const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
+    {
+        Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
+    },
+    {
+        Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_D,
+    },
+}};
+
 void Config::ReadValues() {
     qt_config->beginGroup("Controls");
     for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
@@ -34,6 +43,20 @@ void Config::ReadValues() {
         if (Settings::values.buttons[i].empty())
             Settings::values.buttons[i] = default_param;
     }
+
+    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+        std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+            default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+            default_analogs[i][3], default_analogs[i][4], 0.5f);
+        Settings::values.analogs[i] =
+            qt_config
+                ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
+                .toString()
+                .toStdString();
+        if (Settings::values.analogs[i].empty())
+            Settings::values.analogs[i] = default_param;
+    }
+
     qt_config->endGroup();
 
     qt_config->beginGroup("Core");
@@ -158,6 +181,10 @@ void Config::SaveValues() {
         qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
                             QString::fromStdString(Settings::values.buttons[i]));
     }
+    for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+        qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+                            QString::fromStdString(Settings::values.analogs[i]));
+    }
     qt_config->endGroup();
 
     qt_config->beginGroup("Core");
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h
index d7bf994420..cbf745ea25 100644
--- a/src/citra_qt/config.h
+++ b/src/citra_qt/config.h
@@ -24,5 +24,7 @@ public:
 
     void Reload();
     void Save();
+
     static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
+    static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
 };
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index ac1ad45a93..9f4422269e 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -1,9 +1,11 @@
 set(SRCS
+            analog_from_button.cpp
             keyboard.cpp
             main.cpp
             )
 
 set(HEADERS
+            analog_from_button.h
             keyboard.h
             main.h
             )
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
new file mode 100755
index 0000000000..e1a260762f
--- /dev/null
+++ b/src/input_common/analog_from_button.cpp
@@ -0,0 +1,58 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "input_common/analog_from_button.h"
+
+namespace InputCommon {
+
+class Analog final : public Input::AnalogDevice {
+public:
+    using Button = std::unique_ptr<Input::ButtonDevice>;
+
+    Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
+           float modifier_scale_)
+        : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
+          right(std::move(right_)), modifier(std::move(modifier_)),
+          modifier_scale(modifier_scale_) {}
+
+    std::tuple<float, float> GetStatus() const override {
+        constexpr float SQRT_HALF = 0.707106781f;
+        int x = 0, y = 0;
+
+        if (right->GetStatus())
+            ++x;
+        if (left->GetStatus())
+            --x;
+        if (up->GetStatus())
+            ++y;
+        if (down->GetStatus())
+            --y;
+
+        float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
+        return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF),
+                               y * coef * (x == 0 ? 1.0f : SQRT_HALF));
+    }
+
+private:
+    Button up;
+    Button down;
+    Button left;
+    Button right;
+    Button modifier;
+    float modifier_scale;
+};
+
+std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
+    const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
+    auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
+    auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
+    auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
+    auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
+    auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
+    auto modifier_scale = params.Get("modifier_scale", 0.5f);
+    return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
+                                    std::move(right), std::move(modifier), modifier_scale);
+}
+
+} // namespace InputCommon
diff --git a/src/input_common/analog_from_button.h b/src/input_common/analog_from_button.h
new file mode 100755
index 0000000000..bbd583dd93
--- /dev/null
+++ b/src/input_common/analog_from_button.h
@@ -0,0 +1,31 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+
+/**
+ * An analog device factory that takes direction button devices and combines them into a analog
+ * device.
+ */
+class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> {
+public:
+    /**
+     * Creates an analog device from direction button devices
+     * @param params contains parameters for creating the device:
+     *     - "up": a serialized ParamPackage for creating a button device for up direction
+     *     - "down": a serialized ParamPackage for creating a button device for down direction
+     *     - "left": a serialized ParamPackage for creating a button device for left direction
+     *     - "right": a serialized ParamPackage  for creating a button device for right direction
+     *     - "modifier": a serialized ParamPackage for creating a button device as the modifier
+     *     - "modifier_scale": a float for the multiplier the modifier gives to the position
+     */
+    std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index ff25220b42..8455fdc176 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,6 +4,7 @@
 
 #include <memory>
 #include "common/param_package.h"
+#include "input_common/analog_from_button.h"
 #include "input_common/keyboard.h"
 #include "input_common/main.h"
 
@@ -14,11 +15,14 @@ static std::shared_ptr<Keyboard> keyboard;
 void Init() {
     keyboard = std::make_shared<InputCommon::Keyboard>();
     Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
+    Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
+                                                std::make_shared<InputCommon::AnalogFromButton>());
 }
 
 void Shutdown() {
     Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
     keyboard.reset();
+    Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
 }
 
 Keyboard* GetKeyboard() {
@@ -32,4 +36,18 @@ std::string GenerateKeyboardParam(int key_code) {
     return param.Serialize();
 }
 
+std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
+                                        int key_modifier, float modifier_scale) {
+    Common::ParamPackage circle_pad_param{
+        {"engine", "analog_from_button"},
+        {"up", GenerateKeyboardParam(key_up)},
+        {"down", GenerateKeyboardParam(key_down)},
+        {"left", GenerateKeyboardParam(key_left)},
+        {"right", GenerateKeyboardParam(key_right)},
+        {"modifier", GenerateKeyboardParam(key_modifier)},
+        {"modifier_scale", std::to_string(modifier_scale)},
+    };
+    return circle_pad_param.Serialize();
+}
+
 } // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index a490dd8298..140bbd0145 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -22,4 +22,8 @@ Keyboard* GetKeyboard();
 /// Generates a serialized param package for creating a keyboard button device
 std::string GenerateKeyboardParam(int key_code);
 
+/// Generates a serialized param package for creating an analog device taking input from keyboard
+std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
+                                        int key_modifier, float modifier_scale);
+
 } // namespace InputCommon
-- 
GitLab