changeset 211:ac99f440ee44

client: add more javascript bindings
author David Demelier <markand@malikania.fr>
date Fri, 04 Jan 2019 17:00:04 +0100
parents 1599919b5de6
children e50f51702df4
files libmlk-client-js/CMakeLists.txt libmlk-client-js/malikania/client/js/context.cpp libmlk-client-js/malikania/client/js/context.hpp libmlk-client-js/malikania/client/js/event_js_api.cpp libmlk-client-js/malikania/client/js/event_js_api.hpp libmlk-client-js/malikania/client/js/key_js_api.cpp libmlk-client-js/malikania/client/js/key_js_api.hpp libmlk-client-js/malikania/client/js/window_js_api.cpp libmlk-client/malikania/client/event.hpp libmlk-client/malikania/client/window.cpp libmlk-js/CMakeLists.txt libmlk-js/malikania/js/context.cpp libmlk-js/malikania/js/context.hpp mlk-client/main.cpp
diffstat 14 files changed, 672 insertions(+), 61 deletions(-) [+]
line wrap: on
line diff
--- a/libmlk-client-js/CMakeLists.txt	Fri Dec 07 19:31:10 2018 +0100
+++ b/libmlk-client-js/CMakeLists.txt	Fri Jan 04 17:00:04 2019 +0100
@@ -26,10 +26,16 @@
 	${PROJECT_SOURCE_DIR}/malikania/client/js/animator_js_api.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/color_js_api.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/color_js_api.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/js/context.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/js/context.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/js/event_js_api.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/js/event_js_api.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/font_js_api.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/font_js_api.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/image_js_api.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/image_js_api.hpp
+	${PROJECT_SOURCE_DIR}/malikania/client/js/key_js_api.cpp
+	${PROJECT_SOURCE_DIR}/malikania/client/js/key_js_api.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/loader_js_api.cpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/loader_js_api.hpp
 	${PROJECT_SOURCE_DIR}/malikania/client/js/painter_js_api.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/context.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,49 @@
+/*
+ * context.cpp -- convenient Duktape context for libmlk-client-js
+ *
+ * Copyright (c) 2017-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 "animation_js_api.hpp"
+#include "animator_js_api.hpp"
+#include "color_js_api.hpp"
+#include "context.hpp"
+#include "event_js_api.hpp"
+#include "font_js_api.hpp"
+#include "image_js_api.hpp"
+#include "key_js_api.hpp"
+#include "painter_js_api.hpp"
+#include "sprite_js_api.hpp"
+#include "texture_js_api.hpp"
+#include "window_js_api.hpp"
+
+namespace mlk::client::js {
+
+context::context()
+{
+	load_animation_api(*this);
+	load_animator_api(*this);
+	load_color_api(*this);
+	load_event_api(*this);
+	load_font_api(*this);
+	load_image_api(*this);
+	load_key_api(*this);
+	load_painter_api(*this);
+	load_sprite_api(*this);
+	load_texture_api(*this);
+	load_window_api(*this);
+}
+
+} // !mlk::client::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/context.hpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,46 @@
+/*
+ * context.hpp -- convenient Duktape context for libmlk-client-js
+ *
+ * Copyright (c) 2017-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_JS_CONTEXT_HPP
+#define MALIKANIA_CLIENT_JS_CONTEXT_HPP
+
+/**
+ * \file context.hpp
+ * \brief Convenient Duktape context for libmlk-client-js.
+ */
+
+#include <malikania/js/context.hpp>
+
+namespace mlk::client::js {
+
+/**
+ * \brief Convenient Duktape context for libmlk-client-js.
+ *
+ * This class automatically load libmlk-client-js API.
+ */
+class context : public mlk::js::context {
+public:
+	/**
+	 * Constructor.
+	 */
+	context();
+};
+
+} // !mlk::client::js
+
+#endif // !MALIKANIA_CLIENT_JS_CONTEXT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/event_js_api.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,127 @@
+/*
+ * event_js_api.cpp -- event object (JavaScript binding)
+ *
+ * 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 <malikania/client/event.hpp>
+
+#include "event_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+enum class type {
+	key_event,
+	mouse_click_event,
+	mouse_motion_event,
+	mouse_wheel_event,
+	text_event,
+	quit_event,
+};
+
+const duk_number_list_entry codes[] = {
+	{ "KEY",                static_cast<int>(type::key_event)               },
+	{ "MOUSE_CLICK",        static_cast<int>(type::mouse_click_event)       },
+	{ "MOUSE_MOTION",       static_cast<int>(type::mouse_motion_event)      },
+	{ "MOUSE_WHEEL",        static_cast<int>(type::mouse_wheel_event)       },
+	{ "TEXT",               static_cast<int>(type::text_event)              },
+	{ "QUIT",               static_cast<int>(type::quit_event)              },
+	{ nullptr,              -1                                              }
+};
+
+struct converter {
+	duk_context* ctx{nullptr};
+
+	void operator()(const std::monostate&)
+	{
+		// no-op.
+	}
+
+	void operator()(const quit_event& ev)
+	{
+		duk_push_int(ctx, static_cast<int>(type::quit_event));
+		duk_put_prop_string(ctx, -2, "type");
+	}
+
+	void operator()(const key_event& ev)
+	{
+		duk_push_int(ctx, static_cast<int>(type::key_event));
+		duk_put_prop_string(ctx, -2, "type");
+		duk_push_int(ctx, static_cast<int>(ev.keycode));
+		duk_put_prop_string(ctx, -2, "keycode");
+		duk_push_int(ctx, static_cast<int>(ev.scancode));
+		duk_put_prop_string(ctx, -2, "scancode");
+		duk_push_int(ctx, static_cast<int>(ev.modifiers));
+		duk_put_prop_string(ctx, -2, "modifiers");
+	}
+
+	void operator()(const mouse_click_event& ev)
+	{
+		duk_push_int(ctx, static_cast<int>(type::mouse_click_event));
+		duk_put_prop_string(ctx, -2, "type");
+	}
+
+	void operator()(const mouse_motion_event& ev)
+	{
+		duk_push_int(ctx, static_cast<int>(type::mouse_motion_event));
+		duk_put_prop_string(ctx, -2, "type");
+	}
+
+	void operator()(const mouse_wheel_event& ev)
+	{
+		duk_push_int(ctx, static_cast<int>(type::mouse_wheel_event));
+		duk_put_prop_string(ctx, -2, "type");
+	}
+
+	void operator()(const text_event& ev)
+	{
+		duk_push_int(ctx, static_cast<int>(type::text_event));
+		duk_put_prop_string(ctx, -2, "type");
+	}
+};
+
+} // !namespace
+
+void load_event_api(duk_context* ctx)
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_object(ctx);
+	duk_put_number_list(ctx, -1, codes);
+	duk_put_prop_string(ctx, -2, "Event");
+	duk_pop(ctx);
+}
+
+} // !namespace mlk::client::js
+
+namespace mlk::js::duk {
+
+void type_traits<mlk::client::event>::push(duk_context* ctx, const mlk::client::event& ev)
+{
+	if (ev.index() == 0)
+		duk_push_undefined(ctx);
+	else {
+		duk_push_object(ctx);
+
+		// TODO: remove until libstdc++/libc++ is fixed.
+		// https://stackoverflow.com/questions/51309467/using-stdvisit-on-a-class-inheriting-from-stdvariant-libstdc-vs-libc
+		std::visit(mlk::client::js::converter{ctx}, static_cast<mlk::client::variant>(ev));
+	}
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/event_js_api.hpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,63 @@
+/*
+ * event_js_api.hpp -- event object (JavaScript binding)
+ *
+ * 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_JS_EVENT_HPP
+#define MALIKANIA_CLIENT_JS_EVENT_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class event;
+
+namespace js {
+
+/**
+ * Load Malikania.Event API into the context.
+ *
+ * \param ctx the context
+ */
+void load_event_api(duk_context* ctx);
+
+} // !js
+
+} // !client
+
+} // !mlk
+
+namespace mlk::js::duk {
+
+/**
+ * \brief Specialize type_traits with mlk::client::event.
+ */
+template <>
+struct type_traits<mlk::client::event> {
+	/**
+	 * Push an event.
+	 *
+	 * \param ctx the context
+	 * \param ev the event
+	 */
+	static void push(duk_context* ctx, const mlk::client::event& ev);
+};
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_CLIENT_JS_EVENT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/key_js_api.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,206 @@
+/*
+ * key_js_api.cpp -- key definitions (JavaScript binding)
+ *
+ * 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 <malikania/client/key.hpp>
+
+#include "key_js_api.hpp"
+
+namespace duk = mlk::js::duk;
+
+namespace mlk::client::js {
+
+namespace {
+
+const duk_number_list_entry modifiers[] = {
+	{ "NONE",               static_cast<int>(mod::none)             },
+	{ "LEFT_CONTROL",       static_cast<int>(mod::left_control)     },
+	{ "LEFT_ALT",           static_cast<int>(mod::left_alt)         },
+	{ "LEFT_SHIFT",         static_cast<int>(mod::left_shift)       },
+	{ "LEFT_SUPER",         static_cast<int>(mod::left_super)       },
+	{ "RIGHT_CONTROL",      static_cast<int>(mod::right_control)    },
+	{ "RIGHT_ALT",          static_cast<int>(mod::right_alt)        },
+	{ "RIGHT_SHIFT",        static_cast<int>(mod::right_shift)      },
+	{ "RIGHT_SUPER",        static_cast<int>(mod::right_super)      },
+	{ "NUM_LOCK",           static_cast<int>(mod::num_lock)         },
+	{ "CAPS_LOCK",          static_cast<int>(mod::caps_lock)        },
+	{ "CONTROL",            static_cast<int>(mod::control)          },
+	{ "SHIFT",              static_cast<int>(mod::shift)            },
+	{ "ALT",                static_cast<int>(mod::alt)              },
+	{ "SUPER",              static_cast<int>(mod::super)            },
+	{ nullptr,              -1                                      }
+};
+
+const duk_number_list_entry keys[] = {
+	{ "UNKNOWN",            static_cast<int>(key::unknown)                  },
+	{ "A",                  static_cast<int>(key::a)                        },
+	{ "B",                  static_cast<int>(key::b)                        },
+	{ "C",                  static_cast<int>(key::c)                        },
+	{ "D",                  static_cast<int>(key::d)                        },
+	{ "E",                  static_cast<int>(key::e)                        },
+	{ "F",                  static_cast<int>(key::f)                        },
+	{ "G",                  static_cast<int>(key::g)                        },
+	{ "H",                  static_cast<int>(key::h)                        },
+	{ "I",                  static_cast<int>(key::i)                        },
+	{ "J",                  static_cast<int>(key::j)                        },
+	{ "K",                  static_cast<int>(key::k)                        },
+	{ "L",                  static_cast<int>(key::l)                        },
+	{ "M",                  static_cast<int>(key::m)                        },
+	{ "N",                  static_cast<int>(key::n)                        },
+	{ "O",                  static_cast<int>(key::o)                        },
+	{ "P",                  static_cast<int>(key::p)                        },
+	{ "Q",                  static_cast<int>(key::q)                        },
+	{ "R",                  static_cast<int>(key::r)                        },
+	{ "S",                  static_cast<int>(key::s)                        },
+	{ "T",                  static_cast<int>(key::t)                        },
+	{ "U",                  static_cast<int>(key::u)                        },
+	{ "V",                  static_cast<int>(key::v)                        },
+	{ "W",                  static_cast<int>(key::w)                        },
+	{ "X",                  static_cast<int>(key::x)                        },
+	{ "Y",                  static_cast<int>(key::y)                        },
+	{ "Z",                  static_cast<int>(key::z)                        },
+	{ "EXCLAIM",            static_cast<int>(key::exclaim)                  },
+	{ "DOUBLE_QUOTE",       static_cast<int>(key::double_quote)             },
+	{ "PERCENT",            static_cast<int>(key::percent)                  },
+	{ "DOLLAR",             static_cast<int>(key::dollar)                   },
+	{ "AMPERSAND",          static_cast<int>(key::ampersand)                },
+	{ "LEFT_PARENTHESE",    static_cast<int>(key::left_parenthese)          },
+	{ "RIGHT_PARENTHESE",   static_cast<int>(key::right_parenthese)         },
+	{ "ASTERISK",           static_cast<int>(key::asterisk)                 },
+	{ "PLUS",               static_cast<int>(key::plus)                     },
+	{ "COLON",              static_cast<int>(key::colon)                    },
+	{ "LESS",               static_cast<int>(key::less)                     },
+	{ "GREATER",            static_cast<int>(key::greater)                  },
+	{ "QUESTION",           static_cast<int>(key::question)                 },
+	{ "AT",                 static_cast<int>(key::at)                       },
+	{ "CARET",              static_cast<int>(key::caret)                    },
+	{ "UNDERSCORE",         static_cast<int>(key::underscore)               },
+	{ "BACK_QUOTE",         static_cast<int>(key::back_quote)               },
+	{ "QUOTE",              static_cast<int>(key::quote)                    },
+	{ "ONE",                static_cast<int>(key::one)                      },
+	{ "TWO",                static_cast<int>(key::two)                      },
+	{ "THREE",              static_cast<int>(key::three)                    },
+	{ "FOUR",               static_cast<int>(key::four)                     },
+	{ "FIVE",               static_cast<int>(key::five)                     },
+	{ "SIX",                static_cast<int>(key::six)                      },
+	{ "SEVEN",              static_cast<int>(key::seven)                    },
+	{ "EIGHT",              static_cast<int>(key::eight)                    },
+	{ "NINE",               static_cast<int>(key::nine)                     },
+	{ "ZERO",               static_cast<int>(key::zero)                     },
+	{ "ENTER",              static_cast<int>(key::enter)                    },
+	{ "ESCAPE",             static_cast<int>(key::escape)                   },
+	{ "BACKSPACE",          static_cast<int>(key::backspace)                },
+	{ "TAB",                static_cast<int>(key::tab)                      },
+	{ "SPACE",              static_cast<int>(key::space)                    },
+	{ "MINUS",              static_cast<int>(key::minus)                    },
+	{ "EQUALS",             static_cast<int>(key::equals)                   },
+	{ "LEFT_BRACKET",       static_cast<int>(key::left_bracket)             },
+	{ "RIGHT_BRACKET",      static_cast<int>(key::right_bracket)            },
+	{ "BACKSLASH",          static_cast<int>(key::backslash)                },
+	{ "HASH",               static_cast<int>(key::hash)                     },
+	{ "SEMICOLON",          static_cast<int>(key::semicolon)                },
+	{ "APOSTROPHE",         static_cast<int>(key::apostrophe)               },
+	{ "GRAVE",              static_cast<int>(key::grave)                    },
+	{ "COMMA",              static_cast<int>(key::comma)                    },
+	{ "PERIOD",             static_cast<int>(key::period)                   },
+	{ "SLASH",              static_cast<int>(key::slash)                    },
+	{ "CAPS_LOCK",          static_cast<int>(key::caps_lock)                },
+	{ "F1",                 static_cast<int>(key::f1)                       },
+	{ "F2",                 static_cast<int>(key::f2)                       },
+	{ "F3",                 static_cast<int>(key::f3)                       },
+	{ "F4",                 static_cast<int>(key::f4)                       },
+	{ "F5",                 static_cast<int>(key::f5)                       },
+	{ "F6",                 static_cast<int>(key::f6)                       },
+	{ "F7",                 static_cast<int>(key::f7)                       },
+	{ "F8",                 static_cast<int>(key::f8)                       },
+	{ "F9",                 static_cast<int>(key::f9)                       },
+	{ "F1",                 static_cast<int>(key::f10)                      },
+	{ "F11",                static_cast<int>(key::f11)                      },
+	{ "F12",                static_cast<int>(key::f12)                      },
+	{ "F13",                static_cast<int>(key::f13)                      },
+	{ "F14",                static_cast<int>(key::f14)                      },
+	{ "F15",                static_cast<int>(key::f15)                      },
+	{ "F16",                static_cast<int>(key::f16)                      },
+	{ "F17",                static_cast<int>(key::f17)                      },
+	{ "F18",                static_cast<int>(key::f18)                      },
+	{ "F19",                static_cast<int>(key::f19)                      },
+	{ "F20",                static_cast<int>(key::f20)                      },
+	{ "F21",                static_cast<int>(key::f21)                      },
+	{ "F22",                static_cast<int>(key::f22)                      },
+	{ "F23",                static_cast<int>(key::f23)                      },
+	{ "F24",                static_cast<int>(key::f24)                      },
+	{ "PRINT_SCREEN",       static_cast<int>(key::print_screen)             },
+	{ "SCROLL_LOCK",        static_cast<int>(key::scroll_lock)              },
+	{ "PAUSE",              static_cast<int>(key::pause)                    },
+	{ "INSERT",             static_cast<int>(key::insert)                   },
+	{ "HOME",               static_cast<int>(key::home)                     },
+	{ "PAGE_UP",            static_cast<int>(key::page_up)                  },
+	{ "PAGE_DOWN",          static_cast<int>(key::page_down)                },
+	{ "DELETE",             static_cast<int>(key::del)                      },
+	{ "END",                static_cast<int>(key::end)                      },
+	{ "RIGHT",              static_cast<int>(key::right)                    },
+	{ "LEFT",               static_cast<int>(key::left)                     },
+	{ "DOWN",               static_cast<int>(key::down)                     },
+	{ "UP",                 static_cast<int>(key::up)                       },
+	{ "KP_DIVIDE",          static_cast<int>(key::kp_divide)                },
+	{ "KP_MULTIPLY",        static_cast<int>(key::kp_multiply)              },
+	{ "KP_MINUS",           static_cast<int>(key::kp_minus)                 },
+	{ "KP_PLUS",            static_cast<int>(key::kp_plus)                  },
+	{ "KP_ENTER",           static_cast<int>(key::kp_enter)                 },
+	{ "KP_ONE",             static_cast<int>(key::kp_one)                   },
+	{ "KP_TWO",             static_cast<int>(key::kp_two)                   },
+	{ "KP_THREE",           static_cast<int>(key::kp_three)                 },
+	{ "KP_FOUR",            static_cast<int>(key::kp_four)                  },
+	{ "KP_FIVE",            static_cast<int>(key::kp_five)                  },
+	{ "KP_SIX",             static_cast<int>(key::kp_six)                   },
+	{ "KP_SEVEN",           static_cast<int>(key::kp_seven)                 },
+	{ "KP_EIGHT",           static_cast<int>(key::kp_eight)                 },
+	{ "KP_NINE",            static_cast<int>(key::kp_nine)                  },
+	{ "KP_ZERO",            static_cast<int>(key::kp_zero)                  },
+	{ "MUTE",               static_cast<int>(key::mute)                     },
+	{ "VOLUME_UP",          static_cast<int>(key::volume_up)                },
+	{ "VOLUME_DOWN",        static_cast<int>(key::volume_down)              },
+	{ "LEFT_CONTROL",       static_cast<int>(key::left_control)             },
+	{ "LEFT_ALT",           static_cast<int>(key::left_alt)                 },
+	{ "LEFT_SHIFT",         static_cast<int>(key::left_shift)               },
+	{ "LEFT_SUPER",         static_cast<int>(key::left_super)               },
+	{ "RIGHT_CONTROL",      static_cast<int>(key::right_control)            },
+	{ "RIGHT_ALT",          static_cast<int>(key::right_alt)                },
+	{ "RIGHT_SHIFT",        static_cast<int>(key::right_shift)              },
+	{ "RIGHT_SUPER",        static_cast<int>(key::right_super)              },
+	{ nullptr,              -1                                              }
+};
+
+} // !namespace
+
+void load_key_api(duk_context* ctx)
+{
+	duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_object(ctx);
+	duk_push_object(ctx);
+	duk_put_number_list(ctx, -1, modifiers);
+	duk_put_prop_string(ctx, -2, "Modifier");
+	duk_push_object(ctx);
+	duk_put_number_list(ctx, -1, keys);
+	duk_put_prop_string(ctx, -2, "Sym");
+	duk_put_prop_string(ctx, -2, "Key");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/key_js_api.hpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,42 @@
+/*
+ * key_js_api.hpp -- key definitions (JavaScript binding)
+ *
+ * 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_KEY_JS_API_HPP
+#define MALIKANIA_CLIENT_KEY_JS_API_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk::client {
+
+enum class mod;
+enum class key;
+
+namespace js {
+
+/**
+ * Load Malikania.Key API into the context.
+ *
+ * \param ctx the context
+ */
+void load_key_api(duk_context* ctx);
+
+} // !js
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_KEY_JS_API_HPP
--- a/libmlk-client-js/malikania/client/js/window_js_api.cpp	Fri Dec 07 19:31:10 2018 +0100
+++ b/libmlk-client-js/malikania/client/js/window_js_api.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -22,6 +22,7 @@
 
 #include <malikania/client/window.hpp>
 
+#include "event_js_api.hpp"
 #include "window_js_api.hpp"
 
 namespace mlk::client::js {
@@ -73,8 +74,20 @@
 	return 0;
 }
 
+auto Window_prototype_poll(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		mlk::js::duk::push(ctx, self(ctx).poll());
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 1;
+}
+
 const duk_function_list_entry methods[] = {
 	{ "sleep",      Window_prototype_sleep,         1 },
+	{ "poll",       Window_prototype_poll,          0 },
 	{ nullptr,      nullptr,                        0 }
 };
 
--- a/libmlk-client/malikania/client/event.hpp	Fri Dec 07 19:31:10 2018 +0100
+++ b/libmlk-client/malikania/client/event.hpp	Fri Jan 04 17:00:04 2019 +0100
@@ -75,12 +75,6 @@
 };
 
 /**
- * \brief Event occured but is unknown.
- */
-struct unknown_event {
-};
-
-/**
  * \brief User has requested quit.
  */
 struct quit_event {
@@ -96,8 +90,7 @@
 	mouse_click_event,
 	mouse_motion_event,
 	mouse_wheel_event,
-	text_event,
-	unknown_event
+	text_event
 >;
 
 /**
--- a/libmlk-client/malikania/client/window.cpp	Fri Dec 07 19:31:10 2018 +0100
+++ b/libmlk-client/malikania/client/window.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -432,23 +432,23 @@
 {
 	SDL_Event event;
 
-	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_QUIT:
-		return quit_event{};
-	default:
-		break;
+	while (SDL_PollEvent(&event)) {
+		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_QUIT:
+			return quit_event();
+		default:
+			// Skip non-managed events.
+			break;
+		}
 	}
 
-	return unknown_event{};
+	return std::monostate();
 }
 
 } // !mlk::client
--- a/libmlk-js/CMakeLists.txt	Fri Dec 07 19:31:10 2018 +0100
+++ b/libmlk-js/CMakeLists.txt	Fri Jan 04 17:00:04 2019 +0100
@@ -22,6 +22,8 @@
 
 set(
 	SOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/context.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/context.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/duk.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/duk.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/elapsed_timer_js_api.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/context.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,54 @@
+/*
+ * context.cpp -- convenient Duktape context for libmlk-js
+ *
+ * Copyright (c) 2017-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 <iostream>
+
+#include "elapsed_timer_js_api.hpp"
+#include "line_js_api.hpp"
+#include "point_js_api.hpp"
+#include "rectangle_js_api.hpp"
+#include "size_js_api.hpp"
+
+#include "context.hpp"
+
+namespace mlk::js {
+
+namespace {
+
+auto print(duk_context* ctx) -> duk_ret_t
+{
+	std::cout << duk_to_string(ctx, -1) << std::endl;
+
+	return 0;
+}
+
+} // !namespace
+
+context::context()
+{
+	duk_push_c_function(*this, print, 1);
+	duk_put_global_string(*this, "print");
+
+	load_elapsed_timer_api(*this);
+	load_line_api(*this);
+	load_point_api(*this);
+	load_rectangle_api(*this);
+	load_size_api(*this);
+}
+
+} // !mlk::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/context.hpp	Fri Jan 04 17:00:04 2019 +0100
@@ -0,0 +1,46 @@
+/*
+ * context.hpp -- convenient Duktape context for libmlk-js
+ *
+ * Copyright (c) 2017-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_JS_CONTEXT_HPP
+#define MALIKANIA_JS_CONTEXT_HPP
+
+/**
+ * \file context.hpp
+ * \brief Convenient Duktape context for libmlk-js.
+ */
+
+#include "duk.hpp"
+
+namespace mlk::js {
+
+/**
+ * \brief Convenient Duktape context for libmlk-js.
+ *
+ * This class automatically load libmlk-js API.
+ */
+class context : public duk::context {
+public:
+	/**
+	 * Constructor.
+	 */
+	context();
+};
+
+} // !mlk::js
+
+#endif // !MALIKANIA_JS_CONTEXT_HPP
--- a/mlk-client/main.cpp	Fri Dec 07 19:31:10 2018 +0100
+++ b/mlk-client/main.cpp	Fri Jan 04 17:00:04 2019 +0100
@@ -22,27 +22,7 @@
 #include <iostream>
 #include <iterator>
 
-#include <malikania/js/elapsed_timer_js_api.hpp>
-#include <malikania/js/line_js_api.hpp>
-#if 0
-#include <malikania/js/loader_js_api.hpp>
-#endif
-#include <malikania/js/point_js_api.hpp>
-#include <malikania/js/rectangle_js_api.hpp>
-#include <malikania/js/size_js_api.hpp>
-
-#include <malikania/client/js/animation_js_api.hpp>
-#include <malikania/client/js/animator_js_api.hpp>
-#include <malikania/client/js/color_js_api.hpp>
-#include <malikania/client/js/font_js_api.hpp>
-#include <malikania/client/js/image_js_api.hpp>
-#if 0
-#include <malikania/client/js/loader_js_api.hpp>
-#endif
-#include <malikania/client/js/painter_js_api.hpp>
-#include <malikania/client/js/sprite_js_api.hpp>
-#include <malikania/client/js/texture_js_api.hpp>
-#include <malikania/client/js/window_js_api.hpp>
+#include <malikania/client/js/context.hpp>
 
 int main(int argc, char** argv)
 {
@@ -55,23 +35,7 @@
 	}
 
 	try {
-		mlk::js::duk::context ctx;
-
-		mlk::js::load_elapsed_timer_api(ctx);
-		mlk::js::load_line_api(ctx);
-		mlk::js::load_point_api(ctx);
-		mlk::js::load_rectangle_api(ctx);
-		mlk::js::load_size_api(ctx);
-
-		mlk::client::js::load_animation_api(ctx);
-		mlk::client::js::load_animator_api(ctx);
-		mlk::client::js::load_color_api(ctx);
-		mlk::client::js::load_image_api(ctx);
-		mlk::client::js::load_painter_api(ctx);
-		mlk::client::js::load_sprite_api(ctx);
-		mlk::client::js::load_texture_api(ctx);
-		mlk::client::js::load_window_api(ctx);
-
+		mlk::client::js::context ctx;
 		std::ifstream input(argv[0]);
 		std::string script(
 			std::istreambuf_iterator<char>(input.rdbuf()),