From 38e800f70d122051e12ac9f22e23d84b97fec424 Mon Sep 17 00:00:00 2001
From: wwylele <wwylele@gmail.com>
Date: Sat, 21 Jan 2017 11:53:03 +0200
Subject: [PATCH] InputCommon: add Keyboard

---
 src/CMakeLists.txt                       |  1 +
 src/citra/CMakeLists.txt                 |  2 +-
 src/citra/config.cpp                     | 25 ++++----
 src/citra/emu_window/emu_window_sdl2.cpp | 21 ++----
 src/citra/emu_window/emu_window_sdl2.h   |  6 --
 src/citra_qt/CMakeLists.txt              |  2 +-
 src/citra_qt/bootmanager.cpp             | 25 ++++----
 src/citra_qt/bootmanager.h               |  6 +-
 src/citra_qt/config.cpp                  | 35 +++++-----
 src/citra_qt/config.h                    |  3 +-
 src/citra_qt/configure_input.cpp         |  9 +--
 src/core/frontend/emu_window.h           |  2 -
 src/input_common/CMakeLists.txt          | 15 +++++
 src/input_common/keyboard.cpp            | 82 ++++++++++++++++++++++++
 src/input_common/keyboard.h              | 45 +++++++++++++
 src/input_common/main.cpp                | 35 ++++++++++
 src/input_common/main.h                  | 25 ++++++++
 17 files changed, 254 insertions(+), 85 deletions(-)
 create mode 100644 src/input_common/CMakeLists.txt
 create mode 100644 src/input_common/keyboard.cpp
 create mode 100644 src/input_common/keyboard.h
 create mode 100644 src/input_common/main.cpp
 create mode 100644 src/input_common/main.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1e1245160..a45439481 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(common)
 add_subdirectory(core)
 add_subdirectory(video_core)
 add_subdirectory(audio_core)
+add_subdirectory(input_common)
 add_subdirectory(tests)
 if (ENABLE_SDL2)
     add_subdirectory(citra)
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index ecb5d2dfe..47231ba71 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -18,7 +18,7 @@ create_directory_groups(${SRCS} ${HEADERS})
 include_directories(${SDL2_INCLUDE_DIR})
 
 add_executable(citra ${SRCS} ${HEADERS})
-target_link_libraries(citra core video_core audio_core common)
+target_link_libraries(citra core video_core audio_core common input_common)
 target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
 if (MSVC)
     target_link_libraries(citra getopt)
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index fac1c9a0e..818824596 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -10,6 +10,7 @@
 #include "common/logging/log.h"
 #include "config.h"
 #include "core/settings.h"
+#include "input_common/main.h"
 
 Config::Config() {
     // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -37,25 +38,21 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
     return true;
 }
 
-static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
-    // directly mapped keys
-    SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W,
-    SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T,
-    SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J,
-    SDL_SCANCODE_L,
-
-    // indirectly mapped keys
-    SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D,
+static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = {
+    SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T,
+    SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W,
+    SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B,
 };
 
 void Config::ReadValues() {
     // Controls
-    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
-        Settings::values.input_mappings[Settings::NativeInput::All[i]] =
-            sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
+    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+        std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+        Settings::values.buttons[i] =
+            sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
+        if (Settings::values.buttons[i].empty())
+            Settings::values.buttons[i] = default_param;
     }
-    Settings::values.pad_circle_modifier_scale =
-        (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
 
     // Core
     Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 00d00905a..6bc0b0d00 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -12,9 +12,9 @@
 #include "common/logging/log.h"
 #include "common/scm_rev.h"
 #include "common/string_util.h"
-#include "core/frontend/key_map.h"
-#include "core/hle/service/hid/hid.h"
 #include "core/settings.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
 #include "video_core/video_core.h"
 
 void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
@@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
 
 void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
     if (state == SDL_PRESSED) {
-        KeyMap::PressKey(*this, {key, keyboard_id});
+        InputCommon::GetKeyboard()->PressKey(key);
     } else if (state == SDL_RELEASED) {
-        KeyMap::ReleaseKey(*this, {key, keyboard_id});
+        InputCommon::GetKeyboard()->ReleaseKey(key);
     }
 }
 
@@ -57,9 +57,8 @@ void EmuWindow_SDL2::OnResize() {
 }
 
 EmuWindow_SDL2::EmuWindow_SDL2() {
-    keyboard_id = KeyMap::NewDeviceId();
+    InputCommon::Init();
 
-    ReloadSetKeymaps();
     motion_emu = std::make_unique<Motion::MotionEmu>(*this);
 
     SDL_SetMainReady();
@@ -117,6 +116,7 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
     SDL_GL_DeleteContext(gl_context);
     SDL_Quit();
     motion_emu = nullptr;
+    InputCommon::Shutdown();
 }
 
 void EmuWindow_SDL2::SwapBuffers() {
@@ -169,15 +169,6 @@ void EmuWindow_SDL2::DoneCurrent() {
     SDL_GL_MakeCurrent(render_window, nullptr);
 }
 
-void EmuWindow_SDL2::ReloadSetKeymaps() {
-    KeyMap::ClearKeyMapping(keyboard_id);
-    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
-        KeyMap::SetKeyMapping(
-            {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
-            KeyMap::mapping_targets[i]);
-    }
-}
-
 void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
     const std::pair<unsigned, unsigned>& minimal_size) {
 
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h
index b1cbf16d7..1ce2991f7 100644
--- a/src/citra/emu_window/emu_window_sdl2.h
+++ b/src/citra/emu_window/emu_window_sdl2.h
@@ -31,9 +31,6 @@ public:
     /// Whether the window is still open, and a close request hasn't yet been sent
     bool IsOpen() const;
 
-    /// Load keymap from configuration
-    void ReloadSetKeymaps() override;
-
 private:
     /// Called by PollEvents when a key is pressed or released.
     void OnKeyEvent(int key, u8 state);
@@ -61,9 +58,6 @@ private:
     /// The OpenGL context associated with the window
     SDL_GLContext gl_context;
 
-    /// Device id of keyboard for use with KeyMap
-    int keyboard_id;
-
     /// Motion sensors emulation
     std::unique_ptr<Motion::MotionEmu> motion_emu;
 };
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 15a6ccf9a..2b1c59a92 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -97,7 +97,7 @@ if (APPLE)
 else()
     add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
 endif()
-target_link_libraries(citra-qt core video_core audio_core common)
+target_link_libraries(citra-qt core video_core audio_core common input_common)
 target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
 target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads)
 
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 69d18cf0c..66c883d9a 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -13,7 +13,8 @@
 #include "common/scm_rev.h"
 #include "common/string_util.h"
 #include "core/core.h"
-#include "core/frontend/key_map.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
 #include "video_core/debug_utils/debug_utils.h"
 #include "video_core/video_core.h"
 
@@ -99,14 +100,17 @@ private:
 };
 
 GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
-    : QWidget(parent), child(nullptr), keyboard_id(0), emu_thread(emu_thread) {
+    : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
 
     std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name,
                                                         Common::g_scm_branch, Common::g_scm_desc);
     setWindowTitle(QString::fromStdString(window_title));
 
-    keyboard_id = KeyMap::NewDeviceId();
-    ReloadSetKeymaps();
+    InputCommon::Init();
+}
+
+GRenderWindow::~GRenderWindow() {
+    InputCommon::Shutdown();
 }
 
 void GRenderWindow::moveContext() {
@@ -197,11 +201,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
 }
 
 void GRenderWindow::keyPressEvent(QKeyEvent* event) {
-    KeyMap::PressKey(*this, {event->key(), keyboard_id});
+    InputCommon::GetKeyboard()->PressKey(event->key());
 }
 
 void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
-    KeyMap::ReleaseKey(*this, {event->key(), keyboard_id});
+    InputCommon::GetKeyboard()->ReleaseKey(event->key());
 }
 
 void GRenderWindow::mousePressEvent(QMouseEvent* event) {
@@ -230,14 +234,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
         motion_emu->EndTilt();
 }
 
-void GRenderWindow::ReloadSetKeymaps() {
-    KeyMap::ClearKeyMapping(keyboard_id);
-    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
-        KeyMap::SetKeyMapping(
-            {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id},
-            KeyMap::mapping_targets[i]);
-    }
-}
+void GRenderWindow::ReloadSetKeymaps() {}
 
 void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) {
     NotifyClientAreaSizeChanged(std::make_pair(width, height));
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 7dac1c480..923a5b456 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -104,6 +104,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
 
 public:
     GRenderWindow(QWidget* parent, EmuThread* emu_thread);
+    ~GRenderWindow();
 
     // EmuWindow implementation
     void SwapBuffers() override;
@@ -127,7 +128,7 @@ public:
     void mouseMoveEvent(QMouseEvent* event) override;
     void mouseReleaseEvent(QMouseEvent* event) override;
 
-    void ReloadSetKeymaps() override;
+    void ReloadSetKeymaps();
 
     void OnClientAreaResized(unsigned width, unsigned height);
 
@@ -152,9 +153,6 @@ private:
 
     QByteArray geometry;
 
-    /// Device id of keyboard for use with KeyMap
-    int keyboard_id;
-
     EmuThread* emu_thread;
 
     /// Motion sensors emulation
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 5fe57dfa2..5855c7105 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -6,6 +6,7 @@
 #include "citra_qt/config.h"
 #include "citra_qt/ui_settings.h"
 #include "common/file_util.h"
+#include "input_common/main.h"
 
 Config::Config() {
     // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
@@ -16,25 +17,23 @@ Config::Config() {
     Reload();
 }
 
-const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = {
-    // directly mapped keys
-    Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
-    Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I,
-    Qt::Key_K, Qt::Key_J, Qt::Key_L,
-
-    // indirectly mapped keys
-    Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D,
+const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
+    Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
+    Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B,
 };
 
 void Config::ReadValues() {
     qt_config->beginGroup("Controls");
-    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
-        Settings::values.input_mappings[Settings::NativeInput::All[i]] =
-            qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i])
-                .toInt();
+    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+        std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+        Settings::values.buttons[i] =
+            qt_config
+                ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
+                .toString()
+                .toStdString();
+        if (Settings::values.buttons[i].empty())
+            Settings::values.buttons[i] = default_param;
     }
-    Settings::values.pad_circle_modifier_scale =
-        qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
     qt_config->endGroup();
 
     qt_config->beginGroup("Core");
@@ -155,12 +154,10 @@ void Config::ReadValues() {
 
 void Config::SaveValues() {
     qt_config->beginGroup("Controls");
-    for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
-        qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
-                            Settings::values.input_mappings[Settings::NativeInput::All[i]]);
+    for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+        qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
+                            QString::fromStdString(Settings::values.buttons[i]));
     }
-    qt_config->setValue("pad_circle_modifier_scale",
-                        (double)Settings::values.pad_circle_modifier_scale);
     qt_config->endGroup();
 
     qt_config->beginGroup("Core");
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h
index 79c901804..d7bf99442 100644
--- a/src/citra_qt/config.h
+++ b/src/citra_qt/config.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <array>
 #include <string>
 #include <QVariant>
 #include "core/settings.h"
@@ -23,5 +24,5 @@ public:
 
     void Reload();
     void Save();
-    static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults;
+    static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
 };
diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp
index c29652f32..8846a68b2 100644
--- a/src/citra_qt/configure_input.cpp
+++ b/src/citra_qt/configure_input.cpp
@@ -92,14 +92,7 @@ void ConfigureInput::loadConfiguration() {
     updateButtonLabels();
 }
 
-void ConfigureInput::restoreDefaults() {
-    for (const auto& input_id : Settings::NativeInput::All) {
-        const size_t index = static_cast<size_t>(input_id);
-        key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt());
-    }
-    updateButtonLabels();
-    applyConfiguration();
-}
+void ConfigureInput::restoreDefaults() {}
 
 void ConfigureInput::updateButtonLabels() {
     for (const auto& input_id : Settings::NativeInput::All) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 1ba64c92b..de9b64953 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -52,8 +52,6 @@ public:
     /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
     virtual void DoneCurrent() = 0;
 
-    virtual void ReloadSetKeymaps() = 0;
-
     /**
      * Signals a button press action to the HID module.
      * @param pad_state indicates which button to press
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
new file mode 100644
index 000000000..ac1ad45a9
--- /dev/null
+++ b/src/input_common/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(SRCS
+            keyboard.cpp
+            main.cpp
+            )
+
+set(HEADERS
+            keyboard.h
+            main.h
+            )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(input_common STATIC ${SRCS} ${HEADERS})
+target_link_libraries(input_common common core)
+
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
new file mode 100644
index 000000000..a8fc01f2e
--- /dev/null
+++ b/src/input_common/keyboard.cpp
@@ -0,0 +1,82 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <atomic>
+#include <list>
+#include <mutex>
+#include "input_common/keyboard.h"
+
+namespace InputCommon {
+
+class KeyButton final : public Input::ButtonDevice {
+public:
+    explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_)
+        : key_button_list(key_button_list_) {}
+
+    ~KeyButton();
+
+    bool GetStatus() const override {
+        return status.load();
+    }
+
+    friend class KeyButtonList;
+
+private:
+    std::shared_ptr<KeyButtonList> key_button_list;
+    std::atomic<bool> status{false};
+};
+
+struct KeyButtonPair {
+    int key_code;
+    KeyButton* key_button;
+};
+
+class KeyButtonList {
+public:
+    void AddKeyButton(int key_code, KeyButton* key_button) {
+        std::lock_guard<std::mutex> guard(mutex);
+        list.push_back(KeyButtonPair{key_code, key_button});
+    }
+
+    void RemoveKeyButton(const KeyButton* key_button) {
+        std::lock_guard<std::mutex> guard(mutex);
+        list.remove_if(
+            [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
+    }
+
+    void ChangeKeyStatus(int key_code, bool pressed) {
+        std::lock_guard<std::mutex> guard(mutex);
+        for (const KeyButtonPair& pair : list) {
+            if (pair.key_code == key_code)
+                pair.key_button->status.store(pressed);
+        }
+    }
+
+private:
+    std::mutex mutex;
+    std::list<KeyButtonPair> list;
+};
+
+Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {}
+
+KeyButton::~KeyButton() {
+    key_button_list->RemoveKeyButton(this);
+}
+
+std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
+    int key_code = params.Get("code", 0);
+    std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
+    key_button_list->AddKeyButton(key_code, button.get());
+    return std::move(button);
+}
+
+void Keyboard::PressKey(int key_code) {
+    key_button_list->ChangeKeyStatus(key_code, true);
+}
+
+void Keyboard::ReleaseKey(int key_code) {
+    key_button_list->ChangeKeyStatus(key_code, false);
+}
+
+} // namespace InputCommon
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h
new file mode 100644
index 000000000..76359aa30
--- /dev/null
+++ b/src/input_common/keyboard.h
@@ -0,0 +1,45 @@
+// 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 {
+
+class KeyButtonList;
+
+/**
+ * A button device factory representing a keyboard. It receives keyboard events and forward them
+ * to all button devices it created.
+ */
+class Keyboard final : public Input::Factory<Input::ButtonDevice> {
+public:
+    Keyboard();
+
+    /**
+     * Creates a button device from a keyboard key
+     * @param params contains parameters for creating the device:
+     *     - "code": the code of the key to bind with the button
+     */
+    std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
+
+    /**
+     * 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);
+
+private:
+    std::shared_ptr<KeyButtonList> key_button_list;
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
new file mode 100644
index 000000000..ff25220b4
--- /dev/null
+++ b/src/input_common/main.cpp
@@ -0,0 +1,35 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "common/param_package.h"
+#include "input_common/keyboard.h"
+#include "input_common/main.h"
+
+namespace InputCommon {
+
+static std::shared_ptr<Keyboard> keyboard;
+
+void Init() {
+    keyboard = std::make_shared<InputCommon::Keyboard>();
+    Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
+}
+
+void Shutdown() {
+    Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
+    keyboard.reset();
+}
+
+Keyboard* GetKeyboard() {
+    return keyboard.get();
+}
+
+std::string GenerateKeyboardParam(int key_code) {
+    Common::ParamPackage param{
+        {"engine", "keyboard"}, {"code", std::to_string(key_code)},
+    };
+    return param.Serialize();
+}
+
+} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
new file mode 100644
index 000000000..a490dd829
--- /dev/null
+++ b/src/input_common/main.h
@@ -0,0 +1,25 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+namespace InputCommon {
+
+/// Initializes and registers all built-in input device factories.
+void Init();
+
+/// Unresisters all build-in input device factories and shut them down.
+void Shutdown();
+
+class Keyboard;
+
+/// Gets the keyboard button device factory.
+Keyboard* GetKeyboard();
+
+/// Generates a serialized param package for creating a keyboard button device
+std::string GenerateKeyboardParam(int key_code);
+
+} // namespace InputCommon