diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index dd078107956371a71de72c25c2c76408a41a4667..bf9514d5ba4c9d5dbf8d41be2a489dae826886a8 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -66,7 +66,7 @@ namespace vkcv
         std::vector<vk::ImageView>      m_swapchainImageViews;
         std::vector<vk::Image>          m_swapchainImages;
 		std::vector<vk::ImageLayout>    m_swapchainImageLayouts;
-        const Window&                   m_window;
+        Window&                   		m_window;
 
         std::unique_ptr<PassManager>            m_PassManager;
         std::unique_ptr<PipelineManager>        m_PipelineManager;
@@ -79,8 +79,8 @@ namespace vkcv
 		CommandResources    m_CommandResources;
 		SyncResources       m_SyncResources;
 		uint32_t            m_currentSwapchainImageIndex;
-
-        std::function<void(int, int)> e_resizeHandle;
+	
+		event_handle<int,int> e_resizeHandle;
 
         static std::vector<vk::ImageView> createImageViews( Context &context, Swapchain& swapChain);
 
diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp
index 0836e836e84ff7dfc4931a7cedd65497bf9a89cf..e324917674cd2e1773ee23a9411ab28f6eb0d684 100644
--- a/include/vkcv/Event.hpp
+++ b/include/vkcv/Event.hpp
@@ -3,10 +3,18 @@
 #include <functional>
 
 namespace vkcv {
+	
+	template<typename... T>
+	struct event_handle {
+		uint32_t id;
+	};
 
     template<typename... T>
     struct event_function {
         typedef std::function<void(T...)> type;
+	
+		event_handle<T...> handle;
+        type callback;
     };
 
     /**
@@ -16,7 +24,8 @@ namespace vkcv {
     template<typename... T>
     struct event {
     private:
-        std::vector<typename event_function<T...>::type> m_handles;
+        std::vector< event_function<T...> > m_functions;
+        uint32_t m_id_counter;
 
     public:
 
@@ -25,28 +34,34 @@ namespace vkcv {
          * @param arguments of the given function
          */
         void operator()(T... arguments) {
-            for (auto &handle : this->m_handles) {
-                handle(arguments...);
+            for (auto &function : this->m_functions) {
+				function.callback(arguments...);
             }
         }
 
         /**
          * adds a function handle to the event to be called
-         * @param handle of the function
+         * @param callback of the function
+         * @return handle of the function
          */
-        typename event_function<T...>::type add(typename event_function<T...>::type handle) {
-            this->m_handles.push_back(handle);
-            return handle;
+		event_handle<T...> add(typename event_function<T...>::type callback) {
+			event_function<T...> function;
+			function.handle = { m_id_counter++ };
+			function.callback = callback;
+            this->m_functions.push_back(function);
+            return function.handle;
         }
 
         /**
          * removes a function handle of the event
          * @param handle of the function
          */
-        void remove(typename event_function<T...>::type handle) {
-            this->m_handles.erase(
-                    remove(this->m_handles.begin(), this->m_handles.end(), handle),
-                    this->m_handles.end()
+        void remove(event_handle<T...> handle) {
+            this->m_functions.erase(
+					std::remove_if(this->m_functions.begin(), this->m_functions.end(), [&handle](auto function){
+						return (handle.id == function.handle.id);
+					}),
+                    this->m_functions.end()
             );
         }
 
diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp
index 5db19bc716844ff11e5e99e1686e6837fe750403..51d6e2245a8b588334b38254c05276ee0eb10150 100644
--- a/include/vkcv/Window.hpp
+++ b/include/vkcv/Window.hpp
@@ -61,6 +61,13 @@ namespace vkcv {
          * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down.
          */
         static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods);
+	
+        /**
+         * char callback for any typed character
+         * @param[in] window The window that received the event
+         * @param[in] c The character that got typed
+         */
+		static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c);
 
     public:
         /**
@@ -98,6 +105,7 @@ namespace vkcv {
         event< double, double > e_mouseScroll;
         event< int, int > e_resize;
         event< int, int, int, int > e_key;
+        event< unsigned int > e_char;
 
         /**
          * returns the current window
diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp
index 0c5041795ece9cd84e740a04c0d64e4964d0f4c8..5755d6cdc20f0321197b7755e459725eb363fc90 100644
--- a/modules/camera/include/vkcv/camera/CameraManager.hpp
+++ b/modules/camera/include/vkcv/camera/CameraManager.hpp
@@ -25,11 +25,11 @@ namespace vkcv::camera {
      */
     class CameraManager{
     private:
-        std::function<void(int, int, int, int)> m_keyHandle;
-        std::function<void(double, double)> m_mouseMoveHandle;
-        std::function<void(double, double)> m_mouseScrollHandle;
-        std::function<void(int, int, int)> m_mouseButtonHandle;
-        std::function<void(int, int)> m_resizeHandle;
+		event_handle<int, int, int, int> m_keyHandle;
+		event_handle<double, double> m_mouseMoveHandle;
+		event_handle<double, double> m_mouseScrollHandle;
+		event_handle<int, int, int> m_mouseButtonHandle;
+		event_handle<int, int> m_resizeHandle;
 
         Window& m_window;
         std::vector<Camera> m_cameras;
diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp
index 561596c25a66334b56b3253b998c8c63428ef121..84a0d7ca3049846c4fbb234bab02b5f4d3c7ffd5 100644
--- a/modules/camera/src/vkcv/camera/CameraManager.cpp
+++ b/modules/camera/src/vkcv/camera/CameraManager.cpp
@@ -14,7 +14,13 @@ namespace vkcv::camera {
         m_lastY = static_cast<float>(window.getHeight()) / 2.0f;
     }
 
-    CameraManager::~CameraManager() {}
+    CameraManager::~CameraManager() {
+    	m_window.e_key.remove(m_keyHandle);
+		m_window.e_mouseMove.remove(m_mouseMoveHandle);
+		m_window.e_mouseScroll.remove(m_mouseScrollHandle);
+		m_window.e_mouseButton.remove(m_mouseButtonHandle);
+		m_window.e_resize.remove(m_resizeHandle);
+    }
 
     void CameraManager::bindCameraToEvents() {
         m_keyHandle = m_window.e_key.add( [&](int key, int scancode, int action, int mods) { this->keyCallback(key, scancode, action, mods); });
diff --git a/modules/gui/include/vkcv/gui/GUI.hpp b/modules/gui/include/vkcv/gui/GUI.hpp
index 5d62f1519b2e6e6e25daeffc4bc08e9388a758fe..0f9d5f47eabdd0e63f4528e0598f36f57dc893f3 100644
--- a/modules/gui/include/vkcv/gui/GUI.hpp
+++ b/modules/gui/include/vkcv/gui/GUI.hpp
@@ -11,7 +11,7 @@ namespace vkcv::gui {
 
 	class GUI final {
 	private:
-		GLFWwindow* m_window;
+		Window& m_window;
 		Core& m_core;
 		
 		const Context& m_context;
@@ -21,12 +21,21 @@ namespace vkcv::gui {
 		vk::DescriptorPool m_descriptor_pool;
 		vk::RenderPass m_render_pass;
 		
-		GUI(GLFWwindow* window, Core& core);
+		event_handle<int,int,int> f_mouseButton;
+		event_handle<double,double> f_mouseScroll;
+		event_handle<int,int,int,int> f_key;
+		event_handle<unsigned int> f_char;
 		
 	public:
-		virtual ~GUI();
+		GUI(Core& core, Window& window);
+		
+		GUI(const GUI& other) = delete;
+		GUI(GUI&& other) = delete;
 		
-		static GUI create(Core& core, Window& window);
+		GUI& operator=(const GUI& other) = delete;
+		GUI& operator=(GUI&& other) = delete;
+		
+		virtual ~GUI();
 		
 		void beginGUI();
 		
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index 11f87b236050bd7705ca4ba73da372ecf188fe7d..096a857a13f01840d8a3a7e2bf74ba571bd2c249 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -15,15 +15,32 @@ namespace vkcv::gui {
 		vkcv_log(LogLevel::ERROR, "ImGui has a problem with Vulkan! (%s)", vk::to_string(result).c_str());
 	}
 	
-	GUI::GUI(GLFWwindow* window, Core& core) :
+	GUI::GUI(Core& core, Window& window) :
+	m_window(window),
 	m_core(core),
-	m_context(core.getContext()),
+	m_context(m_core.getContext()),
 	m_gui_context(nullptr) {
 		IMGUI_CHECKVERSION();
 		
 		m_gui_context = ImGui::CreateContext();
 		
-		ImGui_ImplGlfw_InitForVulkan(window, false);
+		ImGui_ImplGlfw_InitForVulkan(m_window.getWindow(), false);
+		
+		f_mouseButton = m_window.e_mouseButton.add([&](int button, int action, int mods) {
+			ImGui_ImplGlfw_MouseButtonCallback(m_window.getWindow(), button, action, mods);
+		});
+		
+		f_mouseScroll = m_window.e_mouseScroll.add([&](double xoffset, double yoffset) {
+			ImGui_ImplGlfw_ScrollCallback(m_window.getWindow(), xoffset, yoffset);
+		});
+		
+		f_key = m_window.e_key.add([&](int key, int scancode, int action, int mods) {
+			ImGui_ImplGlfw_KeyCallback(m_window.getWindow(), key, scancode, action, mods);
+		});
+		
+		f_char = m_window.e_char.add([&](unsigned int c) {
+			ImGui_ImplGlfw_CharCallback(m_window.getWindow(), c);
+		});
 		
 		vk::DescriptorPoolSize pool_sizes[] = {
 				vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000),
@@ -49,7 +66,7 @@ namespace vkcv::gui {
 		m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo);
 		
 		const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice();
-		const Swapchain& swapchain = core.getSwapchain();
+		const Swapchain& swapchain = m_core.getSwapchain();
 		
 		const uint32_t graphicsQueueFamilyIndex = (
 				m_context.getQueueManager().getGraphicsQueues()[0].familyIndex
@@ -124,7 +141,7 @@ namespace vkcv::gui {
 		
 		const SubmitInfo submitInfo { QueueType::Graphics, {}, {} };
 		
-		core.recordAndSubmitCommands(submitInfo, [](const vk::CommandBuffer& commandBuffer) {
+		m_core.recordAndSubmitCommands(submitInfo, [](const vk::CommandBuffer& commandBuffer) {
 			ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
 		}, []() {
 			ImGui_ImplVulkan_DestroyFontUploadObjects();
@@ -143,15 +160,16 @@ namespace vkcv::gui {
 		
 		ImGui_ImplGlfw_Shutdown();
 		
+		m_window.e_mouseButton.remove(f_mouseButton);
+		m_window.e_mouseScroll.remove(f_mouseScroll);
+		m_window.e_key.remove(f_key);
+		m_window.e_char.remove(f_char);
+		
 		if (m_gui_context) {
 			ImGui::DestroyContext(m_gui_context);
 		}
 	}
 	
-	GUI GUI::create(Core &core, Window& window) {
-		return GUI(window.getWindow(), core);
-	}
-	
 	void GUI::beginGUI() {
 		const Swapchain& swapchain = m_core.getSwapchain();
 		const auto extent = swapchain.getExtent();
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 2a7e8ba804b50ee8ffd20e7bac02ce6c926e6105..5a962b8983f6735530b38de5be679096fa997bd5 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -30,7 +30,7 @@ int main(int argc, const char** argv) {
 		{ "VK_KHR_swapchain" }
 	);
 	
-	vkcv::gui::GUI gui = vkcv::gui::GUI::create(core, window);
+	vkcv::gui::GUI gui (core, window);
 
 	const auto& context = core.getContext();
 	const vk::Instance& instance = context.getInstance();
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index ced339d6b306b63e95b4ef48705b0d46c356df85..49707d4cffc18719d8fbb18a9e632e12ba679c2e 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -80,8 +80,8 @@ namespace vkcv
 		m_CommandStreamManager->init(this);
 
 		m_ImageManager->m_core = this;
-
-		e_resizeHandle = window.e_resize.add( [&](int width, int height) {
+		
+		e_resizeHandle = m_window.e_resize.add( [&](int width, int height) {
 			m_swapchain.signalSwapchainRecreation();
 		});
 
@@ -90,6 +90,8 @@ namespace vkcv
 	}
 
 	Core::~Core() noexcept {
+    	m_window.e_resize.remove(e_resizeHandle);
+    	
 		m_Context.getDevice().waitIdle();
 		for (auto image : m_swapchainImageViews) {
 			m_Context.m_Device.destroyImageView(image);
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index 1d745a4b11bb361b2742b1a97b6d4d8a9f04db04..2436619300c24f035cba727481dfce8e1b397c9b 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -58,6 +58,8 @@ namespace vkcv {
         glfwSetKeyCallback(m_window, Window::onKeyEvent);
 
         glfwSetScrollCallback(m_window, Window::onMouseScrollEvent);
+	
+		glfwSetCharCallback(m_window, Window::onCharEvent);
     }
 
     void Window::pollEvents() {
@@ -65,7 +67,6 @@ namespace vkcv {
     }
 
     void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) {
-
         auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
 
         if (window != nullptr) {
@@ -74,7 +75,6 @@ namespace vkcv {
     }
 
     void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) {
-
         auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
 
         if (window != nullptr) {
@@ -91,7 +91,6 @@ namespace vkcv {
     }
 
     void Window::onResize(GLFWwindow *callbackWindow, int width, int height) {
-
         auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
 
         if (window != nullptr) {
@@ -100,13 +99,20 @@ namespace vkcv {
     }
 
     void Window::onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) {
-
         auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
 
         if (window != nullptr) {
             window->e_key(key, scancode, action, mods);
         }
     }
+    
+    void Window::onCharEvent(GLFWwindow *callbackWindow, unsigned int c) {
+		auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow));
+	
+		if (window != nullptr) {
+			window->e_char(c);
+		}
+    }
 
     bool Window::isWindowOpen() const {
         return !glfwWindowShouldClose(m_window);