diff --git a/library/phx/input/device.cpp b/library/phx/input/device.cpp
index 5c68859d70073a2d10a384ed647c75afdd66c9b0..fd9d038ce9eb4aa322fa8a8e5332c8f3b1936f19 100644
--- a/library/phx/input/device.cpp
+++ b/library/phx/input/device.cpp
@@ -20,6 +20,19 @@
 // limitations under the License.
 //------------------------------------------------------------------------------
 
+#include <algorithm>
+
 #include "phx/input/device.hpp"
 
-namespace phx {}  // namespace phx
+namespace phx {
+
+void Device::EventDistributer::AddDevice(Device* device) {
+  devices_.push_back(device);
+}
+void Device::EventDistributer::RemoveDevice(Device* device) {
+  auto iterator =
+      std::remove_if(devices_.begin(), devices_.end(),
+                     [device](Device* iteratee) { return device == iteratee; });
+  devices_.erase(iterator, devices_.end());
+}
+}  // namespace phx
diff --git a/library/phx/input/device.hpp b/library/phx/input/device.hpp
index c477639764a905efdc712809d5a74034be7aaf98..995d210a3eaa2e825c1a14a30f4fbd12f96ff474 100644
--- a/library/phx/input/device.hpp
+++ b/library/phx/input/device.hpp
@@ -23,6 +23,8 @@
 #ifndef LIBRARY_PHX_INPUT_DEVICE_HPP_
 #define LIBRARY_PHX_INPUT_DEVICE_HPP_
 
+#include <vector>
+
 #include "phx/export.hpp"
 
 namespace phx {
@@ -31,6 +33,16 @@ class PHOENIX_EXPORT Device {
  public:
   virtual ~Device() = default;
   virtual void Update() = 0;
+
+  class EventDistributer {
+   public:
+    virtual void Update() = 0;
+    void AddDevice(Device* device);
+    void RemoveDevice(Device* device);
+
+   protected:
+    std::vector<Device*> devices_;
+  };
 };
 
 }  // namespace phx
diff --git a/library/phx/input/sdl_device.cpp b/library/phx/input/sdl_device.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..298d6cdc9f3612721dbba816aae9e340b36c7fa9
--- /dev/null
+++ b/library/phx/input/sdl_device.cpp
@@ -0,0 +1,64 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
+
+#include "phx/input/sdl_device.hpp"
+
+#include "phx/core/logger.hpp"
+
+namespace phx {
+
+int SDLDevice::reference_counter_ = 0;
+std::mutex SDLDevice::SDL_event_init_mutex_;
+SDLDevice::SDLEventDistributer SDLDevice::SDL_event_distributer;
+
+SDLDevice::SDLDevice() {
+  SDL_event_init_mutex_.lock();
+  if (reference_counter_ == 0) {
+    SDL_Init(SDL_INIT_EVENTS);
+  }
+  SDL_event_distributer.AddDevice(this);
+  reference_counter_++;
+  SDL_event_init_mutex_.unlock();
+}
+
+SDLDevice::~SDLDevice() {
+  SDL_event_init_mutex_.lock();
+  reference_counter_--;
+  if (reference_counter_ == 0) {
+    SDL_QuitSubSystem(SDL_INIT_EVENTS);
+  }
+  SDL_event_distributer.RemoveDevice(this);
+  SDL_event_init_mutex_.unlock();
+}
+
+void SDLDevice::Update() { SDL_event_distributer.Update(); }
+
+void SDLDevice::SDLEventDistributer::Update() {
+  SDL_Event event;
+  while (SDL_PollEvent(&event) != 0) {
+    for (Device* device : devices_) {
+      dynamic_cast<SDLDevice*>(device)->OnSDLEvent(event);
+    }
+  }
+}
+
+}  // namespace phx
diff --git a/library/phx/input/sdl_device.hpp b/library/phx/input/sdl_device.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..30e1947ea84c6940bda6bd683bcc836e7652ea46
--- /dev/null
+++ b/library/phx/input/sdl_device.hpp
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// Project Phoenix
+//
+// Copyright (c) 2017-2018 RWTH Aachen University, Germany,
+// Virtual Reality & Immersive Visualization Group.
+//------------------------------------------------------------------------------
+//                                 License
+//
+// Licensed under the 3-Clause BSD License (the "License");
+// you may not use this file except in compliance with the License.
+// See the file LICENSE for the full text.
+// You may obtain a copy of the License at
+//
+//     https://opensource.org/licenses/BSD-3-Clause
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//------------------------------------------------------------------------------
+
+#ifndef LIBRARY_PHX_INPUT_SDL_DEVICE_HPP_
+#define LIBRARY_PHX_INPUT_SDL_DEVICE_HPP_
+
+#include <mutex>
+#include <vector>
+
+#include "phx/input/device.hpp"
+#include "phx/suppress_warnings.hpp"
+
+SUPPRESS_WARNINGS_BEGIN
+#include "SDL.h"
+SUPPRESS_WARNINGS_END
+
+#include "phx/export.hpp"
+
+namespace phx {
+
+class PHOENIX_EXPORT SDLDevice : public Device {
+ public:
+  SDLDevice();
+  virtual ~SDLDevice();
+
+  virtual void Update();
+
+  virtual void OnSDLEvent(const SDL_Event& event) = 0;
+
+ protected:
+  class SDLEventDistributer : public EventDistributer {
+   public:
+    void Update() override;
+  };
+  static SDLEventDistributer SDL_event_distributer;
+
+ private:
+  static int reference_counter_;
+  static std::mutex SDL_event_init_mutex_;
+};
+
+}  // namespace phx
+
+#endif  // LIBRARY_PHX_INPUT_SDL_DEVICE_HPP_
diff --git a/library/phx/input/tracked_device.cpp b/library/phx/input/tracked_device.cpp
index 0800a10dc0068f407972ce018bb7a60a2d17636a..05582a0dc7bde089eb12fc24f5b79779a2b5d4e3 100644
--- a/library/phx/input/tracked_device.cpp
+++ b/library/phx/input/tracked_device.cpp
@@ -94,23 +94,11 @@ bool TrackedDevice::IsActive() const {
   return id_ != vr::k_unTrackedDeviceIndexInvalid;
 }
 
-void TrackedDevice::OpenVREventDistributer::AddDevice(TrackedDevice* device) {
-  tracked_devices_.push_back(device);
-}
-
-void TrackedDevice::OpenVREventDistributer::RemoveDevice(
-    TrackedDevice* device) {
-  auto iterator =
-      std::remove_if(tracked_devices_.begin(), tracked_devices_.end(),
-                     [device](Device* iteratee) { return device == iteratee; });
-  tracked_devices_.erase(iterator, tracked_devices_.end());
-}
-
 void TrackedDevice::OpenVREventDistributer::Update() {
   vr::VREvent_t event;
   while (vr::VRSystem()->PollNextEvent(&event, sizeof(event))) {
-    for (TrackedDevice* device : tracked_devices_) {
-      device->OnOpenVREvent(event);
+    for (Device* device : devices_) {
+      dynamic_cast<TrackedDevice*>(device)->OnOpenVREvent(event);
     }
   }
 }
diff --git a/library/phx/input/tracked_device.hpp b/library/phx/input/tracked_device.hpp
index d02805708c3326d5f9e6cbf24954f938b4e8c79f..a6ee0bb904daa675fb9a999b982c9c73b50b459e 100644
--- a/library/phx/input/tracked_device.hpp
+++ b/library/phx/input/tracked_device.hpp
@@ -54,18 +54,13 @@ class PHOENIX_EXPORT TrackedDevice : public Device {
   virtual void OnOpenVREvent(const vr::VREvent_t& event) = 0;
 
  protected:
-  class OpenVREventDistributer {
+  class OpenVREventDistributer : public EventDistributer {
    public:
-    void Update();
-    void AddDevice(TrackedDevice* device);
-    void RemoveDevice(TrackedDevice* device);
-
-   private:
-    std::vector<TrackedDevice*> tracked_devices_;
+    void Update() override;
   };
+  static OpenVREventDistributer vr_event_distributer;
 
   static vr::IVRSystem* vr_system_;
-  static OpenVREventDistributer vr_event_distributer;
   static std::vector<vr::TrackedDevicePose_t> last_wait_get_poses_;
   glm::mat4 pose_;
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f63ed905d8e76ba508e35cf4c75092d0c56031d9..7ad4f915c9263e626f06cab1b067a86e49645072 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -174,7 +174,9 @@ add_test_cpplint(NAME "phoenix-tests--cpplint"
 autoremove_mocked_test_source_from(${PHOENIX_TEST_SOURCES})
 
 add_mocked_test(test_clear_pass                     LIBRARIES phoenix                MOCKS opengl_mock)
-add_mocked_test(test_input_system                   LIBRARIES phoenix                MOCKS openvr_mock sdl_mock)
+add_mocked_test(test_SDL_device	                    LIBRARIES phoenix                MOCKS sdl_mock)
+add_mocked_test(test_mouse	                    LIBRARIES phoenix                MOCKS sdl_mock)
+add_mocked_test(test_keyboard	                    LIBRARIES phoenix                MOCKS sdl_mock)
 add_mocked_test(test_geometry_pass                  LIBRARIES phoenix test_utilities MOCKS opengl_mock sdl_mock)
 add_mocked_test(test_rendering_system               LIBRARIES phoenix                MOCKS opengl_mock sdl_mock)
 add_mocked_test(test_shader                         LIBRARIES phoenix                MOCKS opengl_mock)
diff --git a/tests/src/test_input_system.cpp b/tests/src/test_SDL_device.cpp
similarity index 68%
rename from tests/src/test_input_system.cpp
rename to tests/src/test_SDL_device.cpp
index 7ac9cbc35cd66467dc29edadfd45c862b2613313..766f74b41bc99a005c29b7f8dfbaebd31c27b92d 100644
--- a/tests/src/test_input_system.cpp
+++ b/tests/src/test_SDL_device.cpp
@@ -25,15 +25,12 @@
 #include "catch/catch.hpp"
 
 #include "phx/core/engine.hpp"
-#include "phx/input/input_system.hpp"
+#include "phx/input/sdl_device.hpp"
 
 #include "trompeloeil.hpp"
 
 #include "phx/suppress_warnings.hpp"
 
-SUPPRESS_WARNINGS_BEGIN
-#include "mocks/openvr_mock.hpp"
-SUPPRESS_WARNINGS_END
 #include "mocks/sdl_mock.hpp"
 
 using trompeloeil::_;
@@ -41,26 +38,30 @@ using trompeloeil::ne;
 
 extern template struct trompeloeil::reporter<trompeloeil::specialized>;
 
+class SomeSDLDevice : public phx::SDLDevice {
+  MAKE_MOCK1(OnSDLEvent, void(const SDL_Event&));
+};
+
 SCENARIO("The input system captures low-level input events and forwards them.",
-         "[phx][phx::InputSystem]") {
+         "[phx][phx::SDLDevice]") {
   SDL_MOCK_ALLOW_ANY_CALL
-  OPENVR_MOCK_ALLOW_ANY_CALL
-  GIVEN("An engine with a DisplaySystem.") {
-    phx::Engine engine;
 
-    WHEN("I create an input system.") {
-      THEN("SDL event subsystem is initialized.") {
-        REQUIRE_CALL(sdl_mock.Get(), SDL_Init(SDL_INIT_EVENTS)).RETURN(0);
-        engine.CreateSystem<phx::InputSystem>();
-      }
-    }
+  WHEN("I create an SDL device.") {
+    THEN("SDL event subsystem is initialized.") {
+      REQUIRE_CALL(sdl_mock.Get(), SDL_Init(SDL_INIT_EVENTS)).RETURN(0);
+      auto device = std::make_unique<SomeSDLDevice>();
 
-    GIVEN("An input system.") {
-      phx::InputSystem* input_system = engine.CreateSystem<phx::InputSystem>();
-      WHEN("The input system is updated.") {
+      WHEN("The SDL device is updated.") {
         THEN("SDL is requested to poll the pending events.") {
-          REQUIRE_CALL(sdl_mock.Get(), SDL_PollEvent(_)).RETURN(0);
-          input_system->Update(phx::FrameTimer::TimeInfo());
+          REQUIRE_CALL(sdl_mock.Get(), SDL_PollEvent(_)).RETURN(false);
+          device->Update();
+
+          WHEN("The SDL device is destroyed.") {
+            THEN("SDL is requested to shutdown the event stuff.") {
+              REQUIRE_CALL(sdl_mock.Get(), SDL_QuitSubSystem(SDL_INIT_EVENTS));
+              device.reset();
+            }
+          }
         }
       }
     }