diff --git a/src/Magnum/Platform/EmscriptenApplication.cpp b/src/Magnum/Platform/EmscriptenApplication.cpp index 95fda6497d..35b06613a1 100644 --- a/src/Magnum/Platform/EmscriptenApplication.cpp +++ b/src/Magnum/Platform/EmscriptenApplication.cpp @@ -77,14 +77,23 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const attrs.depth = glConfiguration.depthBufferSize() > 0; attrs.stencil = glConfiguration.stencilBufferSize() > 0; attrs.antialias = glConfiguration.sampleCount() > 0; - // attrs.premultipliedAlpha = ; - // attrs.preserveDrawingBuffer = ; - // attrs.preferLowPowerToHighPerformance = ; - // attrs.failIfMajorPerformanceCaveat = ; - // attrs.enableExtensionsByDefault = ; + + attrs.premultipliedAlpha = + (glConfiguration.flags() & GLConfiguration::Flag::PremultipliedAlpha) != GLConfiguration::Flags{}; + attrs.preserveDrawingBuffer = + (glConfiguration.flags() & GLConfiguration::Flag::PreserveDrawingBuffer) != GLConfiguration::Flags{}; + attrs.preferLowPowerToHighPerformance = + (glConfiguration.flags() & GLConfiguration::Flag::PreferLowPowerToHighPerformance) != GLConfiguration::Flags{}; + attrs.failIfMajorPerformanceCaveat = + (glConfiguration.flags() & GLConfiguration::Flag::FailIfMajorPerformanceCaveat) != GLConfiguration::Flags{}; + attrs.enableExtensionsByDefault = + (glConfiguration.flags() & GLConfiguration::Flag::EnableExtensionsByDefault) != GLConfiguration::Flags{}; + attrs.renderViaOffscreenBackBuffer = + (glConfiguration.flags() & GLConfiguration::Flag::RenderViaOffscreenBackBuffer) != GLConfiguration::Flags{}; + attrs.proxyContextToMainThread = + (glConfiguration.flags() & GLConfiguration::Flag::ProxyContextToMainThread) != GLConfiguration::Flags{}; + attrs.explicitSwapControl = true; - // attrs.proxyContextToMainThread = ; - // attrs.renderViaOffscreenBackBuffer = ; /* Resize window and match it to the selected format */ double w, h; @@ -122,7 +131,78 @@ void EmscriptenApplication::swapBuffers() { emscripten_webgl_commit_frame(); } +void EmscriptenApplication::setupCallbacks() { + auto mousePressedCallback = + [](int, const EmscriptenMouseEvent* event, void* userData) -> Int { + MouseEvent e{event}; + reinterpret_cast(userData)->mousePressEvent(e); + return e.isAccepted(); + }; + emscripten_set_mousedown_callback(nullptr, this, false, mousePressedCallback); + + auto mouseReleaseCallback = + [](int, const EmscriptenMouseEvent* event, void* userData) -> Int { + MouseEvent e{event}; + reinterpret_cast(userData)->mouseReleaseEvent(e); + return e.isAccepted(); + }; + emscripten_set_mouseup_callback(nullptr, this, false, mouseReleaseCallback); + + auto mouseMoveCallback = + [](int, const EmscriptenMouseEvent* event, void* userData) -> Int { + MouseMoveEvent e{event}; + reinterpret_cast(userData)->mouseMoveEvent(e); + return e.isAccepted(); + }; + emscripten_set_mousemove_callback(nullptr, this, false, mouseMoveCallback); + + /* + emscripten_set_touchstart_callback(nullptr, this, false, + [](int, const EmscriptenTouchEvent* event, void* userData) -> Int { + MouseEvent e{event}; + reinterpret_cast(userData)->mousePressEvent(e); + return e.isAccepted(); + }); + emscripten_set_touchend_callback(nullptr, this, false, + [](int, const EmscriptenTouchEvent* event, void* userData) -> Int { + MouseEvent e{event}; + reinterpret_cast(userData)->mouseReleaseEvent(e); + return e.isAccepted(); + }); + emscripten_set_touchmove_callback(nullptr, this, false, + [](int, const EmscriptenTouchEvent* event, void* userData) -> Int { + MouseMoveEvent e{event}; + reinterpret_cast(userData)->mouseMoveEvent(e); + return e.isAccepted(); + }); + // TODO: touchcancel event? + // TODO: multiTouchGesture event? + + */ + auto keyPressCallback = + [](int, const EmscriptenKeyboardEvent* event, void* userData) -> Int { + KeyEvent e{event}; + reinterpret_cast(userData)->keyPressEvent(e); + return e.isAccepted(); + }; + emscripten_set_keypress_callback(nullptr, this, false, keyPressCallback); + + auto keyReleaseCallback = + [](int, const EmscriptenKeyboardEvent* event, void* userData) -> Int { + KeyEvent e{event}; + reinterpret_cast(userData)->keyReleaseEvent(e); + return e.isAccepted(); + }; + emscripten_set_keyup_callback(nullptr, this, false, keyReleaseCallback); +} + +EmscriptenApplication::KeyEvent::Key EmscriptenApplication::KeyEvent::toKey(const EM_UTF8* keyCode) { + return Key::A; +} + void EmscriptenApplication::viewportEvent(ViewportEvent&) {} +void EmscriptenApplication::keyPressEvent(KeyEvent&) {} +void EmscriptenApplication::keyReleaseEvent(KeyEvent&) {} void EmscriptenApplication::mousePressEvent(MouseEvent&) {} void EmscriptenApplication::mouseReleaseEvent(MouseEvent&) {} void EmscriptenApplication::mouseMoveEvent(MouseMoveEvent&) {} diff --git a/src/Magnum/Platform/EmscriptenApplication.h b/src/Magnum/Platform/EmscriptenApplication.h index 63a8a5a207..57e7241afd 100644 --- a/src/Magnum/Platform/EmscriptenApplication.h +++ b/src/Magnum/Platform/EmscriptenApplication.h @@ -127,6 +127,7 @@ class EmscriptenApplication { class InputEvent; class MouseEvent; class MouseMoveEvent; + class KeyEvent; /** * @brief Execute the application @@ -267,6 +268,16 @@ class EmscriptenApplication { /*@}*/ + /** @{ @name Keyboard handling */ + + /** @copydoc Sdl2Application::keyPressEvent() */ + virtual void keyPressEvent(KeyEvent& event); + + /** @copydoc Sdl2Application::keyReleaseEvent() */ + virtual void keyReleaseEvent(KeyEvent& event); + + /*@}*/ + /** @{ @name Mouse handling */ #ifdef DOXYGEN_GENERATING_OUTPUT @@ -309,6 +320,8 @@ class EmscriptenApplication { std::unique_ptr _context; CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) + + void setupCallbacks(); }; CORRADE_ENUMSET_OPERATORS(EmscriptenApplication::Flags) @@ -322,8 +335,101 @@ Double-buffered RGBA canvas with depth and stencil buffers. */ class EmscriptenApplication::GLConfiguration { public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags(), @ref Context::Flag + * @requires_gles Context flags are not available in WebGL. + */ + enum class Flag: int { + /** + * Premultiplied alpha + * + * If set, the alpha channel of the rendering context will be + * treated as representing premultiplied alpha values. If not set, the + * alpha channel represents non-premultiplied alpha. + */ + PremultipliedAlpha, + + /** @TODO */ + PreserveDrawingBuffer, + + /** @TODO */ + PreferLowPowerToHighPerformance, + + /** @TODO */ + FailIfMajorPerformanceCaveat, + + /** @TODO */ + EnableExtensionsByDefault, + + /** @TODO */ + RenderViaOffscreenBackBuffer, + + /** @TODO */ + ProxyContextToMainThread, + }; + + /** + * @brief Context flags + * + * @see @ref setFlags(), @ref Context::Flags + * @requires_gles Context flags are not available in WebGL. + */ + //typedef Containers::EnumSet Flags; + typedef Containers::EnumSet Flags; + /*implicit*/ GLConfiguration(); + /** + * @brief Context flags + * + * @requires_gles Context flags are not available in WebGL. + */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is @ref Flag::ForwardCompatible on desktop GL and no flags + * on OpenGL ES. + * @see @ref addFlags(), @ref clearFlags(), @ref GL::Context::flags() + * @requires_gles Context flags are not available in WebGL. + */ + GLConfiguration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + /** + * @brief Add context flags + * @return Reference to self (for method chaining) + * + * Unlike @ref setFlags(), ORs the flags with existing instead of + * replacing them. Useful for preserving the defaults. + * @see @ref clearFlags() + * @requires_gles Context flags are not available in WebGL. + */ + GLConfiguration& addFlags(Flags flags) { + _flags |= flags; + return *this; + } + + /** + * @brief Clear context flags + * @return Reference to self (for method chaining) + * + * Unlike @ref setFlags(), ANDs the inverse of @p flags with existing + * instead of replacing them. Useful for removing default flags. + * @see @ref addFlags() + * @requires_gles Context flags are not available in WebGL. + */ + GLConfiguration& clearFlags(Flags flags) { + _flags &= ~flags; + return *this; + } + /** * @brief Set context version * @@ -398,6 +504,8 @@ class EmscriptenApplication::GLConfiguration { Vector4i _colorBufferSize; Int _depthBufferSize, _stencilBufferSize; Int _sampleCount; + + Flags _flags; }; /** @@ -474,11 +582,55 @@ class EmscriptenApplication::ViewportEvent { /** @brief Base for input events -@see @ref MouseEvent, @ref MouseMoveEvent, @ref mousePressEvent(), - @ref mouseReleaseEvent(), @ref mouseMoveEvent() +@see @ref KeyEvent, @ref MouseEvent, @ref MouseMoveEvent, @ref keyPressEvent(), + @ref mousePressEvent(), @ref mouseReleaseEvent(), @ref mouseMoveEvent() */ class EmscriptenApplication::InputEvent { public: + /** + * @brief Modifier + * + * @see @ref Modifiers, @ref KeyEvent::modifiers(), + * @ref MouseEvent::modifiers() + */ + enum class Modifier: Int { + /** + * Shift + * + * @see @ref KeyEvent::Key::LeftShift, @ref KeyEvent::Key::RightShift + */ + Shift, + + /** + * Ctrl + * + * @see @ref KeyEvent::Key::LeftCtrl, @ref KeyEvent::Key::RightCtrl + */ + Ctrl, + + /** + * Alt + * + * @see @ref KeyEvent::Key::LeftAlt, @ref KeyEvent::Key::RightAlt + */ + Alt, + + /** + * Super key (Windows/⌘) + * + * @see @ref KeyEvent::Key::LeftSuper, @ref KeyEvent::Key::RightSuper + */ + Super + }; + + /** + * @brief Set of modifiers + * + * @see @ref KeyEvent::modifiers(), @ref MouseEvent::modifiers(), + * @ref MouseMoveEvent::modifiers() + */ + typedef Containers::EnumSet Modifiers; + /** @brief Copying is not allowed */ InputEvent(const InputEvent&) = delete; @@ -491,28 +643,17 @@ class EmscriptenApplication::InputEvent { /** @brief Moving is not allowed */ InputEvent& operator=(InputEvent&&) = delete; - /** - * @brief Set event as accepted - * - * If the event is ignored (i.e., not set as accepted), it will be - * propagated elsewhere, for example to the Emscripten system or to - * another screen when using @ref BasicScreenedApplication "ScreenedApplication". - * By default is each event ignored and thus propagated. - */ + /** @copydoc Sdl2Application::InputEvent::setAccepted() */ void setAccepted(bool accepted = true) { _accepted = accepted; } - /** @brief Whether the event is accepted */ + /** @copydoc Sdl2Application::InputEvent::isAccepted() */ bool isAccepted() const { return _accepted; } - #ifndef DOXYGEN_GENERATING_OUTPUT protected: - explicit InputEvent(EmscriptenMouseEvent* event): _event(event), _accepted(false) {} + explicit InputEvent(): _accepted(false) {} ~InputEvent() = default; - EmscriptenMouseEvent* _event; - #endif - private: bool _accepted; }; @@ -522,7 +663,7 @@ class EmscriptenApplication::InputEvent { @see @ref MouseMoveEvent, @ref mousePressEvent(), @ref mouseReleaseEvent() */ -class EmscriptenApplication::MouseEvent: public InputEvent { +class EmscriptenApplication::MouseEvent: public EmscriptenApplication::InputEvent { friend EmscriptenApplication; public: @@ -532,42 +673,14 @@ class EmscriptenApplication::MouseEvent: public InputEvent { * @see @ref button() */ enum class Button: std::int32_t { - /** No button was pressed (touch or stylus event) */ - None = 0, - - /** - * Left mouse button. Note that this button is not set if only - * touch or stylus event occured. - * @attention Available since Emscripten 4.0 (API level 14), not - * detectable in earlier versions. - */ - #if defined(DOXYGEN_GENERATING_OUTPUT) || __EMSCRIPTEN_API__ >= 14 - Left = AMOTION_EVENT_BUTTON_PRIMARY, - #else + /** Left mouse button */ Left = 1 << 0, - #endif - /** - * Middle mouse button or second stylus button - * @attention Available since Emscripten 4.0 (API level 14), not - * detectable in earlier versions. - */ - #if defined(DOXYGEN_GENERATING_OUTPUT) || __EMSCRIPTEN_API__ >= 14 - Middle = AMOTION_EVENT_BUTTON_TERTIARY, - #else + /** Middle mouse button */ Middle = 1 << 1, - #endif - /** - * Right mouse button or first stylus button - * @attention Available since Emscripten 4.0 (API level 14), not - * detectable in earlier versions. - */ - #if defined(DOXYGEN_GENERATING_OUTPUT) || __EMSCRIPTEN_API__ >= 14 - Right = AMOTION_EVENT_BUTTON_SECONDARY - #else + /** Right mouse button */ Right = 1 << 2 - #endif }; /** @brief Button */ @@ -577,12 +690,13 @@ class EmscriptenApplication::MouseEvent: public InputEvent { /** @brief Position */ Vector2i position() { - return {Int(_event->movementX), - Int(_event->movementY)}; + return {Int(_event->clientX), Int(_event->clientY)}; } private: - explicit MouseEvent(EmscriptenMouseEvent* event): InputEvent(event) {} + explicit MouseEvent(const EmscriptenMouseEvent* event): _event(event) {} + + const EmscriptenMouseEvent* _event; }; /** @@ -590,7 +704,7 @@ class EmscriptenApplication::MouseEvent: public InputEvent { @see @ref MouseEvent, @ref mouseMoveEvent() */ -class EmscriptenApplication::MouseMoveEvent: public InputEvent { +class EmscriptenApplication::MouseMoveEvent: public EmscriptenApplication::InputEvent { friend EmscriptenApplication; public: @@ -600,39 +714,14 @@ class EmscriptenApplication::MouseMoveEvent: public InputEvent { * @see @ref buttons() */ enum class Button: std::int32_t { - /** - * Left mouse button. Note that this button is not set if only - * touch or stylus event occured. - * @attention Available since Emscripten 4.0 (API level 14), not - * detectable in earlier versions. - */ - #if defined(DOXYGEN_GENERATING_OUTPUT) || __EMSCRIPTEN_API__ >= 14 - Left = AMOTION_EVENT_BUTTON_PRIMARY, - #else + /** Left mouse button */ Left = 1 << 0, - #endif - /** - * Middle mouse button or second stylus button - * @attention Available since Emscripten 4.0 (API level 14), not - * detectable in earlier versions. - */ - #if defined(DOXYGEN_GENERATING_OUTPUT) || __EMSCRIPTEN_API__ >= 14 - Middle = AMOTION_EVENT_BUTTON_TERTIARY, - #else + /** Middle mouse button */ Middle = 1 << 1, - #endif - /** - * Right mouse button or first stylus button - * @attention Available since Emscripten 4.0 (API level 14), not - * detectable in earlier versions. - */ - #if defined(DOXYGEN_GENERATING_OUTPUT) || __EMSCRIPTEN_API__ >= 14 - Right = AMOTION_EVENT_BUTTON_SECONDARY - #else + /** Right mouse button */ Right = 1 << 2 - #endif }; /** @@ -644,18 +733,217 @@ class EmscriptenApplication::MouseMoveEvent: public InputEvent { /** @brief Position */ Vector2i position() const { - return {Int(_event->clientX), - Int(_event->clientY)}; + return {Int(_event->clientX), Int(_event->clientY)}; } /** @brief Mouse buttons */ Buttons buttons() const { - #if __EMSCRIPTEN_API__ >= 14 - return Button(AMotionEvent_getButtonState(_event)); - #else - return {}; - #endif + return Button(_event->buttons); + } + + private: + explicit MouseMoveEvent(const EmscriptenMouseEvent* event): _event(event) {} + + const EmscriptenMouseEvent* _event; +}; + +/** +@brief Key event + +@see @ref keyPressEvent(), @ref keyReleaseEvent() +*/ +class EmscriptenApplication::KeyEvent: public EmscriptenApplication::InputEvent { + friend EmscriptenApplication; + + public: + + /** + * @brief Key + * + * @see @ref key() + */ + enum class Key: Int { + Unknown, /**< Unknown key */ + + /** + * Left Shift + * + * @see @ref InputEvent::Modifier::Shift + */ + LeftShift, + + /** + * Right Shift + * + * @see @ref InputEvent::Modifier::Shift + */ + RightShift, + + /** + * Left Ctrl + * + * @see @ref InputEvent::Modifier::Ctrl + */ + LeftCtrl, + + /** + * Right Ctrl + * + * @see @ref InputEvent::Modifier::Ctrl + */ + RightCtrl, + + /** + * Left Alt + * + * @see @ref InputEvent::Modifier::Alt + */ + LeftAlt, + + /** + * Right Alt + * + * @see @ref InputEvent::Modifier::Alt + */ + RightAlt, + + /** + * Left Super key (Windows/⌘) + * + * @see @ref InputEvent::Modifier::Super + */ + LeftSuper, + + /** + * Right Super key (Windows/⌘) + * + * @see @ref InputEvent::Modifier::Super + */ + RightSuper, + + Enter, /**< Enter */ + Esc, /**< Escape */ + + Up, /**< Up arrow */ + Down, /**< Down arrow */ + Left, /**< Left arrow */ + Right, /**< Right arrow */ + Home, /**< Home */ + End, /**< End */ + PageUp, /**< Page up */ + PageDown, /**< Page down */ + Backspace, /**< Backspace */ + Insert, /**< Insert */ + Delete, /**< Delete */ + + F1, /**< F1 */ + F2, /**< F2 */ + F3, /**< F3 */ + F4, /**< F4 */ + F5, /**< F5 */ + F6, /**< F6 */ + F7, /**< F7 */ + F8, /**< F8 */ + F9, /**< F9 */ + F10, /**< F10 */ + F11, /**< F11 */ + F12, /**< F12 */ + + Space, /**< Space */ + Tab, /**< Tab */ + Comma, /**< Comma */ + Period, /**< Period */ + Minus, /**< Minus */ + /* Note: This may only be represented as SHIFT + = */ + Plus, /**< Plus */ + Slash, /**< Slash */ + /* Note: This may only be represented as SHIFT + 5 */ + Percent, /**< Percent */ + Smicolon, /**< Semicolon */ + Equal, /**< Equal */ + + Zero, /**< Zero */ + One, /**< One */ + Two, /**< Two */ + Three, /**< Three */ + Four, /**< Four */ + Five, /**< Five */ + Six, /**< Six */ + Seven, /**< Seven */ + Eight, /**< Eight */ + Nine, /**< Nine */ + + A, /**< Letter A */ + B, /**< Letter B */ + C, /**< Letter C */ + D, /**< Letter D */ + E, /**< Letter E */ + F, /**< Letter F */ + G, /**< Letter G */ + H, /**< Letter H */ + I, /**< Letter I */ + J, /**< Letter J */ + K, /**< Letter K */ + L, /**< Letter L */ + M, /**< Letter M */ + N, /**< Letter N */ + O, /**< Letter O */ + P, /**< Letter P */ + Q, /**< Letter Q */ + R, /**< Letter R */ + S, /**< Letter S */ + T, /**< Letter T */ + U, /**< Letter U */ + V, /**< Letter V */ + W, /**< Letter W */ + X, /**< Letter X */ + Y, /**< Letter Y */ + Z, /**< Letter Z */ + + CapsLock, /**< Caps lock */ + ScrollLock, /**< Scroll lock */ + NumLock, /**< Num lock */ + PrintScreen,/**< Print screen */ + Pause, /**< Pause */ + Menu, /**< Menu */ + + NumZero, /**< Numpad zero */ + NumOne, /**< Numpad one */ + NumTwo, /**< Numpad two */ + NumThree, /**< Numpad three */ + NumFour, /**< Numpad four */ + NumFive, /**< Numpad five */ + NumSix, /**< Numpad six */ + NumSeven, /**< Numpad seven */ + NumEight, /**< Numpad eight */ + NumNine, /**< Numpad nine */ + NumDecimal, /**< Numpad decimal */ + NumDivide, /**< Numpad divide */ + NumMultiply, /**< Numpad multiply */ + NumSubtract, /**< Numpad subtract */ + NumAdd, /**< Numpad add */ + NumEnter, /**< Numpad enter */ + NumEqual /**< Numpad equal */ + }; + + + /** @brief Position */ + Key key() const { + return toKey(_event->key); + } + + /** @brief Modifiers */ + Modifiers modifiers() const { + return {}; //TODO! } + + private: + explicit KeyEvent(const EmscriptenKeyboardEvent* event): _event(event) {} + + const EmscriptenKeyboardEvent* _event; + + /* Translate emscripten key code to Key enum */ + static Key toKey(const EM_UTF8* keyCode); }; CORRADE_ENUMSET_OPERATORS(EmscriptenApplication::MouseMoveEvent::Buttons)