From 414a65f9db563b25e1fb195406e9255061f05c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 21 Oct 2024 19:56:17 +0200 Subject: [PATCH] Platform: expose key scan codes and related APIs. Unlike the Key enum, which shows what a user would perceive as given key in a particular layout, the scancode is a layout-independent identifier for e.g. WASD movement in games. Unfortunately the API availability is wildly different among the toolkits -- SDL's is the most complete, GLFW is second, and then there's Emscripten / HTML5 which provides just string identifiers. I tried to add these for X11 as well, but quick googling led to a SO question where it was left unanswered. Not worth my time. --- doc/changelog.dox | 24 +- src/Magnum/Platform/EmscriptenApplication.cpp | 4 + src/Magnum/Platform/EmscriptenApplication.h | 28 +- src/Magnum/Platform/GlfwApplication.cpp | 21 +- src/Magnum/Platform/GlfwApplication.h | 85 ++++-- src/Magnum/Platform/Sdl2Application.cpp | 42 ++- src/Magnum/Platform/Sdl2Application.h | 145 ++++++++-- .../Test/EmscriptenApplicationTest.cpp | 4 +- .../Platform/Test/GlfwApplicationTest.cpp | 12 +- .../Platform/Test/Sdl2ApplicationTest.cpp | 256 ++++++++++-------- 10 files changed, 442 insertions(+), 179 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index a9d7bf6e2..708a226fe 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -827,6 +827,16 @@ See also: - Added more keys to @ref Platform::AbstractXApplication::KeyEvent::Key and implemented @ref Platform::AbstractXApplication::scrollEvent() for better consistency with other application implementations +- Added @ref Platform::Sdl2Application::KeyEvent::scanCode(), + @relativeref{Platform::Sdl2Application::KeyEvent,scanCodeName()}, + @ref Platform::GlfwApplication::KeyEvent::scanCode() and + @ref Platform::EmscriptenApplication::KeyEvent::scanCodeName() for querying + layout-independent key identifiers as well as + @ref Platform::Sdl2Application::scanCodeName(UnsignedInt), + @relativeref{Platform::Sdl2Application,keyToScanCode()}, + @relativeref{Platform::Sdl2Application,scanCodeToKey()} and + @ref Platform::GlfwApplication::keyToScanCode() helpers for key / scan code + conversion outside of event handlers @subsubsection changelog-latest-changes-scenegraph SceneGraph library @@ -1423,12 +1433,14 @@ See also: wrappers as well. - @cpp Platform::Sdl2Application::InputEvent::Modifier @ce, @cpp Modifiers @ce and @cpp Platform::Sdl2Application::KeyEvent::Key @ce - enums are deprecated in favor of @ref Platform::Sdl2Application::Modifier, - @relativeref{Platform::Sdl2Application,Modifiers} and - @relativeref{Platform::Sdl2Application,Key} enums contained directly in - the application class. Besides the obvious advantage of them being shorter - to type, this allows them to be used outside of the event class scope. The - same change is done in @ref Platform::AbstractXApplication, + enums and the @cpp Platform::Sdl2Application::KeyEvent::keyName(Key) @ce + function are deprecated in favor of @ref Platform::Sdl2Application::Modifier, + @relativeref{Platform::Sdl2Application,Modifiers}, + @relativeref{Platform::Sdl2Application,Key} and + @relativeref{Platform::Sdl2Application,keyName()} APIs contained directly + in the application class. Besides the obvious advantage of them being + shorter to type, this allows the enums to be used outside of the event + class scope. The same change is done in @ref Platform::AbstractXApplication, @ref Platform::EmscriptenApplication and @ref Platform::GlfwApplication. - @cpp Platform::AbstractXApplication::MouseEvent::Button::WheelUp @ce and `WheelDown` members are deprecated in favor of a dedicated diff --git a/src/Magnum/Platform/EmscriptenApplication.cpp b/src/Magnum/Platform/EmscriptenApplication.cpp index b536ab61c..f8d457c1d 100644 --- a/src/Magnum/Platform/EmscriptenApplication.cpp +++ b/src/Magnum/Platform/EmscriptenApplication.cpp @@ -1283,6 +1283,10 @@ Containers::StringView EmscriptenApplication::KeyEvent::keyName() const { return _event.code; } +Containers::StringView EmscriptenApplication::KeyEvent::scanCodeName() const { + return _event.code; +} + EmscriptenApplication::Modifiers EmscriptenApplication::KeyEvent::modifiers() const { return eventModifiers(_event); } diff --git a/src/Magnum/Platform/EmscriptenApplication.h b/src/Magnum/Platform/EmscriptenApplication.h index fa62fe3d4..7c227c601 100644 --- a/src/Magnum/Platform/EmscriptenApplication.h +++ b/src/Magnum/Platform/EmscriptenApplication.h @@ -2456,23 +2456,43 @@ class EmscriptenApplication::KeyEvent: public EmscriptenApplication::InputEvent /** * @brief Key * - * Note that the key is mapped from @m_class{m-doc-external} + * Layout-dependent name of given key. Mapped from @m_class{m-doc-external} * [EmscriptenKeyboardEvent::code](https://emscripten.org/docs/api_reference/html5.h.html#c.EmscriptenKeyboardEvent.code) * in all cases except A--Z, which are mapped from * @m_class{m-doc-external} [EmscriptenkeyboardEvent::key](https://emscripten.org/docs/api_reference/html5.h.html#c.EmscriptenKeyboardEvent.key), - * which respects the keyboard layout. + * which respects the keyboard layout. Note that, unlike e.g. + * @ref Sdl2Application::KeyEvent::scanCode(), there's no numeric + * layout-independent identifier of given key, you have to use + * @ref scanCodeName() instead. */ EmscriptenApplication::Key key() const; /** * @brief Key name * - * The returned view is always + * Layout-dependent name of given key. Returns + * @m_class{m-doc-external} [EmscriptenkeyboardEvent::key](https://emscripten.org/docs/api_reference/html5.h.html#c.EmscriptenKeyboardEvent.key) + * for keys A--Z and @m_class{m-doc-external} [EmscriptenKeyboardEvent::code](https://emscripten.org/docs/api_reference/html5.h.html#c.EmscriptenKeyboardEvent.code) + * for other keys, the view is always * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and - * is valid until the event is destroyed. + * is valid until the event is destroyed. Use @ref scanCodeName() to + * get a platform-specific but layout-independent identifier of given + * key. */ Containers::StringView keyName() const; + /** + * @brief Scancode name + * @m_since_latest + * + * Platform-specific but layout-independent identifier of given key. + * Returns @m_class{m-doc-external} [EmscriptenKeyboardEvent::code](https://emscripten.org/docs/api_reference/html5.h.html#c.EmscriptenKeyboardEvent.code), + * the view is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} + * and is valid until the event is destroyed. Use @ref keyName() to get + * a key name in the currently used layout. + */ + Containers::StringView scanCodeName() const; + /** @brief Modifiers */ EmscriptenApplication::Modifiers modifiers() const; diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index d4556f7f0..5fdc42735 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -662,10 +662,10 @@ void GlfwApplication::setupCallbacks() { #endif app.viewportEvent(e); }); - glfwSetKeyCallback(_window, [](GLFWwindow* const window, const int key, int, const int action, const int mods) { + glfwSetKeyCallback(_window, [](GLFWwindow* const window, const int key, const int scancode, const int action, const int mods) { auto& app = *static_cast(glfwGetWindowUserPointer(window)); - KeyEvent e(Key(key), Modifiers{mods}, action == GLFW_REPEAT); + KeyEvent e(Key(key), scancode, Modifiers{mods}, action == GLFW_REPEAT); if(action == GLFW_PRESS || action == GLFW_REPEAT) app.keyPressEvent(e); @@ -741,6 +741,19 @@ GlfwApplication::~GlfwApplication() { glfwTerminate(); } +Containers::StringView GlfwApplication::keyName(const Key key, const UnsignedInt scancode) const { + return glfwGetKeyName(int(key), scancode); +} + +#if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 303 +Containers::Optional GlfwApplication::keyToScanCode(const Key key) const { + const int scancode = glfwGetKeyScancode(int(key)); + if(scancode == -1) + return {}; + return UnsignedInt(scancode); +} +#endif + Vector2i GlfwApplication::windowSize() const { CORRADE_ASSERT(_window, "Platform::GlfwApplication::windowSize(): no window opened", {}); @@ -1107,12 +1120,14 @@ GlfwApplication::Configuration::Configuration(): GlfwApplication::Configuration::~Configuration() = default; +#ifdef MAGNUM_BUILD_DEPRECATED Containers::StringView GlfwApplication::KeyEvent::keyName(const GlfwApplication::Key key) { return glfwGetKeyName(int(key), 0); } +#endif Containers::StringView GlfwApplication::KeyEvent::keyName() const { - return keyName(_key); + return glfwGetKeyName(Int(_key), _scancode); } GlfwApplication::Pointers GlfwApplication::PointerMoveEvent::pointers() { diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index 764ce802d..80b3ea403 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -276,6 +276,43 @@ class GlfwApplication { /** @brief Moving is not allowed */ GlfwApplication& operator=(GlfwApplication&&) = delete; + /** + * @brief Name for given key + * @m_since_latest + * + * Human-readable localized UTF-8 name for given @p key, intended for + * displaying to the user in e.g. a key binding configuration. For + * @ref Key::Unknown, a concrete @p scanCode can be passed. If there is + * no name for given key, an empty string is returned. If non-empty, + * the returned view is always + * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and + * is valid until the keyboard layout is changed or the application + * exits. + * + * Unlike e.g. @ref Sdl2Application::keyName(), the function isn't + * @cpp static @ce because it relies on GLFW being initialized. + * @see @ref KeyEvent::keyName(), @ref keyToScanCode() + */ + Containers::StringView keyName(Key key, UnsignedInt scanCode = 0) const; + + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 303 || defined(DOXYGEN_GENERATING_OUTPUT) + /** + * @brief Scan code for given key + * @m_since_latest + * + * If @p key doesn't correspond to a physical key supported on given + * platform, returns @relativeref{Corrade,Containers::NullOpt}. Unlike + * e.g. @ref Sdl2Application::scanCodeToKey(), GLFW doesn't provide any + * way to map from a scan code to a key. + * + * Unlike e.g. @ref Sdl2Application::keyToScanCode(), the function + * isn't @cpp static @ce because it relies on GLFW being initialized. + * @note Available since GLFW 3.3. + * @see @ref KeyEvent::key(), @ref KeyEvent::scanCode(), @ref keyName() + */ + Containers::Optional keyToScanCode(Key key) const; + #endif + /** * @brief Execute main loop * @return Value for returning from @cpp main() @ce @@ -2090,33 +2127,42 @@ class GlfwApplication::KeyEvent: public GlfwApplication::InputEvent { * @m_deprecated_since_latest Use @ref GlfwApplication::Key instead. */ typedef CORRADE_DEPRECATED("use GlfwApplication::Key instead") GlfwApplication::Key Key; + + /** + * @brief @copybrief GlfwApplication::keyName() + * @m_deprecated_since_latest Use @ref GlfwApplication::keyName() + * instead. + */ + CORRADE_DEPRECATED("use GlfwApplication::keyName() instead") static Containers::StringView keyName(GlfwApplication::Key key); #endif /** - * @brief Name for given key + * @brief Key * - * Human-readable localized UTF-8 name for given @p key, intended for - * displaying to the user in e.g. key binding configuration. If there - * is no name for given key, empty string is returned. The returned - * view is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} - * and is valid until the keyboard layout is changed or the application - * exits. - * @see @ref keyName(Key) + * Layout-dependent name of given key. Use @ref scanCode() to get a + * platform-specific but layout-independent identifier of given key. + * @see @ref keyName() const */ - static Containers::StringView keyName(GlfwApplication::Key key); - - /** @copydoc Sdl2Application::KeyEvent::key() */ GlfwApplication::Key key() const { return _key; } + /** + * @brief Scancode + * @m_since_latest + * + * Platform-specific but layout-independent identifier of given key. + * Use @ref key() to get a key name in the currently used layout. + */ + UnsignedInt scanCode() const { return _scancode; } + /** * @brief Key name * * Human-readable localized UTF-8 name for the key returned by - * @ref key(), intended for displaying to the user in e.g. - * key binding configuration. If there is no name for that key, empty - * string is returned. The returned view is always - * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and - * is valid until the keyboard layout is changed or the application + * @ref key() and @ref scanCode(), intended for displaying to the user + * in e.g. key binding configuration. If there is no name for that key, + * an empty string is returned. If non-empty, the returned view is + * always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} + * and is valid until the keyboard layout is changed or the application * exits. * @see @ref keyName(Key) */ @@ -2131,11 +2177,12 @@ class GlfwApplication::KeyEvent: public GlfwApplication::InputEvent { private: friend GlfwApplication; - explicit KeyEvent(GlfwApplication::Key key, GlfwApplication::Modifiers modifiers, bool repeated): _key{key}, _modifiers{modifiers}, _repeated{repeated} {} + explicit KeyEvent(GlfwApplication::Key key, UnsignedInt scancode, GlfwApplication::Modifiers modifiers, bool repeated): _repeated{repeated}, _modifiers{modifiers}, _key{key}, _scancode{scancode} {} - const GlfwApplication::Key _key; - const GlfwApplication::Modifiers _modifiers; const bool _repeated; + const GlfwApplication::Modifiers _modifiers; + const GlfwApplication::Key _key; + const UnsignedInt _scancode; }; /** diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 1b0fa8539..e8264fdcc 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -121,6 +121,34 @@ enum class Sdl2Application::Flag: UnsignedByte { #endif }; +Containers::StringView Sdl2Application::keyName(const Key key) { + return SDL_GetKeyName(SDL_Keycode(key)); +} + +#ifndef CORRADE_TARGET_EMSCRIPTEN +Containers::StringView Sdl2Application::scanCodeName(const UnsignedInt scanCode) { + return SDL_GetScancodeName(SDL_Scancode(scanCode)); +} +#endif + +/* https://github.com/emscripten-core/emscripten/pull/18060 */ +#if !defined(CORRADE_TARGET_EMSCRIPTEN) || __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 30125 +Containers::Optional Sdl2Application::keyToScanCode(const Key key) { + static_assert(SDL_SCANCODE_UNKNOWN == 0, "assumed SDL_SCANCODE_UNKNOWN to be 0"); + if(const SDL_Scancode scanCode = SDL_GetScancodeFromKey(SDL_Keycode(key))) + return UnsignedInt(scanCode); + return {}; +} +#endif + +#ifndef CORRADE_TARGET_EMSCRIPTEN +Containers::Optional Sdl2Application::scanCodeToKey(const UnsignedInt scanCode) { + if(const SDL_Keycode key = SDL_GetKeyFromScancode(SDL_Scancode(scanCode))) + return Key(key); + return {}; +} +#endif + Sdl2Application::Sdl2Application(const Arguments& arguments): Sdl2Application{arguments, Configuration{}} {} Sdl2Application::Sdl2Application(const Arguments& arguments, const Configuration& configuration): Sdl2Application{arguments, NoCreate} { @@ -1041,7 +1069,7 @@ bool Sdl2Application::mainLoopIteration() { case SDL_KEYDOWN: case SDL_KEYUP: { - KeyEvent e{event, Key(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod), event.key.repeat != 0}; + KeyEvent e{event, Key(event.key.keysym.sym), UnsignedInt(event.key.keysym.scancode), fixedModifiers(event.key.keysym.mod), event.key.repeat != 0}; event.type == SDL_KEYDOWN ? keyPressEvent(e) : keyReleaseEvent(e); } break; @@ -1598,14 +1626,22 @@ Sdl2Application::Configuration::Configuration(): Sdl2Application::Configuration::~Configuration() = default; +#ifdef MAGNUM_BUILD_DEPRECATED Containers::StringView Sdl2Application::KeyEvent::keyName(const Sdl2Application::Key key) { - return SDL_GetKeyName(SDL_Keycode(key)); + return Sdl2Application::keyName(key); } +#endif Containers::StringView Sdl2Application::KeyEvent::keyName() const { - return keyName(_key); + return Sdl2Application::keyName(_key); } +#ifndef CORRADE_TARGET_EMSCRIPTEN +Containers::StringView Sdl2Application::KeyEvent::scanCodeName() const { + return Sdl2Application::scanCodeName(_scancode); +} +#endif + Sdl2Application::Modifiers Sdl2Application::PointerEvent::modifiers() { if(!_modifiers) _modifiers = fixedModifiers(Uint16(SDL_GetModState())); diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index 59940c0f6..2ceea761c 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -86,6 +86,18 @@ #include #endif +#if defined(CORRADE_TARGET_EMSCRIPTEN) || defined(DOXYGEN_GENERATING_OUTPUT) +/* The __EMSCRIPTEN_major__ etc macros used to be passed implicitly, version + 3.1.4 moved them to a version header and version 3.1.23 dropped the + backwards compatibility. To work consistently on all versions, including the + header only if the version macros aren't present. + https://github.com/emscripten-core/emscripten/commit/f99af02045357d3d8b12e63793cef36dfde4530a + https://github.com/emscripten-core/emscripten/commit/f76ddc702e4956aeedb658c49790cc352f892e4c */ +#ifndef __EMSCRIPTEN_major__ +#include +#endif +#endif + #ifndef DOXYGEN_GENERATING_OUTPUT union SDL_Event; /* for anyEvent() */ #endif @@ -574,6 +586,71 @@ class Sdl2Application { */ typedef Containers::EnumSet Pointers; + /** + * @brief Name for given key + * @m_since_latest + * + * Human-readable localized UTF-8 name for given @p key, intended for + * displaying to the user in e.g. a key binding configuration. If there + * is no name for given key, empty string is returned. The returned + * view is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} + * and is valid at least until the next call to this function, to + * @ref KeyEvent::keyName() const or to the underlying + * @cpp SDL_GetKeyName() @ce API. + * @see @ref scanCodeName(), @ref keyToScanCode(), @ref KeyEvent::key() + */ + static Containers::StringView keyName(Key key); + + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Name for given key scan code + * @m_since_latest + * + * Human-readable localized UTF-8 name for given @p scancode. Note that + * unlike @ref keyName(), the scancode names are not consistent across + * platforms. If there is no name for given scancode, empty string is + * returned. The returned view is always + * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and + * is valid at least until the next call to this function, to + * @ref KeyEvent::scanCodeName() const or to the underlying + * @cpp SDL_GetScancodeName() @ce API. + * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref scanCodeToKey(), @ref KeyEvent::scanCode() + */ + static Containers::StringView scanCodeName(UnsignedInt scanCode); + #endif + + #if !defined(CORRADE_TARGET_EMSCRIPTEN) || __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 30125 + /** + * @brief Scan code for given key + * @m_since_latest + * + * If @p key doesn't correspond to a physical key supported on given + * platform, returns @relativeref{Corrade,Containers::NullOpt}. Note + * that this is implemented as a linear lookup inside SDL, prefer to + * query the scan code directly via @ref KeyEvent::scanCode() rather + * than converting it from a @ref KeyEvent::key() at a later time. + * @note On @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" available only + * since version 3.1.25. + * @see @ref scanCodeToKey(), @ref keyName() + */ + static Containers::Optional keyToScanCode(Key key); + #endif + + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Scan code for given key + * @m_since_latest + * + * If @p scanCode isn't a known scan code, returns + * @relativeref{Corrade,Containers::NullOpt}. + * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref KeyEvent::key(), @ref KeyEvent::scanCode(), + * @ref keyToScanCode(), @ref scanCodeName() + */ + static Containers::Optional scanCodeToKey(UnsignedInt scanCode); + #endif + #ifdef MAGNUM_TARGET_GL /** * @brief Construct with an OpenGL context @@ -2769,41 +2846,68 @@ class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent { * @m_deprecated_since_latest Use @ref Sdl2Application::Key instead. */ typedef CORRADE_DEPRECATED("use Sdl2Application::Key instead") Sdl2Application::Key Key; - #endif /** - * @brief Name for given key - * - * Human-readable localized UTF-8 name for given @p key, intended for - * displaying to the user in e.g. key binding configuration. If there - * is no name for given key, empty string is returned. The returned - * view is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} - * and is valid at least until the next call to this function, to - * @ref keyName() const or to the underlying @cpp SDL_GetKeyName() @ce - * API. + * @brief @copybrief Sdl2Application::keyName() + * @m_deprecated_since_latest Use @ref Sdl2Application::keyName() + * instead. */ - static Containers::StringView keyName(Sdl2Application::Key key); + CORRADE_DEPRECATED("use Sdl2Application::keyName() instead") static Containers::StringView keyName(Sdl2Application::Key key); + #endif /** * @brief Key * - * @see @ref keyName() + * Layout-dependent name of given key. Use @ref scanCode() to get a + * platform-specific but layout-independent identifier of given key. + * @see @ref keyName() const, @ref keyToScanCode() */ Sdl2Application::Key key() const { return _key; } + /** + * @brief Scancode + * @m_since_latest + * + * Platform-specific but layout-independent identifier of given key. + * Use @ref key() to get a key name in the currently used layout. + * @see @ref scanCodeName(), @ref scanCodeToKey() + */ + UnsignedInt scanCode() const { return _scancode; } + /** * @brief Key name * * Human-readable localized UTF-8 name for the key returned by - * @ref key(), intended for displaying to the user in e.g. - * key binding configuration. If there is no name for that key, empty - * string is returned. The returned string is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} + * @ref key(), intended for displaying to the user in e.g. a key + * binding configuration. If there is no name for that key, an empty + * string is returned. The returned string is always + * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} * and is valid at least until the next call to this function, to - * @ref keyName(Key) or to the underlying @cpp SDL_GetKeyName() @ce - * API. + * @ref Sdl2Application::keyName() or to the underlying + * @cpp SDL_GetKeyName() @ce API. + * @see @ref scanCodeName() */ Containers::StringView keyName() const; + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Scan code name + * @m_since_latest + * + * Human-readable localized UTF-8 name for the scancode returned by + * @ref scanCode(), intended for displaying to the user in e.g. a key + * binding configuration. If there is no name for that key, an + * empty string is returned. The returned string is always + * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} + * and is valid at least until the next call to this function, to + * @ref Sdl2Application::scanCodeName() or to the underlying + * @cpp SDL_GetScancodeName() @ce API. + * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref keyName() + */ + Containers::StringView scanCodeName() const; + #endif + /** @brief Modifiers */ Sdl2Application::Modifiers modifiers() const { return _modifiers; } @@ -2818,11 +2922,12 @@ class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent { private: friend Sdl2Application; - explicit KeyEvent(const SDL_Event& event, Sdl2Application::Key key, Sdl2Application::Modifiers modifiers, bool repeated): InputEvent{event}, _key{key}, _modifiers{modifiers}, _repeated{repeated} {} + explicit KeyEvent(const SDL_Event& event, Sdl2Application::Key key, UnsignedInt scancode, Sdl2Application::Modifiers modifiers, bool repeated): InputEvent{event}, _repeated{repeated}, _modifiers{modifiers}, _key{key}, _scancode{scancode} {} - const Sdl2Application::Key _key; - const Sdl2Application::Modifiers _modifiers; const bool _repeated; + const Sdl2Application::Modifiers _modifiers; + const Sdl2Application::Key _key; + const UnsignedInt _scancode; }; /** diff --git a/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp b/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp index 334bda353..dc74c2f9c 100644 --- a/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp +++ b/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp @@ -368,7 +368,7 @@ struct EmscriptenApplicationTest: Platform::Application { /* For testing keyboard capture */ void keyPressEvent(KeyEvent& event) override { - Debug{} << "key press:" << event.key() << event.keyName() << event.modifiers(); + Debug{} << "key press:" << event.key() << event.keyName() << "scancode:" << event.scanCodeName() << event.modifiers(); if(event.key() == Key::F1) { Debug{} << "starting text input"; @@ -395,7 +395,7 @@ struct EmscriptenApplicationTest: Platform::Application { } void keyReleaseEvent(KeyEvent& event) override { - Debug{} << "key release:" << event.key() << event.keyName() << event.modifiers(); + Debug{} << "key release:" << event.key() << event.keyName() << "scancode:" << event.scanCodeName() << event.modifiers(); event.setAccepted(); } diff --git a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp index 781ca7717..4ffc9e8e6 100644 --- a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp +++ b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp @@ -299,7 +299,11 @@ struct GlfwApplicationTest: Platform::Application { } void keyPressEvent(KeyEvent& event) override { - Debug{} << "key press:" << event.key() << int(event.key()) << event.keyName() << event.modifiers(); + Debug{} << "key press:" << event.key() << int(event.key()) << event.keyName() << "scancode:" << event.scanCode() << event.modifiers() + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 303 + << "converted:" << keyToScanCode(event.key()) + #endif + ; if(event.key() == Key::F1) { Debug{} << "starting text input"; @@ -337,7 +341,11 @@ struct GlfwApplicationTest: Platform::Application { } void keyReleaseEvent(KeyEvent& event) override { - Debug{} << "key release:" << event.key() << int(event.key()) << event.keyName() << event.modifiers(); + Debug{} << "key release:" << event.key() << int(event.key()) << event.keyName() << "scancode:" << event.scanCode() << event.modifiers() + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 303 + << "converted:" << keyToScanCode(event.key()) + #endif + ; } /* Set to 0 to test the deprecated mouse events instead */ diff --git a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp index 4e70faf1e..fe099181f 100644 --- a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp +++ b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp @@ -56,8 +56,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() / + Optional below wouldn't be able to pick them up */ static Debug& operator<<(Debug& debug, Application::Modifier value) { debug << "Modifier" << Debug::nospace; @@ -77,118 +77,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) - #ifndef CORRADE_TARGET_EMSCRIPTEN - _c(Finger) - #endif - #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) - _c(X1) - _c(X2) - #undef _c - } - - return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; -} -CORRADE_IGNORE_DEPRECATED_POP -#endif - -namespace Test { namespace { - -static Debug& operator<<(Debug& debug, Application::PointerEventSource value) { - debug << "PointerEventSource" << Debug::nospace; - - switch(value) { - #define _c(value) case Application::PointerEventSource::value: return debug << "::" #value; - _c(Mouse) - #ifndef CORRADE_TARGET_EMSCRIPTEN - _c(Touch) - #endif - #undef _c - } - - return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; -} - -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, - Application::Modifier::AltGr, - Application::Modifier::CapsLock, - Application::Modifier::NumLock, - }); -} - -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, - #ifndef CORRADE_TARGET_EMSCRIPTEN - Application::Pointer::Finger, - #endif - }); -} - -#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(X1) - _c(X2) - #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, - Application::MouseMoveEvent::Button::X1, - Application::MouseMoveEvent::Button::X2, - }); -} -CORRADE_IGNORE_DEPRECATED_POP -#endif - -Debug& operator<<(Debug& debug, Application::Key value) { +static Debug& operator<<(Debug& debug, Application::Key value) { debug << "Key" << Debug::nospace; switch(value) { @@ -308,6 +197,117 @@ Debug& operator<<(Debug& debug, 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) + #ifndef CORRADE_TARGET_EMSCRIPTEN + _c(Finger) + #endif + #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) + _c(X1) + _c(X2) + #undef _c + } + + return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; +} +CORRADE_IGNORE_DEPRECATED_POP +#endif + +namespace Test { namespace { + +static Debug& operator<<(Debug& debug, Application::PointerEventSource value) { + debug << "PointerEventSource" << Debug::nospace; + + switch(value) { + #define _c(value) case Application::PointerEventSource::value: return debug << "::" #value; + _c(Mouse) + #ifndef CORRADE_TARGET_EMSCRIPTEN + _c(Touch) + #endif + #undef _c + } + + return debug << "(" << Debug::nospace << UnsignedInt(value) << Debug::nospace << ")"; +} + +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, + Application::Modifier::AltGr, + Application::Modifier::CapsLock, + Application::Modifier::NumLock, + }); +} + +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, + #ifndef CORRADE_TARGET_EMSCRIPTEN + Application::Pointer::Finger, + #endif + }); +} + +#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(X1) + _c(X2) + #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, + Application::MouseMoveEvent::Button::X1, + Application::MouseMoveEvent::Button::X2, + }); +} +CORRADE_IGNORE_DEPRECATED_POP +#endif + using namespace Containers::Literals; using namespace Math::Literals; @@ -386,11 +386,19 @@ struct Sdl2ApplicationTest: Platform::Application { } void keyPressEvent(KeyEvent& event) override { - Debug{} << "key press:" << event.key() << "keycode:" << SDL_Keycode(event.key()) << event.keyName() << "scancode:" << event.event().key.keysym.scancode + Debug{} << "key press:" << event.key() << event.keyName() << "scancode:" << event.scanCode() + #ifndef CORRADE_TARGET_EMSCRIPTEN + << event.scanCodeName() + #endif + << event.modifiers() + #if !defined(CORRADE_TARGET_EMSCRIPTEN) || __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 30125 + << "converted:" #ifndef CORRADE_TARGET_EMSCRIPTEN - << SDL_GetScancodeName(event.event().key.keysym.scancode) + << scanCodeToKey(event.scanCode()) + #endif + << keyToScanCode(event.key()) #endif - << event.modifiers(); + ; if(event.key() == Key::F1) { Debug{} << "starting text input"; @@ -451,11 +459,19 @@ struct Sdl2ApplicationTest: Platform::Application { } void keyReleaseEvent(KeyEvent& event) override { - Debug{} << "key release:" << event.key() << "keycode:" << SDL_Keycode(event.key()) << event.keyName() << "scancode:" << event.event().key.keysym.scancode + Debug{} << "key release:" << event.key() << event.keyName() << "scancode:" << event.scanCode() #ifndef CORRADE_TARGET_EMSCRIPTEN - << SDL_GetScancodeName(event.event().key.keysym.scancode) + << event.scanCodeName() + #endif + << event.modifiers() + #if !defined(CORRADE_TARGET_EMSCRIPTEN) || __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 30125 + << "converted:" + #ifndef CORRADE_TARGET_EMSCRIPTEN + << scanCodeToKey(event.scanCode()) + #endif + << keyToScanCode(event.key()) #endif - << event.modifiers(); + ; /* With EmscriptenApplication, this makes the event stop from propagating further to the page (such as when pressing F1).