diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp
index e324917674cd2e1773ee23a9411ab28f6eb0d684..da5cbc72fbb3eee3a71a35c1da6fe32dff06b057 100644
--- a/include/vkcv/Event.hpp
+++ b/include/vkcv/Event.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <functional>
+#include <mutex>
 
 namespace vkcv {
 	
@@ -26,6 +27,7 @@ namespace vkcv {
     private:
         std::vector< event_function<T...> > m_functions;
         uint32_t m_id_counter;
+		std::mutex m_mutex;
 
     public:
 
@@ -34,9 +36,13 @@ namespace vkcv {
          * @param arguments of the given function
          */
         void operator()(T... arguments) {
+			lock();
+
             for (auto &function : this->m_functions) {
 				function.callback(arguments...);
-            }
+			}
+            
+            unlock();
         }
 
         /**
@@ -64,8 +70,26 @@ namespace vkcv {
                     this->m_functions.end()
             );
         }
+        
+        /**
+         * locks the event so its function handles won't be called
+         */
+        void lock() {
+			m_mutex.lock();
+        }
+	
+		/**
+		* unlocks the event so its function handles can be called after locking
+		*/
+        void unlock() {
+			m_mutex.unlock();
+        }
 
-        event() = default;
+        explicit event(bool locked = false) {
+        	if (locked) {
+        		lock();
+        	}
+        }
 
         event(const event &other) = delete;
 
diff --git a/include/vkcv/Logger.hpp b/include/vkcv/Logger.hpp
index 251b6b528c45ea509dbfcd0cfb7135b77031f1ac..7f97915a62c20a5b6498fec011c49f8acd81e848 100644
--- a/include/vkcv/Logger.hpp
+++ b/include/vkcv/Logger.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <iostream>
+#include <stdio.h>
 
 namespace vkcv {
 	
diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp
index 51d6e2245a8b588334b38254c05276ee0eb10150..7dc6c1b7dc8fef4d5de7de5b0a9976bf714e6ac2 100644
--- a/include/vkcv/Window.hpp
+++ b/include/vkcv/Window.hpp
@@ -7,6 +7,7 @@
 
 #define NOMINMAX
 #include <algorithm>
+
 #include "Event.hpp"
 
 struct GLFWwindow;
@@ -23,8 +24,6 @@ namespace vkcv {
          */
 		explicit Window(GLFWwindow *window);
 		
-		static GLFWwindow* createGLFWWindow(const char *windowTitle, int width, int height, bool resizable);
-		
     private:
         /**
          * mouse callback for moving the mouse on the screen
@@ -42,6 +41,12 @@ namespace vkcv {
          */
         static void onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods);
 
+        /**
+         * @brief A callback function for handling mouse scrolling events.
+         * @param[in] callbackWindow The window that received the event.
+         * @param[in] xoffset The extent of horizontal scrolling.
+         * @param[in] yoffset The extent of vertical scrolling.
+         */
         static void onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset);
 
         /**
@@ -69,6 +74,12 @@ namespace vkcv {
          */
 		static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c);
 
+        /**
+         * @brief A callback function for gamepad input events.
+         * @param gamepadIndex The gamepad index.
+         */
+        static void onGamepadEvent(int gamepadIndex);
+
     public:
         /**
          * creates a GLFWwindow with the parameters in the function
@@ -87,11 +98,6 @@ namespace vkcv {
         [[nodiscard]]
         bool isWindowOpen() const;
 
-        /**
-         * binds windowEvents to lambda events
-         */
-        void initEvents();
-
         /**
          * polls all events on the GLFWwindow
          */
@@ -106,6 +112,7 @@ namespace vkcv {
         event< int, int > e_resize;
         event< int, int, int, int > e_key;
         event< unsigned int > e_char;
+        event< int > e_gamepad;
 
         /**
          * returns the current window
@@ -152,4 +159,4 @@ namespace vkcv {
         virtual ~Window();
     };
 
-}
\ No newline at end of file
+}
diff --git a/modules/camera/include/vkcv/camera/CameraController.hpp b/modules/camera/include/vkcv/camera/CameraController.hpp
index 5fe7aba586068beff15525617d8e4817662746b7..90fc97401851851194ec89a10757bbfb1453990d 100644
--- a/modules/camera/include/vkcv/camera/CameraController.hpp
+++ b/modules/camera/include/vkcv/camera/CameraController.hpp
@@ -59,6 +59,14 @@ namespace vkcv::camera {
          * @param[in] camera The camera object.
          */
         virtual void mouseButtonCallback(int button, int action, int mods, Camera &camera) = 0;
+
+        /**
+         * @brief A callback function for gamepad input events.
+         * @param gamepadIndex The gamepad index.
+         * @param camera The camera object.
+         * @param frametime The current frametime.
+         */
+        virtual void gamepadCallback(int gamepadIndex, Camera &camera, double frametime) = 0;
     };
 
 }
\ No newline at end of file
diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp
index 5755d6cdc20f0321197b7755e459725eb363fc90..409f9196599be02e4215f3924c1102f0b8c72899 100644
--- a/modules/camera/include/vkcv/camera/CameraManager.hpp
+++ b/modules/camera/include/vkcv/camera/CameraManager.hpp
@@ -30,6 +30,7 @@ namespace vkcv::camera {
 		event_handle<double, double> m_mouseScrollHandle;
 		event_handle<int, int, int> m_mouseButtonHandle;
 		event_handle<int, int> m_resizeHandle;
+        event_handle<int> m_gamepadHandle;
 
         Window& m_window;
         std::vector<Camera> m_cameras;
@@ -42,6 +43,9 @@ namespace vkcv::camera {
         double m_lastX;
         double m_lastY;
 
+        double m_inputDelayTimer;
+        double m_frameTime;
+
         /**
          * @brief Binds the camera object to the window event handles.
          */
@@ -86,6 +90,13 @@ namespace vkcv::camera {
          * @param[in] height The new height of the window.
          */
         void resizeCallback(int width, int height);
+
+        /**
+         * @brief A callback function for gamepad input events. Currently, inputs are handled only for the first
+         * connected gamepad!
+         * @param gamepadIndex The gamepad index.
+         */
+        void gamepadCallback(int gamepadIndex);
 	
 		/**
 		 * @brief Gets a camera controller object of specified @p controllerType.
diff --git a/modules/camera/include/vkcv/camera/PilotCameraController.hpp b/modules/camera/include/vkcv/camera/PilotCameraController.hpp
index c6a9f7c7ffa9a3be77f12c29e456291fb8f6b845..2b64cdc0dd3045714aba7b3b7c6241af2337c706 100644
--- a/modules/camera/include/vkcv/camera/PilotCameraController.hpp
+++ b/modules/camera/include/vkcv/camera/PilotCameraController.hpp
@@ -17,6 +17,10 @@ namespace vkcv::camera {
         bool m_left;
         bool m_right;
 
+        float m_gamepadX;
+        float m_gamepadY;
+        float m_gamepadZ;
+
         bool m_rotationActive;
 
         float m_cameraSpeed;
@@ -133,6 +137,14 @@ namespace vkcv::camera {
          * @param[in] camera The camera object.
          */
         void mouseButtonCallback(int button, int action, int mods, Camera &camera);
+
+        /**
+         * @brief A callback function for gamepad input events.
+         * @param gamepadIndex The gamepad index.
+         * @param camera The camera object.
+         * @param frametime The current frametime.
+         */
+        void gamepadCallback(int gamepadIndex, Camera &camera, double frametime);
     };
 
 }
\ No newline at end of file
diff --git a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp
index 0211043a9c6b862df8e500af190ad1f75a3c78aa..4166bda9f6cb62e4c8f1b650557b00c6ec94b2a1 100644
--- a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp
+++ b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp
@@ -95,6 +95,13 @@ namespace vkcv::camera {
          */
         void mouseButtonCallback(int button, int action, int mods, Camera &camera);
 
+        /**
+         * @brief A callback function for gamepad input events.
+         * @param gamepadIndex The gamepad index.
+         * @param camera The camera object.
+         * @param frametime The current frametime.
+         */
+        void gamepadCallback(int gamepadIndex, Camera &camera, double frametime);
     };
 
 }
\ No newline at end of file
diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp
index 84a0d7ca3049846c4fbb234bab02b5f4d3c7ffd5..f129f3a248325957cb56470e2547a0146bc7c971 100644
--- a/modules/camera/src/vkcv/camera/CameraManager.cpp
+++ b/modules/camera/src/vkcv/camera/CameraManager.cpp
@@ -1,6 +1,5 @@
 
 #include "vkcv/camera/CameraManager.hpp"
-
 #include <vkcv/Logger.hpp>
 
 namespace vkcv::camera {
@@ -12,6 +11,8 @@ namespace vkcv::camera {
         m_activeCameraIndex = 0;
         m_lastX = static_cast<float>(window.getWidth()) / 2.0f;
         m_lastY = static_cast<float>(window.getHeight()) / 2.0f;
+        m_inputDelayTimer = glfwGetTime() + 0.2;
+        m_frameTime = 0;
     }
 
     CameraManager::~CameraManager() {
@@ -20,6 +21,7 @@ namespace vkcv::camera {
 		m_window.e_mouseScroll.remove(m_mouseScrollHandle);
 		m_window.e_mouseButton.remove(m_mouseButtonHandle);
 		m_window.e_resize.remove(m_resizeHandle);
+		m_window.e_gamepad.remove(m_gamepadHandle);
     }
 
     void CameraManager::bindCameraToEvents() {
@@ -28,11 +30,14 @@ namespace vkcv::camera {
         m_mouseScrollHandle =  m_window.e_mouseScroll.add([&](double offsetX, double offsetY) {this->scrollCallback( offsetX, offsetY);} );
         m_mouseButtonHandle = m_window.e_mouseButton.add([&] (int button, int action, int mods) {this->mouseButtonCallback( button,  action,  mods);});
         m_resizeHandle = m_window.e_resize.add([&](int width, int height) {this->resizeCallback(width, height);});
+        m_gamepadHandle = m_window.e_gamepad.add([&](int gamepadIndex) {this->gamepadCallback(gamepadIndex);});
     }
 
     void CameraManager::resizeCallback(int width, int height) {
-        for (size_t i = 0; i < m_cameras.size(); i++) {
-            getCamera(i).setRatio(static_cast<float>(width) / static_cast<float>(height));;
+        if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_ICONIFIED) == GLFW_FALSE) {
+            for (size_t i = 0; i < m_cameras.size(); i++) {
+                getCamera(i).setRatio(static_cast<float>(width) / static_cast<float>(height));;
+            }
         }
     }
 
@@ -81,7 +86,29 @@ namespace vkcv::camera {
                 break;
         }
     }
-    
+
+    void CameraManager::gamepadCallback(int gamepadIndex) {
+        // handle camera switching
+        GLFWgamepadstate gamepadState;
+        glfwGetGamepadState(gamepadIndex, &gamepadState);
+
+        double time = glfwGetTime();
+        if (time - m_inputDelayTimer > 0.2) {
+            int switchDirection = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] - gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT];
+            m_activeCameraIndex += switchDirection;
+            if (std::greater<int>{}(m_activeCameraIndex, m_cameras.size() - 1)) {
+                m_activeCameraIndex = 0;
+            }
+            else if (std::less<int>{}(m_activeCameraIndex, 0)) {
+                m_activeCameraIndex = m_cameras.size() - 1;
+            }
+            uint32_t triggered = abs(switchDirection);
+            m_inputDelayTimer = (1-triggered)*m_inputDelayTimer + triggered * time; // Only reset timer, if dpad was pressed - is this cheaper than if-clause?
+        }
+
+        getActiveController().gamepadCallback(gamepadIndex, getActiveCamera(), m_frameTime);     // handle camera rotation, translation
+    }
+
     CameraController& CameraManager::getActiveController() {
     	const ControllerType type = getControllerType(getActiveCameraIndex());
     	return getControllerByType(type);
@@ -158,7 +185,10 @@ namespace vkcv::camera {
     }
 
     void CameraManager::update(double deltaTime) {
-		getActiveController().updateCamera(deltaTime, getActiveCamera());
+        m_frameTime = deltaTime;
+        if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_FOCUSED) == GLFW_TRUE) {
+            getActiveController().updateCamera(deltaTime, getActiveCamera());
+        }
 	}
 	
-}
\ No newline at end of file
+}
diff --git a/modules/camera/src/vkcv/camera/PilotCameraController.cpp b/modules/camera/src/vkcv/camera/PilotCameraController.cpp
index 1a50a0efa4b4e75adb81ce869d6b927bd0046758..5460858ab48d81252787b3c0141dd72982faca7d 100644
--- a/modules/camera/src/vkcv/camera/PilotCameraController.cpp
+++ b/modules/camera/src/vkcv/camera/PilotCameraController.cpp
@@ -1,5 +1,4 @@
 #include "vkcv/camera/PilotCameraController.hpp"
-
 #include <GLFW/glfw3.h>
 
 namespace vkcv::camera {
@@ -12,9 +11,13 @@ namespace vkcv::camera {
         m_left = false;
         m_right = false;
 
+        m_gamepadX = 0.0f;
+        m_gamepadY = 0.0f;
+        m_gamepadZ = 0.0f;
+
         m_rotationActive = false;
 
-        m_cameraSpeed = 2.0f;
+        m_cameraSpeed = 2.5f;
 
         m_fov_nsteps = 100;
         m_fov_min = 10;
@@ -22,6 +25,11 @@ namespace vkcv::camera {
     }
 
     void PilotCameraController::changeFov(double offset, Camera &camera){
+        // update only if there is (valid) input
+        if (offset == 0.0) {
+            return;
+        }
+
         float fov = camera.getFov();
         float fov_range = m_fov_max - m_fov_min;
         float fov_stepsize = glm::radians(fov_range) / static_cast<float>(m_fov_nsteps);
@@ -36,24 +44,19 @@ namespace vkcv::camera {
     }
 
     void PilotCameraController::panView(double xOffset, double yOffset, Camera &camera) {
-        // handle yaw rotation
-        float yaw = camera.getYaw() + xOffset;
-        if (yaw < -180.0f) {
-            yaw += 360.0f;
-        }
-        else if (yaw > 180.0f) {
-            yaw -= 360.0f;
+        // update only if there is (valid) input
+        if (xOffset == 0.0 && yOffset == 0.0) {
+            return;
         }
+
+        // handle yaw rotation
+        float yaw = camera.getYaw() + static_cast<float>(xOffset);
+        yaw += 360.0f * (yaw < -180.0f) - 360.0f * (yaw > 180.0f);
         camera.setYaw(yaw);
 
         // handle pitch rotation
-        float pitch = camera.getPitch() - yOffset;
-        if (pitch > 89.0f) {
-            pitch = 89.0f;
-        }
-        if (pitch < -89.0f) {
-            pitch = -89.0f;
-        }
+        float pitch = camera.getPitch() - static_cast<float>(yOffset);
+        pitch = glm::clamp(pitch, -89.0f, 89.0f);
         camera.setPitch(pitch);
     }
     
@@ -70,9 +73,9 @@ namespace vkcv::camera {
 	
 		const float distance = m_cameraSpeed * static_cast<float>(deltaTime);
 	
-		position += distance * getDirectionFactor(m_forward, m_backward) * front;
-		position += distance * getDirectionFactor(m_left, m_right) * left;
-		position += distance * getDirectionFactor(m_upward, m_downward) * up;
+		position += distance * (getDirectionFactor(m_forward, m_backward) + m_gamepadZ) * front;
+		position += distance * (getDirectionFactor(m_left, m_right) + m_gamepadX) * left;
+		position += distance * (getDirectionFactor(m_upward, m_downward) + m_gamepadY) * up;
 	
 		camera.lookAt(position, position + front, up);
     }
@@ -127,6 +130,39 @@ namespace vkcv::camera {
         }
     }
 
+    void PilotCameraController::gamepadCallback(int gamepadIndex, Camera &camera, double frametime) {
+        GLFWgamepadstate gamepadState;
+        glfwGetGamepadState(gamepadIndex, &gamepadState);
+
+        float sensitivity = 100.0f;
+        double threshold = 0.1;
+
+        // handle rotations
+        double stickRightX = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
+        double stickRightY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
+
+        double rightXVal = glm::clamp(std::abs(stickRightX) - threshold, 0.0, 1.0)
+                * copysign(1.0, stickRightX) * sensitivity * frametime;
+        double rightYVal = glm::clamp(std::abs(stickRightY) - threshold, 0.0, 1.0)
+                * copysign(1.0, stickRightY) * sensitivity * frametime;
+        panView(rightXVal, rightYVal, camera);
+
+        // handle zooming
+        double zoom = static_cast<double>((gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER]
+                - gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER])
+                * sensitivity * frametime);
+        changeFov(zoom, camera);
+
+        // handle translation
+        m_gamepadY = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] - gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER];
+        float stickLeftX = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X];
+        float stickLeftY = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y];
+        m_gamepadZ = glm::clamp(std::abs(stickLeftY) - threshold, 0.0, 1.0)
+                     * -copysign(1.0, stickLeftY);
+        m_gamepadX = glm::clamp(std::abs(stickLeftX) - threshold, 0.0, 1.0)
+                     * -copysign(1.0, stickLeftX);
+    }
+
 
     void PilotCameraController::moveForward(int action){
         m_forward = static_cast<bool>(action);
diff --git a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp
index 201c6ecdc1c703dbcd53b7dc4b179c86576f2312..cdd66cdb7fdd650d5112fe7bb4738f1fcded7783 100644
--- a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp
+++ b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp
@@ -1,5 +1,4 @@
 #include "vkcv/camera/TrackballCameraController.hpp"
-
 #include <GLFW/glfw3.h>
 
 namespace vkcv::camera {
@@ -12,41 +11,36 @@ namespace vkcv::camera {
     }
 
     void TrackballCameraController::setRadius(const float radius) {
-        if (radius < 0.1f) {
-            m_radius = 0.1f;
-        }
-        else {
-            m_radius = radius;
-        }
+        m_radius = 0.1f * (radius < 0.1f) + radius * (1 - (radius < 0.1f));
     }
 
     void TrackballCameraController::panView(double xOffset, double yOffset, Camera &camera) {
-        // handle yaw rotation
-        float yaw = camera.getYaw() + xOffset * m_cameraSpeed;
-        if (yaw < 0.0f) {
-            yaw += 360.0f;
-        }
-        else if (yaw > 360.0f) {
-            yaw -= 360.0f;
+        // update only if there is (valid) input
+        if (xOffset == 0.0 && yOffset == 0.0) {
+            return;
         }
+
+        // handle yaw rotation
+        float yaw = camera.getYaw() + static_cast<float>(xOffset) * m_cameraSpeed;
+        yaw += 360.0f * (yaw < 0.0f) - 360.0f * (yaw > 360.0f);
         camera.setYaw(yaw);
 
         // handle pitch rotation
-        float pitch = camera.getPitch() + yOffset * m_cameraSpeed;
-        if (pitch < 0.0f) {
-            pitch += 360.0f;
-        }
-        else if (pitch > 360.0f) {
-            pitch -= 360.0f;
-        }
+        float pitch = camera.getPitch() + static_cast<float>(yOffset) * m_cameraSpeed;
+        pitch += 360.0f * (pitch < 0.0f) - 360.0f * (pitch > 360.0f);
         camera.setPitch(pitch);
     }
 
     void TrackballCameraController::updateRadius(double offset, Camera &camera) {
+        // update only if there is (valid) input
+        if (offset == 0.0) {
+            return;
+        }
+
         glm::vec3 cameraPosition = camera.getPosition();
         glm::vec3 cameraCenter = camera.getCenter();
         float radius = glm::length(cameraCenter - cameraPosition);  // get current camera radius
-        setRadius(radius - offset * m_scrollSensitivity);
+        setRadius(radius - static_cast<float>(offset) * m_scrollSensitivity);
     }
 
     void TrackballCameraController::updateCamera(double deltaTime, Camera &camera) {
@@ -82,7 +76,7 @@ namespace vkcv::camera {
             return;
         }
 
-        float sensitivity = 0.05f;
+        float sensitivity = 0.025f;
         xoffset *= sensitivity;
         yoffset *= sensitivity;
 
@@ -97,4 +91,28 @@ namespace vkcv::camera {
             m_rotationActive = false;
         }
     }
+
+    void TrackballCameraController::gamepadCallback(int gamepadIndex, Camera &camera, double frametime) {
+        GLFWgamepadstate gamepadState;
+        glfwGetGamepadState(gamepadIndex, &gamepadState);
+
+        float sensitivity = 100.0f;
+        double threshold = 0.1;
+
+        // handle rotations
+        double stickRightX = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
+        double stickRightY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
+        
+        double rightXVal = glm::clamp((abs(stickRightX)-threshold), 0.0, 1.0)
+                * std::copysign(1.0, stickRightX) * sensitivity * frametime;
+        double rightYVal = glm::clamp((abs(stickRightY)-threshold), 0.0, 1.0)
+                * std::copysign(1.0, stickRightY) * sensitivity * frametime;
+        panView(rightXVal, rightYVal, camera);
+
+        // handle translation
+        double stickLeftY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
+        double leftYVal = glm::clamp((abs(stickLeftY)-threshold), 0.0, 1.0)
+                * std::copysign(1.0, stickLeftY) * sensitivity * frametime;
+        updateRadius(-leftYVal, camera);
+    }
 }
\ No newline at end of file
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index a9a8bd01df379a0f4615c2c53aee09082d107b1c..38bb6894fb2b40c6ab10445f19431f87f7370afc 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -73,13 +73,13 @@ namespace vkcv::gui {
 		);
 		
 		ImGui_ImplVulkan_InitInfo init_info = {};
-		init_info.Instance = m_context.getInstance();
-		init_info.PhysicalDevice = m_context.getPhysicalDevice();
-		init_info.Device = m_context.getDevice();
+		init_info.Instance = static_cast<VkInstance>(m_context.getInstance());
+		init_info.PhysicalDevice = static_cast<VkPhysicalDevice>(m_context.getPhysicalDevice());
+		init_info.Device = static_cast<VkDevice>(m_context.getDevice());
 		init_info.QueueFamily = graphicsQueueFamilyIndex;
-		init_info.Queue = m_context.getQueueManager().getGraphicsQueues()[0].handle;
-		init_info.PipelineCache = nullptr;
-		init_info.DescriptorPool = m_descriptor_pool;
+		init_info.Queue = static_cast<VkQueue>(m_context.getQueueManager().getGraphicsQueues()[0].handle);
+		init_info.PipelineCache = 0;
+		init_info.DescriptorPool = static_cast<VkDescriptorPool>(m_descriptor_pool);
 		init_info.Allocator = nullptr;
 		init_info.MinImageCount = swapchain.getImageCount();
 		init_info.ImageCount = swapchain.getImageCount();
@@ -137,12 +137,12 @@ namespace vkcv::gui {
 		
 		m_render_pass = m_context.getDevice().createRenderPass(passCreateInfo);
 		
-		ImGui_ImplVulkan_Init(&init_info, m_render_pass);
+		ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(m_render_pass));
 		
 		const SubmitInfo submitInfo { QueueType::Graphics, {}, {} };
 		
 		m_core.recordAndSubmitCommandsImmediate(submitInfo, [](const vk::CommandBuffer& commandBuffer) {
-			ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
+			ImGui_ImplVulkan_CreateFontsTexture(static_cast<VkCommandBuffer>(commandBuffer));
 		}, []() {
 			ImGui_ImplVulkan_DestroyFontUploadObjects();
 		});
@@ -230,7 +230,7 @@ namespace vkcv::gui {
 			
 			commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eInline);
 			
-			ImGui_ImplVulkan_RenderDrawData(drawData, commandBuffer);
+			ImGui_ImplVulkan_RenderDrawData(drawData, static_cast<VkCommandBuffer>(commandBuffer));
 			
 			commandBuffer.endRenderPass();
 		}, [&]() {
diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp
index 7ec54582aac6b16a484b74183036539e91cfe731..eccc0af7331dc140f3a15ddf12c5645e685abc90 100644
--- a/projects/cmd_sync_test/src/main.cpp
+++ b/projects/cmd_sync_test/src/main.cpp
@@ -230,7 +230,7 @@ int main(int argc, const char** argv) {
 	auto start = std::chrono::system_clock::now();
 	const auto appStartTime = start;
 	while (window.isWindowOpen()) {
-		vkcv::Window::pollEvents();
+		window.pollEvents();
 		
 		uint32_t swapchainWidth, swapchainHeight;
 		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 74e6de3ff6d9f80d764b774fea5dc55b4b53b2f8..dc43c905784525a34732bc0e66343fbdcc17a639 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -18,8 +18,6 @@ int main(int argc, const char** argv) {
 		true
 	);
 
-	window.initEvents();
-
 	vkcv::Core core = vkcv::Core::create(
 		window,
 		applicationName,
@@ -162,7 +160,7 @@ int main(int argc, const char** argv) {
     auto start = std::chrono::system_clock::now();
     
 	while (window.isWindowOpen()) {
-        vkcv::Window::pollEvents();
+        window.pollEvents();
 		
 		if(window.getHeight() == 0 || window.getWidth() == 0)
 			continue;
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 00a862cfd77b522e9d83b51e703ea48ce45e5d5c..420400cdd04865ddd48eec7cf6000e36417d8095 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -38,8 +38,6 @@ int main(int argc, const char** argv) {
 	
 	cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f);
 
-	window.initEvents();
-
 	vkcv::Core core = vkcv::Core::create(
 		window,
 		applicationName,
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 5a962b8983f6735530b38de5be679096fa997bd5..20cfdddf5c1baa9e8727312daa36de94bd56672f 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -19,8 +19,6 @@ int main(int argc, const char** argv) {
 		false
 	);
 
-	window.initEvents();
-	
 	vkcv::Core core = vkcv::Core::create(
 		window,
 		applicationName,
@@ -174,15 +172,17 @@ int main(int argc, const char** argv) {
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
     vkcv::camera::CameraManager cameraManager(window);
-    uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
-    uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
+    uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
+    uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
 	
-	cameraManager.getCamera(camIndex).setPosition(glm::vec3(0, 0, -2));
+	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2));
+    cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, 0.0f));
+    cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f));
 
 	while (window.isWindowOpen())
 	{
         window.pollEvents();
-		
+
 		uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
 		if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
 			continue;
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index f5a73c0957504c007253bdd3f9da1ba92853794f..5036dadc92a1e83f5172efb7a5cf49495c10fde4 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -37,8 +37,6 @@ int main(int argc, const char** argv) {
 	
 	cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f);
 
-	window.initEvents();
-
 	vkcv::Core core = vkcv::Core::create(
 		window,
 		applicationName,
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index 2436619300c24f035cba727481dfce8e1b397c9b..03a58a23b994209c7a0ee195732dc98543f0eddc 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -5,65 +5,100 @@
  */
 
 #include <GLFW/glfw3.h>
-
 #include "vkcv/Window.hpp"
 
 namespace vkcv {
 
-    static uint32_t s_WindowCount = 0;
-
-    Window::Window(GLFWwindow *window)
-            : m_window(window) {
+	static std::vector<GLFWwindow*> s_Windows;
+
+    Window::Window(GLFWwindow *window) :
+    m_window(window),
+	e_mouseButton(true),
+	e_mouseMove(true),
+	e_mouseScroll(true),
+	e_resize(true),
+	e_key(true),
+	e_char(true),
+	e_gamepad(true)
+    {
+		glfwSetWindowUserPointer(m_window, this);
+	
+		// combine Callbacks with Events
+		glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent);
+		glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent);
+		glfwSetWindowSizeCallback(m_window, Window::onResize);
+		glfwSetKeyCallback(m_window, Window::onKeyEvent);
+		glfwSetScrollCallback(m_window, Window::onMouseScrollEvent);
+		glfwSetCharCallback(m_window, Window::onCharEvent);
     }
 
     Window::~Window() {
+        Window::e_mouseButton.unlock();
+        Window::e_mouseMove.unlock();
+        Window::e_mouseScroll.unlock();
+        Window::e_resize.unlock();
+        Window::e_key.unlock();
+        Window::e_char.unlock();
+        Window::e_gamepad.unlock();
+
+		s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window));
         glfwDestroyWindow(m_window);
-        s_WindowCount--;
 
-        if(s_WindowCount == 0) {
+        if(s_Windows.empty()) {
             glfwTerminate();
         }
     }
-	
-    GLFWwindow* Window::createGLFWWindow(const char *windowTitle, int width, int height, bool resizable) {
-		if(s_WindowCount == 0) {
+
+    Window Window::create( const char *windowTitle, int width, int height, bool resizable) {
+		if(s_Windows.empty()) {
 			glfwInit();
 		}
 	
-		s_WindowCount++;
-	
 		width = std::max(width, 1);
 		height = std::max(height, 1);
 	
 		glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
 		glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
-		
-		return glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
-    }
-
-    Window Window::create( const char *windowTitle, int width, int height, bool resizable) {
-        return Window(createGLFWWindow(windowTitle, width, height, resizable));
-    }
-
-    void Window::initEvents() {
-        glfwSetWindowUserPointer(m_window, this);
-
-        // combine Callbacks with Events
-        glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent);
-
-        glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent);
-
-        glfwSetWindowSizeCallback(m_window, Window::onResize);
-
-        glfwSetKeyCallback(m_window, Window::onKeyEvent);
-
-        glfwSetScrollCallback(m_window, Window::onMouseScrollEvent);
+		GLFWwindow *window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr);
 	
-		glfwSetCharCallback(m_window, Window::onCharEvent);
+		s_Windows.push_back(window);
+	
+		return Window(window);
     }
 
     void Window::pollEvents() {
+
+    	for (auto glfwWindow : s_Windows) {
+			auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+			
+			window->e_mouseButton.unlock();
+			window->e_mouseMove.unlock();
+			window->e_mouseScroll.unlock();
+			window->e_resize.unlock();
+			window->e_key.unlock();
+			window->e_char.unlock();
+			window->e_gamepad.unlock();
+    	}
+
         glfwPollEvents();
+    	
+    	for (int gamepadIndex = GLFW_JOYSTICK_1; gamepadIndex <= GLFW_JOYSTICK_LAST; gamepadIndex++) {
+    		if (glfwJoystickPresent(gamepadIndex)) {
+				onGamepadEvent(gamepadIndex);
+			}
+		}
+	
+		for (auto glfwWindow : s_Windows) {
+			auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow));
+		
+			window->e_mouseButton.lock();
+			window->e_mouseMove.lock();
+			window->e_mouseScroll.lock();
+			window->e_resize.lock();
+			window->e_key.lock();
+			window->e_char.lock();
+			window->e_gamepad.lock();
+		}
     }
 
     void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) {
@@ -114,6 +149,19 @@ namespace vkcv {
 		}
     }
 
+    void Window::onGamepadEvent(int gamepadIndex) {
+        int activeWindowIndex = std::find_if(s_Windows.begin(),
+                                             s_Windows.end(),
+                                             [](GLFWwindow* window){return glfwGetWindowAttrib(window, GLFW_FOCUSED);})
+                                - s_Windows.begin();
+        activeWindowIndex *= (activeWindowIndex < s_Windows.size());    // fixes index getting out of bounds (e.g. if there is no focused window)
+        auto window = static_cast<Window *>(glfwGetWindowUserPointer(s_Windows[activeWindowIndex]));
+
+        if (window != nullptr) {
+            window->e_gamepad(gamepadIndex);
+        }
+    }
+
     bool Window::isWindowOpen() const {
         return !glfwWindowShouldClose(m_window);
     }
@@ -133,4 +181,4 @@ namespace vkcv {
     GLFWwindow *Window::getWindow() const {
         return m_window;
     }
-}
\ No newline at end of file
+}