diff --git a/doc/changelog.dox b/doc/changelog.dox index 3f5df63cf..11b4471f0 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -882,6 +882,9 @@ See also: @relativeref{Platform::Sdl2Application,scanCodeToKey()} and @ref Platform::GlfwApplication::keyToScanCode() helpers for key / scan code conversion outside of event handlers +- Added @ref Platform::Sdl2Application::isKeyPressed() and + @ref Platform::GlfwApplication::isKeyPressed() for immediate key state + queries - Updated @ref Platform::AndroidApplication to not use a deprecated API that was removed in NDK 27 ([mosra/magnum#659](https://github.com/mosra/magnum/pull/659)) diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index 4e148b2ac..141d8ec29 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -905,6 +905,13 @@ void GlfwApplication::exit(int exitCode) { if(_window) glfwSetWindowShouldClose(_window, true); } +bool GlfwApplication::isKeyPressed(const Key key) { + /* Documentation says GLFW_KEY_UNKNOWN is not valid for glfwGetKey() */ + if(key == Key::Unknown) + return false; + return glfwGetKey(_window, int(key)) == GLFW_PRESS; +} + namespace { constexpr Int CursorMap[] { diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index 24341b54d..a7310cb32 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -649,11 +649,30 @@ class GlfwApplication { /** @{ @name Keyboard handling */ + public: + /** + * @brief Whether a key is pressed + * @m_since_latest + * + * If a key is pressed, i.e. @ref keyPressEvent() with given @p key was + * fired but not a @ref keyReleaseEvent() yet, the function returns + * @cpp true @ce. For unknown @ref Key values returns @cpp false @ce. + * + * This function only queries immediate keyboard state at given point + * in time, which means that if a particular key got pressed and + * released again in between calls to this function, it will not be + * reported as pressed. To avoid losing key presses, prefer to get + * keyboard press and release events through @ref keyPressEvent() and + * @ref keyReleaseEvent() instead if possible. + */ + bool isKeyPressed(Key key); + + private: /** * @brief Key press event * * Called when a key is pressed. Default implementation does nothing. - * @see @ref platform-windowed-key-events + * @see @ref platform-windowed-key-events, @ref isKeyPressed() */ virtual void keyPressEvent(KeyEvent& event); @@ -661,7 +680,7 @@ class GlfwApplication { * @brief Key release event * * Called when a key is released. Default implementation does nothing. - * @see @ref platform-windowed-key-events + * @see @ref platform-windowed-key-events, @ref isKeyPressed() */ virtual void keyReleaseEvent(KeyEvent& event); @@ -1061,7 +1080,8 @@ CORRADE_ENUMSET_OPERATORS(GlfwApplication::Modifiers) @brief Key @m_since_latest -@see @ref KeyEvent::key(), @ref platform-windowed-key-events +@see @ref KeyEvent::key(), @ref isKeyPressed(), @ref keyToScanCode(), + @ref platform-windowed-key-events */ enum class GlfwApplication::Key: Int { Unknown = GLFW_KEY_UNKNOWN, /**< Unknown key */ diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 3adc63f03..c4581a714 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -1306,6 +1306,16 @@ bool Sdl2Application::mainLoopIteration() { return !(_flags & Flag::Exit); } +#ifndef CORRADE_TARGET_EMSCRIPTEN +bool Sdl2Application::isKeyPressed(const Key key) { + int count; + const Uint8* const state = SDL_GetKeyboardState(&count); + const SDL_Scancode scancode = SDL_GetScancodeFromKey(SDL_Keycode(key)); + CORRADE_INTERNAL_DEBUG_ASSERT(scancode < count); + return state[scancode]; +} +#endif + namespace { #ifndef CORRADE_TARGET_EMSCRIPTEN diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index f11123f90..bb2eb7d4b 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -1132,11 +1132,33 @@ class Sdl2Application { /** @{ @name Keyboard handling */ + public: + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Whether a key is pressed + * @m_since_latest + * + * If a key is pressed, i.e. @ref keyPressEvent() with given @p key was + * fired but not a @ref keyReleaseEvent() yet, the function returns + * @cpp true @ce. For unknown @ref Key values returns @cpp false @ce. + * + * This function only queries immediate keyboard state at given point + * in time, which means that if a particular key got pressed and + * released again in between calls to this function, it will not be + * reported as pressed. To avoid losing key presses, prefer to get + * keyboard press and release events through @ref keyPressEvent() and + * @ref keyReleaseEvent() instead if possible. + * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + */ + bool isKeyPressed(Key key); + #endif + + private: /** * @brief Key press event * * Called when a key is pressed. Default implementation does nothing. - * @see @ref platform-windowed-key-events + * @see @ref platform-windowed-key-events, @ref isKeyPressed() */ virtual void keyPressEvent(KeyEvent& event); @@ -1144,7 +1166,7 @@ class Sdl2Application { * @brief Key release event * * Called when a key is released. Default implementation does nothing. - * @see @ref platform-windowed-key-events + * @see @ref platform-windowed-key-events, @ref isKeyPressed() */ virtual void keyReleaseEvent(KeyEvent& event); @@ -1632,7 +1654,8 @@ CORRADE_ENUMSET_OPERATORS(Sdl2Application::Modifiers) @brief Key @m_since_latest -@see @ref KeyEvent::key(), @ref platform-windowed-key-events +@see @ref KeyEvent::key(), @ref isKeyPressed(), @ref keyToScanCode(), + @ref scanCodeToKey(), @ref platform-windowed-key-events */ enum class Sdl2Application::Key: SDL_Keycode { Unknown = SDLK_UNKNOWN, /**< Unknown key */ diff --git a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp index 350ecb9ad..bc0198bfc 100644 --- a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp +++ b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp @@ -39,8 +39,8 @@ namespace Magnum { namespace Platform { -/* These cannot be in an anonymous namespace as enumSetDebugOutput() below - wouldn't be able to pick them up */ +/* These cannot be in an anonymous namespace as enumSetDebugOutput() / Key + below wouldn't be able to pick them up */ static Debug& operator<<(Debug& debug, Application::Modifier value) { debug << "Modifier" << Debug::nospace; @@ -57,99 +57,7 @@ static Debug& operator<<(Debug& debug, Application::Modifier value) { return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; } -static Debug& operator<<(Debug& debug, Application::Pointer value) { - debug << "Pointer" << Debug::nospace; - - switch(value) { - #define _c(value) case Application::Pointer::value: return debug << "::" #value; - _c(MouseLeft) - _c(MouseMiddle) - _c(MouseRight) - _c(MouseButton4) - _c(MouseButton5) - _c(MouseButton6) - _c(MouseButton7) - _c(MouseButton8) - #undef _c - } - - return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; -} - -#ifdef MAGNUM_BUILD_DEPRECATED -CORRADE_IGNORE_DEPRECATED_PUSH -CORRADE_UNUSED static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value) { - debug << "Button" << Debug::nospace; - - switch(value) { - #define _c(value) case Application::MouseMoveEvent::Button::value: return debug << "::" #value; - _c(Left) - _c(Middle) - _c(Right) - #undef _c - } - - return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; -} -CORRADE_IGNORE_DEPRECATED_POP -#endif - -namespace Test { namespace { - -Debug& operator<<(Debug& debug, Application::Modifiers value) { - return Containers::enumSetDebugOutput(debug, value, "Modifiers{}", { - Application::Modifier::Shift, - Application::Modifier::Ctrl, - Application::Modifier::Alt, - Application::Modifier::Super - }); -} - -Debug& operator<<(Debug& debug, Application::Pointers value) { - return Containers::enumSetDebugOutput(debug, value, "Pointers{}", { - Application::Pointer::MouseLeft, - Application::Pointer::MouseMiddle, - Application::Pointer::MouseRight, - Application::Pointer::MouseButton4, - Application::Pointer::MouseButton5, - Application::Pointer::MouseButton6, - Application::Pointer::MouseButton7, - Application::Pointer::MouseButton8, - }); -} - -#ifdef MAGNUM_BUILD_DEPRECATED -CORRADE_IGNORE_DEPRECATED_PUSH -CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) { - debug << "Button" << Debug::nospace; - - switch(value) { - #define _c(value) case Application::MouseEvent::Button::value: return debug << "::" #value; - _c(Left) - _c(Middle) - _c(Right) - _c(Button4) - _c(Button5) - _c(Button6) - _c(Button7) - _c(Button8) - #undef _c - } - - return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; -} - -CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) { - return Containers::enumSetDebugOutput(debug, value, "Buttons{}", { - Application::MouseMoveEvent::Button::Left, - Application::MouseMoveEvent::Button::Middle, - Application::MouseMoveEvent::Button::Right, - }); -} -CORRADE_IGNORE_DEPRECATED_POP -#endif - -Debug& operator<<(Debug& debug, const Application::Key value) { +static Debug& operator<<(Debug& debug, const Application::Key value) { debug << "Key" << Debug::nospace; switch(value) { @@ -270,6 +178,98 @@ Debug& operator<<(Debug& debug, const Application::Key value) { return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; } +static Debug& operator<<(Debug& debug, Application::Pointer value) { + debug << "Pointer" << Debug::nospace; + + switch(value) { + #define _c(value) case Application::Pointer::value: return debug << "::" #value; + _c(MouseLeft) + _c(MouseMiddle) + _c(MouseRight) + _c(MouseButton4) + _c(MouseButton5) + _c(MouseButton6) + _c(MouseButton7) + _c(MouseButton8) + #undef _c + } + + return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_UNUSED static Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Button value) { + debug << "Button" << Debug::nospace; + + switch(value) { + #define _c(value) case Application::MouseMoveEvent::Button::value: return debug << "::" #value; + _c(Left) + _c(Middle) + _c(Right) + #undef _c + } + + return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; +} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +namespace Test { namespace { + +Debug& operator<<(Debug& debug, Application::Modifiers value) { + return Containers::enumSetDebugOutput(debug, value, "Modifiers{}", { + Application::Modifier::Shift, + Application::Modifier::Ctrl, + Application::Modifier::Alt, + Application::Modifier::Super + }); +} + +Debug& operator<<(Debug& debug, Application::Pointers value) { + return Containers::enumSetDebugOutput(debug, value, "Pointers{}", { + Application::Pointer::MouseLeft, + Application::Pointer::MouseMiddle, + Application::Pointer::MouseRight, + Application::Pointer::MouseButton4, + Application::Pointer::MouseButton5, + Application::Pointer::MouseButton6, + Application::Pointer::MouseButton7, + Application::Pointer::MouseButton8, + }); +} + +#ifdef MAGNUM_BUILD_DEPRECATED +CORRADE_IGNORE_DEPRECATED_PUSH +CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseEvent::Button value) { + debug << "Button" << Debug::nospace; + + switch(value) { + #define _c(value) case Application::MouseEvent::Button::value: return debug << "::" #value; + _c(Left) + _c(Middle) + _c(Right) + _c(Button4) + _c(Button5) + _c(Button6) + _c(Button7) + _c(Button8) + #undef _c + } + + return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; +} + +CORRADE_UNUSED Debug& operator<<(Debug& debug, Application::MouseMoveEvent::Buttons value) { + return Containers::enumSetDebugOutput(debug, value, "Buttons{}", { + Application::MouseMoveEvent::Button::Left, + Application::MouseMoveEvent::Button::Middle, + Application::MouseMoveEvent::Button::Right, + }); +} +CORRADE_IGNORE_DEPRECATED_POP +#endif + using namespace Containers::Literals; using namespace Math::Literals; @@ -294,6 +294,10 @@ struct GlfwApplicationTest: Platform::Application { Debug{} << "draw"; swapBuffers(); + /* Invalid keys are tested in the constructor */ + if(isKeyPressed(Key::M)) + Debug{} << Key::M << "is pressed"; + if(_redraw) redraw(); } @@ -458,6 +462,9 @@ GlfwApplicationTest::GlfwApplicationTest(const Arguments& arguments): Platform:: importer->openData(rs.getRaw("icon-32.tga")) && (image32 = importer->image2D(0)) && importer->openData(rs.getRaw("icon-64.tga")) && (image64 = importer->image2D(0))) setWindowIcon({*image16, *image32, *image64}); else Warning{} << "Can't load the plugin / images, not setting window icon"; + + /* This shouldn't blow up */ + CORRADE_INTERNAL_ASSERT(!isKeyPressed(Key::Unknown) && !isKeyPressed(Key(0x7fffffff))); } }}}} diff --git a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp index 2f4d5ff46..0c686157b 100644 --- a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp +++ b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp @@ -334,6 +334,12 @@ struct Sdl2ApplicationTest: Platform::Application { GL::defaultFramebuffer.clear(GL::FramebufferClear::Color); #endif + #ifndef CORRADE_TARGET_EMSCRIPTEN + /* Invalid keys are tested in the constructor */ + if(isKeyPressed(Key::M)) + Debug{} << Key::M << "is pressed"; + #endif + swapBuffers(); if(_redraw) @@ -578,6 +584,11 @@ Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform:: Debug{} << "SDL too old, can't set window icon"; #endif #endif + + #ifndef CORRADE_TARGET_EMSCRIPTEN + /* This shouldn't blow up */ + CORRADE_INTERNAL_ASSERT(!isKeyPressed(Key::Unknown) && !isKeyPressed(Key(0x7fffffff))); + #endif } }}}}