changeset 206:12873699ad8b

client: create variant instead of dispatcher
author David Demelier <markand@malikania.fr>
date Thu, 29 Nov 2018 14:04:58 +0100
parents c973501abe36
children 10687519f46e
files libmlk-client/CMakeLists.txt libmlk-client/malikania/client/button.cpp libmlk-client/malikania/client/button.hpp libmlk-client/malikania/client/client.cpp libmlk-client/malikania/client/client.hpp libmlk-client/malikania/client/dispatcher.hpp libmlk-client/malikania/client/event.cpp libmlk-client/malikania/client/event.hpp libmlk-client/malikania/client/mouse.hpp libmlk-client/malikania/client/state.hpp libmlk-client/malikania/client/state/lobby_state.cpp libmlk-client/malikania/client/state/lobby_state.hpp libmlk-client/malikania/client/state/login_state.cpp libmlk-client/malikania/client/state/login_state.hpp libmlk-client/malikania/client/state/map_state.cpp libmlk-client/malikania/client/state/map_state.hpp libmlk-client/malikania/client/widget.hpp libmlk-client/malikania/client/window.cpp libmlk-client/malikania/client/window.hpp mlk-client/main.cpp
diffstat 20 files changed, 300 insertions(+), 389 deletions(-) [+]
line wrap: on
line diff
--- a/libmlk-client/CMakeLists.txt	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/CMakeLists.txt	Thu Nov 29 14:04:58 2018 +0100
@@ -22,54 +22,55 @@
 
 set(
 	SOURCES
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/animation.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/animator.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/animator.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/button.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/button.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/client.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/client.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/color.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/color.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/connection.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/connection.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/dispatcher.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/font.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/font.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/image.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/image.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/key.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/label.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/label.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/loader.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/loader.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/mouse.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sdl_util.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sdl_util.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sprite.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sprite.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/lobby_state.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/lobby_state.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/login_state.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/login_state.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/map_state.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/map_state.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/theme.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/theme.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/widget.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/widget.hpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/window.cpp
-	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/window.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/animation.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/animator.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/animator.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/button.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/button.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/client.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/client.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/color.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/color.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/connection.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/connection.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/event.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/event.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/font.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/font.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/image.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/image.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/key.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/label.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/label.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/loader.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/loader.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/mouse.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/sdl_util.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/sdl_util.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/sprite.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/sprite.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state/lobby_state.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state/lobby_state.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state/login_state.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state/login_state.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state/map_state.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/state/map_state.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/theme.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/theme.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/widget.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/widget.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/window.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/window.hpp
 )
 
 malikania_define_library(
 	PROJECT libmlk-client
 	TARGET libmlk-client
 	SOURCES ${SOURCES}
-	ASSETS ${libmlk-client_SOURCE_DIR}/assets/dejavu_sans.ttf
+	ASSETS ${PROJECT_SOURCE_DIR}/assets/dejavu_sans.ttf
 	PUBLIC_INCLUDES
-		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+		$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
 		${SDL2_INCLUDE_DIRS}
 		${SDL2_IMAGE_INCLUDE_DIRS}
 		${SDL2_TTF_INCLUDE_DIRS}
--- a/libmlk-client/malikania/client/button.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/button.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -39,12 +39,19 @@
 	text_ = std::move(text);
 }
 
-void button::handle_mouse_down(const mouse_click_event& ev)
+void button::handle_event(const event& ev)
 {
-	if (ev.button == mouse::left && on_press_)
-		on_press_();
+	const auto mev = std::get_if<mouse_click_event>(&ev);
+
+	if (!mev)
+		return;
 
-	// TODO: implement release.
+	if (mev->button == mouse::left) {
+		if (mev->pressed && on_press_)
+			on_press_();
+		else if (on_release_)
+			on_release_();
+	}
 }
 
 void button::draw(window& w)
--- a/libmlk-client/malikania/client/button.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/button.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -41,7 +41,7 @@
 
 private:
 	on_press_func on_press_;
-	on_release_func on_released_;
+	on_release_func on_release_;
 
 	std::string text_;
 
@@ -68,9 +68,9 @@
 	void set_text(std::string text) noexcept;
 
 	/**
-	 * \copydoc widget::handle_mouse_down
+	 * \copydoc widget::handle_event
 	 */
-	void handle_mouse_down(const mouse_click_event& ev) override;
+	void handle_event(const event& ev);
 
 	/**
 	 * Bring back other functions.
--- a/libmlk-client/malikania/client/client.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/client.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -41,6 +41,7 @@
 
 void client::recv()
 {
+#if 0
 	// Network thread space.
 	connection_.recv([this] (auto code, auto message) {
 		if (!code)
@@ -50,10 +51,12 @@
 
 		nqueue_.push_back(std::bind(&client::handle_message, this, code, message));
 	});
+#endif
 }
 
 void client::connect(std::string host, std::uint16_t port)
 {
+#if 0
 	service_.post([this, host, port] () {
 		connection_.connect(host, port, [this] (auto code) {
 			// Start receiveing on success.
@@ -66,14 +69,17 @@
 			nqueue_.push_back(std::bind(&client::handle_connect, this, code));
 		});
 	});
+#endif
 }
 
 void client::send(nlohmann::json message)
 {
+#if 0
 	service_.post([this, message] () {
 		// TODO: error checking.
 		connection_.send(std::move(message), nullptr);
 	});
+#endif
 }
 
 void client::set_state(std::unique_ptr<state> state)
@@ -81,6 +87,8 @@
 	state_next_ = std::move(state);
 }
 
+#if 0
+
 void client::handle_connect(const std::error_code& code)
 {
 	if (state_)
@@ -171,6 +179,8 @@
 	t.join();
 }
 
+#endif
+
 } // !client
 
 } // !mlk
--- a/libmlk-client/malikania/client/client.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/client.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -34,8 +34,6 @@
 
 #include <json.hpp>
 
-#include "dispatcher.hpp"
-
 namespace mlk {
 
 namespace client {
@@ -47,7 +45,7 @@
 /**
  * \brief Main client game class
  */
-class client : public dispatcher {
+class client {
 private:
 	boost::asio::io_service& service_;
 
@@ -126,51 +124,6 @@
 	 * Run the client until the game window is closed.
 	 */
 	void run();
-
-	/**
-	 * \copydoc dispatcher::handle_connect
-	 */
-	void handle_connect(const std::error_code& code) override;
-
-	/**
-	 * \copydoc dispatcher::handle_message
-	 */
-	void handle_message(const std::error_code& code, const nlohmann::json& msg) override;
-
-	/**
-	 * \copydoc dispatcher::handle_key_down
-	 */
-	void handle_key_down(const key_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_key_up
-	 */
-	void handle_key_up(const key_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_mouse_down
-	 */
-	void handle_mouse_down(const mouse_click_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_mouse_up
-	 */
-	void handle_mouse_up(const mouse_click_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_mouse_wheel
-	 */
-	void handle_mouse_wheel(const mouse_wheel_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_text
-	 */
-	void handle_text(const text_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_quit
-	 */
-	void handle_quit() override;
 };
 
 } // !client
--- a/libmlk-client/malikania/client/dispatcher.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-/*
- * dispatcher.hpp -- client event dispatcher
- *
- * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MALIKANIA_CLIENT_DISPATCHER_HPP
-#define MALIKANIA_CLIENT_DISPATCHER_HPP
-
-/**
- * \file dispatcher.hpp
- * \brief Client event dispatcher.
- */
-
-#include <system_error>
-
-#include <json.hpp>
-
-#include <malikania/point.hpp>
-
-#include "mouse.hpp"
-#include "key.hpp"
-
-namespace mlk::client {
-
-/**
- * \brief Describe key event.
- */
-struct key_event {
-	key keycode{key::unknown};      //!< layout-dependant key
-	key scancode{key::unknown};     //!< physical key
-	mod modifiers{mod::none};       //!< optional modifiers
-};
-
-/**
- * \brief Describe mouse click event.
- */
-struct mouse_click_event {
-	mouse button{mouse::none};      //!< which mouse button
-	point pos;                      //!< current mouse position
-};
-
-/**
- * \brief Describe a mouse motion event.
- */
-struct mouse_motion_event {
-	point pos;                      //!< current mouse position
-};
-
-/**
- * \brief Describe mouse wheel (up/down) event.
- */
-struct mouse_wheel_event {
-	point pos;                      //!< current mouse position
-};
-
-/**
- * \brief Describe text edition event.
- */
-struct text_event {
-	std::u32string text;            //!< text added from this event
-};
-
-/**
- * \brief Client event dispatcher.
- */
-class dispatcher {
-public:
-	/**
-	 * Default constructor.
-	 */
-	dispatcher() noexcept = default;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~dispatcher() noexcept = default;
-
-	/**
-	 * Connection event.
-	 *
-	 * \param code the result code
-	 */
-	virtual void handle_connect(const std::error_code& code)
-	{
-		(void)code;
-	}
-
-	/**
-	 * Message event.
-	 *
-	 * \param code the result code
-	 * \param msg the network message
-	 */
-	virtual void handle_message(const std::error_code& code, const nlohmann::json& msg)
-	{
-		(void)code;
-		(void)msg;
-	}
-
-	/**
-	 * Key down event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_key_down(const key_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Key released event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_key_up(const key_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Mouse click event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_mouse_down(const mouse_click_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Mouse click release event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_mouse_up(const mouse_click_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Mouse wheel event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_mouse_wheel(const mouse_wheel_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Text editing event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_text(const text_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Quit request.
-	 */
-	virtual void handle_quit()
-	{
-	}
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_DISPATCHER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/event.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -0,0 +1,28 @@
+/*
+ * event.cpp -- event object
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "event.hpp"
+
+namespace mlk::client {
+
+event::operator bool() const noexcept
+{
+	return index() != 0U;
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/event.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -0,0 +1,121 @@
+/*
+ * event.hpp -- event object
+ *
+ * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MALIKANIA_CLIENT_EVENT_HPP
+#define MALIKANIA_CLIENT_EVENT_HPP
+
+/**
+ * \file event.hpp
+ * \brief Event object.
+ */
+
+#include <string>
+#include <variant>
+
+#include <malikania/point.hpp>
+
+#include "mouse.hpp"
+#include "key.hpp"
+
+namespace mlk::client {
+
+/**
+ * \brief Describe key event.
+ */
+struct key_event {
+	bool pressed{true};             //!< is pressed?
+	key keycode{key::unknown};      //!< layout-dependant key
+	key scancode{key::unknown};     //!< physical key
+	mod modifiers{mod::none};       //!< optional modifiers
+};
+
+/**
+ * \brief Describe mouse click event.
+ */
+struct mouse_click_event {
+	bool pressed{true};             //!< is pressed?
+	mouse button{mouse::none};      //!< which mouse button
+	point pos;                      //!< current mouse position
+};
+
+/**
+ * \brief Describe a mouse motion event.
+ */
+struct mouse_motion_event {
+	point pos;                      //!< current mouse position
+};
+
+/**
+ * \brief Describe mouse wheel (up/down) event.
+ */
+struct mouse_wheel_event {
+	point pos;                      //!< current mouse position
+};
+
+/**
+ * \brief Describe text edition event.
+ */
+struct text_event {
+	std::u32string text;            //!< text added from this event
+};
+
+/**
+ * \brief Event occured but is unknown.
+ */
+struct unknown_event {
+};
+
+/**
+ * \brief User has requested quit.
+ */
+struct quit_event {
+};
+
+/**
+ * \brief Variant with all possible events.
+ */
+using variant = std::variant<
+	std::monostate,
+	quit_event,
+	key_event,
+	mouse_click_event,
+	mouse_motion_event,
+	mouse_wheel_event,
+	text_event,
+	unknown_event
+>;
+
+/**
+ * \brief Event object.
+ */
+class event : public variant {
+public:
+	/**
+	 * Inherited constructor.
+	 */
+	using variant::variant;
+
+	/**
+	 * Tells if the event contain an event.
+	 */
+	operator bool() const noexcept;
+};
+
+} // !mlk::client
+
+#endif // MALIKANIA_CLIENT_EVENT_HPP
--- a/libmlk-client/malikania/client/mouse.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/mouse.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -33,7 +33,8 @@
 	none,                   //!< no buttons are pressed
 	left,                   //!< left click is pressed
 	right,                  //!< right click is pressed
-	middle                  //!< middle click is pressed
+	middle,                 //!< middle click is pressed
+	unknown                 //!< unknown button pressed
 };
 
 } // !mlk::client
--- a/libmlk-client/malikania/client/state.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -24,8 +24,6 @@
  * \brief Game client state.
  */
 
-#include "dispatcher.hpp"
-
 namespace mlk::client {
 
 class client;
@@ -33,7 +31,7 @@
 /**
  * \brief Game client state.
  */
-class state : public dispatcher {
+class state {
 public:
 	/**
 	 * Default constructor.
--- a/libmlk-client/malikania/client/state/lobby_state.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state/lobby_state.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -34,6 +34,8 @@
 {
 }
 
+#if 0
+
 void lobby_state::handle_key_down(const key_event& ev)
 {
 	if (ev.keycode == key::down)
@@ -44,6 +46,8 @@
 		client_.set_state(std::make_unique<map_state>());
 }
 
+#endif
+
 void lobby_state::update(client&)
 {
 }
--- a/libmlk-client/malikania/client/state/lobby_state.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state/lobby_state.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -43,8 +43,6 @@
 public:
 	lobby_state(client& client);
 
-	void handle_key_down(const key_event& ev) override;
-
 	void update(client&) override;
 
 	void draw(client& clt) override;
--- a/libmlk-client/malikania/client/state/login_state.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state/login_state.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -54,6 +54,8 @@
 	client_.window().start_edit();
 }
 
+#if 0
+
 void login_state::handle_text(const text_event& ev)
 {
 	if (index_ == 0)
@@ -62,10 +64,14 @@
 		password_ += ev.text;
 }
 
+#endif
+
 void login_state::update(client&)
 {
 }
 
+#if 0
+
 void login_state::handle_connect(const std::error_code& code)
 {
 	if (code)
@@ -106,6 +112,8 @@
 	}
 }
 
+#endif
+
 void login_state::draw(client& clt)
 {
 	auto& win = clt.window();
--- a/libmlk-client/malikania/client/state/login_state.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state/login_state.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -56,10 +56,6 @@
 
 	void update(client&) override;
 	void draw(client& clt) override;
-	void handle_connect(const std::error_code& code) override;
-	void handle_message(const std::error_code& code, const nlohmann::json& msg) override;
-	void handle_key_down(const key_event& ev) override;
-	void handle_text(const text_event& ev) override;
 };
 
 } // !client
--- a/libmlk-client/malikania/client/state/map_state.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state/map_state.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -12,6 +12,8 @@
 
 namespace client {
 
+#if 0
+
 void map_state::handle_key_down(const key_event& ev)
 {
 	if (ev.keycode == key::down)
@@ -32,6 +34,8 @@
 		delta_ = {0, delta_.y};
 }
 
+#endif
+
 void map_state::update(client&)
 {
 	position_ = {
--- a/libmlk-client/malikania/client/state/map_state.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/state/map_state.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -43,8 +43,6 @@
 public:
 	void update(client&) override;
 	void draw(client& clt) override;
-	void handle_key_down(const key_event& ev) override;
-	void handle_key_up(const key_event& ev) override;
 };
 
 } // !client
--- a/libmlk-client/malikania/client/widget.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/widget.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -27,19 +27,18 @@
 #include <malikania/point.hpp>
 #include <malikania/size.hpp>
 
-#include "dispatcher.hpp"
-
 namespace mlk {
 
 namespace client {
 
+class event;
+class theme;
 class window;
-class theme;
 
 /**
  * \brief Abstract widget
  */
-class widget : public dispatcher {
+class widget {
 protected:
 	theme* theme_;                  //!< widget theme
 	point position_;                //!< widget position
@@ -111,6 +110,13 @@
 	void set_theme(theme& theme) noexcept;
 
 	/**
+	 * Handle the input event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_event(const event& ev) = 0;
+
+	/**
 	 * Draw the widget at the specified position.
 	 *
 	 * \param w the window
--- a/libmlk-client/malikania/client/window.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/window.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -16,8 +16,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <cassert>
 #include <iostream>
 #include <stdexcept>
+#include <unordered_map>
 
 #include <malikania/line.hpp>
 #include <malikania/rectangle.hpp>
@@ -324,9 +326,7 @@
 	{ SDL_BUTTON_MIDDLE,                    mouse::middle           }
 };
 
-} // !namespace
-
-void window::dispatch_key_event(dispatcher& dp, const SDL_Event& ev) const
+auto create_key_event(const SDL_Event& ev) -> event
 {
 	assert(ev.type == SDL_KEYDOWN || ev.type == SDL_KEYUP);
 
@@ -334,56 +334,53 @@
 	const auto kc = keycodes_map.find(ev.key.keysym.sym);
 	const auto sc = scancodes_map.find(ev.key.keysym.scancode);
 
-	if (kc == keycodes_map.end() || sc == scancodes_map.end())
-		return;
-
-	key_event kev{kc->second, sc->second, mod::none};
+	key_event kev{
+		ev.type == SDL_KEYDOWN,
+		kc == keycodes_map.end() ? key::unknown : kc->second,
+		sc == scancodes_map.end() ? key::unknown : sc->second,
+		mod::none
+	};
 
 	// Modifier is a mask.
 	for (const auto& pair : modifiers_map)
 		if (ev.key.keysym.mod & pair.first)
 			kev.modifiers |= pair.second;
 
-	if (ev.type == SDL_KEYDOWN)
-		dp.handle_key_down(kev);
-	else
-		dp.handle_key_up(kev);
+	return kev;
 }
 
-void window::dispatch_mouse_event(dispatcher& dp, const SDL_Event& ev) const
+auto create_mouse_event(const SDL_Event& ev) -> event
 {
 	assert(ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP);
 
 	const auto which = button_map.find(ev.button.button);
 
-	if (which == button_map.end())
-		return;
-
 	mouse_click_event mev{
-		which->second,
+		ev.type == SDL_MOUSEBUTTONDOWN,
+		which == button_map.end() ? mouse::unknown : which->second,
 		point{ev.button.x, ev.button.y}
 	};
 
-	if (ev.type == SDL_MOUSEBUTTONDOWN)
-		dp.handle_mouse_down(mev);
-	else
-		dp.handle_mouse_up(mev);
+	return mev;
 }
 
-void window::dispatch_mouse_wheel_event(dispatcher& dp, const SDL_Event& ev) const
+auto create_mouse_wheel_event(const SDL_Event& ev) -> event
 {
 	assert(ev.type == SDL_MOUSEWHEEL);
 
-	dp.handle_mouse_wheel({{ev.wheel.x, ev.wheel.y}});
+	return mouse_wheel_event{
+		{ ev.wheel.x, ev.wheel.y }
+	};
 }
 
-void window::dispatch_text_event(dispatcher& dp, const SDL_Event& ev) const
+auto create_text_event(const SDL_Event& ev) -> event
 {
 	assert(ev.type == SDL_TEXTINPUT);
 
-	dp.handle_text({unicode::to_utf32(ev.text.text)});
+	return text_event{unicode::to_utf32(ev.text.text)};
 }
 
+} // !namespace
 
 void window::init()
 {
@@ -429,23 +426,14 @@
 
 auto window::get_renderer() const noexcept -> const SDL_Renderer*
 {
-	assert(is_open_);
-
 	return renderer_.get();
 }
 
 auto window::get_renderer() noexcept -> SDL_Renderer*
 {
-	assert(is_open_);
-
 	return renderer_.get();
 }
 
-auto window::is_open() const noexcept -> bool
-{
-	return is_open_;
-}
-
 void window::start_edit()
 {
 	if (!is_editing_) {
@@ -472,13 +460,6 @@
 	SDL_RenderPresent(renderer_.get());
 }
 
-void window::close() noexcept
-{
-	is_open_ = false;
-	renderer_ = nullptr;
-	window_ = nullptr;
-}
-
 auto window::get_drawing_color() const -> color
 {
 	SDL_Color color;
@@ -556,33 +537,31 @@
 	SDL_DestroyTexture(texture);
 }
 
-void window::poll(dispatcher& dp)
+auto window::poll() -> event
 {
 	SDL_Event event;
 
-	while (SDL_PollEvent(&event)) {
-		switch (event.type) {
-		case SDL_KEYUP:
-		case SDL_KEYDOWN:
-			dispatch_key_event(dp, event);
-			break;
-		case SDL_MOUSEBUTTONDOWN:
-		case SDL_MOUSEBUTTONUP:
-			dispatch_mouse_event(dp, event);
-			break;
-		case SDL_MOUSEWHEEL:
-			dispatch_mouse_wheel_event(dp, event);
-			break;
-		case SDL_TEXTINPUT:
-			dispatch_text_event(dp, event);
-			break;
-		case SDL_QUIT:
-			dp.handle_quit();
-			break;
-		default:
-			break;
-		}
+	if (!SDL_PollEvent(&event))
+		return std::monostate{};
+
+	switch (event.type) {
+	case SDL_KEYUP:
+	case SDL_KEYDOWN:
+		return create_key_event(event);
+	case SDL_MOUSEBUTTONDOWN:
+	case SDL_MOUSEBUTTONUP:
+		return create_mouse_event(event);
+	case SDL_MOUSEWHEEL:
+		return create_mouse_wheel_event(event);
+	case SDL_TEXTINPUT:
+		return create_text_event(event);
+	case SDL_QUIT:
+		return quit_event{};
+	default:
+		break;
 	}
+
+	return unknown_event{};
 }
 
 } // !mlk::client
--- a/libmlk-client/malikania/client/window.hpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/libmlk-client/malikania/client/window.hpp	Thu Nov 29 14:04:58 2018 +0100
@@ -29,7 +29,7 @@
 
 #include <SDL.h>
 
-#include "dispatcher.hpp"
+#include "event.hpp"
 
 namespace mlk {
 
@@ -48,17 +48,11 @@
  */
 class window {
 private:
-	std::unique_ptr<SDL_Window, void (*)(SDL_Window *)> window_{nullptr, nullptr};
-	std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer *)> renderer_{nullptr, nullptr};
+	std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window_{nullptr, nullptr};
+	std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer_{nullptr, nullptr};
 
-	bool is_open_{true};
 	bool is_editing_{false};
 
-	void dispatch_key_event(dispatcher&, const SDL_Event&) const;
-	void dispatch_mouse_event(dispatcher&, const SDL_Event&) const;
-	void dispatch_mouse_wheel_event(dispatcher&, const SDL_Event&) const;
-	void dispatch_text_event(dispatcher&, const SDL_Event&) const;
-
 	void init();
 
 public:
@@ -80,7 +74,6 @@
 	/**
 	 * Get the renderer.
 	 *
-	 * \pre is_open()
 	 * \return the renderer
 	 */
 	auto get_renderer() const noexcept -> const SDL_Renderer*;
@@ -88,19 +81,11 @@
 	/**
 	 * Overloaded function.
 	 *
-	 * \pre is_open()
 	 * \return the renderer
 	 */
 	auto get_renderer() noexcept -> SDL_Renderer*;
 
 	/**
-	 * Tells if the window is open.
-	 *
-	 * \return true if open
-	 */
-	auto is_open() const noexcept -> bool;
-
-	/**
 	 * Start editing input.
 	 *
 	 * \note may open a visual keyboard on some platforms
@@ -127,11 +112,6 @@
 	void present();
 
 	/**
-	 * Close the window.
-	 */
-	void close() noexcept;
-
-	/**
 	 * Get the current drawing color.
 	 *
 	 * \return the color
@@ -198,11 +178,11 @@
 	void draw_text(const std::string& text, const font& font, const point& point);
 
 	/**
-	 * Poll all pending events and call appropriate functions.
+	 * Poll the next event and return it.
 	 *
-	 * \param dispatcher the event dispatcher
+	 * \return the event or empty one if not available
 	 */
-	void poll(dispatcher& dp);
+	auto poll() -> event;
 };
 
 } // !client
--- a/mlk-client/main.cpp	Thu Nov 29 12:57:30 2018 +0100
+++ b/mlk-client/main.cpp	Thu Nov 29 14:04:58 2018 +0100
@@ -28,8 +28,7 @@
 
 #include <assets/ui.hpp>
 
-class loop : public mlk::client::dispatcher {
-};
+using namespace std::chrono_literals;
 
 int main(int, char**)
 {
@@ -37,10 +36,12 @@
 	mlk::client::image image(std::string(ui, sizeof (ui)));
 	mlk::client::sprite sprite(std::move(image), {16, 16});
 
-	loop looper;
+	for (;;) {
+		while (auto ev = win.poll()) {
+			if (std::holds_alternative<mlk::client::quit_event>(ev))
+				return 0;
+		}
 
-	while (win.is_open()) {
-		win.poll(looper);
 		win.clear();
 		win.set_drawing_color(mlk::client::color::from_hex(0xffffffff));
 
@@ -71,6 +72,7 @@
 		sprite.draw(win, 37, mlk::point{10 + 8 + width, 10 + 8 + 16});
 
 		win.present();
+		std::this_thread::sleep_for(50ms);
 	}
 
 	return 0;