From 3974895e08fc133c4e000c2a654f401662325718 Mon Sep 17 00:00:00 2001
From: wwylele <wwylele@gmail.com>
Date: Fri, 20 Jan 2017 21:52:32 +0200
Subject: [PATCH] Input: add device and factory template

---
 src/common/logging/backend.cpp |  1 +
 src/common/logging/log.h       |  1 +
 src/core/CMakeLists.txt        |  1 +
 src/core/frontend/input.h      | 97 ++++++++++++++++++++++++++++++++++
 4 files changed, 100 insertions(+)
 create mode 100644 src/core/frontend/input.h

diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 737e1d57f..42f6a9918 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -71,6 +71,7 @@ namespace Log {
     CLS(Audio)                                                                                     \
     SUB(Audio, DSP)                                                                                \
     SUB(Audio, Sink)                                                                               \
+    CLS(Input)                                                                                     \
     CLS(Loader)
 
 // GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 4b0f8ff03..1b905f66c 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -89,6 +89,7 @@ enum class Class : ClassType {
     Audio_DSP,         ///< The HLE implementation of the DSP
     Audio_Sink,        ///< Emulator audio output backend
     Loader,            ///< ROM loader
+    Input,             ///< Input emulation
     Count              ///< Total number of logging classes
 };
 
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ffd67f074..dd9f6df41 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -218,6 +218,7 @@ set(HEADERS
             frontend/camera/factory.h
             frontend/camera/interface.h
             frontend/emu_window.h
+            frontend/input.h
             frontend/key_map.h
             frontend/motion_emu.h
             gdbstub/gdbstub.h
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
new file mode 100644
index 000000000..a0c51df0c
--- /dev/null
+++ b/src/core/frontend/input.h
@@ -0,0 +1,97 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <utility>
+#include "common/logging/log.h"
+#include "common/param_package.h"
+
+namespace Input {
+
+/// An abstract class template for an input device (a button, an analog input, etc.).
+template <typename StatusType>
+class InputDevice {
+public:
+    virtual ~InputDevice() = default;
+    virtual StatusType GetStatus() const {
+        return {};
+    }
+};
+
+/// An abstract class template for a factory that can create input devices.
+template <typename InputDeviceType>
+class Factory {
+public:
+    virtual ~Factory() = default;
+    virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0;
+};
+
+namespace Impl {
+
+template <typename InputDeviceType>
+using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
+
+template <typename InputDeviceType>
+struct FactoryList {
+    static FactoryListType<InputDeviceType> list;
+};
+
+template <typename InputDeviceType>
+FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list;
+
+} // namespace Impl
+
+/**
+ * Registers an input device factory.
+ * @tparam InputDeviceType the type of input devices the factory can create
+ * @param name the name of the factory. Will be used to match the "engine" parameter when creating
+ *     a device
+ * @param factory the factory object to register
+ */
+template <typename InputDeviceType>
+void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) {
+    auto pair = std::make_pair(name, std::move(factory));
+    if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) {
+        LOG_ERROR(Input, "Factory %s already registered", name.c_str());
+    }
+}
+
+/**
+ * Unregisters an input device factory.
+ * @tparam InputDeviceType the type of input devices the factory can create
+ * @param name the name of the factory to unregister
+ */
+template <typename InputDeviceType>
+void UnregisterFactory(const std::string& name) {
+    if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) {
+        LOG_ERROR(Input, "Factory %s not registered", name.c_str());
+    }
+}
+
+/**
+ * Create an input device from given paramters.
+ * @tparam InputDeviceType the type of input devices to create
+ * @param params a serialized ParamPackage string contains all parameters for creating the device
+ */
+template <typename InputDeviceType>
+std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) {
+    const Common::ParamPackage package(params);
+    const std::string engine = package.Get("engine", "null");
+    const auto& factory_list = Impl::FactoryList<InputDeviceType>::list;
+    const auto pair = factory_list.find(engine);
+    if (pair == factory_list.end()) {
+        if (engine != "null") {
+            LOG_ERROR(Input, "Unknown engine name: %s", engine.c_str());
+        }
+        return std::make_unique<InputDeviceType>();
+    }
+    return pair->second->Create(package);
+}
+
+} // namespace Input