diff --git a/include/SFML/Window/Event.hpp b/include/SFML/Window/Event.hpp index 5f17d25d..76eb6f27 100644 --- a/include/SFML/Window/Event.hpp +++ b/include/SFML/Window/Event.hpp @@ -60,11 +60,12 @@ public: //////////////////////////////////////////////////////////// struct KeyEvent { - Keyboard::Key code; //!< Code of the key that has been pressed - bool alt; //!< Is the Alt key pressed? - bool control; //!< Is the Control key pressed? - bool shift; //!< Is the Shift key pressed? - bool system; //!< Is the System key pressed? + Keyboard::Key code; //!< Code of the key that has been pressed + Keyboard::Scancode scancode; //!< Physical code of the key that has been pressed + bool alt; //!< Is the Alt key pressed? + bool control; //!< Is the Control key pressed? + bool shift; //!< Is the Shift key pressed? + bool system; //!< Is the System key pressed? }; //////////////////////////////////////////////////////////// diff --git a/include/SFML/Window/Keyboard.hpp b/include/SFML/Window/Keyboard.hpp index 5cd09dec..f9befc55 100644 --- a/include/SFML/Window/Keyboard.hpp +++ b/include/SFML/Window/Keyboard.hpp @@ -30,15 +30,23 @@ #include +namespace sf +{ +class String; + //////////////////////////////////////////////////////////// /// \brief Give access to the real-time state of the keyboard /// //////////////////////////////////////////////////////////// -namespace sf::Keyboard +namespace Keyboard { //////////////////////////////////////////////////////////// /// \brief Key codes /// +/// The enumerators refer to the "localized" key; i.e. depending +/// on the layout set by the operating system, a key can be mapped +/// to `Y` or `Z`. +/// //////////////////////////////////////////////////////////// enum Key { @@ -83,21 +91,21 @@ enum Key LControl, //!< The left Control key LShift, //!< The left Shift key LAlt, //!< The left Alt key - LSystem, //!< The left OS specific key: window (Windows and Linux), apple (MacOS X), ... + LSystem, //!< The left OS specific key: window (Windows and Linux), apple (macOS), ... RControl, //!< The right Control key RShift, //!< The right Shift key RAlt, //!< The right Alt key - RSystem, //!< The right OS specific key: window (Windows and Linux), apple (MacOS X), ... + RSystem, //!< The right OS specific key: window (Windows and Linux), apple (macOS), ... Menu, //!< The Menu key LBracket, //!< The [ key RBracket, //!< The ] key Semicolon, //!< The ; key Comma, //!< The , key Period, //!< The . key - Quote, //!< The ' key + Apostrophe, //!< The ' key Slash, //!< The / key Backslash, //!< The \ key - Tilde, //!< The ~ key + Grave, //!< The ` key Equal, //!< The = key Hyphen, //!< The - key (hyphen) Space, //!< The Space key @@ -145,9 +153,205 @@ enum Key F15, //!< The F15 key Pause, //!< The Pause key - KeyCount //!< Keep last -- the total number of keyboard keys + KeyCount, //!< Keep last -- the total number of keyboard keys + + // Deprecated values: + + Tilde = Grave, //!< \deprecated Use Grave instead + Dash = Hyphen, //!< \deprecated Use Hyphen instead + BackSpace = Backspace, //!< \deprecated Use Backspace instead + BackSlash = Backslash, //!< \deprecated Use Backslash instead + SemiColon = Semicolon, //!< \deprecated Use Semicolon instead + Return = Enter, //!< \deprecated Use Enter instead + Quote = Apostrophe //!< \deprecated Use Apostrophe instead }; +//////////////////////////////////////////////////////////// +/// \brief Scancodes +/// +/// The enumerators are bound to a physical key and do not depend on +/// the keyboard layout used by the operating system. Usually, the AT-101 +/// keyboard can be used as reference for the physical position of the keys. +/// +//////////////////////////////////////////////////////////// +struct Scan +{ +// TODO: replace with enum class in SFML 3. +// Clang warns us rightfully that Scancode names shadow Key names. +// A safer solution would be to use a C++11 scoped enumeration (enum class), +// but it is not possible in SFML 2 which uses C++03. +// For now, we just ignore those warnings. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" +#endif + + enum Scancode + { + Unknown = -1, //!< Represents any scancode not present in this enum + A = 0, //!< Keyboard a and A key + B, //!< Keyboard b and B key + C, //!< Keyboard c and C key + D, //!< Keyboard d and D key + E, //!< Keyboard e and E key + F, //!< Keyboard f and F key + G, //!< Keyboard g and G key + H, //!< Keyboard h and H key + I, //!< Keyboard i and I key + J, //!< Keyboard j and J key + K, //!< Keyboard k and K key + L, //!< Keyboard l and L key + M, //!< Keyboard m and M key + N, //!< Keyboard n and N key + O, //!< Keyboard o and O key + P, //!< Keyboard p and P key + Q, //!< Keyboard q and Q key + R, //!< Keyboard r and R key + S, //!< Keyboard s and S key + T, //!< Keyboard t and T key + U, //!< Keyboard u and U key + V, //!< Keyboard v and V key + W, //!< Keyboard w and W key + X, //!< Keyboard x and X key + Y, //!< Keyboard y and Y key + Z, //!< Keyboard z and Z key + Num1, //!< Keyboard 1 and ! key + Num2, //!< Keyboard 2 and @ key + Num3, //!< Keyboard 3 and # key + Num4, //!< Keyboard 4 and $ key + Num5, //!< Keyboard 5 and % key + Num6, //!< Keyboard 6 and ^ key + Num7, //!< Keyboard 7 and & key + Num8, //!< Keyboard 8 and * key + Num9, //!< Keyboard 9 and ) key + Num0, //!< Keyboard 0 and ) key + Enter, //!< Keyboard Enter/Return key + Escape, //!< Keyboard Escape key + Backspace, //!< Keyboard Backspace key + Tab, //!< Keyboard Tab key + Space, //!< Keyboard Space key + Hyphen, //!< Keyboard - and _ key + Equal, //!< Keyboard = and + + LBracket, //!< Keyboard [ and { key + RBracket, //!< Keyboard ] and } key + // For US keyboards mapped to key 29 (Microsoft Keyboard Scan Code Specification) + // For Non-US keyboards mapped to key 42 (Microsoft Keyboard Scan Code Specification) + // Typical language mappings: Belg:£µ` FrCa:<>} Dan:*' Dutch:`´ Fren:µ* Ger:'# Ital:§ù LatAm:[}` Nor:*@ Span:ç} Swed:*' Swiss:$£} UK:~# Brazil:}] + Backslash, //!< Keyboard \ and | key OR various keys for Non-US keyboards + Semicolon, //!< Keyboard ; and : key + Apostrophe, //!< Keyboard ' and " key + Grave, //!< Keyboard ` and ~ key + Comma, //!< Keyboard , and < key + Period, //!< Keyboard . and > key + Slash, //!< Keyboard / and ? key + F1, //!< Keyboard F1 key + F2, //!< Keyboard F2 key + F3, //!< Keyboard F3 key + F4, //!< Keyboard F4 key + F5, //!< Keyboard F5 key + F6, //!< Keyboard F6 key + F7, //!< Keyboard F7 key + F8, //!< Keyboard F8 key + F9, //!< Keyboard F9 key + F10, //!< Keyboard F10 key + F11, //!< Keyboard F11 key + F12, //!< Keyboard F12 key + F13, //!< Keyboard F13 key + F14, //!< Keyboard F14 key + F15, //!< Keyboard F15 key + F16, //!< Keyboard F16 key + F17, //!< Keyboard F17 key + F18, //!< Keyboard F18 key + F19, //!< Keyboard F19 key + F20, //!< Keyboard F20 key + F21, //!< Keyboard F21 key + F22, //!< Keyboard F22 key + F23, //!< Keyboard F23 key + F24, //!< Keyboard F24 key + CapsLock, //!< Keyboard Caps %Lock key + PrintScreen, //!< Keyboard Print Screen key + ScrollLock, //!< Keyboard Scroll %Lock key + Pause, //!< Keyboard Pause key + Insert, //!< Keyboard Insert key + Home, //!< Keyboard Home key + PageUp, //!< Keyboard Page Up key + Delete, //!< Keyboard Delete Forward key + End, //!< Keyboard End key + PageDown, //!< Keyboard Page Down key + Right, //!< Keyboard Right Arrow key + Left, //!< Keyboard Left Arrow key + Down, //!< Keyboard Down Arrow key + Up, //!< Keyboard Up Arrow key + NumLock, //!< Keypad Num %Lock and Clear key + NumpadDivide, //!< Keypad / key + NumpadMultiply, //!< Keypad * key + NumpadMinus, //!< Keypad - key + NumpadPlus, //!< Keypad + key + NumpadEqual, //!< keypad = key + NumpadEnter, //!< Keypad Enter/Return key + NumpadDecimal, //!< Keypad . and Delete key + Numpad1, //!< Keypad 1 and End key + Numpad2, //!< Keypad 2 and Down Arrow key + Numpad3, //!< Keypad 3 and Page Down key + Numpad4, //!< Keypad 4 and Left Arrow key + Numpad5, //!< Keypad 5 key + Numpad6, //!< Keypad 6 and Right Arrow key + Numpad7, //!< Keypad 7 and Home key + Numpad8, //!< Keypad 8 and Up Arrow key + Numpad9, //!< Keypad 9 and Page Up key + Numpad0, //!< Keypad 0 and Insert key + // For US keyboards doesn't exist + // For Non-US keyboards mapped to key 45 (Microsoft Keyboard Scan Code Specification) + // Typical language mappings: Belg:<\> FrCa:«°» Dan:<\> Dutch:]|[ Fren:<> Ger:<|> Ital:<> LatAm:<> Nor:<> Span:<> Swed:<|> Swiss:<\> UK:\| Brazil: \|. + NonUsBackslash, //!< Keyboard Non-US \ and | key + Application, //!< Keyboard Application key + Execute, //!< Keyboard Execute key + ModeChange, //!< Keyboard Mode Change key + Help, //!< Keyboard Help key + Menu, //!< Keyboard Menu key + Select, //!< Keyboard Select key + Redo, //!< Keyboard Redo key + Undo, //!< Keyboard Undo key + Cut, //!< Keyboard Cut key + Copy, //!< Keyboard Copy key + Paste, //!< Keyboard Paste key + VolumeMute, //!< Keyboard Volume Mute key + VolumeUp, //!< Keyboard Volume Up key + VolumeDown, //!< Keyboard Volume Down key + MediaPlayPause, //!< Keyboard Media Play Pause key + MediaStop, //!< Keyboard Media Stop key + MediaNextTrack, //!< Keyboard Media Next Track key + MediaPreviousTrack, //!< Keyboard Media Previous Track key + LControl, //!< Keyboard Left Control key + LShift, //!< Keyboard Left Shift key + LAlt, //!< Keyboard Left Alt key + LSystem, //!< Keyboard Left System key + RControl, //!< Keyboard Right Control key + RShift, //!< Keyboard Right Shift key + RAlt, //!< Keyboard Right Alt key + RSystem, //!< Keyboard Right System key + Back, //!< Keyboard Back key + Forward, //!< Keyboard Forward key + Refresh, //!< Keyboard Refresh key + Stop, //!< Keyboard Stop key + Search, //!< Keyboard Search key + Favorites, //!< Keyboard Favorites key + HomePage, //!< Keyboard Home Page key + LaunchApplication1, //!< Keyboard Launch Application 1 key + LaunchApplication2, //!< Keyboard Launch Application 2 key + LaunchMail, //!< Keyboard Launch Mail key + LaunchMediaSelect, //!< Keyboard Launch Media Select key + + ScancodeCount //!< Keep last -- the total number of scancodes + }; + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +}; + +using Scancode = Scan::Scancode; + //////////////////////////////////////////////////////////// /// \brief Check if a key is pressed /// @@ -158,12 +362,73 @@ enum Key //////////////////////////////////////////////////////////// SFML_WINDOW_API bool isKeyPressed(Key key); +//////////////////////////////////////////////////////////// +/// \brief Check if a key is pressed +/// +/// \param code Scancode to check +/// +/// \return True if the physical key is pressed, false otherwise +/// +//////////////////////////////////////////////////////////// +SFML_WINDOW_API bool isKeyPressed(Scancode code); + +//////////////////////////////////////////////////////////// +/// \brief Localize a physical key to a logical one +/// +/// \param code Scancode to localize +/// +/// \return The key corresponding to the scancode under the current +/// keyboard layout used by the operating system, or +/// sf::Keyboard::Unknown when the scancode cannot be mapped +/// to a Key. +/// +/// \see delocalize +/// +//////////////////////////////////////////////////////////// +SFML_WINDOW_API Key localize(Scancode code); + +//////////////////////////////////////////////////////////// +/// \brief Identify the physical key corresponding to a logical one +/// +/// \param key Key to "delocalize" +/// +/// \return The scancode corresponding to the key under the current +/// keyboard layout used by the operating system, or +/// sf::Keyboard::Scan::Unknown when the key cannot be mapped +/// to a sf::Keyboard::Scancode. +/// +/// \see localize +/// +//////////////////////////////////////////////////////////// +SFML_WINDOW_API Scancode delocalize(Key key); + +//////////////////////////////////////////////////////////// +/// \brief Provide a string representation for a given scancode +/// +/// The returned string is a short, non-technical description of +/// the key represented with the given scancode. Most effectively +/// used in user interfaces, as the description for the key takes +/// the users keyboard layout into consideration. +/// +/// \warning The result is OS-dependent: for example, sf::Keyboard::Scan::LSystem +/// is "Left Meta" on Linux, "Left Windows" on Windows and +/// "Left Command" on macOS. +/// +/// The current keyboard layout set by the operating system is used to +/// interpret the scancode: for example, sf::Keyboard::Semicolon is +/// mapped to ";" for layout and to "é" for others. +/// +/// \return The localized description of the code +/// +//////////////////////////////////////////////////////////// +SFML_WINDOW_API String getDescription(Scancode code); + //////////////////////////////////////////////////////////// /// \brief Show or hide the virtual keyboard /// -/// Warning: the virtual keyboard is not supported on all -/// systems. It will typically be implemented on mobile OSes -/// (Android, iOS) but not on desktop OSes (Windows, Linux, ...). +/// \warning The virtual keyboard is not supported on all +/// systems. It will typically be implemented on mobile OSes +/// (Android, iOS) but not on desktop OSes (Windows, Linux, ...). /// /// If the virtual keyboard is not available, this function does /// nothing. @@ -172,7 +437,9 @@ SFML_WINDOW_API bool isKeyPressed(Key key); /// //////////////////////////////////////////////////////////// SFML_WINDOW_API void setVirtualKeyboardVisible(bool visible); -} // namespace sf::Keyboard +} // namespace Keyboard + +} // namespace sf //////////////////////////////////////////////////////////// @@ -206,6 +473,10 @@ SFML_WINDOW_API void setVirtualKeyboardVisible(bool visible); /// { /// // quit... /// } +/// else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Scan::Grave)) +/// { +/// // open in-game command line (if it's not already open) +/// } /// \endcode /// /// \see sf::Joystick, sf::Mouse, sf::Touch diff --git a/src/SFML/Window/Android/InputImpl.cpp b/src/SFML/Window/Android/InputImpl.cpp index e9b65c32..e75f8289 100644 --- a/src/SFML/Window/Android/InputImpl.cpp +++ b/src/SFML/Window/Android/InputImpl.cpp @@ -44,6 +44,30 @@ bool InputImpl::isKeyPressed(Keyboard::Key /* key */) return false; } +bool InputImpl::isKeyPressed(Keyboard::Scancode /* codes */) +{ + // Not applicable + return false; +} + +Keyboard::Key InputImpl::localize(Keyboard::Scancode /* code */) +{ + // Not applicable + return Keyboard::Unknown; +} + +Keyboard::Scancode InputImpl::delocalize(Keyboard::Key /* key */) +{ + // Not applicable + return Keyboard::Scan::Unknown; +} + +String InputImpl::getDescription(Keyboard::Scancode /* code */) +{ + // Not applicable + return ""; +} + //////////////////////////////////////////////////////////// void InputImpl::setVirtualKeyboardVisible(bool visible) { diff --git a/src/SFML/Window/Android/InputImpl.hpp b/src/SFML/Window/Android/InputImpl.hpp index b3795861..43bdcae3 100644 --- a/src/SFML/Window/Android/InputImpl.hpp +++ b/src/SFML/Window/Android/InputImpl.hpp @@ -41,19 +41,37 @@ class InputImpl { public: //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise + /// \copydoc sf::Keyboard::isKeyPressed(Key) /// //////////////////////////////////////////////////////////// static bool isKeyPressed(Keyboard::Key key); //////////////////////////////////////////////////////////// - /// \brief Show or hide the virtual keyboard + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) /// - /// \param visible True to show, false to hide + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::setVirtualKeyboardVisible /// //////////////////////////////////////////////////////////// static void setVirtualKeyboardVisible(bool visible); diff --git a/src/SFML/Window/Android/WindowImplAndroid.cpp b/src/SFML/Window/Android/WindowImplAndroid.cpp index 070b60d7..ccba57ab 100644 --- a/src/SFML/Window/Android/WindowImplAndroid.cpp +++ b/src/SFML/Window/Android/WindowImplAndroid.cpp @@ -640,7 +640,7 @@ Keyboard::Key WindowImplAndroid::androidKeyToSF(std::int32_t key) case AKEYCODE_ENTER: return Keyboard::Enter; case AKEYCODE_DEL: return Keyboard::Backspace; case AKEYCODE_FORWARD_DEL: return Keyboard::Delete; - case AKEYCODE_GRAVE: return Keyboard::Tilde; + case AKEYCODE_GRAVE: return Keyboard::Grave; case AKEYCODE_MINUS: return Keyboard::Subtract; case AKEYCODE_EQUALS: return Keyboard::Equal; case AKEYCODE_LEFT_BRACKET: return Keyboard::LBracket; diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index 65d89c94..a622ffe3 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -116,6 +116,12 @@ elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD OR SFML_OS_OPENBSD OR SFML_OS_NETBSD) ${SRCROOT}/Unix/ClipboardImpl.cpp ${SRCROOT}/Unix/InputImpl.cpp ${SRCROOT}/Unix/InputImpl.hpp + ${SRCROOT}/Unix/KeyboardImpl.hpp + ${SRCROOT}/Unix/KeyboardImpl.cpp + ${SRCROOT}/Unix/KeySymToKeyMapping.hpp + ${SRCROOT}/Unix/KeySymToKeyMapping.cpp + ${SRCROOT}/Unix/KeySymToUnicodeMapping.hpp + ${SRCROOT}/Unix/KeySymToUnicodeMapping.cpp ${SRCROOT}/Unix/SensorImpl.cpp ${SRCROOT}/Unix/SensorImpl.hpp ${SRCROOT}/Unix/Display.cpp diff --git a/src/SFML/Window/DRM/InputImplUDev.cpp b/src/SFML/Window/DRM/InputImplUDev.cpp index 9704de67..01612f1a 100644 --- a/src/SFML/Window/DRM/InputImplUDev.cpp +++ b/src/SFML/Window/DRM/InputImplUDev.cpp @@ -227,7 +227,7 @@ sf::Keyboard::Key toKey(int code) case KEY_L: return sf::Keyboard::L; case KEY_SEMICOLON: return sf::Keyboard::Semicolon; case KEY_APOSTROPHE: return sf::Keyboard::Quote; - case KEY_GRAVE: return sf::Keyboard::Tilde; + case KEY_GRAVE: return sf::Keyboard::Grave; case KEY_LEFTSHIFT: return sf::Keyboard::LShift; case KEY_BACKSLASH: return sf::Keyboard::Backslash; case KEY_Z: return sf::Keyboard::Z; @@ -414,12 +414,13 @@ bool eventProcess(sf::Event& event) { // key down and key up events // - event.type = inputEvent.value ? sf::Event::KeyPressed : sf::Event::KeyReleased; - event.key.code = kb; - event.key.alt = altDown(); - event.key.control = controlDown(); - event.key.shift = shiftDown(); - event.key.system = systemDown(); + event.type = inputEvent.value ? sf::Event::KeyPressed : sf::Event::KeyReleased; + event.key.code = kb; + event.key.scancode = sf::Keyboard::Scan::Unknown; // TODO: not implemented + event.key.alt = altDown(); + event.key.control = controlDown(); + event.key.shift = shiftDown(); + event.key.system = systemDown(); keyMap[static_cast(kb)] = inputEvent.value; @@ -575,6 +576,41 @@ bool InputImpl::isKeyPressed(Keyboard::Key key) return keyMap[static_cast(key)]; } +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Scancode /* code */) +{ + // TODO: not implemented + err() << "sf::Keyboard::isKeyPressed(Keyboard::Scancode) is not implemented for DRM." << std::endl; + return false; +} + + +//////////////////////////////////////////////////////////// +Keyboard::Key InputImpl::localize(Keyboard::Scancode /* code */) +{ + // TODO: not implemented + err() << "sf::Keyboard::localize is not implemented for DRM." << std::endl; + return Keyboard::Unknown; +} + + +//////////////////////////////////////////////////////////// +Keyboard::Scancode InputImpl::delocalize(Keyboard::Key /* key */) +{ + // TODO: not implemented + err() << "sf::Keyboard::delocalize is not implemented for DRM." << std::endl; + return Keyboard::Scan::Unknown; +} + + +//////////////////////////////////////////////////////////// +String InputImpl::getDescription(Keyboard::Scancode /* code */) +{ + // TODO: not implemented + err() << "sf::Keyboard::getDescription is not implemented for DRM." << std::endl; + return ""; +} + //////////////////////////////////////////////////////////// void InputImpl::setVirtualKeyboardVisible(bool /*visible*/) diff --git a/src/SFML/Window/DRM/InputImplUDev.hpp b/src/SFML/Window/DRM/InputImplUDev.hpp index 062fb04a..13ad80d4 100644 --- a/src/SFML/Window/DRM/InputImplUDev.hpp +++ b/src/SFML/Window/DRM/InputImplUDev.hpp @@ -42,19 +42,37 @@ class InputImpl { public: //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise + /// \copydoc sf::Keyboard::isKeyPressed(Key) /// //////////////////////////////////////////////////////////// static bool isKeyPressed(Keyboard::Key key); //////////////////////////////////////////////////////////// - /// \brief Show or hide the virtual keyboard + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) /// - /// \param visible True to show, false to hide + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::setVirtualKeyboardVisible /// //////////////////////////////////////////////////////////// static void setVirtualKeyboardVisible(bool visible); diff --git a/src/SFML/Window/Keyboard.cpp b/src/SFML/Window/Keyboard.cpp index 0107105e..e5923686 100644 --- a/src/SFML/Window/Keyboard.cpp +++ b/src/SFML/Window/Keyboard.cpp @@ -25,6 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include @@ -37,6 +38,29 @@ bool Keyboard::isKeyPressed(Key key) return priv::InputImpl::isKeyPressed(key); } +//////////////////////////////////////////////////////////// +bool Keyboard::isKeyPressed(Scancode code) +{ + return priv::InputImpl::isKeyPressed(code); +} + +//////////////////////////////////////////////////////////// +Keyboard::Key Keyboard::localize(Scancode code) +{ + return priv::InputImpl::localize(code); +} + +//////////////////////////////////////////////////////////// +Keyboard::Scancode Keyboard::delocalize(Key key) +{ + return priv::InputImpl::delocalize(key); +} + +//////////////////////////////////////////////////////////// +String Keyboard::getDescription(Scancode code) +{ + return priv::InputImpl::getDescription(code); +} //////////////////////////////////////////////////////////// void Keyboard::setVirtualKeyboardVisible(bool visible) diff --git a/src/SFML/Window/OSX/HIDInputManager.hpp b/src/SFML/Window/OSX/HIDInputManager.hpp index 1b645343..6fd0c1a2 100644 --- a/src/SFML/Window/OSX/HIDInputManager.hpp +++ b/src/SFML/Window/OSX/HIDInputManager.hpp @@ -73,16 +73,6 @@ public: //////////////////////////////////////////////////////////// static HIDInputManager& getInstance(); - //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise - /// - //////////////////////////////////////////////////////////// - bool isKeyPressed(Keyboard::Key key); - //////////////////////////////////////////////////////////// /// \brief Get the usb location ID of a given device /// @@ -102,28 +92,69 @@ public: /// \return a retained CFDictionaryRef /// //////////////////////////////////////////////////////////// - static CFDictionaryRef copyDevicesMask(UInt32 page, UInt32 usage); + static CFDictionaryRef copyDevicesMask(std::uint32_t page, std::uint32_t usage); //////////////////////////////////////////////////////////// - /// Try to convert a character into a SFML key code. + /// \brief Try to convert a character into a SFML key code /// /// Return sf::Keyboard::Unknown if it doesn't match any 'localized' keys. /// - /// By 'localized' I mean keys that depend on the keyboard layout - /// and might not be the same as the US keycode in some country - /// (e.g. the keys 'Y' and 'Z' are switched on QWERTZ keyboard and + /// By 'localized' we mean keys that depend on the keyboard layout + /// and might not be the same as the US keycode for some countries + /// (e.g. the keys 'Y' and 'Z' are swapped on QWERTZ keyboard and /// US keyboard layouts.) /// //////////////////////////////////////////////////////////// - static Keyboard::Key localizedKeys(UniChar ch); + static Keyboard::Key localizedKey(UniChar ch); //////////////////////////////////////////////////////////// - /// Try to convert a virtual keycode into a SFML key code. + /// \brief Opposite transformation as localizedKeys /// - /// Return sf::Keyboard::Unknown if the keycode is unknown. + /// Return 0x00 (NULL) for non-convertible keys/numpad numbers. + /// For letters, uppercase codes are returned. + /// Some returned value are specific to macOS. /// //////////////////////////////////////////////////////////// - static Keyboard::Key nonLocalizedKeys(UniChar virtualKeycode); + static UniChar toUnicode(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \brief Try to convert a virtual keycode (HID level) into a + /// SFML scancode. + /// + /// Return sf::Keyboard::Scan::Unknown if the keycode is unknown. + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode nonLocalizedKey(UniChar virtualKeycode); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::isKeyPressed(Key) + /// + //////////////////////////////////////////////////////////// + bool isKeyPressed(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) + /// + //////////////////////////////////////////////////////////// + bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + String getDescription(Keyboard::Scancode code); private: //////////////////////////////////////////////////////////// @@ -141,7 +172,13 @@ private: //////////////////////////////////////////////////////////// /// \brief Initialize the keyboard part of this class /// - /// If something went wrong freeUp is called + /// If something went wrong freeUp is called. + /// + /// In a nutshell, this function does this: + /// + /// for each connected keyboard kb: + /// for each key k of kb: + /// memorize k -> scancode mapping /// //////////////////////////////////////////////////////////// void initializeKeyboard(); @@ -150,28 +187,50 @@ private: /// \brief Load the given keyboard into m_keys /// /// If the given keyboard has no key this function simply - /// returns. freeUp is _not_ called because this is not fatal. + /// returns without calling freeUp because this is not fatal. /// /// \param keyboard Keyboard to load /// + /// \see initializeKeyboard + /// //////////////////////////////////////////////////////////// void loadKeyboard(IOHIDDeviceRef keyboard); //////////////////////////////////////////////////////////// /// \brief Load the given key into m_keys /// - /// freeUp is _not_ called by this function. + /// On error, freeUp is _not_ called by this function. /// /// \param key Key to load /// + /// \see initializeKeyboard + /// //////////////////////////////////////////////////////////// void loadKey(IOHIDElementRef key); + //////////////////////////////////////////////////////////// + /// \brief Regenerate the mappings from/to Key and Scancode + /// + //////////////////////////////////////////////////////////// + void buildMappings(); + + //////////////////////////////////////////////////////////// + /// \brief Callback to regenerate mappings from/to Key and + /// Scancode when there's a keyboard layout change + /// + //////////////////////////////////////////////////////////// + static void keyboardChanged(CFNotificationCenterRef /* center */, + void* observer, + CFStringRef /* name */, + const void* /* object */, + CFDictionaryRef /* userInfo */); + //////////////////////////////////////////////////////////// /// \brief Release all resources /// - /// Close all connections to any devices, if required - /// Set m_isValid to false + /// Close all connections to any devices. + /// + /// \see initializeKeyboard /// //////////////////////////////////////////////////////////// void freeUp(); @@ -179,14 +238,14 @@ private: //////////////////////////////////////////////////////////// /// \brief Filter the devices and return them /// - /// freeUp is _not_ called by this function. + /// On error, freeUp is _not_ called by this function. /// /// \param page HID page like kHIDPage_GenericDesktop /// \param usage HID usage page like kHIDUsage_GD_Keyboard or kHIDUsage_GD_Mouse - /// \return a retained CFSetRef of IOHIDDeviceRef or a null pointer + /// \return a retained, non-empty CFSetRef of IOHIDDeviceRef or a null pointer /// //////////////////////////////////////////////////////////// - CFSetRef copyDevices(UInt32 page, UInt32 usage); + CFSetRef copyDevices(std::uint32_t page, std::uint32_t usage); //////////////////////////////////////////////////////////// /// \brief Check if a key is pressed @@ -201,34 +260,46 @@ private: bool isPressed(IOHIDElements& elements); //////////////////////////////////////////////////////////// - /// \brief Convert a HID key usage to its corresponding virtual code - /// - /// See IOHIDUsageTables.h + /// \brief Convert a HID key usage to its corresponding scancode /// /// \param usage Any kHIDUsage_Keyboard* usage - /// \return the virtual code associate with the given HID key usage - /// or 0xff if it is associate with no virtual code + /// \return the scancode associated with the given HID key usage + /// or Scan::Unknown if it is associated with no scancode. /// //////////////////////////////////////////////////////////// - static UInt8 usageToVirtualCode(UInt32 usage); + static Keyboard::Scancode usageToScancode(std::uint32_t usage); + + //////////////////////////////////////////////////////////// + /// Convert the scancode to the expected virtual code. + /// + //////////////////////////////////////////////////////////// + static std::uint8_t scanToVirtualCode(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// Fallback convertion for keys which aren't expected to be impacted + /// by the layout. Can return Unknown. + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localizedKeyFallback(Keyboard::Scancode code); //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - bool m_isValid{true}; ///< If any error occurs this variable is false - CFDataRef m_layoutData{}; ///< CFData containing the layout - UCKeyboardLayout* m_layout{}; ///< Current Keyboard Layout - IOHIDManagerRef m_manager{}; ///< HID Manager - - IOHIDElements m_keys[Keyboard::KeyCount]; ///< All the keys on any connected keyboard + IOHIDManagerRef m_manager{}; ///< Underlying HID Manager + IOHIDElements m_keys[Keyboard::Scan::ScancodeCount]; ///< All the keys on any connected keyboard + Keyboard::Scancode m_keyToScancodeMapping[Keyboard::KeyCount]; ///< Mapping from Key to Scancode + Keyboard::Key m_scancodeToKeyMapping[Keyboard::Scan::ScancodeCount]; ///< Mapping from Scancode to Key //////////////////////////////////////////////////////////// - /// m_keys' index corresponds to sf::Keyboard::Key enum. - /// if no key is assigned with key XYZ then m_keys[XYZ].size() == 0. - /// if there are several keyboards connected and several HID keys associate + /// m_keys' index corresponds to sf::Keyboard::Scancode enum. + /// If no key is assigned with key XYZ then m_keys[XYZ].size() == 0. + /// If there are several keyboards connected and several HID keys associated /// with the same sf::Keyboard::Key then m_keys[XYZ] contains all these /// HID keys. /// + /// The mappings (both directions) get invalidated when the + /// keyboard layout changes. They both default to (Scan::)Unknown. + /// //////////////////////////////////////////////////////////// }; diff --git a/src/SFML/Window/OSX/HIDInputManager.mm b/src/SFML/Window/OSX/HIDInputManager.mm index 296503cd..989a2467 100644 --- a/src/SFML/Window/OSX/HIDInputManager.mm +++ b/src/SFML/Window/OSX/HIDInputManager.mm @@ -32,6 +32,12 @@ #include #include +namespace +{ +static const std::uint8_t unknownVirtualCode = 0xff; +static const bool isIsoKeyboard = (KBGetLayoutType(LMGetKbdType()) == kKeyboardISO); +} + namespace sf::priv { //////////////////////////////////////////////////////////// @@ -42,13 +48,6 @@ HIDInputManager& HIDInputManager::getInstance() } -//////////////////////////////////////////////////////////// -bool HIDInputManager::isKeyPressed(Keyboard::Key key) -{ - return isPressed(m_keys[key]); -} - - //////////////////////////////////////////////////////////// long HIDInputManager::getLocationID(IOHIDDeviceRef device) { @@ -69,7 +68,7 @@ long HIDInputManager::getLocationID(IOHIDDeviceRef device) //////////////////////////////////////////////////////////// -CFDictionaryRef HIDInputManager::copyDevicesMask(UInt32 page, UInt32 usage) +CFDictionaryRef HIDInputManager::copyDevicesMask(std::uint32_t page, std::uint32_t usage) { // Create the dictionary. CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, @@ -91,42 +90,641 @@ CFDictionaryRef HIDInputManager::copyDevicesMask(UInt32 page, UInt32 usage) } +//////////////////////////////////////////////////////// +Keyboard::Key HIDInputManager::localizedKey(UniChar ch) +{ + // Apple is using the private range 0xE000 - 0xF8FF for special keys. + // Otherwise one can refer to the usual Unicode table. + // clang-format off + switch (ch) + { + case 0x41: return Keyboard::A; // uppercase A + case 0x42: return Keyboard::B; + case 0x43: return Keyboard::C; + case 0x44: return Keyboard::D; + case 0x45: return Keyboard::E; + case 0x46: return Keyboard::F; + case 0x47: return Keyboard::G; + case 0x48: return Keyboard::H; + case 0x49: return Keyboard::I; + case 0x4a: return Keyboard::J; + case 0x4b: return Keyboard::K; + case 0x4c: return Keyboard::L; + case 0x4d: return Keyboard::M; + case 0x4e: return Keyboard::N; + case 0x4f: return Keyboard::O; + case 0x50: return Keyboard::P; + case 0x51: return Keyboard::Q; + case 0x52: return Keyboard::R; + case 0x53: return Keyboard::S; + case 0x54: return Keyboard::T; + case 0x55: return Keyboard::U; + case 0x56: return Keyboard::V; + case 0x57: return Keyboard::W; + case 0x58: return Keyboard::X; + case 0x59: return Keyboard::Y; + case 0x5a: return Keyboard::Z; + + case 0x61: return Keyboard::A; // lowercase A + case 0x62: return Keyboard::B; + case 0x63: return Keyboard::C; + case 0x64: return Keyboard::D; + case 0x65: return Keyboard::E; + case 0x66: return Keyboard::F; + case 0x67: return Keyboard::G; + case 0x68: return Keyboard::H; + case 0x69: return Keyboard::I; + case 0x6a: return Keyboard::J; + case 0x6b: return Keyboard::K; + case 0x6c: return Keyboard::L; + case 0x6d: return Keyboard::M; + case 0x6e: return Keyboard::N; + case 0x6f: return Keyboard::O; + case 0x70: return Keyboard::P; + case 0x71: return Keyboard::Q; + case 0x72: return Keyboard::R; + case 0x73: return Keyboard::S; + case 0x74: return Keyboard::T; + case 0x75: return Keyboard::U; + case 0x76: return Keyboard::V; + case 0x77: return Keyboard::W; + case 0x78: return Keyboard::X; + case 0x79: return Keyboard::Y; + case 0x7a: return Keyboard::Z; + + case 0x30: return Keyboard::Num0; + case 0x31: return Keyboard::Num1; + case 0x32: return Keyboard::Num2; + case 0x33: return Keyboard::Num3; + case 0x34: return Keyboard::Num4; + case 0x35: return Keyboard::Num5; + case 0x36: return Keyboard::Num6; + case 0x37: return Keyboard::Num7; + case 0x38: return Keyboard::Num8; + case 0x39: return Keyboard::Num9; + + case 0x1b: return Keyboard::Escape; + + /* Those are not represented using Unicode. + * case 0x: return Keyboard::LControl; + * case 0x: return Keyboard::LShift; + * case 0x: return Keyboard::LAlt; + * case 0x: return Keyboard::LSystem; + * case 0x: return Keyboard::RControl; + * case 0x: return Keyboard::RShift; + * case 0x: return Keyboard::RAlt; + * case 0x: return Keyboard::RSystem; + */ + + case NSMenuFunctionKey: return Keyboard::Menu; + + case 0x5b: return Keyboard::LBracket; + case 0x5d: return Keyboard::RBracket; + case 0x3b: return Keyboard::Semicolon; + case 0x2c: return Keyboard::Comma; + case 0x2e: return Keyboard::Period; + case 0x27: return Keyboard::Apostrophe; + case 0x2f: return Keyboard::Slash; + case 0x5c: return Keyboard::Backslash; + case 0x60: return Keyboard::Grave; + case 0x3d: return Keyboard::Equal; + case 0x2d: return Keyboard::Hyphen; + case 0x20: return Keyboard::Space; + case 0x0d: return Keyboard::Enter; + case 0x08: return Keyboard::Backspace; + case 0x09: return Keyboard::Tab; + + case NSPageUpFunctionKey: return Keyboard::PageUp; + case NSPageDownFunctionKey: return Keyboard::PageDown; + case NSEndFunctionKey: return Keyboard::End; + case NSHomeFunctionKey: return Keyboard::Home; + case NSInsertFunctionKey: return Keyboard::Insert; + case NSDeleteFunctionKey: return Keyboard::Delete; + case 0x7f: return Keyboard::Delete; + + case 0x2b: return Keyboard::Add; + // case 0x: return Keyboard::Subtract; // collision with Keyboard::Hyphen + case 0x2a: return Keyboard::Multiply; + // case 0x: return Keyboard::Divide; // collision with Keyboard::Slash + + case NSLeftArrowFunctionKey: return Keyboard::Left; + case NSRightArrowFunctionKey: return Keyboard::Right; + case NSUpArrowFunctionKey: return Keyboard::Up; + case NSDownArrowFunctionKey: return Keyboard::Down; + + /* Those are not represented using Unicode. + * case 0x: return Keyboard::Numpad0; + * case 0x: return Keyboard::Numpad1; + * case 0x: return Keyboard::Numpad2; + * case 0x: return Keyboard::Numpad3; + * case 0x: return Keyboard::Numpad4; + * case 0x: return Keyboard::Numpad5; + * case 0x: return Keyboard::Numpad6; + * case 0x: return Keyboard::Numpad7; + * case 0x: return Keyboard::Numpad8; + * case 0x: return Keyboard::Numpad9; + */ + + case NSF1FunctionKey: return Keyboard::F1; + case NSF2FunctionKey: return Keyboard::F2; + case NSF3FunctionKey: return Keyboard::F3; + case NSF4FunctionKey: return Keyboard::F4; + case NSF5FunctionKey: return Keyboard::F5; + case NSF6FunctionKey: return Keyboard::F6; + case NSF7FunctionKey: return Keyboard::F7; + case NSF8FunctionKey: return Keyboard::F8; + case NSF9FunctionKey: return Keyboard::F9; + case NSF10FunctionKey: return Keyboard::F10; + case NSF11FunctionKey: return Keyboard::F11; + case NSF12FunctionKey: return Keyboard::F12; + case NSF13FunctionKey: return Keyboard::F13; + case NSF14FunctionKey: return Keyboard::F14; + case NSF15FunctionKey: return Keyboard::F15; + + case NSPauseFunctionKey: return Keyboard::Pause; + + default: return Keyboard::Unknown; + } + // clang-format on +} + + +//////////////////////////////////////////////////////// +UniChar HIDInputManager::toUnicode(Keyboard::Key key) +{ + // clang-format off + switch (key) + { + case Keyboard::A: return 0x41; + case Keyboard::B: return 0x42; + case Keyboard::C: return 0x43; + case Keyboard::D: return 0x44; + case Keyboard::E: return 0x45; + case Keyboard::F: return 0x46; + case Keyboard::G: return 0x47; + case Keyboard::H: return 0x48; + case Keyboard::I: return 0x49; + case Keyboard::J: return 0x4a; + case Keyboard::K: return 0x4b; + case Keyboard::L: return 0x4c; + case Keyboard::M: return 0x4d; + case Keyboard::N: return 0x4e; + case Keyboard::O: return 0x4f; + case Keyboard::P: return 0x50; + case Keyboard::Q: return 0x51; + case Keyboard::R: return 0x52; + case Keyboard::S: return 0x53; + case Keyboard::T: return 0x54; + case Keyboard::U: return 0x55; + case Keyboard::V: return 0x56; + case Keyboard::W: return 0x57; + case Keyboard::X: return 0x58; + case Keyboard::Y: return 0x59; + case Keyboard::Z: return 0x5a; + + case Keyboard::Num0: return 0x30; + case Keyboard::Num1: return 0x31; + case Keyboard::Num2: return 0x32; + case Keyboard::Num3: return 0x33; + case Keyboard::Num4: return 0x34; + case Keyboard::Num5: return 0x35; + case Keyboard::Num6: return 0x36; + case Keyboard::Num7: return 0x37; + case Keyboard::Num8: return 0x38; + case Keyboard::Num9: return 0x39; + + case Keyboard::Escape: return 0x1b; + + /* Not representable as Unicode: + * case Keyboard::LControl: return 0x; + * case Keyboard::LShift: return 0x; + * case Keyboard::LAlt: return 0x; + * case Keyboard::LSystem: return 0x; + * case Keyboard::RControl: return 0x; + * case Keyboard::RShift: return 0x; + * case Keyboard::RAlt: return 0x; + * case Keyboard::RSystem: return 0x; + */ + + case Keyboard::Menu: return NSMenuFunctionKey; + + case Keyboard::LBracket: return 0x5b; + case Keyboard::RBracket: return 0x5d; + case Keyboard::Semicolon: return 0x3b; + case Keyboard::Comma: return 0x2c; + case Keyboard::Period: return 0x2e; + case Keyboard::Apostrophe: return 0x27; + case Keyboard::Slash: return 0x2f; + case Keyboard::Backslash: return 0x5c; + case Keyboard::Grave: return 0x60; + case Keyboard::Equal: return 0x3d; + case Keyboard::Hyphen: return 0x2d; + case Keyboard::Space: return 0x20; + case Keyboard::Enter: return 0x0d; + case Keyboard::Backspace: return 0x08; + case Keyboard::Tab: return 0x09; + + case Keyboard::PageUp: return NSPageUpFunctionKey; + case Keyboard::PageDown: return NSPageDownFunctionKey; + case Keyboard::End: return NSEndFunctionKey; + case Keyboard::Home: return NSHomeFunctionKey; + case Keyboard::Insert: return NSInsertFunctionKey; + case Keyboard::Delete: return NSDeleteFunctionKey; + + case Keyboard::Add: return 0x2b; + case Keyboard::Subtract: return 0x2d; + case Keyboard::Multiply: return 0x2a; + case Keyboard::Divide: return 0x2f; + + case Keyboard::Left: return NSLeftArrowFunctionKey; + case Keyboard::Right: return NSRightArrowFunctionKey; + case Keyboard::Up: return NSUpArrowFunctionKey; + case Keyboard::Down: return NSDownArrowFunctionKey; + + /* Those are not represented using Unicode. + * case Keyboard::Numpad0: return 0x; + * case Keyboard::Numpad1: return 0x; + * case Keyboard::Numpad2: return 0x; + * case Keyboard::Numpad3: return 0x; + * case Keyboard::Numpad4: return 0x; + * case Keyboard::Numpad5: return 0x; + * case Keyboard::Numpad6: return 0x; + * case Keyboard::Numpad7: return 0x; + * case Keyboard::Numpad8: return 0x; + * case Keyboard::Numpad9: return 0x; + */ + + case Keyboard::F1: return NSF1FunctionKey; + case Keyboard::F2: return NSF2FunctionKey; + case Keyboard::F3: return NSF3FunctionKey; + case Keyboard::F4: return NSF4FunctionKey; + case Keyboard::F5: return NSF5FunctionKey; + case Keyboard::F6: return NSF6FunctionKey; + case Keyboard::F7: return NSF7FunctionKey; + case Keyboard::F8: return NSF8FunctionKey; + case Keyboard::F9: return NSF9FunctionKey; + case Keyboard::F10: return NSF10FunctionKey; + case Keyboard::F11: return NSF11FunctionKey; + case Keyboard::F12: return NSF12FunctionKey; + case Keyboard::F13: return NSF13FunctionKey; + case Keyboard::F14: return NSF14FunctionKey; + case Keyboard::F15: return NSF15FunctionKey; + + case Keyboard::Pause: return NSPauseFunctionKey; + + default: return 0x00; + } + // clang-format on +} + + +//////////////////////////////////////////////////////// +Keyboard::Scancode HIDInputManager::nonLocalizedKey(UniChar virtualKeycode) +{ + // See Chapter 2, esp. Figure 2-10 of + // https://developer.apple.com/legacy/library/documentation/mac/pdf/MacintoshToolboxEssentials.pdf + // Additional virtual codes come from + // /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h + // clang-format off + switch (virtualKeycode) + { + case 0x00: return Keyboard::Scan::A; + case 0x0b: return Keyboard::Scan::B; + case 0x08: return Keyboard::Scan::C; + case 0x02: return Keyboard::Scan::D; + case 0x0e: return Keyboard::Scan::E; + case 0x03: return Keyboard::Scan::F; + case 0x05: return Keyboard::Scan::G; + case 0x04: return Keyboard::Scan::H; + case 0x22: return Keyboard::Scan::I; + case 0x26: return Keyboard::Scan::J; + case 0x28: return Keyboard::Scan::K; + case 0x25: return Keyboard::Scan::L; + case 0x2e: return Keyboard::Scan::M; + case 0x2d: return Keyboard::Scan::N; + case 0x1f: return Keyboard::Scan::O; + case 0x23: return Keyboard::Scan::P; + case 0x0c: return Keyboard::Scan::Q; + case 0x0f: return Keyboard::Scan::R; + case 0x01: return Keyboard::Scan::S; + case 0x11: return Keyboard::Scan::T; + case 0x20: return Keyboard::Scan::U; + case 0x09: return Keyboard::Scan::V; + case 0x0d: return Keyboard::Scan::W; + case 0x07: return Keyboard::Scan::X; + case 0x10: return Keyboard::Scan::Y; + case 0x06: return Keyboard::Scan::Z; + + case 0x12: return Keyboard::Scan::Num1; + case 0x13: return Keyboard::Scan::Num2; + case 0x14: return Keyboard::Scan::Num3; + case 0x15: return Keyboard::Scan::Num4; + case 0x17: return Keyboard::Scan::Num5; + case 0x16: return Keyboard::Scan::Num6; + case 0x1a: return Keyboard::Scan::Num7; + case 0x1c: return Keyboard::Scan::Num8; + case 0x19: return Keyboard::Scan::Num9; + case 0x1d: return Keyboard::Scan::Num0; + + case 0x24: return Keyboard::Scan::Enter; + case 0x35: return Keyboard::Scan::Escape; + case 0x33: return Keyboard::Scan::Backspace; + case 0x30: return Keyboard::Scan::Tab; + case 0x31: return Keyboard::Scan::Space; + + case 0x18: return Keyboard::Scan::Equal; + case 0x21: return Keyboard::Scan::LBracket; + case 0x1e: return Keyboard::Scan::RBracket; + case 0x2a: return Keyboard::Scan::Backslash; + case 0x1b: return Keyboard::Scan::Hyphen; + case 0x29: return Keyboard::Scan::Semicolon; + case 0x27: return Keyboard::Scan::Apostrophe; + case 0x32: return isIsoKeyboard ? Keyboard::Scan::NonUsBackslash : Keyboard::Scan::Grave; + case 0x0a: return Keyboard::Scan::Grave; + case 0x2b: return Keyboard::Scan::Comma; + case 0x2f: return Keyboard::Scan::Period; + case 0x2c: return Keyboard::Scan::Slash; + + case 0x7a: return Keyboard::Scan::F1; + case 0x78: return Keyboard::Scan::F2; + case 0x63: return Keyboard::Scan::F3; + case 0x76: return Keyboard::Scan::F4; + case 0x60: return Keyboard::Scan::F5; + case 0x61: return Keyboard::Scan::F6; + case 0x62: return Keyboard::Scan::F7; + case 0x64: return Keyboard::Scan::F8; + case 0x65: return Keyboard::Scan::F9; + case 0x6d: return Keyboard::Scan::F10; + case 0x67: return Keyboard::Scan::F11; + case 0x6f: return Keyboard::Scan::F12; + case 0x69: return Keyboard::Scan::F13; + case 0x6b: return Keyboard::Scan::F14; + case 0x71: return Keyboard::Scan::F15; + + case 0x6A: return Keyboard::Scan::F16; + case 0x40: return Keyboard::Scan::F17; + case 0x4F: return Keyboard::Scan::F18; + case 0x50: return Keyboard::Scan::F19; + case 0x5A: return Keyboard::Scan::F20; + + /* TODO Those are missing: + * case 0x: return Keyboard::Scan::F21; + * case 0x: return Keyboard::Scan::F22; + * case 0x: return Keyboard::Scan::F23; + * case 0x: return Keyboard::Scan::F24; + */ + + case 0x39: return Keyboard::Scan::CapsLock; + + /* TODO Those are missing: + * case 0x: return Keyboard::Scan::PrintScreen; + * case 0x: return Keyboard::Scan::ScrollLock; + * case 0x: return Keyboard::Scan::Pause; + */ + + case 0x72: return Keyboard::Scan::Insert; + case 0x73: return Keyboard::Scan::Home; + case 0x74: return Keyboard::Scan::PageUp; + case 0x75: return Keyboard::Scan::Delete; + case 0x77: return Keyboard::Scan::End; + case 0x79: return Keyboard::Scan::PageDown; + + case 0x7c: return Keyboard::Scan::Right; + case 0x7b: return Keyboard::Scan::Left; + case 0x7d: return Keyboard::Scan::Down; + case 0x7e: return Keyboard::Scan::Up; + + case 0x47: return Keyboard::Scan::NumLock; + case 0x4b: return Keyboard::Scan::NumpadDivide; + case 0x43: return Keyboard::Scan::NumpadMultiply; + case 0x4e: return Keyboard::Scan::NumpadMinus; + case 0x45: return Keyboard::Scan::NumpadPlus; + case 0x51: return Keyboard::Scan::NumpadEqual; + case 0x4c: return Keyboard::Scan::NumpadEnter; + case 0x41: return Keyboard::Scan::NumpadDecimal; + + case 0x53: return Keyboard::Scan::Numpad1; + case 0x54: return Keyboard::Scan::Numpad2; + case 0x55: return Keyboard::Scan::Numpad3; + case 0x56: return Keyboard::Scan::Numpad4; + case 0x57: return Keyboard::Scan::Numpad5; + case 0x58: return Keyboard::Scan::Numpad6; + case 0x59: return Keyboard::Scan::Numpad7; + case 0x5b: return Keyboard::Scan::Numpad8; + case 0x5c: return Keyboard::Scan::Numpad9; + case 0x52: return Keyboard::Scan::Numpad0; + + /* TODO Those are missing: + * case 0x: return Keyboard::Scan::Application; + * case 0x: return Keyboard::Scan::Execute; + * case 0x: return Keyboard::Scan::Help; + * case 0x: return Keyboard::Scan::Menu; + * case 0x: return Keyboard::Scan::Select; + * case 0x: return Keyboard::Scan::Stop; + * case 0x: return Keyboard::Scan::Redo; + * case 0x: return Keyboard::Scan::Undo; + * case 0x: return Keyboard::Scan::Cut; + * case 0x: return Keyboard::Scan::Copy; + * case 0x: return Keyboard::Scan::Paste; + * case 0x: return Keyboard::Scan::Search; + */ + + case 0x4a: return Keyboard::Scan::VolumeMute; + case 0x48: return Keyboard::Scan::VolumeUp; + case 0x49: return Keyboard::Scan::VolumeDown; + + /* NOTE Those are symmetric so we leave them out. + * Thankfully handled through modifiers and not virtual codes. + * case 0x3b: return Keyboard::Scan::LControl; + * case 0x38: return Keyboard::Scan::LShift; + * case 0x3a: return Keyboard::Scan::LAlt; + * case 0x37: return Keyboard::Scan::LSystem; + * case 0x3b: return Keyboard::Scan::RControl; + * case 0x38: return Keyboard::Scan::RShift; + * case 0x3a: return Keyboard::Scan::RAlt; + * case 0x37: return Keyboard::Scan::RSystem; + */ + + default: return Keyboard::Scan::Unknown; + } + // clang-format on +} + + +//////////////////////////////////////////////////////////// +bool HIDInputManager::isKeyPressed(Keyboard::Key key) +{ + return isKeyPressed(delocalize(key)); +} + + +//////////////////////////////////////////////////////////// +bool HIDInputManager::isKeyPressed(Keyboard::Scancode code) +{ + return (code != Keyboard::Scan::Unknown) && isPressed(m_keys[code]); +} + + +//////////////////////////////////////////////////////////// +Keyboard::Key HIDInputManager::localize(Keyboard::Scancode code) +{ + if (code == Keyboard::Scan::Unknown) + return Keyboard::Unknown; + + return m_scancodeToKeyMapping[code]; +} + + +//////////////////////////////////////////////////////////// +Keyboard::Scancode HIDInputManager::delocalize(Keyboard::Key key) +{ + if (key == Keyboard::Unknown) + return Keyboard::Scan::Unknown; + + return m_keyToScancodeMapping[key]; +} + + +//////////////////////////////////////////////////////////// +String HIDInputManager::getDescription(Keyboard::Scancode code) +{ + // Phase 1: Get names for layout independent keys + // clang-format off + switch (code) + { + case Keyboard::Scan::Enter: return "Enter"; + case Keyboard::Scan::Escape: return "Escape"; + case Keyboard::Scan::Backspace: return "Backspace"; + case Keyboard::Scan::Tab: return "Tab"; + case Keyboard::Scan::Space: return "Space"; + + case Keyboard::Scan::F1: return "F1"; + case Keyboard::Scan::F2: return "F2"; + case Keyboard::Scan::F3: return "F3"; + case Keyboard::Scan::F4: return "F4"; + case Keyboard::Scan::F5: return "F5"; + case Keyboard::Scan::F6: return "F6"; + case Keyboard::Scan::F7: return "F7"; + case Keyboard::Scan::F8: return "F8"; + case Keyboard::Scan::F9: return "F9"; + case Keyboard::Scan::F10: return "F10"; + case Keyboard::Scan::F11: return "F11"; + case Keyboard::Scan::F12: return "F12"; + case Keyboard::Scan::F13: return "F13"; + case Keyboard::Scan::F14: return "F14"; + case Keyboard::Scan::F15: return "F15"; + case Keyboard::Scan::F16: return "F16"; + case Keyboard::Scan::F17: return "F17"; + case Keyboard::Scan::F18: return "F18"; + case Keyboard::Scan::F19: return "F19"; + case Keyboard::Scan::F20: return "F20"; + case Keyboard::Scan::F21: return "F21"; + case Keyboard::Scan::F22: return "F22"; + case Keyboard::Scan::F23: return "F23"; + case Keyboard::Scan::F24: return "F24"; + + case Keyboard::Scan::CapsLock: return "Caps Lock"; + case Keyboard::Scan::PrintScreen: return "Print Screen"; + case Keyboard::Scan::ScrollLock: return "Scroll Lock"; + + case Keyboard::Scan::Pause: return "Pause"; + case Keyboard::Scan::Insert: return "Insert"; + case Keyboard::Scan::Home: return "Home"; + case Keyboard::Scan::PageUp: return "Page Up"; + case Keyboard::Scan::Delete: return "Delete"; + case Keyboard::Scan::End: return "End"; + case Keyboard::Scan::PageDown: return "Page Down"; + + case Keyboard::Scan::Left: return "Left Arrow"; + case Keyboard::Scan::Right: return "Right Arrow"; + case Keyboard::Scan::Down: return "Down Arrow"; + case Keyboard::Scan::Up: return "Up Arrow"; + + case Keyboard::Scan::NumLock: return "Num Lock"; + case Keyboard::Scan::NumpadDivide: return "Divide (Numpad)"; + case Keyboard::Scan::NumpadMultiply: return "Multiply (Numpad)"; + case Keyboard::Scan::NumpadMinus: return "Minus (Numpad)"; + case Keyboard::Scan::NumpadPlus: return "Plus (Numpad)"; + case Keyboard::Scan::NumpadEqual: return "Equal (Numpad)"; + case Keyboard::Scan::NumpadEnter: return "Enter (Numpad)"; + case Keyboard::Scan::NumpadDecimal: return "Decimal (Numpad)"; + + case Keyboard::Scan::Numpad0: return "0 (Numpad)"; + case Keyboard::Scan::Numpad1: return "1 (Numpad)"; + case Keyboard::Scan::Numpad2: return "2 (Numpad)"; + case Keyboard::Scan::Numpad3: return "3 (Numpad)"; + case Keyboard::Scan::Numpad4: return "4 (Numpad)"; + case Keyboard::Scan::Numpad5: return "5 (Numpad)"; + case Keyboard::Scan::Numpad6: return "6 (Numpad)"; + case Keyboard::Scan::Numpad7: return "7 (Numpad)"; + case Keyboard::Scan::Numpad8: return "8 (Numpad)"; + case Keyboard::Scan::Numpad9: return "9 (Numpad)"; + + case Keyboard::Scan::Application: return "Application"; + case Keyboard::Scan::Execute: return "Execute"; + case Keyboard::Scan::Help: return "Help"; + case Keyboard::Scan::Menu: return "Menu"; + case Keyboard::Scan::Select: return "Select"; + case Keyboard::Scan::Stop: return "Stop"; + case Keyboard::Scan::Redo: return "Redo"; + case Keyboard::Scan::Undo: return "Undo"; + case Keyboard::Scan::Cut: return "Cut"; + case Keyboard::Scan::Copy: return "Copy"; + case Keyboard::Scan::Paste: return "Paste"; + case Keyboard::Scan::Search: return "Search"; + case Keyboard::Scan::VolumeMute: return "Volume Mute"; + case Keyboard::Scan::VolumeUp: return "Volume Up"; + case Keyboard::Scan::VolumeDown: return "Volume Down"; + + case Keyboard::Scan::LControl: return "Left Control"; + case Keyboard::Scan::LShift: return "Left Shift"; + case Keyboard::Scan::LAlt: return "Left Option"; + case Keyboard::Scan::LSystem: return "Left Command"; + case Keyboard::Scan::RControl: return "Right Control"; + case Keyboard::Scan::RShift: return "Right Shift"; + case Keyboard::Scan::RAlt: return "Right Option"; + case Keyboard::Scan::RSystem: return "Right Command"; + + default: + { + // Phase 2: Try to convert the key to unicode + UniChar unicode = toUnicode(localize(code)); + if (unicode != 0x00) + return String(static_cast(unicode)); + } + + // Phase 3: Return final fallback + return "Unknown"; + } + // clang-format on +} + + //////////////////////////////////////////////////////////// HIDInputManager::HIDInputManager() { - // Get the current keyboard layout - TISInputSourceRef tis = TISCopyCurrentKeyboardLayoutInputSource(); - m_layoutData = static_cast(TISGetInputSourceProperty(tis, kTISPropertyUnicodeKeyLayoutData)); - - if (m_layoutData == nil) - { - sf::err() << "Cannot get the keyboard layout" << std::endl; - freeUp(); - return; - } - - // Keep a reference for ourself - CFRetain(m_layoutData); - m_layout = reinterpret_cast(const_cast(CFDataGetBytePtr(m_layoutData))); - - // The TIS is no more needed - CFRelease(tis); - // Create an HID Manager reference - m_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - - // Open the HID Manager reference + m_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); IOReturn openStatus = IOHIDManagerOpen(m_manager, kIOHIDOptionsTypeNone); if (openStatus != kIOReturnSuccess) { - sf::err() << "Error when opening the HID manager" << std::endl; + err() << "Error when opening the HID manager" << std::endl; freeUp(); return; } - // Initialize the keyboard + // Build up our knownledge of the hardware initializeKeyboard(); + buildMappings(); + + // Register for notification on keyboard layout changes + CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), + this, + keyboardChanged, // callback + kTISNotifySelectedKeyboardInputSourceChanged, + nullptr, // use callback + CFNotificationSuspensionBehaviorDeliverImmediately); } @@ -134,6 +732,8 @@ HIDInputManager::HIDInputManager() HIDInputManager::~HIDInputManager() { freeUp(); + + CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDistributedCenter(), this); } @@ -141,31 +741,23 @@ void HIDInputManager::initializeKeyboard() { //////////////////////////////////////////////////////////// // The purpose of this function is to initialize m_keys so we can get - // the associate IOHIDElementRef with a sf::Keyboard::Key in ~constant~ time. + // the associate IOHIDElementRef with a sf::Keyboard::Scancode + // in approximately constant time. // Get only keyboards - CFSetRef keyboards = copyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); - if (keyboards == nullptr) + CFSetRef underlying = copyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); + if (underlying == nullptr) { - sf::err() << "No keyboard detected by the HID manager!" << std::endl; + err() << "No keyboard detected by the HID manager!" << std::endl; freeUp(); return; } - CFIndex keyboardCount = CFSetGetCount(keyboards); // >= 1 (asserted by copyDevices) + NSSet* keyboards = static_cast(underlying); // Toll-Free Bridge + for (id keyboard in keyboards) + loadKeyboard(static_cast(keyboard)); - // Get an iterable array - std::vector devicesArray(static_cast(keyboardCount)); - CFSetGetValues(keyboards, &devicesArray[0]); - - for (CFIndex i = 0; i < keyboardCount; ++i) - { - IOHIDDeviceRef keyboard = static_cast(const_cast(devicesArray[static_cast(i)])); - loadKeyboard(keyboard); - } - - // Release unused stuff - CFRelease(keyboards); + CFRelease(underlying); //////////////////////////////////////////////////////////// // At this point m_keys is filled with as many IOHIDElementRef as possible @@ -175,140 +767,159 @@ void HIDInputManager::initializeKeyboard() //////////////////////////////////////////////////////////// void HIDInputManager::loadKeyboard(IOHIDDeviceRef keyboard) { - CFArrayRef keys = IOHIDDeviceCopyMatchingElements(keyboard, nullptr, kIOHIDOptionsTypeNone); - if (keys == nullptr) + CFArrayRef underlying = IOHIDDeviceCopyMatchingElements(keyboard, nullptr, kIOHIDOptionsTypeNone); + if ((underlying == nullptr) || (CFArrayGetCount(underlying) == 0)) { - sf::err() << "We got a keyboard without any keys (1)" << std::endl; + err() << "We got a keyboard without any keys." << std::endl; return; } - // How many elements are there? - CFIndex keysCount = CFArrayGetCount(keys); - - if (keysCount == 0) + NSArray* keys = static_cast(underlying); // Toll-Free Bridge + for (id key in keys) { - sf::err() << "We got a keyboard without any keys (2)" << std::endl; - CFRelease(keys); - return; + IOHIDElementRef elem = static_cast(key); + if (IOHIDElementGetUsagePage(elem) == kHIDPage_KeyboardOrKeypad) + loadKey(elem); } - // Go through all connected elements. - for (CFIndex i = 0; i < keysCount; ++i) - { - IOHIDElementRef aKey = static_cast(const_cast(CFArrayGetValueAtIndex(keys, i))); - - // Skip non-matching keys elements - if (IOHIDElementGetUsagePage(aKey) != kHIDPage_KeyboardOrKeypad) - continue; - - loadKey(aKey); - } - - // Release unused stuff - CFRelease(keys); + CFRelease(underlying); } //////////////////////////////////////////////////////////// void HIDInputManager::loadKey(IOHIDElementRef key) { - // Get its virtual code - UInt32 usageCode = IOHIDElementGetUsage(key); - UInt8 virtualCode = usageToVirtualCode(usageCode); - - if (virtualCode == 0xff) - return; // no corresponding virtual code -> skip - - // Now translate the virtual code to Unicode according to - // the current keyboard layout - - UInt32 deadKeyState = 0; - // Unicode string length is usually less or equal to 4 - UniCharCount maxStringLength = 4; - UniCharCount actualStringLength = 0; - std::vector unicodeString(maxStringLength); - - OSStatus error; - - error = UCKeyTranslate(m_layout, // current layout - virtualCode, // our key - kUCKeyActionDown, // or kUCKeyActionUp ? - 0x100, // no modifiers - LMGetKbdType(), // keyboard's type - kUCKeyTranslateNoDeadKeysBit, // some sort of option - &deadKeyState, // unused stuff - maxStringLength, // our memory limit - &actualStringLength, // length of what we get - unicodeString.data()); // what we get - - if (error == noErr) + std::uint32_t usage = IOHIDElementGetUsage(key); + Keyboard::Scancode code = usageToScancode(usage); + if (code != Keyboard::Scan::Unknown) { - // Translation went fine + CFRetain(key); + m_keys[code].push_back(key); + } +} - // The corresponding SFML key code - Keyboard::Key code = Keyboard::Unknown; // KeyCound means 'none' - // First we look if the key down is from a list of characters - // that depend on keyboard localization - if (actualStringLength > 0) - code = localizedKeys(unicodeString[0]); +//////////////////////////////////////////////////////////// +void HIDInputManager::buildMappings() +{ + // Reset the mappings + for (auto& scancode : m_keyToScancodeMapping) + scancode = Keyboard::Scan::Unknown; + for (auto& key : m_scancodeToKeyMapping) + key = Keyboard::Unknown; - // The key is not a localized one so we try to find a - // corresponding code through virtual key code - if (code == Keyboard::Unknown) - code = nonLocalizedKeys(virtualCode); + // Get the current keyboard layout + TISInputSourceRef tis = TISCopyCurrentKeyboardLayoutInputSource(); + CFDataRef layoutData = static_cast(TISGetInputSourceProperty(tis, kTISPropertyUnicodeKeyLayoutData)); - // A code was found, wonderful! - if (code != Keyboard::Unknown) + if (layoutData == nullptr) + { + err() << "Cannot get the keyboard layout\n"; + CFRelease(tis); + return; + } + + UCKeyboardLayout* layout = reinterpret_cast(const_cast(CFDataGetBytePtr(layoutData))); + + // For each scancode having a IOHIDElement, we translate the corresponding + // virtual code to a localized Key. + for (int i = 0; i < Keyboard::Scan::ScancodeCount; ++i) + { + Keyboard::Scancode scan = static_cast(i); + std::uint8_t virtualCode = scanToVirtualCode(scan); + + if (virtualCode == unknownVirtualCode) + continue; + + // Translating virtual code to string is not injective. For example, virtual codes corresponding to + // Scan::Num0 and Scan::Numpad0 may be both translated to the same string "0". We want to map those to + // different Keyboard::Key nonetheless. This is why we do not translate some layout-independent keys + // to string and use fallback mapping instead. + bool translateToString = true; + switch (scan) { - // Ok, everything went fine. Now we have a unique - // corresponding sf::Keyboard::Key to one IOHIDElementRef - m_keys[code].push_back(key); - - // And don't forget to keep the reference alive for our usage - CFRetain(m_keys[code].back()); + case Keyboard::Scan::NumpadDivide: + case Keyboard::Scan::NumpadMultiply: + case Keyboard::Scan::NumpadMinus: + case Keyboard::Scan::NumpadPlus: + case Keyboard::Scan::Numpad1: + case Keyboard::Scan::Numpad2: + case Keyboard::Scan::Numpad3: + case Keyboard::Scan::Numpad4: + case Keyboard::Scan::Numpad5: + case Keyboard::Scan::Numpad6: + case Keyboard::Scan::Numpad7: + case Keyboard::Scan::Numpad8: + case Keyboard::Scan::Numpad9: + case Keyboard::Scan::Numpad0: + translateToString = false; + break; + default: + translateToString = true; + break; } - //////////////////////////////////////////////////////////// - // These are known to be unbound: - // Supposed Virtual | HID | Supposed Key - // =============================================== - // 0x1b | 0x2d | Hyphen - // 0x39 | 0x39 | CapsLock - // 0x47 | 0x53 | NumLock - // 0x6e | 0x65 | Application - // 0x4c | 0x77 | Select + Keyboard::Key code = Keyboard::Unknown; + if (translateToString) + { + // Unicode string length is usually less or equal to 4 + UniCharCount const maxLength = 4; + UniChar string[maxLength]; + UniCharCount length = 0; + std::uint32_t deadKeyState = 0; // unused value + std::uint32_t const modifiers = 0x100; // no modifiers - //if (code == Keyboard::Unknown) { // The key is unknown. - // sf::err() << "This is an unknown key. Virtual key code is 0x" - // << std::hex - // << (UInt32)virtualCode - // << " and HID usage code is 0x" - // << usageCode - // << std::dec - // << "." - // << std::endl; - //} + // Use current layout for translation + OSStatus error = UCKeyTranslate(layout, + virtualCode, + kUCKeyActionDown, + modifiers, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, + &deadKeyState, + maxLength, + &length, + string); - } /* if (error == noErr) */ - else - { - sf::err() << "Cannot translate the virtual key code, error: " << error << std::endl; + if (error != noErr) + { + err() << "Cannot translate the virtual key code, error: " << error << "\n"; + continue; + } + + if (length > 0) + code = localizedKey(string[0]); + } + if (code == Keyboard::Unknown) + code = localizedKeyFallback(scan); + if (code == Keyboard::Unknown) + continue; + + // Register the bi-mapping + if (m_keyToScancodeMapping[code] == Keyboard::Scan::Unknown) + m_keyToScancodeMapping[code] = scan; + m_scancodeToKeyMapping[scan] = code; } + + CFRelease(tis); +} + + +//////////////////////////////////////////////////////////// +void HIDInputManager::keyboardChanged(CFNotificationCenterRef /* center */, + void* observer, + CFStringRef /* name */, + const void* /* object */, + CFDictionaryRef /* userInfo */) +{ + HIDInputManager* manager = static_cast(observer); + manager->buildMappings(); } //////////////////////////////////////////////////////////// void HIDInputManager::freeUp() { - m_isValid = false; - - if (m_layoutData != nil) - CFRelease(m_layoutData); - - m_layoutData = nil; - - // Do not release m_layout! It is owned by m_layoutData. if (m_manager != nil) CFRelease(m_manager); @@ -325,7 +936,7 @@ void HIDInputManager::freeUp() //////////////////////////////////////////////////////////// -CFSetRef HIDInputManager::copyDevices(UInt32 page, UInt32 usage) +CFSetRef HIDInputManager::copyDevices(std::uint32_t page, std::uint32_t usage) { // Filter and keep only the requested devices CFDictionaryRef mask = copyDevicesMask(page, usage); @@ -340,8 +951,7 @@ CFSetRef HIDInputManager::copyDevices(UInt32 page, UInt32 usage) return nullptr; // Is there at least one device? - CFIndex deviceCount = CFSetGetCount(devices); - if (deviceCount < 1) + if (CFSetGetCount(devices) < 1) { CFRelease(devices); return nullptr; @@ -350,15 +960,12 @@ CFSetRef HIDInputManager::copyDevices(UInt32 page, UInt32 usage) return devices; } + +//////////////////////////////////////////////////////////// bool HIDInputManager::isPressed(IOHIDElements& elements) { - if (!m_isValid) - return false; - - // state = true if at least one corresponding HID button is pressed - bool state = false; - - for (auto it = elements.begin(); it != elements.end(); /* noop */) + bool pressed = false; + for (auto it = elements.begin(); it != elements.end() && !pressed; /* noop */) { IOHIDValueRef value = nil; @@ -367,498 +974,450 @@ bool HIDInputManager::isPressed(IOHIDElements& elements) if (!value) { - // This means some kind of error / disconnection so we remove this - // element from our buttons + // This means some kind of error / disconnection so we remove this element from our database. CFRelease(*it); it = elements.erase(it); } - else if (IOHIDValueGetIntegerValue(value) == 1) - { - // This means the button is pressed - state = true; - break; // Stop here - } else { - // This means the button is released + pressed = (IOHIDValueGetIntegerValue(value) == 1); ++it; } } - return state; + return pressed; } //////////////////////////////////////////////////////////// -UInt8 HIDInputManager::usageToVirtualCode(UInt32 usage) +Keyboard::Scancode HIDInputManager::usageToScancode(std::uint32_t usage) { - // Some usage key doesn't have any corresponding virtual - // code or it was not found (return 0xff). // clang-format off switch (usage) { - case kHIDUsage_KeyboardErrorRollOver: return 0xff; - case kHIDUsage_KeyboardPOSTFail: return 0xff; - case kHIDUsage_KeyboardErrorUndefined: return 0xff; + case kHIDUsage_KeyboardErrorRollOver: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardPOSTFail: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardErrorUndefined: return Keyboard::Scan::Unknown; - case kHIDUsage_KeyboardA: return 0x00; - case kHIDUsage_KeyboardB: return 0x0b; - case kHIDUsage_KeyboardC: return 0x08; - case kHIDUsage_KeyboardD: return 0x02; - case kHIDUsage_KeyboardE: return 0x0e; - case kHIDUsage_KeyboardF: return 0x03; - case kHIDUsage_KeyboardG: return 0x05; - case kHIDUsage_KeyboardH: return 0x04; - case kHIDUsage_KeyboardI: return 0x22; - case kHIDUsage_KeyboardJ: return 0x26; - case kHIDUsage_KeyboardK: return 0x28; - case kHIDUsage_KeyboardL: return 0x25; - case kHIDUsage_KeyboardM: return 0x2e; - case kHIDUsage_KeyboardN: return 0x2d; - case kHIDUsage_KeyboardO: return 0x1f; - case kHIDUsage_KeyboardP: return 0x23; - case kHIDUsage_KeyboardQ: return 0x0c; - case kHIDUsage_KeyboardR: return 0x0f; - case kHIDUsage_KeyboardS: return 0x01; - case kHIDUsage_KeyboardT: return 0x11; - case kHIDUsage_KeyboardU: return 0x20; - case kHIDUsage_KeyboardV: return 0x09; - case kHIDUsage_KeyboardW: return 0x0d; - case kHIDUsage_KeyboardX: return 0x07; - case kHIDUsage_KeyboardY: return 0x10; - case kHIDUsage_KeyboardZ: return 0x06; + case kHIDUsage_KeyboardA: return Keyboard::Scan::A; + case kHIDUsage_KeyboardB: return Keyboard::Scan::B; + case kHIDUsage_KeyboardC: return Keyboard::Scan::C; + case kHIDUsage_KeyboardD: return Keyboard::Scan::D; + case kHIDUsage_KeyboardE: return Keyboard::Scan::E; + case kHIDUsage_KeyboardF: return Keyboard::Scan::F; + case kHIDUsage_KeyboardG: return Keyboard::Scan::G; + case kHIDUsage_KeyboardH: return Keyboard::Scan::H; + case kHIDUsage_KeyboardI: return Keyboard::Scan::I; + case kHIDUsage_KeyboardJ: return Keyboard::Scan::J; + case kHIDUsage_KeyboardK: return Keyboard::Scan::K; + case kHIDUsage_KeyboardL: return Keyboard::Scan::L; + case kHIDUsage_KeyboardM: return Keyboard::Scan::M; + case kHIDUsage_KeyboardN: return Keyboard::Scan::N; + case kHIDUsage_KeyboardO: return Keyboard::Scan::O; + case kHIDUsage_KeyboardP: return Keyboard::Scan::P; + case kHIDUsage_KeyboardQ: return Keyboard::Scan::Q; + case kHIDUsage_KeyboardR: return Keyboard::Scan::R; + case kHIDUsage_KeyboardS: return Keyboard::Scan::S; + case kHIDUsage_KeyboardT: return Keyboard::Scan::T; + case kHIDUsage_KeyboardU: return Keyboard::Scan::U; + case kHIDUsage_KeyboardV: return Keyboard::Scan::V; + case kHIDUsage_KeyboardW: return Keyboard::Scan::W; + case kHIDUsage_KeyboardX: return Keyboard::Scan::X; + case kHIDUsage_KeyboardY: return Keyboard::Scan::Y; + case kHIDUsage_KeyboardZ: return Keyboard::Scan::Z; - case kHIDUsage_Keyboard1: return 0x12; - case kHIDUsage_Keyboard2: return 0x13; - case kHIDUsage_Keyboard3: return 0x14; - case kHIDUsage_Keyboard4: return 0x15; - case kHIDUsage_Keyboard5: return 0x17; - case kHIDUsage_Keyboard6: return 0x16; - case kHIDUsage_Keyboard7: return 0x1a; - case kHIDUsage_Keyboard8: return 0x1c; - case kHIDUsage_Keyboard9: return 0x19; - case kHIDUsage_Keyboard0: return 0x1d; + case kHIDUsage_Keyboard1: return Keyboard::Scan::Num1; + case kHIDUsage_Keyboard2: return Keyboard::Scan::Num2; + case kHIDUsage_Keyboard3: return Keyboard::Scan::Num3; + case kHIDUsage_Keyboard4: return Keyboard::Scan::Num4; + case kHIDUsage_Keyboard5: return Keyboard::Scan::Num5; + case kHIDUsage_Keyboard6: return Keyboard::Scan::Num6; + case kHIDUsage_Keyboard7: return Keyboard::Scan::Num7; + case kHIDUsage_Keyboard8: return Keyboard::Scan::Num8; + case kHIDUsage_Keyboard9: return Keyboard::Scan::Num9; + case kHIDUsage_Keyboard0: return Keyboard::Scan::Num0; - case kHIDUsage_KeyboardReturnOrEnter: return 0x24; - case kHIDUsage_KeyboardEscape: return 0x35; - case kHIDUsage_KeyboardDeleteOrBackspace: return 0x33; - case kHIDUsage_KeyboardTab: return 0x30; - case kHIDUsage_KeyboardSpacebar: return 0x31; - case kHIDUsage_KeyboardHyphen: return 0x1b; - case kHIDUsage_KeyboardEqualSign: return 0x18; - case kHIDUsage_KeyboardOpenBracket: return 0x21; - case kHIDUsage_KeyboardCloseBracket: return 0x1e; - case kHIDUsage_KeyboardBackslash: return 0x2a; - case kHIDUsage_KeyboardNonUSPound: return 0xff; - case kHIDUsage_KeyboardSemicolon: return 0x29; - case kHIDUsage_KeyboardQuote: return 0x27; - case kHIDUsage_KeyboardGraveAccentAndTilde: return 0x32; - case kHIDUsage_KeyboardComma: return 0x2b; - case kHIDUsage_KeyboardPeriod: return 0x2F; - case kHIDUsage_KeyboardSlash: return 0x2c; - case kHIDUsage_KeyboardCapsLock: return 0x39; + case kHIDUsage_KeyboardReturnOrEnter: return Keyboard::Scan::Enter; + case kHIDUsage_KeyboardEscape: return Keyboard::Scan::Escape; + case kHIDUsage_KeyboardDeleteOrBackspace: return Keyboard::Scan::Backspace; + case kHIDUsage_KeyboardTab: return Keyboard::Scan::Tab; + case kHIDUsage_KeyboardSpacebar: return Keyboard::Scan::Space; + case kHIDUsage_KeyboardHyphen: return Keyboard::Scan::Hyphen; + case kHIDUsage_KeyboardEqualSign: return Keyboard::Scan::Equal; + case kHIDUsage_KeyboardOpenBracket: return Keyboard::Scan::LBracket; + case kHIDUsage_KeyboardCloseBracket: return Keyboard::Scan::RBracket; + case kHIDUsage_KeyboardBackslash: return Keyboard::Scan::Backslash; + case kHIDUsage_KeyboardNonUSPound: return Keyboard::Scan::Backslash; + case kHIDUsage_KeyboardSemicolon: return Keyboard::Scan::Semicolon; + case kHIDUsage_KeyboardQuote: return Keyboard::Scan::Apostrophe; + case kHIDUsage_KeyboardGraveAccentAndTilde: return isIsoKeyboard ? Keyboard::Scan::NonUsBackslash : Keyboard::Scan::Grave; + case kHIDUsage_KeyboardComma: return Keyboard::Scan::Comma; + case kHIDUsage_KeyboardPeriod: return Keyboard::Scan::Period; + case kHIDUsage_KeyboardSlash: return Keyboard::Scan::Slash; + case kHIDUsage_KeyboardCapsLock: return Keyboard::Scan::CapsLock; - case kHIDUsage_KeyboardF1: return 0x7a; - case kHIDUsage_KeyboardF2: return 0x78; - case kHIDUsage_KeyboardF3: return 0x63; - case kHIDUsage_KeyboardF4: return 0x76; - case kHIDUsage_KeyboardF5: return 0x60; - case kHIDUsage_KeyboardF6: return 0x61; - case kHIDUsage_KeyboardF7: return 0x62; - case kHIDUsage_KeyboardF8: return 0x64; - case kHIDUsage_KeyboardF9: return 0x65; - case kHIDUsage_KeyboardF10: return 0x6d; - case kHIDUsage_KeyboardF11: return 0x67; - case kHIDUsage_KeyboardF12: return 0x6f; + case kHIDUsage_KeyboardF1: return Keyboard::Scan::F1; + case kHIDUsage_KeyboardF2: return Keyboard::Scan::F2; + case kHIDUsage_KeyboardF3: return Keyboard::Scan::F3; + case kHIDUsage_KeyboardF4: return Keyboard::Scan::F4; + case kHIDUsage_KeyboardF5: return Keyboard::Scan::F5; + case kHIDUsage_KeyboardF6: return Keyboard::Scan::F6; + case kHIDUsage_KeyboardF7: return Keyboard::Scan::F7; + case kHIDUsage_KeyboardF8: return Keyboard::Scan::F8; + case kHIDUsage_KeyboardF9: return Keyboard::Scan::F9; + case kHIDUsage_KeyboardF10: return Keyboard::Scan::F10; + case kHIDUsage_KeyboardF11: return Keyboard::Scan::F11; + case kHIDUsage_KeyboardF12: return Keyboard::Scan::F12; - case kHIDUsage_KeyboardPrintScreen: return 0xff; - case kHIDUsage_KeyboardScrollLock: return 0xff; - case kHIDUsage_KeyboardPause: return 0xff; - case kHIDUsage_KeyboardInsert: return 0x72; - case kHIDUsage_KeyboardHome: return 0x73; - case kHIDUsage_KeyboardPageUp: return 0x74; - case kHIDUsage_KeyboardDeleteForward: return 0x75; - case kHIDUsage_KeyboardEnd: return 0x77; - case kHIDUsage_KeyboardPageDown: return 0x79; + case kHIDUsage_KeyboardPrintScreen: return Keyboard::Scan::PrintScreen; + case kHIDUsage_KeyboardScrollLock: return Keyboard::Scan::ScrollLock; + case kHIDUsage_KeyboardPause: return Keyboard::Scan::Pause; + case kHIDUsage_KeyboardInsert: return Keyboard::Scan::Insert; + case kHIDUsage_KeyboardHome: return Keyboard::Scan::Home; + case kHIDUsage_KeyboardPageUp: return Keyboard::Scan::PageUp; + case kHIDUsage_KeyboardDeleteForward: return Keyboard::Scan::Delete; + case kHIDUsage_KeyboardEnd: return Keyboard::Scan::End; + case kHIDUsage_KeyboardPageDown: return Keyboard::Scan::PageDown; - case kHIDUsage_KeyboardRightArrow: return 0x7c; - case kHIDUsage_KeyboardLeftArrow: return 0x7b; - case kHIDUsage_KeyboardDownArrow: return 0x7d; - case kHIDUsage_KeyboardUpArrow: return 0x7e; + case kHIDUsage_KeyboardRightArrow: return Keyboard::Scan::Right; + case kHIDUsage_KeyboardLeftArrow: return Keyboard::Scan::Left; + case kHIDUsage_KeyboardDownArrow: return Keyboard::Scan::Down; + case kHIDUsage_KeyboardUpArrow: return Keyboard::Scan::Up; - case kHIDUsage_KeypadNumLock: return 0x47; - case kHIDUsage_KeypadSlash: return 0x4b; - case kHIDUsage_KeypadAsterisk: return 0x43; - case kHIDUsage_KeypadHyphen: return 0x4e; - case kHIDUsage_KeypadPlus: return 0x45; - case kHIDUsage_KeypadEnter: return 0x4c; + case kHIDUsage_KeypadNumLock: return Keyboard::Scan::NumLock; + case kHIDUsage_KeypadSlash: return Keyboard::Scan::NumpadDivide; + case kHIDUsage_KeypadAsterisk: return Keyboard::Scan::NumpadMultiply; + case kHIDUsage_KeypadHyphen: return Keyboard::Scan::NumpadMinus; + case kHIDUsage_KeypadPlus: return Keyboard::Scan::NumpadPlus; + case kHIDUsage_KeypadEqualSign: return Keyboard::Scan::NumpadEqual; + case kHIDUsage_KeypadEnter: return Keyboard::Scan::NumpadEnter; + case kHIDUsage_KeypadPeriod: return Keyboard::Scan::NumpadDecimal; - case kHIDUsage_Keypad1: return 0x53; - case kHIDUsage_Keypad2: return 0x54; - case kHIDUsage_Keypad3: return 0x55; - case kHIDUsage_Keypad4: return 0x56; - case kHIDUsage_Keypad5: return 0x57; - case kHIDUsage_Keypad6: return 0x58; - case kHIDUsage_Keypad7: return 0x59; - case kHIDUsage_Keypad8: return 0x5b; - case kHIDUsage_Keypad9: return 0x5c; - case kHIDUsage_Keypad0: return 0x52; + case kHIDUsage_Keypad1: return Keyboard::Scan::Numpad1; + case kHIDUsage_Keypad2: return Keyboard::Scan::Numpad2; + case kHIDUsage_Keypad3: return Keyboard::Scan::Numpad3; + case kHIDUsage_Keypad4: return Keyboard::Scan::Numpad4; + case kHIDUsage_Keypad5: return Keyboard::Scan::Numpad5; + case kHIDUsage_Keypad6: return Keyboard::Scan::Numpad6; + case kHIDUsage_Keypad7: return Keyboard::Scan::Numpad7; + case kHIDUsage_Keypad8: return Keyboard::Scan::Numpad8; + case kHIDUsage_Keypad9: return Keyboard::Scan::Numpad9; + case kHIDUsage_Keypad0: return Keyboard::Scan::Numpad0; - case kHIDUsage_KeypadPeriod: return 0x41; - case kHIDUsage_KeyboardNonUSBackslash: return 0xff; - case kHIDUsage_KeyboardApplication: return 0x6e; - case kHIDUsage_KeyboardPower: return 0xff; - case kHIDUsage_KeypadEqualSign: return 0x51; + case kHIDUsage_KeyboardNonUSBackslash: return isIsoKeyboard ? Keyboard::Scan::Grave : Keyboard::Scan::NonUsBackslash; + case kHIDUsage_KeyboardApplication: return Keyboard::Scan::Application; + case kHIDUsage_KeyboardPower: return Keyboard::Scan::Unknown; - case kHIDUsage_KeyboardF13: return 0x69; - case kHIDUsage_KeyboardF14: return 0x6b; - case kHIDUsage_KeyboardF15: return 0x71; - case kHIDUsage_KeyboardF16: return 0xff; - case kHIDUsage_KeyboardF17: return 0xff; - case kHIDUsage_KeyboardF18: return 0xff; - case kHIDUsage_KeyboardF19: return 0xff; - case kHIDUsage_KeyboardF20: return 0xff; - case kHIDUsage_KeyboardF21: return 0xff; - case kHIDUsage_KeyboardF22: return 0xff; - case kHIDUsage_KeyboardF23: return 0xff; - case kHIDUsage_KeyboardF24: return 0xff; + case kHIDUsage_KeyboardF13: return Keyboard::Scan::F13; + case kHIDUsage_KeyboardF14: return Keyboard::Scan::F14; + case kHIDUsage_KeyboardF15: return Keyboard::Scan::F15; + case kHIDUsage_KeyboardF16: return Keyboard::Scan::F16; + case kHIDUsage_KeyboardF17: return Keyboard::Scan::F17; + case kHIDUsage_KeyboardF18: return Keyboard::Scan::F18; + case kHIDUsage_KeyboardF19: return Keyboard::Scan::F19; + case kHIDUsage_KeyboardF20: return Keyboard::Scan::F20; + case kHIDUsage_KeyboardF21: return Keyboard::Scan::F21; + case kHIDUsage_KeyboardF22: return Keyboard::Scan::F22; + case kHIDUsage_KeyboardF23: return Keyboard::Scan::F23; + case kHIDUsage_KeyboardF24: return Keyboard::Scan::F24; - case kHIDUsage_KeyboardExecute: return 0xff; - case kHIDUsage_KeyboardHelp: return 0xff; - case kHIDUsage_KeyboardMenu: return 0x7F; - case kHIDUsage_KeyboardSelect: return 0x4c; - case kHIDUsage_KeyboardStop: return 0xff; - case kHIDUsage_KeyboardAgain: return 0xff; - case kHIDUsage_KeyboardUndo: return 0xff; - case kHIDUsage_KeyboardCut: return 0xff; - case kHIDUsage_KeyboardCopy: return 0xff; - case kHIDUsage_KeyboardPaste: return 0xff; - case kHIDUsage_KeyboardFind: return 0xff; + case kHIDUsage_KeyboardExecute: return Keyboard::Scan::Execute; + case kHIDUsage_KeyboardHelp: return Keyboard::Scan::Help; + case kHIDUsage_KeyboardMenu: return Keyboard::Scan::Menu; + case kHIDUsage_KeyboardSelect: return Keyboard::Scan::Select; + case kHIDUsage_KeyboardStop: return Keyboard::Scan::Stop; + case kHIDUsage_KeyboardAgain: return Keyboard::Scan::Redo; + case kHIDUsage_KeyboardUndo: return Keyboard::Scan::Undo; + case kHIDUsage_KeyboardCut: return Keyboard::Scan::Cut; + case kHIDUsage_KeyboardCopy: return Keyboard::Scan::Copy; + case kHIDUsage_KeyboardPaste: return Keyboard::Scan::Paste; + case kHIDUsage_KeyboardFind: return Keyboard::Scan::Search; - case kHIDUsage_KeyboardMute: return 0xff; - case kHIDUsage_KeyboardVolumeUp: return 0xff; - case kHIDUsage_KeyboardVolumeDown: return 0xff; + case kHIDUsage_KeyboardMute: return Keyboard::Scan::VolumeMute; + case kHIDUsage_KeyboardVolumeUp: return Keyboard::Scan::VolumeUp; + case kHIDUsage_KeyboardVolumeDown: return Keyboard::Scan::VolumeDown; - case kHIDUsage_KeyboardLockingCapsLock: return 0xff; - case kHIDUsage_KeyboardLockingNumLock: return 0xff; - case kHIDUsage_KeyboardLockingScrollLock: return 0xff; + case kHIDUsage_KeyboardLockingCapsLock: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLockingNumLock: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLockingScrollLock: return Keyboard::Scan::Unknown; - case kHIDUsage_KeypadComma: return 0xff; - case kHIDUsage_KeypadEqualSignAS400: return 0xff; - case kHIDUsage_KeyboardInternational1: return 0xff; - case kHIDUsage_KeyboardInternational2: return 0xff; - case kHIDUsage_KeyboardInternational3: return 0xff; - case kHIDUsage_KeyboardInternational4: return 0xff; - case kHIDUsage_KeyboardInternational5: return 0xff; - case kHIDUsage_KeyboardInternational6: return 0xff; - case kHIDUsage_KeyboardInternational7: return 0xff; - case kHIDUsage_KeyboardInternational8: return 0xff; - case kHIDUsage_KeyboardInternational9: return 0xff; + case kHIDUsage_KeypadComma: return Keyboard::Scan::Unknown; + case kHIDUsage_KeypadEqualSignAS400: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational1: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational2: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational3: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational4: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational5: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational6: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational7: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational8: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardInternational9: return Keyboard::Scan::Unknown; - case kHIDUsage_KeyboardLANG1: return 0xff; - case kHIDUsage_KeyboardLANG2: return 0xff; - case kHIDUsage_KeyboardLANG3: return 0xff; - case kHIDUsage_KeyboardLANG4: return 0xff; - case kHIDUsage_KeyboardLANG5: return 0xff; - case kHIDUsage_KeyboardLANG6: return 0xff; - case kHIDUsage_KeyboardLANG7: return 0xff; - case kHIDUsage_KeyboardLANG8: return 0xff; - case kHIDUsage_KeyboardLANG9: return 0xff; + case kHIDUsage_KeyboardLANG1: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG2: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG3: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG4: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG5: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG6: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG7: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG8: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardLANG9: return Keyboard::Scan::Unknown; - case kHIDUsage_KeyboardAlternateErase: return 0xff; - case kHIDUsage_KeyboardSysReqOrAttention: return 0xff; - case kHIDUsage_KeyboardCancel: return 0xff; - case kHIDUsage_KeyboardClear: return 0xff; - case kHIDUsage_KeyboardPrior: return 0xff; - case kHIDUsage_KeyboardReturn: return 0xff; - case kHIDUsage_KeyboardSeparator: return 0xff; - case kHIDUsage_KeyboardOut: return 0xff; - case kHIDUsage_KeyboardOper: return 0xff; - case kHIDUsage_KeyboardClearOrAgain: return 0xff; - case kHIDUsage_KeyboardCrSelOrProps: return 0xff; - case kHIDUsage_KeyboardExSel: return 0xff; + case kHIDUsage_KeyboardAlternateErase: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardSysReqOrAttention: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardCancel: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardClear: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardPrior: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardReturn: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardSeparator: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardOut: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardOper: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardClearOrAgain: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardCrSelOrProps: return Keyboard::Scan::Unknown; + case kHIDUsage_KeyboardExSel: return Keyboard::Scan::Unknown; - /* 0xa5-0xdf Reserved */ + /* 0xa5-0xdf Reserved */ - case kHIDUsage_KeyboardLeftControl: return 0x3b; - case kHIDUsage_KeyboardLeftShift: return 0x38; - case kHIDUsage_KeyboardLeftAlt: return 0x3a; - case kHIDUsage_KeyboardLeftGUI: return 0x37; - case kHIDUsage_KeyboardRightControl: return 0x3e; - case kHIDUsage_KeyboardRightShift: return 0x3c; - case kHIDUsage_KeyboardRightAlt: return 0x3d; - case kHIDUsage_KeyboardRightGUI: return 0x36; + case kHIDUsage_KeyboardLeftControl: return Keyboard::Scan::LControl; + case kHIDUsage_KeyboardLeftShift: return Keyboard::Scan::LShift; + case kHIDUsage_KeyboardLeftAlt: return Keyboard::Scan::LAlt; + case kHIDUsage_KeyboardLeftGUI: return Keyboard::Scan::LSystem; + case kHIDUsage_KeyboardRightControl: return Keyboard::Scan::RControl; + case kHIDUsage_KeyboardRightShift: return Keyboard::Scan::RShift; + case kHIDUsage_KeyboardRightAlt: return Keyboard::Scan::RAlt; + case kHIDUsage_KeyboardRightGUI: return Keyboard::Scan::RSystem; - /* 0xe8-0xffff Reserved */ + /* 0xe8-0xffff Reserved */ - case kHIDUsage_Keyboard_Reserved: return 0xff; - default: return 0xff; + case kHIDUsage_Keyboard_Reserved: return Keyboard::Scan::Unknown; + default: return Keyboard::Scan::Unknown; } // clang-format on } //////////////////////////////////////////////////////// -Keyboard::Key HIDInputManager::localizedKeys(UniChar ch) +std::uint8_t HIDInputManager::scanToVirtualCode(Keyboard::Scancode code) { // clang-format off - switch (ch) + switch (code) { - case 'a': - case 'A': return sf::Keyboard::A; + case Keyboard::Scan::A: return 0x00; + case Keyboard::Scan::B: return 0x0b; + case Keyboard::Scan::C: return 0x08; + case Keyboard::Scan::D: return 0x02; + case Keyboard::Scan::E: return 0x0e; + case Keyboard::Scan::F: return 0x03; + case Keyboard::Scan::G: return 0x05; + case Keyboard::Scan::H: return 0x04; + case Keyboard::Scan::I: return 0x22; + case Keyboard::Scan::J: return 0x26; + case Keyboard::Scan::K: return 0x28; + case Keyboard::Scan::L: return 0x25; + case Keyboard::Scan::M: return 0x2e; + case Keyboard::Scan::N: return 0x2d; + case Keyboard::Scan::O: return 0x1f; + case Keyboard::Scan::P: return 0x23; + case Keyboard::Scan::Q: return 0x0c; + case Keyboard::Scan::R: return 0x0f; + case Keyboard::Scan::S: return 0x01; + case Keyboard::Scan::T: return 0x11; + case Keyboard::Scan::U: return 0x20; + case Keyboard::Scan::V: return 0x09; + case Keyboard::Scan::W: return 0x0d; + case Keyboard::Scan::X: return 0x07; + case Keyboard::Scan::Y: return 0x10; + case Keyboard::Scan::Z: return 0x06; - case 'b': - case 'B': return sf::Keyboard::B; + case Keyboard::Scan::Num1: return 0x12; + case Keyboard::Scan::Num2: return 0x13; + case Keyboard::Scan::Num3: return 0x14; + case Keyboard::Scan::Num4: return 0x15; + case Keyboard::Scan::Num5: return 0x17; + case Keyboard::Scan::Num6: return 0x16; + case Keyboard::Scan::Num7: return 0x1a; + case Keyboard::Scan::Num8: return 0x1c; + case Keyboard::Scan::Num9: return 0x19; + case Keyboard::Scan::Num0: return 0x1d; - case 'c': - case 'C': return sf::Keyboard::C; + case Keyboard::Scan::Enter: return 0x24; + case Keyboard::Scan::Escape: return 0x35; + case Keyboard::Scan::Backspace: return 0x33; + case Keyboard::Scan::Tab: return 0x30; + case Keyboard::Scan::Space: return 0x31; - case 'd': - case 'D': return sf::Keyboard::D; + case Keyboard::Scan::Equal: return 0x18; + case Keyboard::Scan::LBracket: return 0x21; + case Keyboard::Scan::RBracket: return 0x1e; + case Keyboard::Scan::Backslash: return 0x2a; + case Keyboard::Scan::Hyphen: return 0x1b; + case Keyboard::Scan::Semicolon: return 0x29; + case Keyboard::Scan::Apostrophe: return 0x27; + case Keyboard::Scan::Grave: return isIsoKeyboard ? 0x0a : 0x32; + case Keyboard::Scan::Comma: return 0x2b; + case Keyboard::Scan::Period: return 0x2f; + case Keyboard::Scan::Slash: return 0x2c; - case 'e': - case 'E': return sf::Keyboard::E; + case Keyboard::Scan::F1: return 0x7a; + case Keyboard::Scan::F2: return 0x78; + case Keyboard::Scan::F3: return 0x63; + case Keyboard::Scan::F4: return 0x76; + case Keyboard::Scan::F5: return 0x60; + case Keyboard::Scan::F6: return 0x61; + case Keyboard::Scan::F7: return 0x62; + case Keyboard::Scan::F8: return 0x64; + case Keyboard::Scan::F9: return 0x65; + case Keyboard::Scan::F10: return 0x6d; + case Keyboard::Scan::F11: return 0x67; + case Keyboard::Scan::F12: return 0x6f; + case Keyboard::Scan::F13: return 0x69; + case Keyboard::Scan::F14: return 0x6b; + case Keyboard::Scan::F15: return 0x71; - case 'f': - case 'F': return sf::Keyboard::F; + case Keyboard::Scan::CapsLock: return 0x39; - case 'g': - case 'G': return sf::Keyboard::G; + /* TODO Those are missing: + * case Keyboard::Scan::PrintScreen: return 0; + * case Keyboard::Scan::ScrollLock: return 0; + * case Keyboard::Scan::Pause: return 0; + */ + case Keyboard::Scan::Insert: return 0x72; + case Keyboard::Scan::Home: return 0x73; + case Keyboard::Scan::PageUp: return 0x74; + case Keyboard::Scan::Delete: return 0x75; + case Keyboard::Scan::End: return 0x77; + case Keyboard::Scan::PageDown: return 0x79; - case 'h': - case 'H': return sf::Keyboard::H; + case Keyboard::Scan::Right: return 0x7c; + case Keyboard::Scan::Left: return 0x7b; + case Keyboard::Scan::Down: return 0x7d; + case Keyboard::Scan::Up: return 0x7e; - case 'i': - case 'I': return sf::Keyboard::I; + case Keyboard::Scan::NumLock: return 0x47; + case Keyboard::Scan::NumpadDivide: return 0x4b; + case Keyboard::Scan::NumpadMultiply: return 0x43; + case Keyboard::Scan::NumpadMinus: return 0x4e; + case Keyboard::Scan::NumpadPlus: return 0x45; + case Keyboard::Scan::NumpadEqual: return 0x51; + case Keyboard::Scan::NumpadEnter: return 0x4c; + case Keyboard::Scan::NumpadDecimal: return 0x41; - case 'j': - case 'J': return sf::Keyboard::J; + case Keyboard::Scan::Numpad1: return 0x53; + case Keyboard::Scan::Numpad2: return 0x54; + case Keyboard::Scan::Numpad3: return 0x55; + case Keyboard::Scan::Numpad4: return 0x56; + case Keyboard::Scan::Numpad5: return 0x57; + case Keyboard::Scan::Numpad6: return 0x58; + case Keyboard::Scan::Numpad7: return 0x59; + case Keyboard::Scan::Numpad8: return 0x5b; + case Keyboard::Scan::Numpad9: return 0x5c; + case Keyboard::Scan::Numpad0: return 0x52; - case 'k': - case 'K': return sf::Keyboard::K; + case Keyboard::Scan::NonUsBackslash: return 0x32; - case 'l': - case 'L': return sf::Keyboard::L; + /* TODO Those are missing: + * case Keyboard::Scan::Application: return 0; + * case Keyboard::Scan::Execute: return 0; + * case Keyboard::Scan::Help: return 0; + * case Keyboard::Scan::Menu: return 0; + * case Keyboard::Scan::Select: return 0; + * case Keyboard::Scan::Stop: return 0; + * case Keyboard::Scan::Redo: return 0; + * case Keyboard::Scan::Undo: return 0; + * case Keyboard::Scan::Cut: return 0; + * case Keyboard::Scan::Copy: return 0; + * case Keyboard::Scan::Paste: return 0; + * case Keyboard::Scan::Search: return 0; + */ - case 'm': - case 'M': return sf::Keyboard::M; + case Keyboard::Scan::VolumeMute: return 0x4a; + case Keyboard::Scan::VolumeUp: return 0x48; + case Keyboard::Scan::VolumeDown: return 0x49; - case 'n': - case 'N': return sf::Keyboard::N; + case Keyboard::Scan::LControl: return 0x3b; + case Keyboard::Scan::LShift: return 0x38; + case Keyboard::Scan::LAlt: return 0x3a; + case Keyboard::Scan::LSystem: return 0x37; + case Keyboard::Scan::RControl: return 0x3b; + case Keyboard::Scan::RShift: return 0x38; + case Keyboard::Scan::RAlt: return 0x3a; + case Keyboard::Scan::RSystem: return 0x37; - case 'o': - case 'O': return sf::Keyboard::O; - - case 'p': - case 'P': return sf::Keyboard::P; - - case 'q': - case 'Q': return sf::Keyboard::Q; - - case 'r': - case 'R': return sf::Keyboard::R; - - case 's': - case 'S': return sf::Keyboard::S; - - case 't': - case 'T': return sf::Keyboard::T; - - case 'u': - case 'U': return sf::Keyboard::U; - - case 'v': - case 'V': return sf::Keyboard::V; - - case 'w': - case 'W': return sf::Keyboard::W; - - case 'x': - case 'X': return sf::Keyboard::X; - - case 'y': - case 'Y': return sf::Keyboard::Y; - - case 'z': - case 'Z': return sf::Keyboard::Z; - - // The key is not 'localized'. - default: return sf::Keyboard::Unknown; + default: return unknownVirtualCode; } // clang-format on } //////////////////////////////////////////////////////// -Keyboard::Key HIDInputManager::nonLocalizedKeys(UniChar virtualKeycode) +Keyboard::Key HIDInputManager::localizedKeyFallback(Keyboard::Scancode code) { - // (Some) 0x code based on https://forums.macrumors.com/showthread.php?t=780577 - // Some sf::Keyboard::Key are present twice. // clang-format off - switch (virtualKeycode) + switch (code) { - // These cases should not be used but anyway... - case 0x00: return sf::Keyboard::A; - case 0x0b: return sf::Keyboard::B; - case 0x08: return sf::Keyboard::C; - case 0x02: return sf::Keyboard::D; - case 0x0e: return sf::Keyboard::E; - case 0x03: return sf::Keyboard::F; - case 0x05: return sf::Keyboard::G; - case 0x04: return sf::Keyboard::H; - case 0x22: return sf::Keyboard::I; - case 0x26: return sf::Keyboard::J; - case 0x28: return sf::Keyboard::K; - case 0x25: return sf::Keyboard::L; - case 0x2e: return sf::Keyboard::M; - case 0x2d: return sf::Keyboard::N; - case 0x1f: return sf::Keyboard::O; - case 0x23: return sf::Keyboard::P; - case 0x0c: return sf::Keyboard::Q; - case 0x0f: return sf::Keyboard::R; - case 0x01: return sf::Keyboard::S; - case 0x11: return sf::Keyboard::T; - case 0x20: return sf::Keyboard::U; - case 0x09: return sf::Keyboard::V; - case 0x0d: return sf::Keyboard::W; - case 0x07: return sf::Keyboard::X; - case 0x10: return sf::Keyboard::Y; - case 0x06: return sf::Keyboard::Z; + case Keyboard::Scan::Enter: return Keyboard::Enter; + case Keyboard::Scan::Escape: return Keyboard::Escape; + case Keyboard::Scan::Backspace: return Keyboard::Backspace; + case Keyboard::Scan::Tab: return Keyboard::Tab; + case Keyboard::Scan::Space: return Keyboard::Space; - // These cases should not be used but anyway... - case 0x1d: return sf::Keyboard::Num0; - case 0x12: return sf::Keyboard::Num1; - case 0x13: return sf::Keyboard::Num2; - case 0x14: return sf::Keyboard::Num3; - case 0x15: return sf::Keyboard::Num4; - case 0x17: return sf::Keyboard::Num5; - case 0x16: return sf::Keyboard::Num6; - case 0x1a: return sf::Keyboard::Num7; - case 0x1c: return sf::Keyboard::Num8; - case 0x19: return sf::Keyboard::Num9; + case Keyboard::Scan::F1: return Keyboard::F1; + case Keyboard::Scan::F2: return Keyboard::F2; + case Keyboard::Scan::F3: return Keyboard::F3; + case Keyboard::Scan::F4: return Keyboard::F4; + case Keyboard::Scan::F5: return Keyboard::F5; + case Keyboard::Scan::F6: return Keyboard::F6; + case Keyboard::Scan::F7: return Keyboard::F7; + case Keyboard::Scan::F8: return Keyboard::F8; + case Keyboard::Scan::F9: return Keyboard::F9; + case Keyboard::Scan::F10: return Keyboard::F10; + case Keyboard::Scan::F11: return Keyboard::F11; + case Keyboard::Scan::F12: return Keyboard::F12; + case Keyboard::Scan::F13: return Keyboard::F13; + case Keyboard::Scan::F14: return Keyboard::F14; + case Keyboard::Scan::F15: return Keyboard::F15; - case 0x35: return sf::Keyboard::Escape; + case Keyboard::Scan::Pause: return Keyboard::Pause; + case Keyboard::Scan::Insert: return Keyboard::Insert; + case Keyboard::Scan::Home: return Keyboard::Home; + case Keyboard::Scan::PageUp: return Keyboard::PageUp; + case Keyboard::Scan::Delete: return Keyboard::Delete; + case Keyboard::Scan::End: return Keyboard::End; + case Keyboard::Scan::PageDown: return Keyboard::PageDown; - // Modifier keys : never happen with keyDown/keyUp methods (?) - case 0x3b: return sf::Keyboard::LControl; - case 0x38: return sf::Keyboard::LShift; - case 0x3a: return sf::Keyboard::LAlt; - case 0x37: return sf::Keyboard::LSystem; - case 0x3e: return sf::Keyboard::RControl; - case 0x3c: return sf::Keyboard::RShift; - case 0x3d: return sf::Keyboard::RAlt; - case 0x36: return sf::Keyboard::RSystem; + case Keyboard::Scan::Right: return Keyboard::Right; + case Keyboard::Scan::Left: return Keyboard::Left; + case Keyboard::Scan::Down: return Keyboard::Down; + case Keyboard::Scan::Up: return Keyboard::Up; - case 0x7f: return sf::Keyboard::Menu; - case NSMenuFunctionKey: return sf::Keyboard::Menu; + case Keyboard::Scan::NumpadDivide: return Keyboard::Divide; + case Keyboard::Scan::NumpadMultiply: return Keyboard::Multiply; + case Keyboard::Scan::NumpadMinus: return Keyboard::Subtract; + case Keyboard::Scan::NumpadPlus: return Keyboard::Add; - case 0x21: return sf::Keyboard::LBracket; - case 0x1e: return sf::Keyboard::RBracket; - case 0x29: return sf::Keyboard::Semicolon; - case 0x2b: return sf::Keyboard::Comma; - case 0x41: /* keypad */ return sf::Keyboard::Period; - case 0x2f: /* keyboard */ return sf::Keyboard::Period; - case 0x27: return sf::Keyboard::Quote; - case 0x2c: return sf::Keyboard::Slash; - case 0x2a: return sf::Keyboard::Backslash; + case Keyboard::Scan::NumpadEnter: return Keyboard::Enter; - // sf::Keyboard::Tilde might be in conflict with some other key. - // 0x0a is for "Non-US Backslash" according to HID Calibrator, - // a sample provided by Apple. - case 0x0a: return sf::Keyboard::Tilde; + case Keyboard::Scan::Numpad1: return Keyboard::Numpad1; + case Keyboard::Scan::Numpad2: return Keyboard::Numpad2; + case Keyboard::Scan::Numpad3: return Keyboard::Numpad3; + case Keyboard::Scan::Numpad4: return Keyboard::Numpad4; + case Keyboard::Scan::Numpad5: return Keyboard::Numpad5; + case Keyboard::Scan::Numpad6: return Keyboard::Numpad6; + case Keyboard::Scan::Numpad7: return Keyboard::Numpad7; + case Keyboard::Scan::Numpad8: return Keyboard::Numpad8; + case Keyboard::Scan::Numpad9: return Keyboard::Numpad9; + case Keyboard::Scan::Numpad0: return Keyboard::Numpad0; - case 0x51: /* keypad */ return sf::Keyboard::Equal; - case 0x18: /* keyboard */ return sf::Keyboard::Equal; - case 0x32: return sf::Keyboard::Hyphen; - case 0x31: return sf::Keyboard::Space; - case 0x4c: /* keypad */ return sf::Keyboard::Enter; - case 0x24: /* keyboard */ return sf::Keyboard::Enter; - case 0x33: return sf::Keyboard::Backspace; - case 0x30: return sf::Keyboard::Tab; + case Keyboard::Scan::LControl: return Keyboard::LControl; + case Keyboard::Scan::LShift: return Keyboard::LShift; + case Keyboard::Scan::LAlt: return Keyboard::LAlt; + case Keyboard::Scan::LSystem: return Keyboard::LSystem; + case Keyboard::Scan::RControl: return Keyboard::RControl; + case Keyboard::Scan::RShift: return Keyboard::RShift; + case Keyboard::Scan::RAlt: return Keyboard::RAlt; + case Keyboard::Scan::RSystem: return Keyboard::RSystem; - // Duplicates (see next section). - case 0x74: return sf::Keyboard::PageUp; - case 0x79: return sf::Keyboard::PageDown; - case 0x77: return sf::Keyboard::End; - case 0x73: return sf::Keyboard::Home; - - case NSPageUpFunctionKey: return sf::Keyboard::PageUp; - case NSPageDownFunctionKey: return sf::Keyboard::PageDown; - case NSEndFunctionKey: return sf::Keyboard::End; - case NSHomeFunctionKey: return sf::Keyboard::Home; - - case 0x72: return sf::Keyboard::Insert; - case NSInsertFunctionKey: return sf::Keyboard::Insert; - case 0x75: return sf::Keyboard::Delete; - case NSDeleteFunctionKey: return sf::Keyboard::Delete; - - case 0x45: return sf::Keyboard::Add; - case 0x4e: return sf::Keyboard::Subtract; - case 0x43: return sf::Keyboard::Multiply; - case 0x4b: return sf::Keyboard::Divide; - - // Duplicates (see next section). - case 0x7b: return sf::Keyboard::Left; - case 0x7c: return sf::Keyboard::Right; - case 0x7e: return sf::Keyboard::Up; - case 0x7d: return sf::Keyboard::Down; - - case NSLeftArrowFunctionKey: return sf::Keyboard::Left; - case NSRightArrowFunctionKey: return sf::Keyboard::Right; - case NSUpArrowFunctionKey: return sf::Keyboard::Up; - case NSDownArrowFunctionKey: return sf::Keyboard::Down; - - case 0x52: return sf::Keyboard::Numpad0; - case 0x53: return sf::Keyboard::Numpad1; - case 0x54: return sf::Keyboard::Numpad2; - case 0x55: return sf::Keyboard::Numpad3; - case 0x56: return sf::Keyboard::Numpad4; - case 0x57: return sf::Keyboard::Numpad5; - case 0x58: return sf::Keyboard::Numpad6; - case 0x59: return sf::Keyboard::Numpad7; - case 0x5b: return sf::Keyboard::Numpad8; - case 0x5c: return sf::Keyboard::Numpad9; - - // Duplicates (see next section). - case 0x7a: return sf::Keyboard::F1; - case 0x78: return sf::Keyboard::F2; - case 0x63: return sf::Keyboard::F3; - case 0x76: return sf::Keyboard::F4; - case 0x60: return sf::Keyboard::F5; - case 0x61: return sf::Keyboard::F6; - case 0x62: return sf::Keyboard::F7; - case 0x64: return sf::Keyboard::F8; - case 0x65: return sf::Keyboard::F9; - case 0x6d: return sf::Keyboard::F10; - case 0x67: return sf::Keyboard::F11; - case 0x6f: return sf::Keyboard::F12; - case 0x69: return sf::Keyboard::F13; - case 0x6b: return sf::Keyboard::F14; - case 0x71: return sf::Keyboard::F15; - - case NSF1FunctionKey: return sf::Keyboard::F1; - case NSF2FunctionKey: return sf::Keyboard::F2; - case NSF3FunctionKey: return sf::Keyboard::F3; - case NSF4FunctionKey: return sf::Keyboard::F4; - case NSF5FunctionKey: return sf::Keyboard::F5; - case NSF6FunctionKey: return sf::Keyboard::F6; - case NSF7FunctionKey: return sf::Keyboard::F7; - case NSF8FunctionKey: return sf::Keyboard::F8; - case NSF9FunctionKey: return sf::Keyboard::F9; - case NSF10FunctionKey: return sf::Keyboard::F10; - case NSF11FunctionKey: return sf::Keyboard::F11; - case NSF12FunctionKey: return sf::Keyboard::F12; - case NSF13FunctionKey: return sf::Keyboard::F13; - case NSF14FunctionKey: return sf::Keyboard::F14; - case NSF15FunctionKey: return sf::Keyboard::F15; - - case NSPauseFunctionKey: return sf::Keyboard::Pause; - - // keycode 0x1b is not bound to any key. - // This key is ' on CH-FR, ) on FR and - on US layouts. - - // An unknown key. - default: return sf::Keyboard::Unknown; + default: return Keyboard::Unknown; } // clang-format on } diff --git a/src/SFML/Window/OSX/InputImpl.hpp b/src/SFML/Window/OSX/InputImpl.hpp index 4b4a4cf4..ab058378 100644 --- a/src/SFML/Window/OSX/InputImpl.hpp +++ b/src/SFML/Window/OSX/InputImpl.hpp @@ -42,19 +42,37 @@ class InputImpl { public: //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise + /// \copydoc sf::Keyboard::isKeyPressed(Key) /// //////////////////////////////////////////////////////////// static bool isKeyPressed(Keyboard::Key key); //////////////////////////////////////////////////////////// - /// \brief Show or hide the virtual keyboard + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) /// - /// \param visible True to show, false to hide + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::setVirtualKeyboardVisible /// //////////////////////////////////////////////////////////// static void setVirtualKeyboardVisible(bool visible); diff --git a/src/SFML/Window/OSX/InputImpl.mm b/src/SFML/Window/OSX/InputImpl.mm index 6ebcb9bf..82e3a6e8 100644 --- a/src/SFML/Window/OSX/InputImpl.mm +++ b/src/SFML/Window/OSX/InputImpl.mm @@ -41,9 +41,6 @@ /// In order to keep track of the keyboard's state and mouse buttons' state /// we use the HID manager. Mouse position is handled differently. /// -/// NB: we probably could use -/// NSEvent +addGlobalMonitorForEventsMatchingMask:handler: for mouse only. -/// //////////////////////////////////////////////////////////// namespace sf::priv @@ -118,6 +115,7 @@ SFOpenGLView* getSFOpenGLViewFromSFMLWindow(const WindowBase& window) return view; } + //////////////////////////////////////////////////////////// bool InputImpl::isKeyPressed(Keyboard::Key key) { @@ -126,6 +124,34 @@ bool InputImpl::isKeyPressed(Keyboard::Key key) } +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Scancode code) +{ + return HIDInputManager::getInstance().isKeyPressed(code); +} + + +//////////////////////////////////////////////////////////// +Keyboard::Key InputImpl::localize(Keyboard::Scancode code) +{ + return HIDInputManager::getInstance().localize(code); +} + + +//////////////////////////////////////////////////////////// +Keyboard::Scancode InputImpl::delocalize(Keyboard::Key key) +{ + return HIDInputManager::getInstance().delocalize(key); +} + + +//////////////////////////////////////////////////////////// +String InputImpl::getDescription(Keyboard::Scancode code) +{ + return HIDInputManager::getInstance().getDescription(code); +} + + //////////////////////////////////////////////////////////// void InputImpl::setVirtualKeyboardVisible(bool /*visible*/) { diff --git a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h index da2d3b94..6dea869c 100644 --- a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h +++ b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h @@ -57,7 +57,7 @@ void initialiseKeyboardHelper(); /// \brief Set up a SFML key event based on the given modifiers flags and key code /// //////////////////////////////////////////////////////////// -sf::Event::KeyEvent keyEventWithModifiers(NSUInteger modifiers, sf::Keyboard::Key key); +sf::Event::KeyEvent keyEventWithModifiers(NSUInteger modifiers, sf::Keyboard::Key key, sf::Keyboard::Scancode code); //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.mm b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.mm index 09c9b47c..73682e68 100644 --- a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.mm +++ b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.mm @@ -62,6 +62,7 @@ struct ModifiersState BOOL leftAlternateWasDown; BOOL leftControlWasDown; BOOL rightControlWasDown; + BOOL capsLockWasOn; }; @@ -95,6 +96,7 @@ void processOneModifier(NSUInteger modifiers, NSUInteger mask, BOOL& wasDown, sf::Keyboard::Key key, + sf::Keyboard::Scancode code, sf::priv::WindowImplCocoa& requester); @@ -112,6 +114,8 @@ void processLeftRightModifiers( BOOL& rightWasDown, sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey, + sf::Keyboard::Scancode leftCode, + sf::Keyboard::Scancode rightCode, sf::priv::WindowImplCocoa& requester); @@ -137,20 +141,22 @@ void initialiseKeyboardHelper() state.rightAlternateWasDown = isKeyMaskActive(modifiers, NSRightAlternateKeyMask); state.leftControlWasDown = isKeyMaskActive(modifiers, NSLeftControlKeyMask); state.rightControlWasDown = isKeyMaskActive(modifiers, NSRightControlKeyMask); + state.capsLockWasOn = isKeyMaskActive(modifiers, NSEventModifierFlagCapsLock); isStateInitialized = YES; } //////////////////////////////////////////////////////// -sf::Event::KeyEvent keyEventWithModifiers(NSUInteger modifiers, sf::Keyboard::Key key) +sf::Event::KeyEvent keyEventWithModifiers(NSUInteger modifiers, sf::Keyboard::Key key, sf::Keyboard::Scancode code) { sf::Event::KeyEvent event; - event.code = key; - event.alt = modifiers & NSAlternateKeyMask; - event.control = modifiers & NSControlKeyMask; - event.shift = modifiers & NSShiftKeyMask; - event.system = modifiers & NSCommandKeyMask; + event.code = key; + event.scancode = code; + event.alt = modifiers & NSAlternateKeyMask; + event.control = modifiers & NSControlKeyMask; + event.shift = modifiers & NSShiftKeyMask; + event.system = modifiers & NSCommandKeyMask; return event; } @@ -167,6 +173,8 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req state.rightShiftWasDown, sf::Keyboard::LShift, sf::Keyboard::RShift, + sf::Keyboard::Scan::LShift, + sf::Keyboard::Scan::RShift, requester); // Handle command @@ -177,6 +185,8 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req state.rightCommandWasDown, sf::Keyboard::LSystem, sf::Keyboard::RSystem, + sf::Keyboard::Scan::LSystem, + sf::Keyboard::Scan::RSystem, requester); // Handle option (alt) @@ -187,6 +197,8 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req state.rightAlternateWasDown, sf::Keyboard::LAlt, sf::Keyboard::RAlt, + sf::Keyboard::Scan::LAlt, + sf::Keyboard::Scan::RAlt, requester); // Handle control @@ -197,7 +209,17 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req state.rightControlWasDown, sf::Keyboard::LControl, sf::Keyboard::RControl, + sf::Keyboard::Scan::LControl, + sf::Keyboard::Scan::RControl, requester); + + // Handle caps lock + processOneModifier(modifiers, + NSEventModifierFlagCapsLock, + state.capsLockWasOn, + sf::Keyboard::Unknown, + sf::Keyboard::Scan::CapsLock, + requester); } @@ -212,10 +234,15 @@ BOOL isKeyMaskActive(NSUInteger modifiers, NSUInteger mask) //////////////////////////////////////////////////////// -void processOneModifier(NSUInteger modifiers, NSUInteger mask, BOOL& wasDown, sf::Keyboard::Key key, sf::priv::WindowImplCocoa& requester) +void processOneModifier(NSUInteger modifiers, + NSUInteger mask, + BOOL& wasDown, + sf::Keyboard::Key key, + sf::Keyboard::Scancode code, + sf::priv::WindowImplCocoa& requester) { // Setup a potential event key. - sf::Event::KeyEvent event = keyEventWithModifiers(modifiers, key); + sf::Event::KeyEvent event = keyEventWithModifiers(modifiers, key, code); // State BOOL isDown = isKeyMaskActive(modifiers, mask); @@ -244,8 +271,10 @@ void processLeftRightModifiers( BOOL& rightWasDown, sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey, + sf::Keyboard::Scancode leftCode, + sf::Keyboard::Scancode rightCode, sf::priv::WindowImplCocoa& requester) { - processOneModifier(modifiers, leftMask, leftWasDown, leftKey, requester); - processOneModifier(modifiers, rightMask, rightWasDown, rightKey, requester); + processOneModifier(modifiers, leftMask, leftWasDown, leftKey, leftCode, requester); + processOneModifier(modifiers, rightMask, rightWasDown, rightKey, rightCode, requester); } diff --git a/src/SFML/Window/OSX/SFOpenGLView+keyboard.mm b/src/SFML/Window/OSX/SFOpenGLView+keyboard.mm index 2d763fc9..e2428fa7 100644 --- a/src/SFML/Window/OSX/SFOpenGLView+keyboard.mm +++ b/src/SFML/Window/OSX/SFOpenGLView+keyboard.mm @@ -86,7 +86,7 @@ { sf::Event::KeyEvent key = [SFOpenGLView convertNSKeyEventToSFMLEvent:theEvent]; - if (key.code != sf::Keyboard::Unknown) // The key is recognized. + if ((key.code != sf::Keyboard::Unknown) || (key.scancode != sf::Keyboard::Scan::Unknown)) m_requester->keyDown(key); } @@ -158,7 +158,7 @@ sf::Event::KeyEvent key = [SFOpenGLView convertNSKeyEventToSFMLEvent:theEvent]; - if (key.code != sf::Keyboard::Unknown) // The key is recognized. + if ((key.code != sf::Keyboard::Unknown) || (key.scancode != sf::Keyboard::Scan::Unknown)) m_requester->keyUp(key); } @@ -180,21 +180,13 @@ //////////////////////////////////////////////////////// + (sf::Event::KeyEvent)convertNSKeyEventToSFMLEvent:(NSEvent*)event { - // Key code - sf::Keyboard::Key key = sf::Keyboard::Unknown; + // The scancode always depends on the hardware keyboard, not some OS setting. + sf::Keyboard::Scancode code = sf::priv::HIDInputManager::nonLocalizedKey([event keyCode]); - // First we look if the key down is from a list of characters - // that depend on keyboard localization. - NSString* string = [event charactersIgnoringModifiers]; - if ([string length] > 0) - key = sf::priv::HIDInputManager::localizedKeys([string characterAtIndex:0]); + // Get the corresponding key under the current keyboard layout. + sf::Keyboard::Key key = sf::Keyboard::localize(code); - // If the key is not a localized one, we try to find a corresponding code - // through virtual key code. - if (key == sf::Keyboard::Unknown) - key = sf::priv::HIDInputManager::nonLocalizedKeys([event keyCode]); - - return keyEventWithModifiers([event modifierFlags], key); + return keyEventWithModifiers([event modifierFlags], key, code); } diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index 4e11597a..d9d4da68 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -25,9 +25,12 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include -#include +#include +#include +#include #include #include @@ -38,145 +41,35 @@ namespace sf::priv //////////////////////////////////////////////////////////// bool InputImpl::isKeyPressed(Keyboard::Key key) { - // Get the corresponding X11 keysym - KeySym keysym = 0; + return KeyboardImpl::isKeyPressed(key); +} - // clang-format off - switch (key) - { - case Keyboard::LShift: keysym = XK_Shift_L; break; - case Keyboard::RShift: keysym = XK_Shift_R; break; - case Keyboard::LControl: keysym = XK_Control_L; break; - case Keyboard::RControl: keysym = XK_Control_R; break; - case Keyboard::LAlt: keysym = XK_Alt_L; break; - case Keyboard::RAlt: keysym = XK_Alt_R; break; - case Keyboard::LSystem: keysym = XK_Super_L; break; - case Keyboard::RSystem: keysym = XK_Super_R; break; - case Keyboard::Menu: keysym = XK_Menu; break; - case Keyboard::Escape: keysym = XK_Escape; break; - case Keyboard::Semicolon: keysym = XK_semicolon; break; - case Keyboard::Slash: keysym = XK_slash; break; - case Keyboard::Equal: keysym = XK_equal; break; - case Keyboard::Hyphen: keysym = XK_minus; break; - case Keyboard::LBracket: keysym = XK_bracketleft; break; - case Keyboard::RBracket: keysym = XK_bracketright; break; - case Keyboard::Comma: keysym = XK_comma; break; - case Keyboard::Period: keysym = XK_period; break; - case Keyboard::Quote: keysym = XK_apostrophe; break; - case Keyboard::Backslash: keysym = XK_backslash; break; - case Keyboard::Tilde: keysym = XK_grave; break; - case Keyboard::Space: keysym = XK_space; break; - case Keyboard::Enter: keysym = XK_Return; break; - case Keyboard::Backspace: keysym = XK_BackSpace; break; - case Keyboard::Tab: keysym = XK_Tab; break; - case Keyboard::PageUp: keysym = XK_Prior; break; - case Keyboard::PageDown: keysym = XK_Next; break; - case Keyboard::End: keysym = XK_End; break; - case Keyboard::Home: keysym = XK_Home; break; - case Keyboard::Insert: keysym = XK_Insert; break; - case Keyboard::Delete: keysym = XK_Delete; break; - case Keyboard::Add: keysym = XK_KP_Add; break; - case Keyboard::Subtract: keysym = XK_KP_Subtract; break; - case Keyboard::Multiply: keysym = XK_KP_Multiply; break; - case Keyboard::Divide: keysym = XK_KP_Divide; break; - case Keyboard::Pause: keysym = XK_Pause; break; - case Keyboard::F1: keysym = XK_F1; break; - case Keyboard::F2: keysym = XK_F2; break; - case Keyboard::F3: keysym = XK_F3; break; - case Keyboard::F4: keysym = XK_F4; break; - case Keyboard::F5: keysym = XK_F5; break; - case Keyboard::F6: keysym = XK_F6; break; - case Keyboard::F7: keysym = XK_F7; break; - case Keyboard::F8: keysym = XK_F8; break; - case Keyboard::F9: keysym = XK_F9; break; - case Keyboard::F10: keysym = XK_F10; break; - case Keyboard::F11: keysym = XK_F11; break; - case Keyboard::F12: keysym = XK_F12; break; - case Keyboard::F13: keysym = XK_F13; break; - case Keyboard::F14: keysym = XK_F14; break; - case Keyboard::F15: keysym = XK_F15; break; - case Keyboard::Left: keysym = XK_Left; break; - case Keyboard::Right: keysym = XK_Right; break; - case Keyboard::Up: keysym = XK_Up; break; - case Keyboard::Down: keysym = XK_Down; break; - case Keyboard::Numpad0: keysym = XK_KP_Insert; break; - case Keyboard::Numpad1: keysym = XK_KP_End; break; - case Keyboard::Numpad2: keysym = XK_KP_Down; break; - case Keyboard::Numpad3: keysym = XK_KP_Page_Down; break; - case Keyboard::Numpad4: keysym = XK_KP_Left; break; - case Keyboard::Numpad5: keysym = XK_KP_Begin; break; - case Keyboard::Numpad6: keysym = XK_KP_Right; break; - case Keyboard::Numpad7: keysym = XK_KP_Home; break; - case Keyboard::Numpad8: keysym = XK_KP_Up; break; - case Keyboard::Numpad9: keysym = XK_KP_Page_Up; break; - case Keyboard::A: keysym = XK_a; break; - case Keyboard::B: keysym = XK_b; break; - case Keyboard::C: keysym = XK_c; break; - case Keyboard::D: keysym = XK_d; break; - case Keyboard::E: keysym = XK_e; break; - case Keyboard::F: keysym = XK_f; break; - case Keyboard::G: keysym = XK_g; break; - case Keyboard::H: keysym = XK_h; break; - case Keyboard::I: keysym = XK_i; break; - case Keyboard::J: keysym = XK_j; break; - case Keyboard::K: keysym = XK_k; break; - case Keyboard::L: keysym = XK_l; break; - case Keyboard::M: keysym = XK_m; break; - case Keyboard::N: keysym = XK_n; break; - case Keyboard::O: keysym = XK_o; break; - case Keyboard::P: keysym = XK_p; break; - case Keyboard::Q: keysym = XK_q; break; - case Keyboard::R: keysym = XK_r; break; - case Keyboard::S: keysym = XK_s; break; - case Keyboard::T: keysym = XK_t; break; - case Keyboard::U: keysym = XK_u; break; - case Keyboard::V: keysym = XK_v; break; - case Keyboard::W: keysym = XK_w; break; - case Keyboard::X: keysym = XK_x; break; - case Keyboard::Y: keysym = XK_y; break; - case Keyboard::Z: keysym = XK_z; break; - case Keyboard::Num0: keysym = XK_0; break; - case Keyboard::Num1: keysym = XK_1; break; - case Keyboard::Num2: keysym = XK_2; break; - case Keyboard::Num3: keysym = XK_3; break; - case Keyboard::Num4: keysym = XK_4; break; - case Keyboard::Num5: keysym = XK_5; break; - case Keyboard::Num6: keysym = XK_6; break; - case Keyboard::Num7: keysym = XK_7; break; - case Keyboard::Num8: keysym = XK_8; break; - case Keyboard::Num9: keysym = XK_9; break; - default: keysym = 0; break; - } - // clang-format on - // Sanity checks - if (key < 0 || key >= sf::Keyboard::KeyCount) - return false; +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Scancode code) +{ + return KeyboardImpl::isKeyPressed(code); +} - // Open a connection with the X server - Display* display = openDisplay(); - // Convert to keycode - KeyCode keycode = XKeysymToKeycode(display, keysym); - if (keycode != 0) - { - // Get the whole keyboard state - char keys[32]; - XQueryKeymap(display, keys); +//////////////////////////////////////////////////////////// +Keyboard::Key InputImpl::localize(Keyboard::Scancode code) +{ + return KeyboardImpl::localize(code); +} - // Close the connection with the X server - closeDisplay(display); - // Check our keycode - return (keys[keycode / 8] & (1 << (keycode % 8))) != 0; - } - else - { - // Close the connection with the X server - closeDisplay(display); +//////////////////////////////////////////////////////////// +Keyboard::Scancode InputImpl::delocalize(Keyboard::Key key) +{ + return KeyboardImpl::delocalize(key); +} - return false; - } + +//////////////////////////////////////////////////////////// +String InputImpl::getDescription(Keyboard::Scancode code) +{ + return KeyboardImpl::getDescription(code); } diff --git a/src/SFML/Window/Unix/InputImpl.hpp b/src/SFML/Window/Unix/InputImpl.hpp index e8141eb0..12d16dbe 100644 --- a/src/SFML/Window/Unix/InputImpl.hpp +++ b/src/SFML/Window/Unix/InputImpl.hpp @@ -41,19 +41,37 @@ class InputImpl { public: //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise + /// \copydoc sf::Keyboard::isKeyPressed(Key) /// //////////////////////////////////////////////////////////// static bool isKeyPressed(Keyboard::Key key); //////////////////////////////////////////////////////////// - /// \brief Show or hide the virtual keyboard + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) /// - /// \param visible True to show, false to hide + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::setVirtualKeyboardVisible /// //////////////////////////////////////////////////////////// static void setVirtualKeyboardVisible(bool visible); diff --git a/src/SFML/Window/Unix/KeySymToKeyMapping.cpp b/src/SFML/Window/Unix/KeySymToKeyMapping.cpp new file mode 100644 index 00000000..3f410775 --- /dev/null +++ b/src/SFML/Window/Unix/KeySymToKeyMapping.cpp @@ -0,0 +1,262 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +namespace sf::priv +{ + +//////////////////////////////////////////////////////////// +Keyboard::Key keySymToKey(KeySym symbol) +{ + // clang-format off + switch (symbol) + { + case XK_Shift_L: return Keyboard::LShift; + case XK_Shift_R: return Keyboard::RShift; + case XK_Control_L: return Keyboard::LControl; + case XK_Control_R: return Keyboard::RControl; + case XK_Alt_L: return Keyboard::LAlt; + case XK_ISO_Level3_Shift: + case XK_Alt_R: return Keyboard::RAlt; + case XK_Super_L: return Keyboard::LSystem; + case XK_Super_R: return Keyboard::RSystem; + case XK_Menu: return Keyboard::Menu; + case XK_Escape: return Keyboard::Escape; + case XK_semicolon: return Keyboard::SemiColon; + case XK_slash: return Keyboard::Slash; + case XK_equal: return Keyboard::Equal; + case XK_minus: return Keyboard::Dash; + case XK_bracketleft: return Keyboard::LBracket; + case XK_bracketright: return Keyboard::RBracket; + case XK_comma: return Keyboard::Comma; + case XK_period: return Keyboard::Period; + case XK_apostrophe: return Keyboard::Quote; + case XK_backslash: return Keyboard::BackSlash; + case XK_grave: return Keyboard::Grave; + case XK_space: return Keyboard::Space; + case XK_Return: return Keyboard::Return; + case XK_KP_Enter: return Keyboard::Return; + case XK_BackSpace: return Keyboard::BackSpace; + case XK_Tab: return Keyboard::Tab; + case XK_Prior: return Keyboard::PageUp; + case XK_Next: return Keyboard::PageDown; + case XK_End: return Keyboard::End; + case XK_Home: return Keyboard::Home; + case XK_Insert: return Keyboard::Insert; + case XK_Delete: return Keyboard::Delete; + case XK_KP_Add: return Keyboard::Add; + case XK_KP_Subtract: return Keyboard::Subtract; + case XK_KP_Multiply: return Keyboard::Multiply; + case XK_KP_Divide: return Keyboard::Divide; + case XK_KP_Delete: return Keyboard::Period; + case XK_Pause: return Keyboard::Pause; + case XK_F1: return Keyboard::F1; + case XK_F2: return Keyboard::F2; + case XK_F3: return Keyboard::F3; + case XK_F4: return Keyboard::F4; + case XK_F5: return Keyboard::F5; + case XK_F6: return Keyboard::F6; + case XK_F7: return Keyboard::F7; + case XK_F8: return Keyboard::F8; + case XK_F9: return Keyboard::F9; + case XK_F10: return Keyboard::F10; + case XK_F11: return Keyboard::F11; + case XK_F12: return Keyboard::F12; + case XK_F13: return Keyboard::F13; + case XK_F14: return Keyboard::F14; + case XK_F15: return Keyboard::F15; + case XK_Left: return Keyboard::Left; + case XK_Right: return Keyboard::Right; + case XK_Up: return Keyboard::Up; + case XK_Down: return Keyboard::Down; + case XK_KP_Insert: return Keyboard::Numpad0; + case XK_KP_End: return Keyboard::Numpad1; + case XK_KP_Down: return Keyboard::Numpad2; + case XK_KP_Page_Down: return Keyboard::Numpad3; + case XK_KP_Left: return Keyboard::Numpad4; + case XK_KP_Begin: return Keyboard::Numpad5; + case XK_KP_Right: return Keyboard::Numpad6; + case XK_KP_Home: return Keyboard::Numpad7; + case XK_KP_Up: return Keyboard::Numpad8; + case XK_KP_Page_Up: return Keyboard::Numpad9; + case XK_a: return Keyboard::A; + case XK_b: return Keyboard::B; + case XK_c: return Keyboard::C; + case XK_d: return Keyboard::D; + case XK_e: return Keyboard::E; + case XK_f: return Keyboard::F; + case XK_g: return Keyboard::G; + case XK_h: return Keyboard::H; + case XK_i: return Keyboard::I; + case XK_j: return Keyboard::J; + case XK_k: return Keyboard::K; + case XK_l: return Keyboard::L; + case XK_m: return Keyboard::M; + case XK_n: return Keyboard::N; + case XK_o: return Keyboard::O; + case XK_p: return Keyboard::P; + case XK_q: return Keyboard::Q; + case XK_r: return Keyboard::R; + case XK_s: return Keyboard::S; + case XK_t: return Keyboard::T; + case XK_u: return Keyboard::U; + case XK_v: return Keyboard::V; + case XK_w: return Keyboard::W; + case XK_x: return Keyboard::X; + case XK_y: return Keyboard::Y; + case XK_z: return Keyboard::Z; + case XK_0: return Keyboard::Num0; + case XK_1: return Keyboard::Num1; + case XK_2: return Keyboard::Num2; + case XK_3: return Keyboard::Num3; + case XK_4: return Keyboard::Num4; + case XK_5: return Keyboard::Num5; + case XK_6: return Keyboard::Num6; + case XK_7: return Keyboard::Num7; + case XK_8: return Keyboard::Num8; + case XK_9: return Keyboard::Num9; + default: return Keyboard::Unknown; + } + // clang-format on +} + + +//////////////////////////////////////////////////////////// +KeySym keyToKeySym(Keyboard::Key key) +{ + // clang-format off + switch (key) + { + case Keyboard::LShift: return XK_Shift_L; + case Keyboard::RShift: return XK_Shift_R; + case Keyboard::LControl: return XK_Control_L; + case Keyboard::RControl: return XK_Control_R; + case Keyboard::LAlt: return XK_Alt_L; + case Keyboard::RAlt: return XK_Alt_R; + case Keyboard::LSystem: return XK_Super_L; + case Keyboard::RSystem: return XK_Super_R; + case Keyboard::Menu: return XK_Menu; + case Keyboard::Escape: return XK_Escape; + case Keyboard::SemiColon: return XK_semicolon; + case Keyboard::Slash: return XK_slash; + case Keyboard::Equal: return XK_equal; + case Keyboard::Dash: return XK_minus; + case Keyboard::LBracket: return XK_bracketleft; + case Keyboard::RBracket: return XK_bracketright; + case Keyboard::Comma: return XK_comma; + case Keyboard::Period: return XK_period; + case Keyboard::Quote: return XK_apostrophe; + case Keyboard::BackSlash: return XK_backslash; + case Keyboard::Grave: return XK_grave; + case Keyboard::Space: return XK_space; + case Keyboard::Return: return XK_Return; + case Keyboard::BackSpace: return XK_BackSpace; + case Keyboard::Tab: return XK_Tab; + case Keyboard::PageUp: return XK_Prior; + case Keyboard::PageDown: return XK_Next; + case Keyboard::End: return XK_End; + case Keyboard::Home: return XK_Home; + case Keyboard::Insert: return XK_Insert; + case Keyboard::Delete: return XK_Delete; + case Keyboard::Add: return XK_KP_Add; + case Keyboard::Subtract: return XK_KP_Subtract; + case Keyboard::Multiply: return XK_KP_Multiply; + case Keyboard::Divide: return XK_KP_Divide; + case Keyboard::Pause: return XK_Pause; + case Keyboard::F1: return XK_F1; + case Keyboard::F2: return XK_F2; + case Keyboard::F3: return XK_F3; + case Keyboard::F4: return XK_F4; + case Keyboard::F5: return XK_F5; + case Keyboard::F6: return XK_F6; + case Keyboard::F7: return XK_F7; + case Keyboard::F8: return XK_F8; + case Keyboard::F9: return XK_F9; + case Keyboard::F10: return XK_F10; + case Keyboard::F11: return XK_F11; + case Keyboard::F12: return XK_F12; + case Keyboard::F13: return XK_F13; + case Keyboard::F14: return XK_F14; + case Keyboard::F15: return XK_F15; + case Keyboard::Left: return XK_Left; + case Keyboard::Right: return XK_Right; + case Keyboard::Up: return XK_Up; + case Keyboard::Down: return XK_Down; + case Keyboard::Numpad0: return XK_KP_Insert; + case Keyboard::Numpad1: return XK_KP_End; + case Keyboard::Numpad2: return XK_KP_Down; + case Keyboard::Numpad3: return XK_KP_Page_Down; + case Keyboard::Numpad4: return XK_KP_Left; + case Keyboard::Numpad5: return XK_KP_Begin; + case Keyboard::Numpad6: return XK_KP_Right; + case Keyboard::Numpad7: return XK_KP_Home; + case Keyboard::Numpad8: return XK_KP_Up; + case Keyboard::Numpad9: return XK_KP_Page_Up; + case Keyboard::A: return XK_a; + case Keyboard::B: return XK_b; + case Keyboard::C: return XK_c; + case Keyboard::D: return XK_d; + case Keyboard::E: return XK_e; + case Keyboard::F: return XK_f; + case Keyboard::G: return XK_g; + case Keyboard::H: return XK_h; + case Keyboard::I: return XK_i; + case Keyboard::J: return XK_j; + case Keyboard::K: return XK_k; + case Keyboard::L: return XK_l; + case Keyboard::M: return XK_m; + case Keyboard::N: return XK_n; + case Keyboard::O: return XK_o; + case Keyboard::P: return XK_p; + case Keyboard::Q: return XK_q; + case Keyboard::R: return XK_r; + case Keyboard::S: return XK_s; + case Keyboard::T: return XK_t; + case Keyboard::U: return XK_u; + case Keyboard::V: return XK_v; + case Keyboard::W: return XK_w; + case Keyboard::X: return XK_x; + case Keyboard::Y: return XK_y; + case Keyboard::Z: return XK_z; + case Keyboard::Num0: return XK_0; + case Keyboard::Num1: return XK_1; + case Keyboard::Num2: return XK_2; + case Keyboard::Num3: return XK_3; + case Keyboard::Num4: return XK_4; + case Keyboard::Num5: return XK_5; + case Keyboard::Num6: return XK_6; + case Keyboard::Num7: return XK_7; + case Keyboard::Num8: return XK_8; + case Keyboard::Num9: return XK_9; + default: return NoSymbol; + } + // clang-format on +} +} // namespace sf::priv diff --git a/src/SFML/Window/Unix/KeySymToKeyMapping.hpp b/src/SFML/Window/Unix/KeySymToKeyMapping.hpp new file mode 100644 index 00000000..3652c34a --- /dev/null +++ b/src/SFML/Window/Unix/KeySymToKeyMapping.hpp @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // sf::Keyboard::Key + +#include // KeySym + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +/// \brief Convert X11 KeySym to sf::Keyboard::Key +/// +/// \param symbol X11 KeySym +/// +/// \return The corresponding sf::Keyboard::Key +/// +//////////////////////////////////////////////////////////// +Keyboard::Key keySymToKey(KeySym symbol); + + +//////////////////////////////////////////////////////////// +/// \brief Convert sf::Keyboard::Key to X11 KeySym +/// +/// \param key X11 sf::Keyboard::Key +/// +/// \return The corresponding X11 KeySym +/// +//////////////////////////////////////////////////////////// +KeySym keyToKeySym(Keyboard::Key key); + +} // namespace sf::priv diff --git a/src/SFML/Window/Unix/KeySymToUnicodeMapping.cpp b/src/SFML/Window/Unix/KeySymToUnicodeMapping.cpp new file mode 100644 index 00000000..dfb98c5b --- /dev/null +++ b/src/SFML/Window/Unix/KeySymToUnicodeMapping.cpp @@ -0,0 +1,1400 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +namespace sf::priv +{ + +std::uint32_t keysymToUnicode(KeySym keysym) +{ + // clang-format off + switch (keysym) + { + case 0xff08: return 0x0008; // BackSpace + case 0xff89: return 0x0009; // KP_Tab + case 0xff09: return 0x0009; // Tab + case 0xff0a: return 0x000a; // Linefeed + case 0xff0b: return 0x000b; // Clear + case 0xff8d: return 0x000d; // KP_Enter + case 0xff0d: return 0x000d; // Return + case 0xff13: return 0x0013; // Pause + case 0xff14: return 0x0014; // Scroll_Lock + case 0xff15: return 0x0015; // Sys_Req + case 0xff1b: return 0x001b; // Escape + case 0xff80: return 0x0020; // KP_Space + case 0x0020: return 0x0020; // space + case 0x0021: return 0x0021; // exclam + case 0x0022: return 0x0022; // quotedbl + case 0x0023: return 0x0023; // numbersign + case 0x0024: return 0x0024; // dollar + case 0x0025: return 0x0025; // percent + case 0x0026: return 0x0026; // ampersand + case 0x0027: return 0x0027; // quoteright + case 0x14a5: return 0x0028; // Armenian_parenleft + case 0x0028: return 0x0028; // parenleft + case 0x14a4: return 0x0029; // Armenian_parenright + case 0x0029: return 0x0029; // parenright + case 0xffaa: return 0x002a; // KP_Multiply + case 0x002a: return 0x002a; // asterisk + case 0xffab: return 0x002b; // KP_Add + case 0x002b: return 0x002b; // plus + case 0x14ab: return 0x002c; // Armenian_comma + case 0xffac: return 0x002c; // KP_Separator + case 0x002c: return 0x002c; // comma + case 0xffad: return 0x002d; // KP_Subtract + case 0x002d: return 0x002d; // minus + case 0x14a9: return 0x002e; // Armenian_mijaket + case 0xffae: return 0x002e; // KP_Decimal + case 0x0abd: return 0x002e; // decimalpoint + case 0x002e: return 0x002e; // period + case 0xffaf: return 0x002f; // KP_Divide + case 0x002f: return 0x002f; // slash + case 0x0030: return 0x0030; // 0 + case 0xffb0: return 0x0030; // KP_0 + case 0x0031: return 0x0031; // 1 + case 0xffb1: return 0x0031; // KP_1 + case 0x0032: return 0x0032; // 2 + case 0xffb2: return 0x0032; // KP_2 + case 0x0033: return 0x0033; // 3 + case 0xffb3: return 0x0033; // KP_3 + case 0x0034: return 0x0034; // 4 + case 0xffb4: return 0x0034; // KP_4 + case 0x0035: return 0x0035; // 5 + case 0xffb5: return 0x0035; // KP_5 + case 0x0036: return 0x0036; // 6 + case 0xffb6: return 0x0036; // KP_6 + case 0x0037: return 0x0037; // 7 + case 0xffb7: return 0x0037; // KP_7 + case 0x0038: return 0x0038; // 8 + case 0xffb8: return 0x0038; // KP_8 + case 0x0039: return 0x0039; // 9 + case 0xffb9: return 0x0039; // KP_9 + case 0x003a: return 0x003a; // colon + case 0x003b: return 0x003b; // semicolon + case 0x0ba3: return 0x003c; // leftcaret + case 0x003c: return 0x003c; // less + case 0xffbd: return 0x003d; // KP_Equal + case 0x003d: return 0x003d; // equal + case 0x003e: return 0x003e; // greater + case 0x0ba6: return 0x003e; // rightcaret + case 0x003f: return 0x003f; // question + case 0x0040: return 0x0040; // at + case 0x0041: return 0x0041; // A + case 0x0042: return 0x0042; // B + case 0x0043: return 0x0043; // C + case 0x0044: return 0x0044; // D + case 0x0045: return 0x0045; // E + case 0x0046: return 0x0046; // F + case 0x0047: return 0x0047; // G + case 0x0048: return 0x0048; // H + case 0x0049: return 0x0049; // I + case 0x004a: return 0x004a; // J + case 0x004b: return 0x004b; // K + case 0x004c: return 0x004c; // L + case 0x004d: return 0x004d; // M + case 0x004e: return 0x004e; // N + case 0x004f: return 0x004f; // O + case 0x0050: return 0x0050; // P + case 0x0051: return 0x0051; // Q + case 0x0052: return 0x0052; // R + case 0x0053: return 0x0053; // S + case 0x0054: return 0x0054; // T + case 0x0055: return 0x0055; // U + case 0x0056: return 0x0056; // V + case 0x0057: return 0x0057; // W + case 0x0058: return 0x0058; // X + case 0x0059: return 0x0059; // Y + case 0x005a: return 0x005a; // Z + case 0x005b: return 0x005b; // bracketleft + case 0x005c: return 0x005c; // backslash + case 0x005d: return 0x005d; // bracketright + case 0x005e: return 0x005e; // asciicircum + case 0x0bc6: return 0x005f; // underbar + case 0x005f: return 0x005f; // underscore + case 0x0060: return 0x0060; // quoteleft + case 0x0061: return 0x0061; // a + case 0x0062: return 0x0062; // b + case 0x0063: return 0x0063; // c + case 0x0064: return 0x0064; // d + case 0x0065: return 0x0065; // e + case 0x0066: return 0x0066; // f + case 0x0067: return 0x0067; // g + case 0x0068: return 0x0068; // h + case 0x0069: return 0x0069; // i + case 0x006a: return 0x006a; // j + case 0x006b: return 0x006b; // k + case 0x006c: return 0x006c; // l + case 0x006d: return 0x006d; // m + case 0x006e: return 0x006e; // n + case 0x006f: return 0x006f; // o + case 0x0070: return 0x0070; // p + case 0x0071: return 0x0071; // q + case 0x0072: return 0x0072; // r + case 0x0073: return 0x0073; // s + case 0x0074: return 0x0074; // t + case 0x0075: return 0x0075; // u + case 0x0076: return 0x0076; // v + case 0x0077: return 0x0077; // w + case 0x0078: return 0x0078; // x + case 0x0079: return 0x0079; // y + case 0x007a: return 0x007a; // z + case 0x007b: return 0x007b; // braceleft + case 0x007c: return 0x007c; // bar + case 0x007d: return 0x007d; // braceright + case 0x007e: return 0x007e; // asciitilde + case 0x00a0: return 0x00a0; // nobreakspace + case 0x00a1: return 0x00a1; // exclamdown + case 0x00a2: return 0x00a2; // cent + case 0x00a3: return 0x00a3; // sterling + case 0x00a4: return 0x00a4; // currency + case 0x00a5: return 0x00a5; // yen + case 0x00a6: return 0x00a6; // brokenbar + case 0x14ff: return 0x00a7; // Armenian_section_sign + case 0x00a7: return 0x00a7; // section + case 0x00a8: return 0x00a8; // diaeresis + case 0x00a9: return 0x00a9; // copyright + case 0x00aa: return 0x00aa; // ordfeminine + case 0x14a7: return 0x00ab; // Armenian_guillemotleft + case 0x00ab: return 0x00ab; // guillemotleft + case 0x00ac: return 0x00ac; // notsign + case 0x00ad: return 0x00ad; // hyphen + case 0x00ae: return 0x00ae; // registered + case 0x00af: return 0x00af; // macron + case 0x0bc0: return 0x00af; // overbar + case 0x00b0: return 0x00b0; // degree + case 0x00b1: return 0x00b1; // plusminus + case 0x00b2: return 0x00b2; // twosuperior + case 0x00b3: return 0x00b3; // threesuperior + case 0x00b4: return 0x00b4; // acute + case 0x00b5: return 0x00b5; // mu + case 0x00b6: return 0x00b6; // paragraph + case 0x00b7: return 0x00b7; // periodcentered + case 0x00b8: return 0x00b8; // cedilla + case 0x00b9: return 0x00b9; // onesuperior + case 0x00ba: return 0x00ba; // masculine + case 0x14a6: return 0x00bb; // Armenian_guillemotright + case 0x00bb: return 0x00bb; // guillemotright + case 0x00bc: return 0x00bc; // onequarter + case 0x00bd: return 0x00bd; // onehalf + case 0x00be: return 0x00be; // threequarters + case 0x00bf: return 0x00bf; // questiondown + case 0x00c0: return 0x00c0; // Agrave + case 0x00c1: return 0x00c1; // Aacute + case 0x00c2: return 0x00c2; // Acircumflex + case 0x00c3: return 0x00c3; // Atilde + case 0x00c4: return 0x00c4; // Adiaeresis + case 0x00c5: return 0x00c5; // Aring + case 0x00c6: return 0x00c6; // AE + case 0x00c7: return 0x00c7; // Ccedilla + case 0x00c8: return 0x00c8; // Egrave + case 0x00c9: return 0x00c9; // Eacute + case 0x00ca: return 0x00ca; // Ecircumflex + case 0x00cb: return 0x00cb; // Ediaeresis + case 0x00cc: return 0x00cc; // Igrave + case 0x00cd: return 0x00cd; // Iacute + case 0x00ce: return 0x00ce; // Icircumflex + case 0x00cf: return 0x00cf; // Idiaeresis + case 0x00d0: return 0x00d0; // Eth + case 0x00d1: return 0x00d1; // Ntilde + case 0x00d2: return 0x00d2; // Ograve + case 0x00d3: return 0x00d3; // Oacute + case 0x00d4: return 0x00d4; // Ocircumflex + case 0x00d5: return 0x00d5; // Otilde + case 0x00d6: return 0x00d6; // Odiaeresis + case 0x00d7: return 0x00d7; // multiply + case 0x00d8: return 0x00d8; // Ooblique + case 0x00d9: return 0x00d9; // Ugrave + case 0x00da: return 0x00da; // Uacute + case 0x00db: return 0x00db; // Ucircumflex + case 0x00dc: return 0x00dc; // Udiaeresis + case 0x00dd: return 0x00dd; // Yacute + case 0x00de: return 0x00de; // Thorn + case 0x00df: return 0x00df; // ssharp + case 0x00e0: return 0x00e0; // agrave + case 0x00e1: return 0x00e1; // aacute + case 0x00e2: return 0x00e2; // acircumflex + case 0x00e3: return 0x00e3; // atilde + case 0x00e4: return 0x00e4; // adiaeresis + case 0x00e5: return 0x00e5; // aring + case 0x00e6: return 0x00e6; // ae + case 0x00e7: return 0x00e7; // ccedilla + case 0x00e8: return 0x00e8; // egrave + case 0x00e9: return 0x00e9; // eacute + case 0x00ea: return 0x00ea; // ecircumflex + case 0x00eb: return 0x00eb; // ediaeresis + case 0x00ec: return 0x00ec; // igrave + case 0x00ed: return 0x00ed; // iacute + case 0x00ee: return 0x00ee; // icircumflex + case 0x00ef: return 0x00ef; // idiaeresis + case 0x00f0: return 0x00f0; // eth + case 0x00f1: return 0x00f1; // ntilde + case 0x00f2: return 0x00f2; // ograve + case 0x00f3: return 0x00f3; // oacute + case 0x00f4: return 0x00f4; // ocircumflex + case 0x00f5: return 0x00f5; // otilde + case 0x00f6: return 0x00f6; // odiaeresis + case 0x00f7: return 0x00f7; // division + case 0x00f8: return 0x00f8; // oslash + case 0x00f9: return 0x00f9; // ugrave + case 0x00fa: return 0x00fa; // uacute + case 0x00fb: return 0x00fb; // ucircumflex + case 0x00fc: return 0x00fc; // udiaeresis + case 0x00fd: return 0x00fd; // yacute + case 0x00fe: return 0x00fe; // thorn + case 0x00ff: return 0x00ff; // ydiaeresis + case 0x03c0: return 0x0100; // Amacron + case 0x03e0: return 0x0101; // amacron + case 0x01c3: return 0x0102; // Abreve + case 0x01e3: return 0x0103; // abreve + case 0x01a1: return 0x0104; // Aogonek + case 0x01b1: return 0x0105; // aogonek + case 0x01c6: return 0x0106; // Cacute + case 0x01e6: return 0x0107; // cacute + case 0x02c6: return 0x0108; // Ccircumflex + case 0x02e6: return 0x0109; // ccircumflex + case 0x02c5: return 0x010a; // Cabovedot + case 0x02e5: return 0x010b; // cabovedot + case 0x01c8: return 0x010c; // Ccaron + case 0x01e8: return 0x010d; // ccaron + case 0x01cf: return 0x010e; // Dcaron + case 0x01ef: return 0x010f; // dcaron + case 0x01d0: return 0x0110; // Dstroke + case 0x01f0: return 0x0111; // dstroke + case 0x03aa: return 0x0112; // Emacron + case 0x03ba: return 0x0113; // emacron + case 0x03cc: return 0x0116; // Eabovedot + case 0x03ec: return 0x0117; // eabovedot + case 0x01ca: return 0x0118; // Eogonek + case 0x01ea: return 0x0119; // eogonek + case 0x01cc: return 0x011a; // Ecaron + case 0x01ec: return 0x011b; // ecaron + case 0x02d8: return 0x011c; // Gcircumflex + case 0x02f8: return 0x011d; // gcircumflex + case 0x02ab: return 0x011e; // Gbreve + case 0x02bb: return 0x011f; // gbreve + case 0x02d5: return 0x0120; // Gabovedot + case 0x02f5: return 0x0121; // gabovedot + case 0x03ab: return 0x0122; // Gcedilla + case 0x03bb: return 0x0123; // gcedilla + case 0x02a6: return 0x0124; // Hcircumflex + case 0x02b6: return 0x0125; // hcircumflex + case 0x02a1: return 0x0126; // Hstroke + case 0x02b1: return 0x0127; // hstroke + case 0x03a5: return 0x0128; // Itilde + case 0x03b5: return 0x0129; // itilde + case 0x03cf: return 0x012a; // Imacron + case 0x03ef: return 0x012b; // imacron + case 0x16a6: return 0x012c; // Ibreve + case 0x16b6: return 0x012d; // ibreve + case 0x03c7: return 0x012e; // Iogonek + case 0x03e7: return 0x012f; // iogonek + case 0x02a9: return 0x0130; // Iabovedot + case 0x02b9: return 0x0131; // idotless + case 0x02ac: return 0x0134; // Jcircumflex + case 0x02bc: return 0x0135; // jcircumflex + case 0x03d3: return 0x0136; // Kcedilla + case 0x03f3: return 0x0137; // kcedilla + case 0x03a2: return 0x0138; // kra + case 0x01c5: return 0x0139; // Lacute + case 0x01e5: return 0x013a; // lacute + case 0x03a6: return 0x013b; // Lcedilla + case 0x03b6: return 0x013c; // lcedilla + case 0x01a5: return 0x013d; // Lcaron + case 0x01b5: return 0x013e; // lcaron + case 0x01a3: return 0x0141; // Lstroke + case 0x01b3: return 0x0142; // lstroke + case 0x01d1: return 0x0143; // Nacute + case 0x01f1: return 0x0144; // nacute + case 0x03d1: return 0x0145; // Ncedilla + case 0x03f1: return 0x0146; // ncedilla + case 0x01d2: return 0x0147; // Ncaron + case 0x01f2: return 0x0148; // ncaron + case 0x03bd: return 0x014a; // ENG + case 0x03bf: return 0x014b; // eng + case 0x03d2: return 0x014c; // Omacron + case 0x03f2: return 0x014d; // omacron + case 0x01d5: return 0x0150; // Odoubleacute + case 0x01f5: return 0x0151; // odoubleacute + case 0x13bc: return 0x0152; // OE + case 0x13bd: return 0x0153; // oe + case 0x01c0: return 0x0154; // Racute + case 0x01e0: return 0x0155; // racute + case 0x03a3: return 0x0156; // Rcedilla + case 0x03b3: return 0x0157; // rcedilla + case 0x01d8: return 0x0158; // Rcaron + case 0x01f8: return 0x0159; // rcaron + case 0x01a6: return 0x015a; // Sacute + case 0x01b6: return 0x015b; // sacute + case 0x02de: return 0x015c; // Scircumflex + case 0x02fe: return 0x015d; // scircumflex + case 0x01aa: return 0x015e; // Scedilla + case 0x01ba: return 0x015f; // scedilla + case 0x01a9: return 0x0160; // Scaron + case 0x01b9: return 0x0161; // scaron + case 0x01de: return 0x0162; // Tcedilla + case 0x01fe: return 0x0163; // tcedilla + case 0x01ab: return 0x0164; // Tcaron + case 0x01bb: return 0x0165; // tcaron + case 0x03ac: return 0x0166; // Tslash + case 0x03bc: return 0x0167; // tslash + case 0x03dd: return 0x0168; // Utilde + case 0x03fd: return 0x0169; // utilde + case 0x03de: return 0x016a; // Umacron + case 0x03fe: return 0x016b; // umacron + case 0x02dd: return 0x016c; // Ubreve + case 0x02fd: return 0x016d; // ubreve + case 0x01d9: return 0x016e; // Uring + case 0x01f9: return 0x016f; // uring + case 0x01db: return 0x0170; // Udoubleacute + case 0x01fb: return 0x0171; // udoubleacute + case 0x03d9: return 0x0172; // Uogonek + case 0x03f9: return 0x0173; // uogonek + case 0x12d0: return 0x0174; // Wcircumflex + case 0x12f0: return 0x0175; // wcircumflex + case 0x12de: return 0x0176; // Ycircumflex + case 0x12fe: return 0x0177; // ycircumflex + case 0x13be: return 0x0178; // Ydiaeresis + case 0x01ac: return 0x0179; // Zacute + case 0x01bc: return 0x017a; // zacute + case 0x01af: return 0x017b; // Zabovedot + case 0x01bf: return 0x017c; // zabovedot + case 0x01ae: return 0x017d; // Zcaron + case 0x01be: return 0x017e; // zcaron + case 0x16c6: return 0x018f; // SCHWA + case 0x08f6: return 0x0192; // function + case 0x16af: return 0x019f; // Obarred + case 0x1efa: return 0x01a0; // Ohorn + case 0x1efb: return 0x01a1; // ohorn + case 0x1efc: return 0x01af; // Uhorn + case 0x1efd: return 0x01b0; // uhorn + case 0x16a9: return 0x01b5; // Zstroke + case 0x16b9: return 0x01b6; // zstroke + case 0x16bd: return 0x01d2; // ocaron + case 0x16aa: return 0x01e6; // Gcaron + case 0x16ba: return 0x01e7; // gcaron + case 0x16f6: return 0x0259; // schwa + case 0x16bf: return 0x0275; // obarred + case 0x01b7: return 0x02c7; // caron + case 0x01a2: return 0x02d8; // breve + case 0x01ff: return 0x02d9; // abovedot + case 0x01b2: return 0x02db; // ogonek + case 0x01bd: return 0x02dd; // doubleacute + + // Edited to map dead keys to non-combining characters when possible. + // The original combining diacritical marks are shown in comments. + case 0x1ef2: return 0x0060; // combining_grave (0x0300) + case 0xfe50: return 0x0060; // dead_grave (0x0300) + case 0x1ef3: return 0x00b4; // combining_acute (0x0301) + case 0xfe51: return 0x00b4; // dead_acute (0x0301) + case 0xfe52: return 0x005e; // dead_circumflex (0x0302) + case 0x1e9f: return 0x007e; // combining_tilde (0x0303) + case 0xfe53: return 0x007e; // dead_tilde (0x0303) + case 0xfe54: return 0x00af; // dead_macron (0x0304) + case 0xfe55: return 0x02d8; // dead_breve (0x0306) + case 0xfe56: return 0x02d9; // dead_abovedot (0x0307) + case 0xfe57: return 0x00a8; // dead_diaeresis (0x0308) + case 0x1efe: return 0x0309; // combining_hook + case 0xfe61: return 0x0309; // dead_hook + case 0xfe58: return 0x02da; // dead_abovering (0x030a) + case 0xfe59: return 0x02dd; // dead_doubleacute (0x030b) + case 0xfe5a: return 0x02c7; // dead_caron (0x030c) + case 0xfe62: return 0x031b; // dead_horn + case 0x1eff: return 0x0323; // combining_belowdot + case 0xfe60: return 0x0323; // dead_belowdot + case 0xfe5b: return 0x00b8; // dead_cedilla (0x0327) + case 0xfe5c: return 0x02db; // dead_ogonek (0x0328) + case 0xfe5d: return 0x037a; // dead_iota (0x0345) + + case 0x07ae: return 0x0385; // Greek_accentdieresis + case 0x07a1: return 0x0386; // Greek_ALPHAaccent + case 0x07a2: return 0x0388; // Greek_EPSILONaccent + case 0x07a3: return 0x0389; // Greek_ETAaccent + case 0x07a4: return 0x038a; // Greek_IOTAaccent + case 0x07a7: return 0x038c; // Greek_OMICRONaccent + case 0x07a8: return 0x038e; // Greek_UPSILONaccent + case 0x07ab: return 0x038f; // Greek_OMEGAaccent + case 0x07b6: return 0x0390; // Greek_iotaaccentdieresis + case 0x07c1: return 0x0391; // Greek_ALPHA + case 0x07c2: return 0x0392; // Greek_BETA + case 0x07c3: return 0x0393; // Greek_GAMMA + case 0x07c4: return 0x0394; // Greek_DELTA + case 0x07c5: return 0x0395; // Greek_EPSILON + case 0x07c6: return 0x0396; // Greek_ZETA + case 0x07c7: return 0x0397; // Greek_ETA + case 0x07c8: return 0x0398; // Greek_THETA + case 0x07c9: return 0x0399; // Greek_IOTA + case 0x07ca: return 0x039a; // Greek_KAPPA + case 0x07cb: return 0x039b; // Greek_LAMDA + case 0x07cc: return 0x039c; // Greek_MU + case 0x07cd: return 0x039d; // Greek_NU + case 0x07ce: return 0x039e; // Greek_XI + case 0x07cf: return 0x039f; // Greek_OMICRON + case 0x07d0: return 0x03a0; // Greek_PI + case 0x07d1: return 0x03a1; // Greek_RHO + case 0x07d2: return 0x03a3; // Greek_SIGMA + case 0x07d4: return 0x03a4; // Greek_TAU + case 0x07d5: return 0x03a5; // Greek_UPSILON + case 0x07d6: return 0x03a6; // Greek_PHI + case 0x07d7: return 0x03a7; // Greek_CHI + case 0x07d8: return 0x03a8; // Greek_PSI + case 0x07d9: return 0x03a9; // Greek_OMEGA + case 0x07a5: return 0x03aa; // Greek_IOTAdiaeresis + case 0x07a9: return 0x03ab; // Greek_UPSILONdieresis + case 0x07b1: return 0x03ac; // Greek_alphaaccent + case 0x07b2: return 0x03ad; // Greek_epsilonaccent + case 0x07b3: return 0x03ae; // Greek_etaaccent + case 0x07b4: return 0x03af; // Greek_iotaaccent + case 0x07ba: return 0x03b0; // Greek_upsilonaccentdieresis + case 0x07e1: return 0x03b1; // Greek_alpha + case 0x07e2: return 0x03b2; // Greek_beta + case 0x07e3: return 0x03b3; // Greek_gamma + case 0x07e4: return 0x03b4; // Greek_delta + case 0x07e5: return 0x03b5; // Greek_epsilon + case 0x07e6: return 0x03b6; // Greek_zeta + case 0x07e7: return 0x03b7; // Greek_eta + case 0x07e8: return 0x03b8; // Greek_theta + case 0x07e9: return 0x03b9; // Greek_iota + case 0x07ea: return 0x03ba; // Greek_kappa + case 0x07eb: return 0x03bb; // Greek_lambda + case 0x07ec: return 0x03bc; // Greek_mu + case 0x07ed: return 0x03bd; // Greek_nu + case 0x07ee: return 0x03be; // Greek_xi + case 0x07ef: return 0x03bf; // Greek_omicron + case 0x07f0: return 0x03c0; // Greek_pi + case 0x07f1: return 0x03c1; // Greek_rho + case 0x07f3: return 0x03c2; // Greek_finalsmallsigma + case 0x07f2: return 0x03c3; // Greek_sigma + case 0x07f4: return 0x03c4; // Greek_tau + case 0x07f5: return 0x03c5; // Greek_upsilon + case 0x07f6: return 0x03c6; // Greek_phi + case 0x07f7: return 0x03c7; // Greek_chi + case 0x07f8: return 0x03c8; // Greek_psi + case 0x07f9: return 0x03c9; // Greek_omega + case 0x07b5: return 0x03ca; // Greek_iotadieresis + case 0x07b9: return 0x03cb; // Greek_upsilondieresis + case 0x07b7: return 0x03cc; // Greek_omicronaccent + case 0x07b8: return 0x03cd; // Greek_upsilonaccent + case 0x07bb: return 0x03ce; // Greek_omegaaccent + case 0x06b3: return 0x0401; // Cyrillic_IO + case 0x06b1: return 0x0402; // Serbian_DJE + case 0x06b2: return 0x0403; // Macedonia_GJE + case 0x06b4: return 0x0404; // Ukrainian_IE + case 0x06b5: return 0x0405; // Macedonia_DSE + case 0x06b6: return 0x0406; // Ukrainian_I + case 0x06b7: return 0x0407; // Ukrainian_YI + case 0x06b8: return 0x0408; // Cyrillic_JE + case 0x06b9: return 0x0409; // Cyrillic_LJE + case 0x06ba: return 0x040a; // Cyrillic_NJE + case 0x06bb: return 0x040b; // Serbian_TSHE + case 0x06bc: return 0x040c; // Macedonia_KJE + case 0x06be: return 0x040e; // Byelorussian_SHORTU + case 0x06bf: return 0x040f; // Cyrillic_DZHE + case 0x06e1: return 0x0410; // Cyrillic_A + case 0x06e2: return 0x0411; // Cyrillic_BE + case 0x06f7: return 0x0412; // Cyrillic_VE + case 0x06e7: return 0x0413; // Cyrillic_GHE + case 0x06e4: return 0x0414; // Cyrillic_DE + case 0x06e5: return 0x0415; // Cyrillic_IE + case 0x06f6: return 0x0416; // Cyrillic_ZHE + case 0x06fa: return 0x0417; // Cyrillic_ZE + case 0x06e9: return 0x0418; // Cyrillic_I + case 0x06ea: return 0x0419; // Cyrillic_SHORTI + case 0x06eb: return 0x041a; // Cyrillic_KA + case 0x06ec: return 0x041b; // Cyrillic_EL + case 0x06ed: return 0x041c; // Cyrillic_EM + case 0x06ee: return 0x041d; // Cyrillic_EN + case 0x06ef: return 0x041e; // Cyrillic_O + case 0x06f0: return 0x041f; // Cyrillic_PE + case 0x06f2: return 0x0420; // Cyrillic_ER + case 0x06f3: return 0x0421; // Cyrillic_ES + case 0x06f4: return 0x0422; // Cyrillic_TE + case 0x06f5: return 0x0423; // Cyrillic_U + case 0x06e6: return 0x0424; // Cyrillic_EF + case 0x06e8: return 0x0425; // Cyrillic_HA + case 0x06e3: return 0x0426; // Cyrillic_TSE + case 0x06fe: return 0x0427; // Cyrillic_CHE + case 0x06fb: return 0x0428; // Cyrillic_SHA + case 0x06fd: return 0x0429; // Cyrillic_SHCHA + case 0x06ff: return 0x042a; // Cyrillic_HARDSIGN + case 0x06f9: return 0x042b; // Cyrillic_YERU + case 0x06f8: return 0x042c; // Cyrillic_SOFTSIGN + case 0x06fc: return 0x042d; // Cyrillic_E + case 0x06e0: return 0x042e; // Cyrillic_YU + case 0x06f1: return 0x042f; // Cyrillic_YA + case 0x06c1: return 0x0430; // Cyrillic_a + case 0x06c2: return 0x0431; // Cyrillic_be + case 0x06d7: return 0x0432; // Cyrillic_ve + case 0x06c7: return 0x0433; // Cyrillic_ghe + case 0x06c4: return 0x0434; // Cyrillic_de + case 0x06c5: return 0x0435; // Cyrillic_ie + case 0x06d6: return 0x0436; // Cyrillic_zhe + case 0x06da: return 0x0437; // Cyrillic_ze + case 0x06c9: return 0x0438; // Cyrillic_i + case 0x06ca: return 0x0439; // Cyrillic_shorti + case 0x06cb: return 0x043a; // Cyrillic_ka + case 0x06cc: return 0x043b; // Cyrillic_el + case 0x06cd: return 0x043c; // Cyrillic_em + case 0x06ce: return 0x043d; // Cyrillic_en + case 0x06cf: return 0x043e; // Cyrillic_o + case 0x06d0: return 0x043f; // Cyrillic_pe + case 0x06d2: return 0x0440; // Cyrillic_er + case 0x06d3: return 0x0441; // Cyrillic_es + case 0x06d4: return 0x0442; // Cyrillic_te + case 0x06d5: return 0x0443; // Cyrillic_u + case 0x06c6: return 0x0444; // Cyrillic_ef + case 0x06c8: return 0x0445; // Cyrillic_ha + case 0x06c3: return 0x0446; // Cyrillic_tse + case 0x06de: return 0x0447; // Cyrillic_che + case 0x06db: return 0x0448; // Cyrillic_sha + case 0x06dd: return 0x0449; // Cyrillic_shcha + case 0x06df: return 0x044a; // Cyrillic_hardsign + case 0x06d9: return 0x044b; // Cyrillic_yeru + case 0x06d8: return 0x044c; // Cyrillic_softsign + case 0x06dc: return 0x044d; // Cyrillic_e + case 0x06c0: return 0x044e; // Cyrillic_yu + case 0x06d1: return 0x044f; // Cyrillic_ya + case 0x06a3: return 0x0451; // Cyrillic_io + case 0x06a1: return 0x0452; // Serbian_dje + case 0x06a2: return 0x0453; // Macedonia_gje + case 0x06a4: return 0x0454; // Ukrainian_ie + case 0x06a5: return 0x0455; // Macedonia_dse + case 0x06a6: return 0x0456; // Ukrainian_i + case 0x06a7: return 0x0457; // Ukrainian_yi + case 0x06a8: return 0x0458; // Cyrillic_je + case 0x06a9: return 0x0459; // Cyrillic_lje + case 0x06aa: return 0x045a; // Cyrillic_nje + case 0x06ab: return 0x045b; // Serbian_tshe + case 0x06ac: return 0x045c; // Macedonia_kje + case 0x06ae: return 0x045e; // Byelorussian_shortu + case 0x06af: return 0x045f; // Cyrillic_dzhe + case 0x06bd: return 0x0490; // Ukrainian_GHE_WITH_UPTURN + case 0x06ad: return 0x0491; // Ukrainian_ghe_with_upturn + case 0x0680: return 0x0492; // Cyrillic_GHE_bar + case 0x0690: return 0x0493; // Cyrillic_ghe_bar + case 0x0681: return 0x0496; // Cyrillic_ZHE_descender + case 0x0691: return 0x0497; // Cyrillic_zhe_descender + case 0x0682: return 0x049a; // Cyrillic_KA_descender + case 0x0692: return 0x049b; // Cyrillic_ka_descender + case 0x0683: return 0x049c; // Cyrillic_KA_vertstroke + case 0x0693: return 0x049d; // Cyrillic_ka_vertstroke + case 0x0684: return 0x04a2; // Cyrillic_EN_descender + case 0x0694: return 0x04a3; // Cyrillic_en_descender + case 0x0685: return 0x04ae; // Cyrillic_U_straight + case 0x0695: return 0x04af; // Cyrillic_u_straight + case 0x0686: return 0x04b0; // Cyrillic_U_straight_bar + case 0x0696: return 0x04b1; // Cyrillic_u_straight_bar + case 0x0687: return 0x04b2; // Cyrillic_HA_descender + case 0x0697: return 0x04b3; // Cyrillic_ha_descender + case 0x0688: return 0x04b6; // Cyrillic_CHE_descender + case 0x0698: return 0x04b7; // Cyrillic_che_descender + case 0x0689: return 0x04b8; // Cyrillic_CHE_vertstroke + case 0x0699: return 0x04b9; // Cyrillic_che_vertstroke + case 0x068a: return 0x04ba; // Cyrillic_SHHA + case 0x069a: return 0x04bb; // Cyrillic_shha + case 0x068c: return 0x04d8; // Cyrillic_SCHWA + case 0x069c: return 0x04d9; // Cyrillic_schwa + case 0x068d: return 0x04e2; // Cyrillic_I_macron + case 0x069d: return 0x04e3; // Cyrillic_i_macron + case 0x068e: return 0x04e8; // Cyrillic_O_bar + case 0x069e: return 0x04e9; // Cyrillic_o_bar + case 0x068f: return 0x04ee; // Cyrillic_U_macron + case 0x069f: return 0x04ef; // Cyrillic_u_macron + case 0x14b2: return 0x0531; // Armenian_AYB + case 0x14b4: return 0x0532; // Armenian_BEN + case 0x14b6: return 0x0533; // Armenian_GIM + case 0x14b8: return 0x0534; // Armenian_DA + case 0x14ba: return 0x0535; // Armenian_YECH + case 0x14bc: return 0x0536; // Armenian_ZA + case 0x14be: return 0x0537; // Armenian_E + case 0x14c0: return 0x0538; // Armenian_AT + case 0x14c2: return 0x0539; // Armenian_TO + case 0x14c4: return 0x053a; // Armenian_ZHE + case 0x14c6: return 0x053b; // Armenian_INI + case 0x14c8: return 0x053c; // Armenian_LYUN + case 0x14ca: return 0x053d; // Armenian_KHE + case 0x14cc: return 0x053e; // Armenian_TSA + case 0x14ce: return 0x053f; // Armenian_KEN + case 0x14d0: return 0x0540; // Armenian_HO + case 0x14d2: return 0x0541; // Armenian_DZA + case 0x14d4: return 0x0542; // Armenian_GHAT + case 0x14d6: return 0x0543; // Armenian_TCHE + case 0x14d8: return 0x0544; // Armenian_MEN + case 0x14da: return 0x0545; // Armenian_HI + case 0x14dc: return 0x0546; // Armenian_NU + case 0x14de: return 0x0547; // Armenian_SHA + case 0x14e0: return 0x0548; // Armenian_VO + case 0x14e2: return 0x0549; // Armenian_CHA + case 0x14e4: return 0x054a; // Armenian_PE + case 0x14e6: return 0x054b; // Armenian_JE + case 0x14e8: return 0x054c; // Armenian_RA + case 0x14ea: return 0x054d; // Armenian_SE + case 0x14ec: return 0x054e; // Armenian_VEV + case 0x14ee: return 0x054f; // Armenian_TYUN + case 0x14f0: return 0x0550; // Armenian_RE + case 0x14f2: return 0x0551; // Armenian_TSO + case 0x14f4: return 0x0552; // Armenian_VYUN + case 0x14f6: return 0x0553; // Armenian_PYUR + case 0x14f8: return 0x0554; // Armenian_KE + case 0x14fa: return 0x0555; // Armenian_O + case 0x14fc: return 0x0556; // Armenian_FE + case 0x14fe: return 0x055a; // Armenian_apostrophe + case 0x14b0: return 0x055b; // Armenian_shesht + case 0x14af: return 0x055c; // Armenian_amanak + case 0x14aa: return 0x055d; // Armenian_but + case 0x14b1: return 0x055e; // Armenian_paruyk + case 0x14b3: return 0x0561; // Armenian_ayb + case 0x14b5: return 0x0562; // Armenian_ben + case 0x14b7: return 0x0563; // Armenian_gim + case 0x14b9: return 0x0564; // Armenian_da + case 0x14bb: return 0x0565; // Armenian_yech + case 0x14bd: return 0x0566; // Armenian_za + case 0x14bf: return 0x0567; // Armenian_e + case 0x14c1: return 0x0568; // Armenian_at + case 0x14c3: return 0x0569; // Armenian_to + case 0x14c5: return 0x056a; // Armenian_zhe + case 0x14c7: return 0x056b; // Armenian_ini + case 0x14c9: return 0x056c; // Armenian_lyun + case 0x14cb: return 0x056d; // Armenian_khe + case 0x14cd: return 0x056e; // Armenian_tsa + case 0x14cf: return 0x056f; // Armenian_ken + case 0x14d1: return 0x0570; // Armenian_ho + case 0x14d3: return 0x0571; // Armenian_dza + case 0x14d5: return 0x0572; // Armenian_ghat + case 0x14d7: return 0x0573; // Armenian_tche + case 0x14d9: return 0x0574; // Armenian_men + case 0x14db: return 0x0575; // Armenian_hi + case 0x14dd: return 0x0576; // Armenian_nu + case 0x14df: return 0x0577; // Armenian_sha + case 0x14e1: return 0x0578; // Armenian_vo + case 0x14e3: return 0x0579; // Armenian_cha + case 0x14e5: return 0x057a; // Armenian_pe + case 0x14e7: return 0x057b; // Armenian_je + case 0x14e9: return 0x057c; // Armenian_ra + case 0x14eb: return 0x057d; // Armenian_se + case 0x14ed: return 0x057e; // Armenian_vev + case 0x14ef: return 0x057f; // Armenian_tyun + case 0x14f1: return 0x0580; // Armenian_re + case 0x14f3: return 0x0581; // Armenian_tso + case 0x14f5: return 0x0582; // Armenian_vyun + case 0x14f7: return 0x0583; // Armenian_pyur + case 0x14f9: return 0x0584; // Armenian_ke + case 0x14fb: return 0x0585; // Armenian_o + case 0x14fd: return 0x0586; // Armenian_fe + case 0x14a2: return 0x0587; // Armenian_ligature_ew + case 0x14a3: return 0x0589; // Armenian_verjaket + case 0x14ad: return 0x058a; // Armenian_yentamna + case 0x0ce0: return 0x05d0; // hebrew_aleph + case 0x0ce1: return 0x05d1; // hebrew_beth + case 0x0ce2: return 0x05d2; // hebrew_gimmel + case 0x0ce3: return 0x05d3; // hebrew_daleth + case 0x0ce4: return 0x05d4; // hebrew_he + case 0x0ce5: return 0x05d5; // hebrew_waw + case 0x0ce6: return 0x05d6; // hebrew_zayin + case 0x0ce7: return 0x05d7; // hebrew_het + case 0x0ce8: return 0x05d8; // hebrew_teth + case 0x0ce9: return 0x05d9; // hebrew_yod + case 0x0cea: return 0x05da; // hebrew_finalkaph + case 0x0ceb: return 0x05db; // hebrew_kaph + case 0x0cec: return 0x05dc; // hebrew_lamed + case 0x0ced: return 0x05dd; // hebrew_finalmem + case 0x0cee: return 0x05de; // hebrew_mem + case 0x0cef: return 0x05df; // hebrew_finalnun + case 0x0cf0: return 0x05e0; // hebrew_nun + case 0x0cf1: return 0x05e1; // hebrew_samekh + case 0x0cf2: return 0x05e2; // hebrew_ayin + case 0x0cf3: return 0x05e3; // hebrew_finalpe + case 0x0cf4: return 0x05e4; // hebrew_pe + case 0x0cf5: return 0x05e5; // hebrew_finalzadi + case 0x0cf6: return 0x05e6; // hebrew_zadi + case 0x0cf7: return 0x05e7; // hebrew_qoph + case 0x0cf8: return 0x05e8; // hebrew_resh + case 0x0cf9: return 0x05e9; // hebrew_shin + case 0x0cfa: return 0x05ea; // hebrew_taw + case 0x05ac: return 0x060c; // Arabic_comma + case 0x05bb: return 0x061b; // Arabic_semicolon + case 0x05bf: return 0x061f; // Arabic_question_mark + case 0x05c1: return 0x0621; // Arabic_hamza + case 0x05c2: return 0x0622; // Arabic_maddaonalef + case 0x05c3: return 0x0623; // Arabic_hamzaonalef + case 0x05c4: return 0x0624; // Arabic_hamzaonwaw + case 0x05c5: return 0x0625; // Arabic_hamzaunderalef + case 0x05c6: return 0x0626; // Arabic_hamzaonyeh + case 0x05c7: return 0x0627; // Arabic_alef + case 0x05c8: return 0x0628; // Arabic_beh + case 0x05c9: return 0x0629; // Arabic_tehmarbuta + case 0x05ca: return 0x062a; // Arabic_teh + case 0x05cb: return 0x062b; // Arabic_theh + case 0x05cc: return 0x062c; // Arabic_jeem + case 0x05cd: return 0x062d; // Arabic_hah + case 0x05ce: return 0x062e; // Arabic_khah + case 0x05cf: return 0x062f; // Arabic_dal + case 0x05d0: return 0x0630; // Arabic_thal + case 0x05d1: return 0x0631; // Arabic_ra + case 0x05d2: return 0x0632; // Arabic_zain + case 0x05d3: return 0x0633; // Arabic_seen + case 0x05d4: return 0x0634; // Arabic_sheen + case 0x05d5: return 0x0635; // Arabic_sad + case 0x05d6: return 0x0636; // Arabic_dad + case 0x05d7: return 0x0637; // Arabic_tah + case 0x05d8: return 0x0638; // Arabic_zah + case 0x05d9: return 0x0639; // Arabic_ain + case 0x05da: return 0x063a; // Arabic_ghain + case 0x05e0: return 0x0640; // Arabic_tatweel + case 0x05e1: return 0x0641; // Arabic_feh + case 0x05e2: return 0x0642; // Arabic_qaf + case 0x05e3: return 0x0643; // Arabic_kaf + case 0x05e4: return 0x0644; // Arabic_lam + case 0x05e5: return 0x0645; // Arabic_meem + case 0x05e6: return 0x0646; // Arabic_noon + case 0x05e7: return 0x0647; // Arabic_ha + case 0x05e8: return 0x0648; // Arabic_waw + case 0x05e9: return 0x0649; // Arabic_alefmaksura + case 0x05ea: return 0x064a; // Arabic_yeh + case 0x05eb: return 0x064b; // Arabic_fathatan + case 0x05ec: return 0x064c; // Arabic_dammatan + case 0x05ed: return 0x064d; // Arabic_kasratan + case 0x05ee: return 0x064e; // Arabic_fatha + case 0x05ef: return 0x064f; // Arabic_damma + case 0x05f0: return 0x0650; // Arabic_kasra + case 0x05f1: return 0x0651; // Arabic_shadda + case 0x05f2: return 0x0652; // Arabic_sukun + case 0x05f3: return 0x0653; // Arabic_madda_above + case 0x05f4: return 0x0654; // Arabic_hamza_above + case 0x05f5: return 0x0655; // Arabic_hamza_below + case 0x05b0: return 0x0660; // Arabic_0 + case 0x05b1: return 0x0661; // Arabic_1 + case 0x05b2: return 0x0662; // Arabic_2 + case 0x05b3: return 0x0663; // Arabic_3 + case 0x05b4: return 0x0664; // Arabic_4 + case 0x05b5: return 0x0665; // Arabic_5 + case 0x05b6: return 0x0666; // Arabic_6 + case 0x05b7: return 0x0667; // Arabic_7 + case 0x05b8: return 0x0668; // Arabic_8 + case 0x05b9: return 0x0669; // Arabic_9 + case 0x05a5: return 0x066a; // Arabic_percent + case 0x05a6: return 0x0670; // Arabic_superscript_alef + case 0x05a7: return 0x0679; // Arabic_tteh + case 0x05a8: return 0x067e; // Arabic_peh + case 0x05a9: return 0x0686; // Arabic_tcheh + case 0x05aa: return 0x0688; // Arabic_ddal + case 0x05ab: return 0x0691; // Arabic_rreh + case 0x05f6: return 0x0698; // Arabic_jeh + case 0x05f7: return 0x06a4; // Arabic_veh + case 0x05f8: return 0x06a9; // Arabic_keheh + case 0x05f9: return 0x06af; // Arabic_gaf + case 0x05fa: return 0x06ba; // Arabic_noon_ghunna + case 0x05fb: return 0x06be; // Arabic_heh_doachashmee + case 0x05fe: return 0x06c1; // Arabic_heh_goal + case 0x05fc: return 0x06cc; // Farsi_yeh + case 0x05fd: return 0x06d2; // Arabic_yeh_baree + case 0x05ae: return 0x06d4; // Arabic_fullstop + case 0x0590: return 0x06f0; // Farsi_0 + case 0x0591: return 0x06f1; // Farsi_1 + case 0x0592: return 0x06f2; // Farsi_2 + case 0x0593: return 0x06f3; // Farsi_3 + case 0x0594: return 0x06f4; // Farsi_4 + case 0x0595: return 0x06f5; // Farsi_5 + case 0x0596: return 0x06f6; // Farsi_6 + case 0x0597: return 0x06f7; // Farsi_7 + case 0x0598: return 0x06f8; // Farsi_8 + case 0x0599: return 0x06f9; // Farsi_9 + case 0x0da1: return 0x0e01; // Thai_kokai + case 0x0da2: return 0x0e02; // Thai_khokhai + case 0x0da3: return 0x0e03; // Thai_khokhuat + case 0x0da4: return 0x0e04; // Thai_khokhwai + case 0x0da5: return 0x0e05; // Thai_khokhon + case 0x0da6: return 0x0e06; // Thai_khorakhang + case 0x0da7: return 0x0e07; // Thai_ngongu + case 0x0da8: return 0x0e08; // Thai_chochan + case 0x0da9: return 0x0e09; // Thai_choching + case 0x0daa: return 0x0e0a; // Thai_chochang + case 0x0dab: return 0x0e0b; // Thai_soso + case 0x0dac: return 0x0e0c; // Thai_chochoe + case 0x0dad: return 0x0e0d; // Thai_yoying + case 0x0dae: return 0x0e0e; // Thai_dochada + case 0x0daf: return 0x0e0f; // Thai_topatak + case 0x0db0: return 0x0e10; // Thai_thothan + case 0x0db1: return 0x0e11; // Thai_thonangmontho + case 0x0db2: return 0x0e12; // Thai_thophuthao + case 0x0db3: return 0x0e13; // Thai_nonen + case 0x0db4: return 0x0e14; // Thai_dodek + case 0x0db5: return 0x0e15; // Thai_totao + case 0x0db6: return 0x0e16; // Thai_thothung + case 0x0db7: return 0x0e17; // Thai_thothahan + case 0x0db8: return 0x0e18; // Thai_thothong + case 0x0db9: return 0x0e19; // Thai_nonu + case 0x0dba: return 0x0e1a; // Thai_bobaimai + case 0x0dbb: return 0x0e1b; // Thai_popla + case 0x0dbc: return 0x0e1c; // Thai_phophung + case 0x0dbd: return 0x0e1d; // Thai_fofa + case 0x0dbe: return 0x0e1e; // Thai_phophan + case 0x0dbf: return 0x0e1f; // Thai_fofan + case 0x0dc0: return 0x0e20; // Thai_phosamphao + case 0x0dc1: return 0x0e21; // Thai_moma + case 0x0dc2: return 0x0e22; // Thai_yoyak + case 0x0dc3: return 0x0e23; // Thai_rorua + case 0x0dc4: return 0x0e24; // Thai_ru + case 0x0dc5: return 0x0e25; // Thai_loling + case 0x0dc6: return 0x0e26; // Thai_lu + case 0x0dc7: return 0x0e27; // Thai_wowaen + case 0x0dc8: return 0x0e28; // Thai_sosala + case 0x0dc9: return 0x0e29; // Thai_sorusi + case 0x0dca: return 0x0e2a; // Thai_sosua + case 0x0dcb: return 0x0e2b; // Thai_hohip + case 0x0dcc: return 0x0e2c; // Thai_lochula + case 0x0dcd: return 0x0e2d; // Thai_oang + case 0x0dce: return 0x0e2e; // Thai_honokhuk + case 0x0dcf: return 0x0e2f; // Thai_paiyannoi + case 0x0dd0: return 0x0e30; // Thai_saraa + case 0x0dd1: return 0x0e31; // Thai_maihanakat + case 0x0dd2: return 0x0e32; // Thai_saraaa + case 0x0dd3: return 0x0e33; // Thai_saraam + case 0x0dd4: return 0x0e34; // Thai_sarai + case 0x0dd5: return 0x0e35; // Thai_saraii + case 0x0dd6: return 0x0e36; // Thai_saraue + case 0x0dd7: return 0x0e37; // Thai_sarauee + case 0x0dd8: return 0x0e38; // Thai_sarau + case 0x0dd9: return 0x0e39; // Thai_sarauu + case 0x0dda: return 0x0e3a; // Thai_phinthu + case 0x0ddf: return 0x0e3f; // Thai_baht + case 0x0de0: return 0x0e40; // Thai_sarae + case 0x0de1: return 0x0e41; // Thai_saraae + case 0x0de2: return 0x0e42; // Thai_sarao + case 0x0de3: return 0x0e43; // Thai_saraaimaimuan + case 0x0de4: return 0x0e44; // Thai_saraaimaimalai + case 0x0de5: return 0x0e45; // Thai_lakkhangyao + case 0x0de6: return 0x0e46; // Thai_maiyamok + case 0x0de7: return 0x0e47; // Thai_maitaikhu + case 0x0de8: return 0x0e48; // Thai_maiek + case 0x0de9: return 0x0e49; // Thai_maitho + case 0x0dea: return 0x0e4a; // Thai_maitri + case 0x0deb: return 0x0e4b; // Thai_maichattawa + case 0x0dec: return 0x0e4c; // Thai_thanthakhat + case 0x0ded: return 0x0e4d; // Thai_nikhahit + case 0x0df0: return 0x0e50; // Thai_leksun + case 0x0df1: return 0x0e51; // Thai_leknung + case 0x0df2: return 0x0e52; // Thai_leksong + case 0x0df3: return 0x0e53; // Thai_leksam + case 0x0df4: return 0x0e54; // Thai_leksi + case 0x0df5: return 0x0e55; // Thai_lekha + case 0x0df6: return 0x0e56; // Thai_lekhok + case 0x0df7: return 0x0e57; // Thai_lekchet + case 0x0df8: return 0x0e58; // Thai_lekpaet + case 0x0df9: return 0x0e59; // Thai_lekkao + case 0x15d0: return 0x10d0; // Georgian_an + case 0x15d1: return 0x10d1; // Georgian_ban + case 0x15d2: return 0x10d2; // Georgian_gan + case 0x15d3: return 0x10d3; // Georgian_don + case 0x15d4: return 0x10d4; // Georgian_en + case 0x15d5: return 0x10d5; // Georgian_vin + case 0x15d6: return 0x10d6; // Georgian_zen + case 0x15d7: return 0x10d7; // Georgian_tan + case 0x15d8: return 0x10d8; // Georgian_in + case 0x15d9: return 0x10d9; // Georgian_kan + case 0x15da: return 0x10da; // Georgian_las + case 0x15db: return 0x10db; // Georgian_man + case 0x15dc: return 0x10dc; // Georgian_nar + case 0x15dd: return 0x10dd; // Georgian_on + case 0x15de: return 0x10de; // Georgian_par + case 0x15df: return 0x10df; // Georgian_zhar + case 0x15e0: return 0x10e0; // Georgian_rae + case 0x15e1: return 0x10e1; // Georgian_san + case 0x15e2: return 0x10e2; // Georgian_tar + case 0x15e3: return 0x10e3; // Georgian_un + case 0x15e4: return 0x10e4; // Georgian_phar + case 0x15e5: return 0x10e5; // Georgian_khar + case 0x15e6: return 0x10e6; // Georgian_ghan + case 0x15e7: return 0x10e7; // Georgian_qar + case 0x15e8: return 0x10e8; // Georgian_shin + case 0x15e9: return 0x10e9; // Georgian_chin + case 0x15ea: return 0x10ea; // Georgian_can + case 0x15eb: return 0x10eb; // Georgian_jil + case 0x15ec: return 0x10ec; // Georgian_cil + case 0x15ed: return 0x10ed; // Georgian_char + case 0x15ee: return 0x10ee; // Georgian_xan + case 0x15ef: return 0x10ef; // Georgian_jhan + case 0x15f0: return 0x10f0; // Georgian_hae + case 0x15f1: return 0x10f1; // Georgian_he + case 0x15f2: return 0x10f2; // Georgian_hie + case 0x15f3: return 0x10f3; // Georgian_we + case 0x15f4: return 0x10f4; // Georgian_har + case 0x15f5: return 0x10f5; // Georgian_hoe + case 0x15f6: return 0x10f6; // Georgian_fi + case 0x0ed4: return 0x11a8; // Hangul_J_Kiyeog + case 0x0ed5: return 0x11a9; // Hangul_J_SsangKiyeog + case 0x0ed6: return 0x11aa; // Hangul_J_KiyeogSios + case 0x0ed7: return 0x11ab; // Hangul_J_Nieun + case 0x0ed8: return 0x11ac; // Hangul_J_NieunJieuj + case 0x0ed9: return 0x11ad; // Hangul_J_NieunHieuh + case 0x0eda: return 0x11ae; // Hangul_J_Dikeud + case 0x0edb: return 0x11af; // Hangul_J_Rieul + case 0x0edc: return 0x11b0; // Hangul_J_RieulKiyeog + case 0x0edd: return 0x11b1; // Hangul_J_RieulMieum + case 0x0ede: return 0x11b2; // Hangul_J_RieulPieub + case 0x0edf: return 0x11b3; // Hangul_J_RieulSios + case 0x0ee0: return 0x11b4; // Hangul_J_RieulTieut + case 0x0ee1: return 0x11b5; // Hangul_J_RieulPhieuf + case 0x0ee2: return 0x11b6; // Hangul_J_RieulHieuh + case 0x0ee3: return 0x11b7; // Hangul_J_Mieum + case 0x0ee4: return 0x11b8; // Hangul_J_Pieub + case 0x0ee5: return 0x11b9; // Hangul_J_PieubSios + case 0x0ee6: return 0x11ba; // Hangul_J_Sios + case 0x0ee7: return 0x11bb; // Hangul_J_SsangSios + case 0x0ee8: return 0x11bc; // Hangul_J_Ieung + case 0x0ee9: return 0x11bd; // Hangul_J_Jieuj + case 0x0eea: return 0x11be; // Hangul_J_Cieuc + case 0x0eeb: return 0x11bf; // Hangul_J_Khieuq + case 0x0eec: return 0x11c0; // Hangul_J_Tieut + case 0x0eed: return 0x11c1; // Hangul_J_Phieuf + case 0x0eee: return 0x11c2; // Hangul_J_Hieuh + case 0x0ef8: return 0x11eb; // Hangul_J_PanSios + case 0x0ef9: return 0x11f0; // Hangul_J_KkogjiDalrinIeung + case 0x0efa: return 0x11f9; // Hangul_J_YeorinHieuh + case 0x12a1: return 0x1e02; // Babovedot + case 0x12a2: return 0x1e03; // babovedot + case 0x12a6: return 0x1e0a; // Dabovedot + case 0x12ab: return 0x1e0b; // dabovedot + case 0x12b0: return 0x1e1e; // Fabovedot + case 0x12b1: return 0x1e1f; // fabovedot + case 0x16d1: return 0x1e36; // Lbelowdot + case 0x16e1: return 0x1e37; // lbelowdot + case 0x12b4: return 0x1e40; // Mabovedot + case 0x12b5: return 0x1e41; // mabovedot + case 0x12b7: return 0x1e56; // Pabovedot + case 0x12b9: return 0x1e57; // pabovedot + case 0x12bb: return 0x1e60; // Sabovedot + case 0x12bf: return 0x1e61; // sabovedot + case 0x12d7: return 0x1e6a; // Tabovedot + case 0x12f7: return 0x1e6b; // tabovedot + case 0x12a8: return 0x1e80; // Wgrave + case 0x12b8: return 0x1e81; // wgrave + case 0x12aa: return 0x1e82; // Wacute + case 0x12ba: return 0x1e83; // wacute + case 0x12bd: return 0x1e84; // Wdiaeresis + case 0x12be: return 0x1e85; // wdiaeresis + case 0x16a3: return 0x1e8a; // Xabovedot + case 0x16b3: return 0x1e8b; // xabovedot + case 0x1ea0: return 0x1ea0; // Abelowdot + case 0x1ea1: return 0x1ea1; // abelowdot + case 0x1ea2: return 0x1ea2; // Ahook + case 0x1ea3: return 0x1ea3; // ahook + case 0x1ea4: return 0x1ea4; // Acircumflexacute + case 0x1ea5: return 0x1ea5; // acircumflexacute + case 0x1ea6: return 0x1ea6; // Acircumflexgrave + case 0x1ea7: return 0x1ea7; // acircumflexgrave + case 0x1ea8: return 0x1ea8; // Acircumflexhook + case 0x1ea9: return 0x1ea9; // acircumflexhook + case 0x1eaa: return 0x1eaa; // Acircumflextilde + case 0x1eab: return 0x1eab; // acircumflextilde + case 0x1eac: return 0x1eac; // Acircumflexbelowdot + case 0x1ead: return 0x1ead; // acircumflexbelowdot + case 0x1eae: return 0x1eae; // Abreveacute + case 0x1eaf: return 0x1eaf; // abreveacute + case 0x1eb0: return 0x1eb0; // Abrevegrave + case 0x1eb1: return 0x1eb1; // abrevegrave + case 0x1eb2: return 0x1eb2; // Abrevehook + case 0x1eb3: return 0x1eb3; // abrevehook + case 0x1eb4: return 0x1eb4; // Abrevetilde + case 0x1eb5: return 0x1eb5; // abrevetilde + case 0x1eb6: return 0x1eb6; // Abrevebelowdot + case 0x1eb7: return 0x1eb7; // abrevebelowdot + case 0x1eb8: return 0x1eb8; // Ebelowdot + case 0x1eb9: return 0x1eb9; // ebelowdot + case 0x1eba: return 0x1eba; // Ehook + case 0x1ebb: return 0x1ebb; // ehook + case 0x1ebc: return 0x1ebc; // Etilde + case 0x1ebd: return 0x1ebd; // etilde + case 0x1ebe: return 0x1ebe; // Ecircumflexacute + case 0x1ebf: return 0x1ebf; // ecircumflexacute + case 0x1ec0: return 0x1ec0; // Ecircumflexgrave + case 0x1ec1: return 0x1ec1; // ecircumflexgrave + case 0x1ec2: return 0x1ec2; // Ecircumflexhook + case 0x1ec3: return 0x1ec3; // ecircumflexhook + case 0x1ec4: return 0x1ec4; // Ecircumflextilde + case 0x1ec5: return 0x1ec5; // ecircumflextilde + case 0x1ec6: return 0x1ec6; // Ecircumflexbelowdot + case 0x1ec7: return 0x1ec7; // ecircumflexbelowdot + case 0x1ec8: return 0x1ec8; // Ihook + case 0x1ec9: return 0x1ec9; // ihook + case 0x1eca: return 0x1eca; // Ibelowdot + case 0x1ecb: return 0x1ecb; // ibelowdot + case 0x1ecc: return 0x1ecc; // Obelowdot + case 0x1ecd: return 0x1ecd; // obelowdot + case 0x1ece: return 0x1ece; // Ohook + case 0x1ecf: return 0x1ecf; // ohook + case 0x1ed0: return 0x1ed0; // Ocircumflexacute + case 0x1ed1: return 0x1ed1; // ocircumflexacute + case 0x1ed2: return 0x1ed2; // Ocircumflexgrave + case 0x1ed3: return 0x1ed3; // ocircumflexgrave + case 0x1ed4: return 0x1ed4; // Ocircumflexhook + case 0x1ed5: return 0x1ed5; // ocircumflexhook + case 0x1ed6: return 0x1ed6; // Ocircumflextilde + case 0x1ed7: return 0x1ed7; // ocircumflextilde + case 0x1ed8: return 0x1ed8; // Ocircumflexbelowdot + case 0x1ed9: return 0x1ed9; // ocircumflexbelowdot + case 0x1eda: return 0x1eda; // Ohornacute + case 0x1edb: return 0x1edb; // ohornacute + case 0x1edc: return 0x1edc; // Ohorngrave + case 0x1edd: return 0x1edd; // ohorngrave + case 0x1ede: return 0x1ede; // Ohornhook + case 0x1edf: return 0x1edf; // ohornhook + case 0x1ee0: return 0x1ee0; // Ohorntilde + case 0x1ee1: return 0x1ee1; // ohorntilde + case 0x1ee2: return 0x1ee2; // Ohornbelowdot + case 0x1ee3: return 0x1ee3; // ohornbelowdot + case 0x1ee4: return 0x1ee4; // Ubelowdot + case 0x1ee5: return 0x1ee5; // ubelowdot + case 0x1ee6: return 0x1ee6; // Uhook + case 0x1ee7: return 0x1ee7; // uhook + case 0x1ee8: return 0x1ee8; // Uhornacute + case 0x1ee9: return 0x1ee9; // uhornacute + case 0x1eea: return 0x1eea; // Uhorngrave + case 0x1eeb: return 0x1eeb; // uhorngrave + case 0x1eec: return 0x1eec; // Uhornhook + case 0x1eed: return 0x1eed; // uhornhook + case 0x1eee: return 0x1eee; // Uhorntilde + case 0x1eef: return 0x1eef; // uhorntilde + case 0x1ef0: return 0x1ef0; // Uhornbelowdot + case 0x1ef1: return 0x1ef1; // uhornbelowdot + case 0x12ac: return 0x1ef2; // Ygrave + case 0x12bc: return 0x1ef3; // ygrave + case 0x1ef4: return 0x1ef4; // Ybelowdot + case 0x1ef5: return 0x1ef5; // ybelowdot + case 0x1ef6: return 0x1ef6; // Yhook + case 0x1ef7: return 0x1ef7; // yhook + case 0x1ef8: return 0x1ef8; // Ytilde + case 0x1ef9: return 0x1ef9; // ytilde + case 0x0aa2: return 0x2002; // enspace + case 0x0aa1: return 0x2003; // emspace + case 0x0aa3: return 0x2004; // em3space + case 0x0aa4: return 0x2005; // em4space + case 0x0aa5: return 0x2007; // digitspace + case 0x0aa6: return 0x2008; // punctspace + case 0x0aa7: return 0x2009; // thinspace + case 0x0aa8: return 0x200a; // hairspace + case 0x0abb: return 0x2012; // figdash + case 0x14ac: return 0x2013; // Armenian_en_dash + case 0x0aaa: return 0x2013; // endash + case 0x14a8: return 0x2014; // Armenian_em_dash + case 0x0aa9: return 0x2014; // emdash + case 0x07af: return 0x2015; // Greek_horizbar + case 0x0cdf: return 0x2017; // hebrew_doublelowline + case 0x0ad0: return 0x2018; // leftsinglequotemark + case 0x0ad1: return 0x2019; // rightsinglequotemark + case 0x0afd: return 0x201a; // singlelowquotemark + case 0x0ad2: return 0x201c; // leftdoublequotemark + case 0x0ad3: return 0x201d; // rightdoublequotemark + case 0x0afe: return 0x201e; // doublelowquotemark + case 0x0af1: return 0x2020; // dagger + case 0x0af2: return 0x2021; // doubledagger + case 0x0ae6: return 0x2022; // enfilledcircbullet + case 0x0aaf: return 0x2025; // doubbaselinedot + case 0x14ae: return 0x2026; // Armenian_ellipsis + case 0x0aae: return 0x2026; // ellipsis + case 0x0ad6: return 0x2032; // minutes + case 0x0ad7: return 0x2033; // seconds + case 0x0afc: return 0x2038; // caret + case 0x047e: return 0x203e; // overline + case 0x20a0: return 0x20a0; // EcuSign + case 0x20a1: return 0x20a1; // ColonSign + case 0x20a2: return 0x20a2; // CruzeiroSign + case 0x20a3: return 0x20a3; // FFrancSign + case 0x20a4: return 0x20a4; // LiraSign + case 0x20a5: return 0x20a5; // MillSign + case 0x20a6: return 0x20a6; // NairaSign + case 0x20a7: return 0x20a7; // PesetaSign + case 0x20a8: return 0x20a8; // RupeeSign + case 0x0eff: return 0x20a9; // Korean_Won + case 0x20a9: return 0x20a9; // WonSign + case 0x20aa: return 0x20aa; // NewSheqelSign + case 0x20ab: return 0x20ab; // DongSign + case 0x20ac: return 0x20ac; // EuroSign + case 0x0ab8: return 0x2105; // careof + case 0x06b0: return 0x2116; // numerosign + case 0x0afb: return 0x2117; // phonographcopyright + case 0x0ad4: return 0x211e; // prescription + case 0x0ac9: return 0x2122; // trademark + case 0x0ab0: return 0x2153; // onethird + case 0x0ab1: return 0x2154; // twothirds + case 0x0ab2: return 0x2155; // onefifth + case 0x0ab3: return 0x2156; // twofifths + case 0x0ab4: return 0x2157; // threefifths + case 0x0ab5: return 0x2158; // fourfifths + case 0x0ab6: return 0x2159; // onesixth + case 0x0ab7: return 0x215a; // fivesixths + case 0x0ac3: return 0x215b; // oneeighth + case 0x0ac4: return 0x215c; // threeeighths + case 0x0ac5: return 0x215d; // fiveeighths + case 0x0ac6: return 0x215e; // seveneighths + case 0x08fb: return 0x2190; // leftarrow + case 0x08fc: return 0x2191; // uparrow + case 0x08fd: return 0x2192; // rightarrow + case 0x08fe: return 0x2193; // downarrow + case 0x08ce: return 0x21d2; // implies + case 0x08cd: return 0x21d4; // ifonlyif + case 0x08ef: return 0x2202; // partialderivative + case 0x08c5: return 0x2207; // nabla + case 0x0bca: return 0x2218; // jot + case 0x08d6: return 0x221a; // radical + case 0x08c1: return 0x221d; // variation + case 0x08c2: return 0x221e; // infinity + case 0x08de: return 0x2227; // logicaland + case 0x0ba9: return 0x2227; // upcaret + case 0x0ba8: return 0x2228; // downcaret + case 0x08df: return 0x2228; // logicalor + case 0x08dc: return 0x2229; // intersection + case 0x0bc3: return 0x2229; // upshoe + case 0x0bd6: return 0x222a; // downshoe + case 0x08dd: return 0x222a; // union + case 0x08bf: return 0x222b; // integral + case 0x08c0: return 0x2234; // therefore + case 0x08c8: return 0x223c; // approximate + case 0x08c9: return 0x2243; // similarequal + case 0x08bd: return 0x2260; // notequal + case 0x08cf: return 0x2261; // identical + case 0x08bc: return 0x2264; // lessthanequal + case 0x08be: return 0x2265; // greaterthanequal + case 0x08da: return 0x2282; // includedin + case 0x0bda: return 0x2282; // leftshoe + case 0x08db: return 0x2283; // includes + case 0x0bd8: return 0x2283; // rightshoe + case 0x0bdc: return 0x22a2; // lefttack + case 0x0bfc: return 0x22a3; // righttack + case 0x0bce: return 0x22a4; // uptack + case 0x0bc2: return 0x22a5; // downtack + case 0x0bd3: return 0x2308; // upstile + case 0x0bc4: return 0x230a; // downstile + case 0x0afa: return 0x2315; // telephonerecorder + case 0x08a4: return 0x2320; // topintegral + case 0x08a5: return 0x2321; // botintegral + case 0x0bcc: return 0x2395; // quad + case 0x08ab: return 0x239b; // topleftparens + case 0x08ac: return 0x239d; // botleftparens + case 0x08ad: return 0x239e; // toprightparens + case 0x08ae: return 0x23a0; // botrightparens + case 0x08a7: return 0x23a1; // topleftsqbracket + case 0x08a8: return 0x23a3; // botleftsqbracket + case 0x08a9: return 0x23a4; // toprightsqbracket + case 0x08aa: return 0x23a6; // botrightsqbracket + case 0x08af: return 0x23a8; // leftmiddlecurlybrace + case 0x08b0: return 0x23ac; // rightmiddlecurlybrace + case 0x08a1: return 0x23b7; // leftradical + case 0x09ef: return 0x23ba; // horizlinescan1 + case 0x09f0: return 0x23bb; // horizlinescan3 + case 0x09f2: return 0x23bc; // horizlinescan7 + case 0x09f3: return 0x23bd; // horizlinescan9 + case 0x09e2: return 0x2409; // ht + case 0x09e5: return 0x240a; // lf + case 0x09e9: return 0x240b; // vt + case 0x09e3: return 0x240c; // ff + case 0x09e4: return 0x240d; // cr + case 0x0aac: return 0x2423; // signifblank + case 0x09e8: return 0x2424; // nl + case 0x08a3: return 0x2500; // horizconnector + case 0x09f1: return 0x2500; // horizlinescan5 + case 0x09f8: return 0x2502; // vertbar + case 0x08a6: return 0x2502; // vertconnector + case 0x08a2: return 0x250c; // topleftradical + case 0x09ec: return 0x250c; // upleftcorner + case 0x09eb: return 0x2510; // uprightcorner + case 0x09ed: return 0x2514; // lowleftcorner + case 0x09ea: return 0x2518; // lowrightcorner + case 0x09f4: return 0x251c; // leftt + case 0x09f5: return 0x2524; // rightt + case 0x09f7: return 0x252c; // topt + case 0x09f6: return 0x2534; // bott + case 0x09ee: return 0x253c; // crossinglines + case 0x09e1: return 0x2592; // checkerboard + case 0x0ae7: return 0x25aa; // enfilledsqbullet + case 0x0ae1: return 0x25ab; // enopensquarebullet + case 0x0adb: return 0x25ac; // filledrectbullet + case 0x0ae2: return 0x25ad; // openrectbullet + case 0x0adf: return 0x25ae; // emfilledrect + case 0x0acf: return 0x25af; // emopenrectangle + case 0x0ae8: return 0x25b2; // filledtribulletup + case 0x0ae3: return 0x25b3; // opentribulletup + case 0x0add: return 0x25b6; // filledrighttribullet + case 0x0acd: return 0x25b7; // rightopentriangle + case 0x0ae9: return 0x25bc; // filledtribulletdown + case 0x0ae4: return 0x25bd; // opentribulletdown + case 0x0adc: return 0x25c0; // filledlefttribullet + case 0x0acc: return 0x25c1; // leftopentriangle + case 0x09e0: return 0x25c6; // soliddiamond + case 0x0bcf: return 0x25cb; // circle + case 0x0ace: return 0x25cb; // emopencircle + case 0x0ade: return 0x25cf; // emfilledcircle + case 0x0ae0: return 0x25e6; // enopencircbullet + case 0x0ae5: return 0x2606; // openstar + case 0x0af9: return 0x260e; // telephone + case 0x0aca: return 0x2613; // signaturemark + case 0x0aea: return 0x261c; // leftpointer + case 0x0aeb: return 0x261e; // rightpointer + case 0x0af8: return 0x2640; // femalesymbol + case 0x0af7: return 0x2642; // malesymbol + case 0x0aec: return 0x2663; // club + case 0x0aee: return 0x2665; // heart + case 0x0aed: return 0x2666; // diamond + case 0x0af6: return 0x266d; // musicalflat + case 0x0af5: return 0x266f; // musicalsharp + case 0x0af3: return 0x2713; // checkmark + case 0x0af4: return 0x2717; // ballotcross + case 0x0ad9: return 0x271d; // latincross + case 0x0af0: return 0x2720; // maltesecross + case 0x0abc: return 0x27e8; // leftanglebracket + case 0x0abe: return 0x27e9; // rightanglebracket + case 0x04a4: return 0x3001; // kana_comma + case 0x04a1: return 0x3002; // kana_fullstop + case 0x04a2: return 0x300c; // kana_openingbracket + case 0x04a3: return 0x300d; // kana_closingbracket + case 0xfe5e: return 0x3099; // dead_voiced_sound + case 0xfe5f: return 0x309a; // dead_semivoiced_sound + case 0x04de: return 0x309b; // voicedsound + case 0x04df: return 0x309c; // semivoicedsound + case 0x04a7: return 0x30a1; // kana_a + case 0x04b1: return 0x30a2; // kana_A + case 0x04a8: return 0x30a3; // kana_i + case 0x04b2: return 0x30a4; // kana_I + case 0x04a9: return 0x30a5; // kana_u + case 0x04b3: return 0x30a6; // kana_U + case 0x04aa: return 0x30a7; // kana_e + case 0x04b4: return 0x30a8; // kana_E + case 0x04ab: return 0x30a9; // kana_o + case 0x04b5: return 0x30aa; // kana_O + case 0x04b6: return 0x30ab; // kana_KA + case 0x04b7: return 0x30ad; // kana_KI + case 0x04b8: return 0x30af; // kana_KU + case 0x04b9: return 0x30b1; // kana_KE + case 0x04ba: return 0x30b3; // kana_KO + case 0x04bb: return 0x30b5; // kana_SA + case 0x04bc: return 0x30b7; // kana_SHI + case 0x04bd: return 0x30b9; // kana_SU + case 0x04be: return 0x30bb; // kana_SE + case 0x04bf: return 0x30bd; // kana_SO + case 0x04c0: return 0x30bf; // kana_TA + case 0x04c1: return 0x30c1; // kana_CHI + case 0x04af: return 0x30c3; // kana_tsu + case 0x04c2: return 0x30c4; // kana_TSU + case 0x04c3: return 0x30c6; // kana_TE + case 0x04c4: return 0x30c8; // kana_TO + case 0x04c5: return 0x30ca; // kana_NA + case 0x04c6: return 0x30cb; // kana_NI + case 0x04c7: return 0x30cc; // kana_NU + case 0x04c8: return 0x30cd; // kana_NE + case 0x04c9: return 0x30ce; // kana_NO + case 0x04ca: return 0x30cf; // kana_HA + case 0x04cb: return 0x30d2; // kana_HI + case 0x04cc: return 0x30d5; // kana_FU + case 0x04cd: return 0x30d8; // kana_HE + case 0x04ce: return 0x30db; // kana_HO + case 0x04cf: return 0x30de; // kana_MA + case 0x04d0: return 0x30df; // kana_MI + case 0x04d1: return 0x30e0; // kana_MU + case 0x04d2: return 0x30e1; // kana_ME + case 0x04d3: return 0x30e2; // kana_MO + case 0x04ac: return 0x30e3; // kana_ya + case 0x04d4: return 0x30e4; // kana_YA + case 0x04ad: return 0x30e5; // kana_yu + case 0x04d5: return 0x30e6; // kana_YU + case 0x04ae: return 0x30e7; // kana_yo + case 0x04d6: return 0x30e8; // kana_YO + case 0x04d7: return 0x30e9; // kana_RA + case 0x04d8: return 0x30ea; // kana_RI + case 0x04d9: return 0x30eb; // kana_RU + case 0x04da: return 0x30ec; // kana_RE + case 0x04db: return 0x30ed; // kana_RO + case 0x04dc: return 0x30ef; // kana_WA + case 0x04a6: return 0x30f2; // kana_WO + case 0x04dd: return 0x30f3; // kana_N + case 0x04a5: return 0x30fb; // kana_conjunctive + case 0x04b0: return 0x30fc; // prolongedsound + case 0x0ea1: return 0x3131; // Hangul_Kiyeog + case 0x0ea2: return 0x3132; // Hangul_SsangKiyeog + case 0x0ea3: return 0x3133; // Hangul_KiyeogSios + case 0x0ea4: return 0x3134; // Hangul_Nieun + case 0x0ea5: return 0x3135; // Hangul_NieunJieuj + case 0x0ea6: return 0x3136; // Hangul_NieunHieuh + case 0x0ea7: return 0x3137; // Hangul_Dikeud + case 0x0ea8: return 0x3138; // Hangul_SsangDikeud + case 0x0ea9: return 0x3139; // Hangul_Rieul + case 0x0eaa: return 0x313a; // Hangul_RieulKiyeog + case 0x0eab: return 0x313b; // Hangul_RieulMieum + case 0x0eac: return 0x313c; // Hangul_RieulPieub + case 0x0ead: return 0x313d; // Hangul_RieulSios + case 0x0eae: return 0x313e; // Hangul_RieulTieut + case 0x0eaf: return 0x313f; // Hangul_RieulPhieuf + case 0x0eb0: return 0x3140; // Hangul_RieulHieuh + case 0x0eb1: return 0x3141; // Hangul_Mieum + case 0x0eb2: return 0x3142; // Hangul_Pieub + case 0x0eb3: return 0x3143; // Hangul_SsangPieub + case 0x0eb4: return 0x3144; // Hangul_PieubSios + case 0x0eb5: return 0x3145; // Hangul_Sios + case 0x0eb6: return 0x3146; // Hangul_SsangSios + case 0x0eb7: return 0x3147; // Hangul_Ieung + case 0x0eb8: return 0x3148; // Hangul_Jieuj + case 0x0eb9: return 0x3149; // Hangul_SsangJieuj + case 0x0eba: return 0x314a; // Hangul_Cieuc + case 0x0ebb: return 0x314b; // Hangul_Khieuq + case 0x0ebc: return 0x314c; // Hangul_Tieut + case 0x0ebd: return 0x314d; // Hangul_Phieuf + case 0x0ebe: return 0x314e; // Hangul_Hieuh + case 0x0ebf: return 0x314f; // Hangul_A + case 0x0ec0: return 0x3150; // Hangul_AE + case 0x0ec1: return 0x3151; // Hangul_YA + case 0x0ec2: return 0x3152; // Hangul_YAE + case 0x0ec3: return 0x3153; // Hangul_EO + case 0x0ec4: return 0x3154; // Hangul_E + case 0x0ec5: return 0x3155; // Hangul_YEO + case 0x0ec6: return 0x3156; // Hangul_YE + case 0x0ec7: return 0x3157; // Hangul_O + case 0x0ec8: return 0x3158; // Hangul_WA + case 0x0ec9: return 0x3159; // Hangul_WAE + case 0x0eca: return 0x315a; // Hangul_OE + case 0x0ecb: return 0x315b; // Hangul_YO + case 0x0ecc: return 0x315c; // Hangul_U + case 0x0ecd: return 0x315d; // Hangul_WEO + case 0x0ece: return 0x315e; // Hangul_WE + case 0x0ecf: return 0x315f; // Hangul_WI + case 0x0ed0: return 0x3160; // Hangul_YU + case 0x0ed1: return 0x3161; // Hangul_EU + case 0x0ed2: return 0x3162; // Hangul_YI + case 0x0ed3: return 0x3163; // Hangul_I + case 0x0eef: return 0x316d; // Hangul_RieulYeorinHieuh + case 0x0ef0: return 0x3171; // Hangul_SunkyeongeumMieum + case 0x0ef1: return 0x3178; // Hangul_SunkyeongeumPieub + case 0x0ef2: return 0x317f; // Hangul_PanSios + case 0x0ef3: return 0x3181; // Hangul_KkogjiDalrinIeung + case 0x0ef4: return 0x3184; // Hangul_SunkyeongeumPhieuf + case 0x0ef5: return 0x3186; // Hangul_YeorinHieuh + case 0x0ef6: return 0x318d; // Hangul_AraeA + case 0x0ef7: return 0x318e; // Hangul_AraeAE + default: return 0; + } + // clang-format on +} + +} // namespace sf::priv diff --git a/src/SFML/Window/Unix/KeySymToUnicodeMapping.hpp b/src/SFML/Window/Unix/KeySymToUnicodeMapping.hpp new file mode 100644 index 00000000..b933e54f --- /dev/null +++ b/src/SFML/Window/Unix/KeySymToUnicodeMapping.hpp @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // KeySym + +#include + +namespace sf::priv +{ + +//////////////////////////////////////////////////////////// +/// \brief Converts a KeySym to UTF-32 +/// +/// This code was autogenerated from the following table: +/// https://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt +/// +/// The generated table was edited so that dead keys are mapped to +/// non-combining characters instead of combining diacritical marks +/// because is it more suitable to describe those keys in user interfaces. +/// +/// \param keysym keysym to be converted +/// +/// \return corresponding UTF-32 +/// +//////////////////////////////////////////////////////////// +std::uint32_t keysymToUnicode(KeySym keysym); + +} // namespace sf::priv diff --git a/src/SFML/Window/Unix/KeyboardImpl.cpp b/src/SFML/Window/Unix/KeyboardImpl.cpp new file mode 100644 index 00000000..1951956e --- /dev/null +++ b/src/SFML/Window/Unix/KeyboardImpl.cpp @@ -0,0 +1,810 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace +{ + +const KeyCode nullKeyCode = 0; +const int maxKeyCode = 256; +KeyCode scancodeToKeycode[sf::Keyboard::Scan::ScancodeCount]; ///< Mapping of SFML scancode to X11 KeyCode +sf::Keyboard::Scancode keycodeToScancode[maxKeyCode]; ///< Mapping of X11 KeyCode to SFML scancode + +//////////////////////////////////////////////////////////// +bool isValidKeycode(KeyCode keycode) +{ + // Valid key code range is [8,255], according to the Xlib manual + return keycode >= 8; +} + + +//////////////////////////////////////////////////////////// +sf::Keyboard::Scancode translateKeyCode(Display* display, KeyCode keycode) +{ + if (!isValidKeycode(keycode)) + return sf::Keyboard::Scan::Unknown; + + // Try secondary keysym, for numeric keypad keys + // Note: This way we always force "NumLock = ON", which is intentional + // since the returned key code should correspond to a physical location. + KeySym keySym = XkbKeycodeToKeysym(display, keycode, 0, 1); + + // clang-format off + switch (keySym) + { + case XK_KP_0: return sf::Keyboard::Scan::Numpad0; + case XK_KP_1: return sf::Keyboard::Scan::Numpad1; + case XK_KP_2: return sf::Keyboard::Scan::Numpad2; + case XK_KP_3: return sf::Keyboard::Scan::Numpad3; + case XK_KP_4: return sf::Keyboard::Scan::Numpad4; + case XK_KP_5: return sf::Keyboard::Scan::Numpad5; + case XK_KP_6: return sf::Keyboard::Scan::Numpad6; + case XK_KP_7: return sf::Keyboard::Scan::Numpad7; + case XK_KP_8: return sf::Keyboard::Scan::Numpad8; + case XK_KP_9: return sf::Keyboard::Scan::Numpad9; + case XK_KP_Separator: return sf::Keyboard::Scan::NumpadDecimal; + case XK_KP_Decimal: return sf::Keyboard::Scan::NumpadDecimal; + case XK_KP_Equal: return sf::Keyboard::Scan::NumpadEqual; + case XK_KP_Enter: return sf::Keyboard::Scan::NumpadEnter; + default: break; + } + // clang-format on + + // Now try primary keysym for function keys (non-printable keys) + // These should not depend on the current keyboard layout + keySym = XkbKeycodeToKeysym(display, keycode, 0, 0); + + // clang-format off + switch (keySym) + { + case XK_Return: return sf::Keyboard::Scan::Enter; + case XK_Escape: return sf::Keyboard::Scan::Escape; + case XK_BackSpace: return sf::Keyboard::Scan::Backspace; + case XK_Tab: return sf::Keyboard::Scan::Tab; + case XK_Shift_L: return sf::Keyboard::Scan::LShift; + case XK_Shift_R: return sf::Keyboard::Scan::RShift; + case XK_Control_L: return sf::Keyboard::Scan::LControl; + case XK_Control_R: return sf::Keyboard::Scan::RControl; + case XK_Alt_L: return sf::Keyboard::Scan::LAlt; + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Alt_R: return sf::Keyboard::Scan::RAlt; + case XK_Meta_L: + case XK_Super_L: return sf::Keyboard::Scan::LSystem; + case XK_Meta_R: + case XK_Super_R: return sf::Keyboard::Scan::RSystem; + case XK_Menu: return sf::Keyboard::Scan::Menu; + + case XK_Num_Lock: return sf::Keyboard::Scan::NumLock; + case XK_Caps_Lock: return sf::Keyboard::Scan::CapsLock; + case XK_Execute: return sf::Keyboard::Scan::Execute; + case XK_Hyper_R: return sf::Keyboard::Scan::Application; + case XK_Select: return sf::Keyboard::Scan::Select; + case XK_Cancel: return sf::Keyboard::Scan::Stop; + case XK_Redo: return sf::Keyboard::Scan::Redo; + case XK_Undo: return sf::Keyboard::Scan::Undo; + case XK_Find: return sf::Keyboard::Scan::Search; + case XK_Mode_switch: return sf::Keyboard::Scan::ModeChange; + + case XK_Print: return sf::Keyboard::Scan::PrintScreen; + case XK_Scroll_Lock: return sf::Keyboard::Scan::ScrollLock; + case XK_Pause: + case XK_Break: return sf::Keyboard::Scan::Pause; + + case XK_Delete: + case XK_Clear: return sf::Keyboard::Scan::Delete; + case XK_Home: return sf::Keyboard::Scan::Home; + case XK_End: return sf::Keyboard::Scan::End; + case XK_Page_Up: return sf::Keyboard::Scan::PageUp; + case XK_Page_Down: return sf::Keyboard::Scan::PageDown; + case XK_Insert: return sf::Keyboard::Scan::Insert; + + case XK_Left: return sf::Keyboard::Scan::Left; + case XK_Right: return sf::Keyboard::Scan::Right; + case XK_Down: return sf::Keyboard::Scan::Down; + case XK_Up: return sf::Keyboard::Scan::Up; + + case XK_F1: return sf::Keyboard::Scan::F1; + case XK_F2: return sf::Keyboard::Scan::F2; + case XK_F3: return sf::Keyboard::Scan::F3; + case XK_F4: return sf::Keyboard::Scan::F4; + case XK_F5: return sf::Keyboard::Scan::F5; + case XK_F6: return sf::Keyboard::Scan::F6; + case XK_F7: return sf::Keyboard::Scan::F7; + case XK_F8: return sf::Keyboard::Scan::F8; + case XK_F9: return sf::Keyboard::Scan::F9; + case XK_F10: return sf::Keyboard::Scan::F10; + case XK_F11: return sf::Keyboard::Scan::F11; + case XK_F12: return sf::Keyboard::Scan::F12; + case XK_F13: return sf::Keyboard::Scan::F13; + case XK_F14: return sf::Keyboard::Scan::F14; + case XK_F15: return sf::Keyboard::Scan::F15; + case XK_F16: return sf::Keyboard::Scan::F16; + case XK_F17: return sf::Keyboard::Scan::F17; + case XK_F18: return sf::Keyboard::Scan::F18; + case XK_F19: return sf::Keyboard::Scan::F19; + case XK_F20: return sf::Keyboard::Scan::F20; + case XK_F21: return sf::Keyboard::Scan::F21; + case XK_F22: return sf::Keyboard::Scan::F22; + case XK_F23: return sf::Keyboard::Scan::F23; + case XK_F24: return sf::Keyboard::Scan::F24; + + // Numeric keypad + case XK_KP_Divide: return sf::Keyboard::Scan::NumpadDivide; + case XK_KP_Multiply: return sf::Keyboard::Scan::NumpadMultiply; + case XK_KP_Subtract: return sf::Keyboard::Scan::NumpadMinus; + case XK_KP_Add: return sf::Keyboard::Scan::NumpadPlus; + + // These should have been detected in secondary keysym test above! + case XK_KP_Insert: return sf::Keyboard::Scan::Numpad0; + case XK_KP_End: return sf::Keyboard::Scan::Numpad1; + case XK_KP_Down: return sf::Keyboard::Scan::Numpad2; + case XK_KP_Page_Down: return sf::Keyboard::Scan::Numpad3; + case XK_KP_Left: return sf::Keyboard::Scan::Numpad4; + case XK_KP_Right: return sf::Keyboard::Scan::Numpad6; + case XK_KP_Home: return sf::Keyboard::Scan::Numpad7; + case XK_KP_Up: return sf::Keyboard::Scan::Numpad8; + case XK_KP_Page_Up: return sf::Keyboard::Scan::Numpad9; + case XK_KP_Delete: return sf::Keyboard::Scan::NumpadDecimal; + case XK_KP_Equal: return sf::Keyboard::Scan::NumpadEqual; + case XK_KP_Enter: return sf::Keyboard::Scan::NumpadEnter; + + // Last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: + case XK_A: return sf::Keyboard::Scan::A; + case XK_b: + case XK_B: return sf::Keyboard::Scan::B; + case XK_c: + case XK_C: return sf::Keyboard::Scan::C; + case XK_d: + case XK_D: return sf::Keyboard::Scan::D; + case XK_e: + case XK_E: return sf::Keyboard::Scan::E; + case XK_f: + case XK_F: return sf::Keyboard::Scan::F; + case XK_g: + case XK_G: return sf::Keyboard::Scan::G; + case XK_h: + case XK_H: return sf::Keyboard::Scan::H; + case XK_i: + case XK_I: return sf::Keyboard::Scan::I; + case XK_j: + case XK_J: return sf::Keyboard::Scan::J; + case XK_k: + case XK_K: return sf::Keyboard::Scan::K; + case XK_l: + case XK_L: return sf::Keyboard::Scan::L; + case XK_m: + case XK_M: return sf::Keyboard::Scan::M; + case XK_n: + case XK_N: return sf::Keyboard::Scan::N; + case XK_o: + case XK_O: return sf::Keyboard::Scan::O; + case XK_p: + case XK_P: return sf::Keyboard::Scan::P; + case XK_q: + case XK_Q: return sf::Keyboard::Scan::Q; + case XK_r: + case XK_R: return sf::Keyboard::Scan::R; + case XK_s: + case XK_S: return sf::Keyboard::Scan::S; + case XK_t: + case XK_T: return sf::Keyboard::Scan::T; + case XK_u: + case XK_U: return sf::Keyboard::Scan::U; + case XK_v: + case XK_V: return sf::Keyboard::Scan::V; + case XK_w: + case XK_W: return sf::Keyboard::Scan::W; + case XK_x: + case XK_X: return sf::Keyboard::Scan::X; + case XK_y: + case XK_Y: return sf::Keyboard::Scan::Y; + case XK_z: + case XK_Z: return sf::Keyboard::Scan::Z; + + case XK_1: return sf::Keyboard::Scan::Num1; + case XK_2: return sf::Keyboard::Scan::Num2; + case XK_3: return sf::Keyboard::Scan::Num3; + case XK_4: return sf::Keyboard::Scan::Num4; + case XK_5: return sf::Keyboard::Scan::Num5; + case XK_6: return sf::Keyboard::Scan::Num6; + case XK_7: return sf::Keyboard::Scan::Num7; + case XK_8: return sf::Keyboard::Scan::Num8; + case XK_9: return sf::Keyboard::Scan::Num9; + case XK_0: return sf::Keyboard::Scan::Num0; + + case XK_space: return sf::Keyboard::Scan::Space; + case XK_minus: return sf::Keyboard::Scan::Hyphen; + case XK_equal: return sf::Keyboard::Scan::Equal; + case XK_bracketleft: return sf::Keyboard::Scan::LBracket; + case XK_bracketright: return sf::Keyboard::Scan::RBracket; + case XK_backslash: return sf::Keyboard::Scan::Backslash; + case XK_semicolon: return sf::Keyboard::Scan::Semicolon; + case XK_apostrophe: return sf::Keyboard::Scan::Apostrophe; + case XK_grave: return sf::Keyboard::Scan::Grave; + case XK_comma: return sf::Keyboard::Scan::Comma; + case XK_period: return sf::Keyboard::Scan::Period; + case XK_slash: return sf::Keyboard::Scan::Slash; + case XK_less: return sf::Keyboard::Scan::NonUsBackslash; + + default: return sf::Keyboard::Scan::Unknown; + } + // clang-format on +} + +//////////////////////////////////////////////////////////// +std::map getNameScancodeMap() +{ + std::map mapping; + + mapping.insert(std::make_pair("LSGT", sf::Keyboard::Scan::NonUsBackslash)); + + mapping.insert(std::make_pair("TLDE", sf::Keyboard::Scan::Grave)); + mapping.insert(std::make_pair("AE01", sf::Keyboard::Scan::Num1)); + mapping.insert(std::make_pair("AE02", sf::Keyboard::Scan::Num2)); + mapping.insert(std::make_pair("AE03", sf::Keyboard::Scan::Num3)); + mapping.insert(std::make_pair("AE04", sf::Keyboard::Scan::Num4)); + mapping.insert(std::make_pair("AE05", sf::Keyboard::Scan::Num5)); + mapping.insert(std::make_pair("AE06", sf::Keyboard::Scan::Num6)); + mapping.insert(std::make_pair("AE07", sf::Keyboard::Scan::Num7)); + mapping.insert(std::make_pair("AE08", sf::Keyboard::Scan::Num8)); + mapping.insert(std::make_pair("AE09", sf::Keyboard::Scan::Num9)); + mapping.insert(std::make_pair("AE10", sf::Keyboard::Scan::Num0)); + mapping.insert(std::make_pair("AE11", sf::Keyboard::Scan::Hyphen)); + mapping.insert(std::make_pair("AE12", sf::Keyboard::Scan::Equal)); + mapping.insert(std::make_pair("BKSP", sf::Keyboard::Scan::Backspace)); + mapping.insert(std::make_pair("TAB", sf::Keyboard::Scan::Tab)); + mapping.insert(std::make_pair("AD01", sf::Keyboard::Scan::Q)); + mapping.insert(std::make_pair("AD02", sf::Keyboard::Scan::W)); + mapping.insert(std::make_pair("AD03", sf::Keyboard::Scan::E)); + mapping.insert(std::make_pair("AD04", sf::Keyboard::Scan::R)); + mapping.insert(std::make_pair("AD05", sf::Keyboard::Scan::T)); + mapping.insert(std::make_pair("AD06", sf::Keyboard::Scan::Y)); + mapping.insert(std::make_pair("AD07", sf::Keyboard::Scan::U)); + mapping.insert(std::make_pair("AD08", sf::Keyboard::Scan::I)); + mapping.insert(std::make_pair("AD09", sf::Keyboard::Scan::O)); + mapping.insert(std::make_pair("AD10", sf::Keyboard::Scan::P)); + mapping.insert(std::make_pair("AD11", sf::Keyboard::Scan::LBracket)); + mapping.insert(std::make_pair("AD12", sf::Keyboard::Scan::RBracket)); + mapping.insert(std::make_pair("BKSL", sf::Keyboard::Scan::Backslash)); + mapping.insert(std::make_pair("RTRN", sf::Keyboard::Scan::Enter)); + + mapping.insert(std::make_pair("CAPS", sf::Keyboard::Scan::CapsLock)); + mapping.insert(std::make_pair("AC01", sf::Keyboard::Scan::A)); + mapping.insert(std::make_pair("AC02", sf::Keyboard::Scan::S)); + mapping.insert(std::make_pair("AC03", sf::Keyboard::Scan::D)); + mapping.insert(std::make_pair("AC04", sf::Keyboard::Scan::F)); + mapping.insert(std::make_pair("AC05", sf::Keyboard::Scan::G)); + mapping.insert(std::make_pair("AC06", sf::Keyboard::Scan::H)); + mapping.insert(std::make_pair("AC07", sf::Keyboard::Scan::J)); + mapping.insert(std::make_pair("AC08", sf::Keyboard::Scan::K)); + mapping.insert(std::make_pair("AC09", sf::Keyboard::Scan::L)); + mapping.insert(std::make_pair("AC10", sf::Keyboard::Scan::Semicolon)); + mapping.insert(std::make_pair("AC11", sf::Keyboard::Scan::Apostrophe)); + mapping.insert(std::make_pair("AC12", sf::Keyboard::Scan::Backslash)); + + mapping.insert(std::make_pair("LFSH", sf::Keyboard::Scan::LShift)); + mapping.insert(std::make_pair("AB01", sf::Keyboard::Scan::Z)); + mapping.insert(std::make_pair("AB02", sf::Keyboard::Scan::X)); + mapping.insert(std::make_pair("AB03", sf::Keyboard::Scan::C)); + mapping.insert(std::make_pair("AB04", sf::Keyboard::Scan::V)); + mapping.insert(std::make_pair("AB05", sf::Keyboard::Scan::B)); + mapping.insert(std::make_pair("AB06", sf::Keyboard::Scan::N)); + mapping.insert(std::make_pair("AB07", sf::Keyboard::Scan::M)); + mapping.insert(std::make_pair("AB08", sf::Keyboard::Scan::Comma)); + mapping.insert(std::make_pair("AB09", sf::Keyboard::Scan::Period)); + mapping.insert(std::make_pair("AB10", sf::Keyboard::Scan::Slash)); + mapping.insert(std::make_pair("RTSH", sf::Keyboard::Scan::RShift)); + + mapping.insert(std::make_pair("LCTL", sf::Keyboard::Scan::LControl)); + mapping.insert(std::make_pair("LALT", sf::Keyboard::Scan::LAlt)); + mapping.insert(std::make_pair("SPCE", sf::Keyboard::Scan::Space)); + mapping.insert(std::make_pair("RCTL", sf::Keyboard::Scan::RControl)); + mapping.insert(std::make_pair("RALT", sf::Keyboard::Scan::RAlt)); + mapping.insert(std::make_pair("LVL3", sf::Keyboard::Scan::RAlt)); + mapping.insert(std::make_pair("ALGR", sf::Keyboard::Scan::RAlt)); + mapping.insert(std::make_pair("LWIN", sf::Keyboard::Scan::LSystem)); + mapping.insert(std::make_pair("RWIN", sf::Keyboard::Scan::RSystem)); + + mapping.insert(std::make_pair("HYPR", sf::Keyboard::Scan::Application)); + mapping.insert(std::make_pair("EXEC", sf::Keyboard::Scan::Execute)); + mapping.insert(std::make_pair("MDSW", sf::Keyboard::Scan::ModeChange)); + mapping.insert(std::make_pair("MENU", sf::Keyboard::Scan::Menu)); + mapping.insert(std::make_pair("COMP", sf::Keyboard::Scan::Menu)); + mapping.insert(std::make_pair("SELE", sf::Keyboard::Scan::Select)); + + mapping.insert(std::make_pair("ESC", sf::Keyboard::Scan::Escape)); + mapping.insert(std::make_pair("FK01", sf::Keyboard::Scan::F1)); + mapping.insert(std::make_pair("FK02", sf::Keyboard::Scan::F2)); + mapping.insert(std::make_pair("FK03", sf::Keyboard::Scan::F3)); + mapping.insert(std::make_pair("FK04", sf::Keyboard::Scan::F4)); + mapping.insert(std::make_pair("FK05", sf::Keyboard::Scan::F5)); + mapping.insert(std::make_pair("FK06", sf::Keyboard::Scan::F6)); + mapping.insert(std::make_pair("FK07", sf::Keyboard::Scan::F7)); + mapping.insert(std::make_pair("FK08", sf::Keyboard::Scan::F8)); + mapping.insert(std::make_pair("FK09", sf::Keyboard::Scan::F9)); + mapping.insert(std::make_pair("FK10", sf::Keyboard::Scan::F10)); + mapping.insert(std::make_pair("FK11", sf::Keyboard::Scan::F11)); + mapping.insert(std::make_pair("FK12", sf::Keyboard::Scan::F12)); + + mapping.insert(std::make_pair("PRSC", sf::Keyboard::Scan::PrintScreen)); + mapping.insert(std::make_pair("SCLK", sf::Keyboard::Scan::ScrollLock)); + mapping.insert(std::make_pair("PAUS", sf::Keyboard::Scan::Pause)); + + mapping.insert(std::make_pair("INS", sf::Keyboard::Scan::Insert)); + mapping.insert(std::make_pair("HOME", sf::Keyboard::Scan::Home)); + mapping.insert(std::make_pair("PGUP", sf::Keyboard::Scan::PageUp)); + mapping.insert(std::make_pair("DELE", sf::Keyboard::Scan::Delete)); + mapping.insert(std::make_pair("END", sf::Keyboard::Scan::End)); + mapping.insert(std::make_pair("PGDN", sf::Keyboard::Scan::PageDown)); + + mapping.insert(std::make_pair("UP", sf::Keyboard::Scan::Up)); + mapping.insert(std::make_pair("RGHT", sf::Keyboard::Scan::Right)); + mapping.insert(std::make_pair("DOWN", sf::Keyboard::Scan::Down)); + mapping.insert(std::make_pair("LEFT", sf::Keyboard::Scan::Left)); + + mapping.insert(std::make_pair("NMLK", sf::Keyboard::Scan::NumLock)); + mapping.insert(std::make_pair("KPDV", sf::Keyboard::Scan::NumpadDivide)); + mapping.insert(std::make_pair("KPMU", sf::Keyboard::Scan::NumpadMultiply)); + mapping.insert(std::make_pair("KPSU", sf::Keyboard::Scan::NumpadMinus)); + + mapping.insert(std::make_pair("KP7", sf::Keyboard::Scan::Numpad7)); + mapping.insert(std::make_pair("KP8", sf::Keyboard::Scan::Numpad8)); + mapping.insert(std::make_pair("KP9", sf::Keyboard::Scan::Numpad9)); + mapping.insert(std::make_pair("KPAD", sf::Keyboard::Scan::NumpadPlus)); + mapping.insert(std::make_pair("KP4", sf::Keyboard::Scan::Numpad4)); + mapping.insert(std::make_pair("KP5", sf::Keyboard::Scan::Numpad5)); + mapping.insert(std::make_pair("KP6", sf::Keyboard::Scan::Numpad6)); + mapping.insert(std::make_pair("KP1", sf::Keyboard::Scan::Numpad1)); + mapping.insert(std::make_pair("KP2", sf::Keyboard::Scan::Numpad2)); + mapping.insert(std::make_pair("KP3", sf::Keyboard::Scan::Numpad3)); + mapping.insert(std::make_pair("KPEN", sf::Keyboard::Scan::NumpadEnter)); + mapping.insert(std::make_pair("KP0", sf::Keyboard::Scan::Numpad0)); + mapping.insert(std::make_pair("KPDL", sf::Keyboard::Scan::NumpadDecimal)); + mapping.insert(std::make_pair("KPEQ", sf::Keyboard::Scan::NumpadEqual)); + + mapping.insert(std::make_pair("FK13", sf::Keyboard::Scan::F13)); + mapping.insert(std::make_pair("FK14", sf::Keyboard::Scan::F14)); + mapping.insert(std::make_pair("FK15", sf::Keyboard::Scan::F15)); + mapping.insert(std::make_pair("FK16", sf::Keyboard::Scan::F16)); + mapping.insert(std::make_pair("FK17", sf::Keyboard::Scan::F17)); + mapping.insert(std::make_pair("FK18", sf::Keyboard::Scan::F18)); + mapping.insert(std::make_pair("FK19", sf::Keyboard::Scan::F19)); + mapping.insert(std::make_pair("FK20", sf::Keyboard::Scan::F20)); + mapping.insert(std::make_pair("FK21", sf::Keyboard::Scan::F21)); + mapping.insert(std::make_pair("FK22", sf::Keyboard::Scan::F22)); + mapping.insert(std::make_pair("FK23", sf::Keyboard::Scan::F23)); + mapping.insert(std::make_pair("FK24", sf::Keyboard::Scan::F24)); + mapping.insert(std::make_pair("LMTA", sf::Keyboard::Scan::LSystem)); + mapping.insert(std::make_pair("RMTA", sf::Keyboard::Scan::RSystem)); + mapping.insert(std::make_pair("MUTE", sf::Keyboard::Scan::VolumeMute)); + mapping.insert(std::make_pair("VOL-", sf::Keyboard::Scan::VolumeDown)); + mapping.insert(std::make_pair("VOL+", sf::Keyboard::Scan::VolumeUp)); + mapping.insert(std::make_pair("STOP", sf::Keyboard::Scan::Stop)); + mapping.insert(std::make_pair("REDO", sf::Keyboard::Scan::Redo)); + mapping.insert(std::make_pair("AGAI", sf::Keyboard::Scan::Redo)); + mapping.insert(std::make_pair("UNDO", sf::Keyboard::Scan::Undo)); + mapping.insert(std::make_pair("COPY", sf::Keyboard::Scan::Copy)); + mapping.insert(std::make_pair("PAST", sf::Keyboard::Scan::Paste)); + mapping.insert(std::make_pair("FIND", sf::Keyboard::Scan::Search)); + mapping.insert(std::make_pair("CUT", sf::Keyboard::Scan::Cut)); + mapping.insert(std::make_pair("HELP", sf::Keyboard::Scan::Help)); + + mapping.insert(std::make_pair("I156", sf::Keyboard::Scan::LaunchApplication1)); + mapping.insert(std::make_pair("I157", sf::Keyboard::Scan::LaunchApplication2)); + mapping.insert(std::make_pair("I164", sf::Keyboard::Scan::Favorites)); + mapping.insert(std::make_pair("I166", sf::Keyboard::Scan::Back)); + mapping.insert(std::make_pair("I167", sf::Keyboard::Scan::Forward)); + mapping.insert(std::make_pair("I171", sf::Keyboard::Scan::MediaNextTrack)); + mapping.insert(std::make_pair("I172", sf::Keyboard::Scan::MediaPlayPause)); + mapping.insert(std::make_pair("I173", sf::Keyboard::Scan::MediaPreviousTrack)); + mapping.insert(std::make_pair("I174", sf::Keyboard::Scan::MediaStop)); + mapping.insert(std::make_pair("I180", sf::Keyboard::Scan::HomePage)); + mapping.insert(std::make_pair("I181", sf::Keyboard::Scan::Refresh)); + mapping.insert(std::make_pair("I223", sf::Keyboard::Scan::LaunchMail)); + mapping.insert(std::make_pair("I234", sf::Keyboard::Scan::LaunchMediaSelect)); + + return mapping; +} + +//////////////////////////////////////////////////////////// +void ensureMapping() +{ + static bool isMappingInitialized = false; + + if (isMappingInitialized) + return; + + // Phase 1: Initialize mappings with default values + for (auto& keycode : scancodeToKeycode) + keycode = nullKeyCode; + + for (auto& scancode : keycodeToScancode) + scancode = sf::Keyboard::Scan::Unknown; + + // Phase 2: Get XKB names with key code + Display* display = sf::priv::openDisplay(); + + char name[XkbKeyNameLength + 1]; + XkbDescPtr descriptor = XkbGetMap(display, 0, XkbUseCoreKbd); + XkbGetNames(display, XkbKeyNamesMask, descriptor); + + std::map nameScancodeMap = getNameScancodeMap(); + sf::Keyboard::Scancode scancode = sf::Keyboard::Scan::Unknown; + + for (int keycode = descriptor->min_key_code; keycode <= descriptor->max_key_code; ++keycode) + { + if (!isValidKeycode(static_cast(keycode))) + { + continue; + } + + std::memcpy(name, descriptor->names->keys[keycode].name, XkbKeyNameLength); + name[XkbKeyNameLength] = '\0'; + + std::map::iterator mappedScancode = nameScancodeMap.find(std::string(name)); + scancode = sf::Keyboard::Scan::Unknown; + + if (mappedScancode != nameScancodeMap.end()) + scancode = mappedScancode->second; + + if (scancode != sf::Keyboard::Scan::Unknown) + scancodeToKeycode[scancode] = static_cast(keycode); + + keycodeToScancode[keycode] = scancode; + } + + XkbFreeNames(descriptor, XkbKeyNamesMask, True); + XkbFreeKeyboard(descriptor, 0, True); + + // Phase 3: Translate un-translated keycodes using traditional X11 KeySym lookups + for (int keycode = 8; keycode < maxKeyCode; ++keycode) + { + if (keycodeToScancode[static_cast(keycode)] == sf::Keyboard::Scan::Unknown) + { + scancode = translateKeyCode(display, static_cast(keycode)); + + if (scancode != sf::Keyboard::Scan::Unknown && scancodeToKeycode[scancode] == nullKeyCode) + scancodeToKeycode[scancode] = static_cast(keycode); + + keycodeToScancode[keycode] = scancode; + } + } + + sf::priv::closeDisplay(display); + + isMappingInitialized = true; +} + + +//////////////////////////////////////////////////////////// +KeyCode scancodeToKeyCode(sf::Keyboard::Scancode code) +{ + ensureMapping(); + + if (code != sf::Keyboard::Scan::Unknown) + return scancodeToKeycode[code]; + + return nullKeyCode; +} + + +//////////////////////////////////////////////////////////// +sf::Keyboard::Scancode keyCodeToScancode(KeyCode code) +{ + ensureMapping(); + + if (isValidKeycode(code)) + return keycodeToScancode[code]; + + return sf::Keyboard::Scan::Unknown; +} + + +//////////////////////////////////////////////////////////// +KeyCode keyToKeyCode(sf::Keyboard::Key key) +{ + KeySym keysym = sf::priv::keyToKeySym(key); + + if (keysym != NoSymbol) + { + Display* display = sf::priv::openDisplay(); + KeyCode keycode = XKeysymToKeycode(display, keysym); + sf::priv::closeDisplay(display); + + if (keycode != nullKeyCode) + return keycode; + } + + // Fallback for when XKeysymToKeycode cannot tell the KeyCode for XK_Alt_R + if (key == sf::Keyboard::RAlt) + return scancodeToKeycode[sf::Keyboard::Scan::RAlt]; + + return nullKeyCode; +} + + +//////////////////////////////////////////////////////////// +KeySym scancodeToKeySym(sf::Keyboard::Scancode code) +{ + Display* display = sf::priv::openDisplay(); + + KeySym keysym = NoSymbol; + KeyCode keycode = scancodeToKeyCode(code); + + if (keycode != nullKeyCode) // ensure that this Scancode is mapped to keycode + keysym = XkbKeycodeToKeysym(display, keycode, 0, 0); + + sf::priv::closeDisplay(display); + + return keysym; +} + + +//////////////////////////////////////////////////////////// +bool isKeyPressedImpl(KeyCode keycode) +{ + if (keycode != nullKeyCode) + { + Display* display = sf::priv::openDisplay(); + + // Get the whole keyboard state + char keys[32]; + XQueryKeymap(display, keys); + + sf::priv::closeDisplay(display); + + // Check our keycode + return (keys[keycode / 8] & (1 << (keycode % 8))) != 0; + } + + return false; +} + +} // anonymous namespace + +namespace sf::priv +{ + +//////////////////////////////////////////////////////////// +bool KeyboardImpl::isKeyPressed(Keyboard::Key key) +{ + KeyCode keycode = keyToKeyCode(key); + return isKeyPressedImpl(keycode); +} + + +//////////////////////////////////////////////////////////// +bool KeyboardImpl::isKeyPressed(Keyboard::Scancode code) +{ + KeyCode keycode = scancodeToKeyCode(code); + return isKeyPressedImpl(keycode); +} + + +//////////////////////////////////////////////////////////// +Keyboard::Scancode KeyboardImpl::delocalize(Keyboard::Key key) +{ + KeyCode keycode = keyToKeyCode(key); + return keyCodeToScancode(keycode); +} + + +//////////////////////////////////////////////////////////// +Keyboard::Key KeyboardImpl::localize(Keyboard::Scancode code) +{ + KeySym keysym = scancodeToKeySym(code); + return keySymToKey(keysym); +} + + +//////////////////////////////////////////////////////////// +String KeyboardImpl::getDescription(Keyboard::Scancode code) +{ + bool checkInput = true; + + // these scancodes actually correspond to keys with input + // but we want to return their description, not their behaviour + if (code == Keyboard::Scan::Enter || code == Keyboard::Scan::Escape || code == Keyboard::Scan::Backspace || + code == Keyboard::Scan::Tab || code == Keyboard::Scan::Space || code == Keyboard::Scan::ScrollLock || + code == Keyboard::Scan::Pause || code == Keyboard::Scan::Delete || code == Keyboard::Scan::NumpadDivide || + code == Keyboard::Scan::NumpadMultiply || code == Keyboard::Scan::NumpadMinus || + code == Keyboard::Scan::NumpadPlus || code == Keyboard::Scan::NumpadEqual || + code == Keyboard::Scan::NumpadEnter || code == Keyboard::Scan::NumpadDecimal) + { + checkInput = false; + } + + if (checkInput) + { + KeySym keysym = scancodeToKeySym(code); + std::uint32_t unicode = keysymToUnicode(keysym); + + if (unicode != 0) + return String(unicode); + } + + // Fallback to our best guess for the keys that are known to be independent of the layout. + // clang-format off + switch (code) + { + case Keyboard::Scan::Enter: return "Enter"; + case Keyboard::Scan::Escape: return "Escape"; + case Keyboard::Scan::Backspace: return "Backspace"; + case Keyboard::Scan::Tab: return "Tab"; + case Keyboard::Scan::Space: return "Space"; + + case Keyboard::Scan::F1: return "F1"; + case Keyboard::Scan::F2: return "F2"; + case Keyboard::Scan::F3: return "F3"; + case Keyboard::Scan::F4: return "F4"; + case Keyboard::Scan::F5: return "F5"; + case Keyboard::Scan::F6: return "F6"; + case Keyboard::Scan::F7: return "F7"; + case Keyboard::Scan::F8: return "F8"; + case Keyboard::Scan::F9: return "F9"; + case Keyboard::Scan::F10: return "F10"; + case Keyboard::Scan::F11: return "F11"; + case Keyboard::Scan::F12: return "F12"; + case Keyboard::Scan::F13: return "F13"; + case Keyboard::Scan::F14: return "F14"; + case Keyboard::Scan::F15: return "F15"; + case Keyboard::Scan::F16: return "F16"; + case Keyboard::Scan::F17: return "F17"; + case Keyboard::Scan::F18: return "F18"; + case Keyboard::Scan::F19: return "F19"; + case Keyboard::Scan::F20: return "F20"; + case Keyboard::Scan::F21: return "F21"; + case Keyboard::Scan::F22: return "F22"; + case Keyboard::Scan::F23: return "F23"; + case Keyboard::Scan::F24: return "F24"; + + case Keyboard::Scan::CapsLock: return "Caps Lock"; + case Keyboard::Scan::PrintScreen: return "Print Screen"; + case Keyboard::Scan::ScrollLock: return "Scroll Lock"; + + case Keyboard::Scan::Pause: return "Pause"; + case Keyboard::Scan::Insert: return "Insert"; + case Keyboard::Scan::Home: return "Home"; + case Keyboard::Scan::PageUp: return "Page Up"; + case Keyboard::Scan::Delete: return "Delete"; + case Keyboard::Scan::End: return "End"; + case Keyboard::Scan::PageDown: return "Page Down"; + + case Keyboard::Scan::Left: return "Left Arrow"; + case Keyboard::Scan::Right: return "Right Arrow"; + case Keyboard::Scan::Down: return "Down Arrow"; + case Keyboard::Scan::Up: return "Up Arrow"; + + case Keyboard::Scan::NumLock: return "Num Lock"; + case Keyboard::Scan::NumpadDivide: return "Divide (Numpad)"; + case Keyboard::Scan::NumpadMultiply: return "Multiply (Numpad)"; + case Keyboard::Scan::NumpadMinus: return "Minus (Numpad)"; + case Keyboard::Scan::NumpadPlus: return "Plus (Numpad)"; + case Keyboard::Scan::NumpadEqual: return "Equal (Numpad)"; + case Keyboard::Scan::NumpadEnter: return "Enter (Numpad)"; + case Keyboard::Scan::NumpadDecimal: return "Decimal (Numpad)"; + + case Keyboard::Scan::Numpad0: return "0 (Numpad)"; + case Keyboard::Scan::Numpad1: return "1 (Numpad)"; + case Keyboard::Scan::Numpad2: return "2 (Numpad)"; + case Keyboard::Scan::Numpad3: return "3 (Numpad)"; + case Keyboard::Scan::Numpad4: return "4 (Numpad)"; + case Keyboard::Scan::Numpad5: return "5 (Numpad)"; + case Keyboard::Scan::Numpad6: return "6 (Numpad)"; + case Keyboard::Scan::Numpad7: return "7 (Numpad)"; + case Keyboard::Scan::Numpad8: return "8 (Numpad)"; + case Keyboard::Scan::Numpad9: return "9 (Numpad)"; + + case Keyboard::Scan::Application: return "Application"; + case Keyboard::Scan::Execute: return "Execute"; + case Keyboard::Scan::Help: return "Help"; + case Keyboard::Scan::Menu: return "Menu"; + case Keyboard::Scan::Select: return "Select"; + case Keyboard::Scan::Stop: return "Stop"; + case Keyboard::Scan::Redo: return "Redo"; + case Keyboard::Scan::Undo: return "Undo"; + case Keyboard::Scan::Cut: return "Cut"; + case Keyboard::Scan::Copy: return "Copy"; + case Keyboard::Scan::Paste: return "Paste"; + case Keyboard::Scan::Search: return "Search"; + + case Keyboard::Scan::VolumeMute: return "Volume Mute"; + case Keyboard::Scan::VolumeUp: return "Volume Up"; + case Keyboard::Scan::VolumeDown: return "Volume Down"; + + case Keyboard::Scan::LControl: return "Left Control"; + case Keyboard::Scan::LShift: return "Left Shift"; + case Keyboard::Scan::LAlt: return "Left Alt"; + case Keyboard::Scan::LSystem: return "Left System"; + case Keyboard::Scan::RControl: return "Right Control"; + case Keyboard::Scan::RShift: return "Right Shift"; + case Keyboard::Scan::RAlt: return "Right Alt"; + case Keyboard::Scan::RSystem: return "Right System"; + + case Keyboard::Scan::LaunchApplication1: return "Launch Application 1"; + case Keyboard::Scan::LaunchApplication2: return "Launch Application 2"; + case Keyboard::Scan::Favorites: return "Favorites"; + case Keyboard::Scan::Back: return "Back"; + case Keyboard::Scan::Forward: return "Forward"; + case Keyboard::Scan::MediaNextTrack: return "Media Next Track"; + case Keyboard::Scan::MediaPlayPause: return "Media Play Pause"; + case Keyboard::Scan::MediaPreviousTrack: return "Media Previous Track"; + case Keyboard::Scan::MediaStop: return "Media Stop"; + case Keyboard::Scan::HomePage: return "Home Page"; + case Keyboard::Scan::Refresh: return "Refresh"; + case Keyboard::Scan::LaunchMail: return "Launch Mail"; + case Keyboard::Scan::LaunchMediaSelect: return "Launch Media Select"; + + default: return "Unknown Scancode"; + } + // clang-format on +} + + +//////////////////////////////////////////////////////////// +Keyboard::Key KeyboardImpl::getKeyFromEvent(XKeyEvent& event) +{ + // Try each KeySym index (modifier group) until we get a match + for (int i = 0; i < 4; ++i) + { + // Get the SFML keyboard code from the keysym of the key that has been pressed + KeySym keysym = XLookupKeysym(&event, i); + Keyboard::Key key = keySymToKey(keysym); + if (key != Keyboard::Unknown) + return key; + } + + return Keyboard::Unknown; +} + + +//////////////////////////////////////////////////////////// +Keyboard::Scancode KeyboardImpl::getScancodeFromEvent(XKeyEvent& event) +{ + return keyCodeToScancode(static_cast(event.keycode)); +} + +} // namespace sf::priv diff --git a/src/SFML/Window/Unix/KeyboardImpl.hpp b/src/SFML/Window/Unix/KeyboardImpl.hpp new file mode 100644 index 00000000..373dda96 --- /dev/null +++ b/src/SFML/Window/Unix/KeyboardImpl.hpp @@ -0,0 +1,97 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include // XKeyEvent + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +/// \brief sf::priv::KeyboardImpl helper +/// +/// This class implements keyboard handling functions +/// to help sf::priv::InputImpl class. +//////////////////////////////////////////////////////////// +class KeyboardImpl +{ +public: + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::isKeyPressed(Key) + /// + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) + /// + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \brief Get the sf::Keyboard::Key from XKeyEvent + /// + /// \param event Event from which key is gotten + /// + /// \return A key being pressed or released + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key getKeyFromEvent(XKeyEvent& event); + + //////////////////////////////////////////////////////////// + /// \brief Get the sf::Keyboard::Scancode from XKeyEvent + /// + /// \param event Event from which scancode is gotten + /// + /// \return A scancode of a key being pressed or released + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode getScancodeFromEvent(XKeyEvent& event); +}; + +} // namespace sf::priv diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 354af03b..a743b78e 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -357,119 +358,6 @@ bool isWMAbsolutePositionGood() return false; } - -sf::Keyboard::Key keysymToSF(KeySym symbol) -{ - // clang-format off - switch (symbol) - { - case XK_Shift_L: return sf::Keyboard::LShift; - case XK_Shift_R: return sf::Keyboard::RShift; - case XK_Control_L: return sf::Keyboard::LControl; - case XK_Control_R: return sf::Keyboard::RControl; - case XK_Alt_L: return sf::Keyboard::LAlt; - case XK_Alt_R: return sf::Keyboard::RAlt; - case XK_Super_L: return sf::Keyboard::LSystem; - case XK_Super_R: return sf::Keyboard::RSystem; - case XK_Menu: return sf::Keyboard::Menu; - case XK_Escape: return sf::Keyboard::Escape; - case XK_semicolon: return sf::Keyboard::Semicolon; - case XK_slash: return sf::Keyboard::Slash; - case XK_equal: return sf::Keyboard::Equal; - case XK_minus: return sf::Keyboard::Hyphen; - case XK_bracketleft: return sf::Keyboard::LBracket; - case XK_bracketright: return sf::Keyboard::RBracket; - case XK_comma: return sf::Keyboard::Comma; - case XK_period: return sf::Keyboard::Period; - case XK_apostrophe: return sf::Keyboard::Quote; - case XK_backslash: return sf::Keyboard::Backslash; - case XK_grave: return sf::Keyboard::Tilde; - case XK_space: return sf::Keyboard::Space; - case XK_Return: return sf::Keyboard::Enter; - case XK_KP_Enter: return sf::Keyboard::Enter; - case XK_BackSpace: return sf::Keyboard::Backspace; - case XK_Tab: return sf::Keyboard::Tab; - case XK_Prior: return sf::Keyboard::PageUp; - case XK_Next: return sf::Keyboard::PageDown; - case XK_End: return sf::Keyboard::End; - case XK_Home: return sf::Keyboard::Home; - case XK_Insert: return sf::Keyboard::Insert; - case XK_Delete: return sf::Keyboard::Delete; - case XK_KP_Add: return sf::Keyboard::Add; - case XK_KP_Subtract: return sf::Keyboard::Subtract; - case XK_KP_Multiply: return sf::Keyboard::Multiply; - case XK_KP_Divide: return sf::Keyboard::Divide; - case XK_Pause: return sf::Keyboard::Pause; - case XK_F1: return sf::Keyboard::F1; - case XK_F2: return sf::Keyboard::F2; - case XK_F3: return sf::Keyboard::F3; - case XK_F4: return sf::Keyboard::F4; - case XK_F5: return sf::Keyboard::F5; - case XK_F6: return sf::Keyboard::F6; - case XK_F7: return sf::Keyboard::F7; - case XK_F8: return sf::Keyboard::F8; - case XK_F9: return sf::Keyboard::F9; - case XK_F10: return sf::Keyboard::F10; - case XK_F11: return sf::Keyboard::F11; - case XK_F12: return sf::Keyboard::F12; - case XK_F13: return sf::Keyboard::F13; - case XK_F14: return sf::Keyboard::F14; - case XK_F15: return sf::Keyboard::F15; - case XK_Left: return sf::Keyboard::Left; - case XK_Right: return sf::Keyboard::Right; - case XK_Up: return sf::Keyboard::Up; - case XK_Down: return sf::Keyboard::Down; - case XK_KP_Insert: return sf::Keyboard::Numpad0; - case XK_KP_End: return sf::Keyboard::Numpad1; - case XK_KP_Down: return sf::Keyboard::Numpad2; - case XK_KP_Page_Down: return sf::Keyboard::Numpad3; - case XK_KP_Left: return sf::Keyboard::Numpad4; - case XK_KP_Begin: return sf::Keyboard::Numpad5; - case XK_KP_Right: return sf::Keyboard::Numpad6; - case XK_KP_Home: return sf::Keyboard::Numpad7; - case XK_KP_Up: return sf::Keyboard::Numpad8; - case XK_KP_Page_Up: return sf::Keyboard::Numpad9; - case XK_a: return sf::Keyboard::A; - case XK_b: return sf::Keyboard::B; - case XK_c: return sf::Keyboard::C; - case XK_d: return sf::Keyboard::D; - case XK_e: return sf::Keyboard::E; - case XK_f: return sf::Keyboard::F; - case XK_g: return sf::Keyboard::G; - case XK_h: return sf::Keyboard::H; - case XK_i: return sf::Keyboard::I; - case XK_j: return sf::Keyboard::J; - case XK_k: return sf::Keyboard::K; - case XK_l: return sf::Keyboard::L; - case XK_m: return sf::Keyboard::M; - case XK_n: return sf::Keyboard::N; - case XK_o: return sf::Keyboard::O; - case XK_p: return sf::Keyboard::P; - case XK_q: return sf::Keyboard::Q; - case XK_r: return sf::Keyboard::R; - case XK_s: return sf::Keyboard::S; - case XK_t: return sf::Keyboard::T; - case XK_u: return sf::Keyboard::U; - case XK_v: return sf::Keyboard::V; - case XK_w: return sf::Keyboard::W; - case XK_x: return sf::Keyboard::X; - case XK_y: return sf::Keyboard::Y; - case XK_z: return sf::Keyboard::Z; - case XK_0: return sf::Keyboard::Num0; - case XK_1: return sf::Keyboard::Num1; - case XK_2: return sf::Keyboard::Num2; - case XK_3: return sf::Keyboard::Num3; - case XK_4: return sf::Keyboard::Num4; - case XK_5: return sf::Keyboard::Num5; - case XK_6: return sf::Keyboard::Num6; - case XK_7: return sf::Keyboard::Num7; - case XK_8: return sf::Keyboard::Num8; - case XK_9: return sf::Keyboard::Num9; - } - // clang-format on - - return sf::Keyboard::Unknown; -} } // namespace WindowsImplX11Impl } // namespace @@ -1870,27 +1758,16 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) // Key down event case KeyPress: { - Keyboard::Key key = Keyboard::Unknown; - - // Try each KeySym index (modifier group) until we get a match - for (int i = 0; i < 4; ++i) - { - // Get the SFML keyboard code from the keysym of the key that has been pressed - key = keysymToSF(XLookupKeysym(&windowEvent.xkey, i)); - - if (key != Keyboard::Unknown) - break; - } - // Fill the event parameters // TODO: if modifiers are wrong, use XGetModifierMapping to retrieve the actual modifiers mapping Event event; - event.type = Event::KeyPressed; - event.key.code = key; - event.key.alt = windowEvent.xkey.state & Mod1Mask; - event.key.control = windowEvent.xkey.state & ControlMask; - event.key.shift = windowEvent.xkey.state & ShiftMask; - event.key.system = windowEvent.xkey.state & Mod4Mask; + event.type = Event::KeyPressed; + event.key.code = KeyboardImpl::getKeyFromEvent(windowEvent.xkey); + event.key.scancode = KeyboardImpl::getScancodeFromEvent(windowEvent.xkey); + event.key.alt = windowEvent.xkey.state & Mod1Mask; + event.key.control = windowEvent.xkey.state & ControlMask; + event.key.shift = windowEvent.xkey.state & ShiftMask; + event.key.system = windowEvent.xkey.state & Mod4Mask; const bool filtered = XFilterEvent(&windowEvent, None); @@ -1976,26 +1853,15 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) // Key up event case KeyRelease: { - Keyboard::Key key = Keyboard::Unknown; - - // Try each KeySym index (modifier group) until we get a match - for (int i = 0; i < 4; ++i) - { - // Get the SFML keyboard code from the keysym of the key that has been released - key = keysymToSF(XLookupKeysym(&windowEvent.xkey, i)); - - if (key != Keyboard::Unknown) - break; - } - // Fill the event parameters Event event; - event.type = Event::KeyReleased; - event.key.code = key; - event.key.alt = windowEvent.xkey.state & Mod1Mask; - event.key.control = windowEvent.xkey.state & ControlMask; - event.key.shift = windowEvent.xkey.state & ShiftMask; - event.key.system = windowEvent.xkey.state & Mod4Mask; + event.type = Event::KeyReleased; + event.key.code = KeyboardImpl::getKeyFromEvent(windowEvent.xkey); + event.key.scancode = KeyboardImpl::getScancodeFromEvent(windowEvent.xkey); + event.key.alt = windowEvent.xkey.state & Mod1Mask; + event.key.control = windowEvent.xkey.state & ControlMask; + event.key.shift = windowEvent.xkey.state & ShiftMask; + event.key.system = windowEvent.xkey.state & Mod4Mask; pushEvent(event); break; @@ -2121,6 +1987,15 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) break; } + // Keyboard mapping changed + case MappingNotify: + { + if (windowEvent.xmapping.request == MappingKeyboard) + XRefreshKeyboardMapping(&windowEvent.xmapping); + + break; + } + // Window unmapped case UnmapNotify: { diff --git a/src/SFML/Window/Win32/InputImpl.cpp b/src/SFML/Window/Win32/InputImpl.cpp index 09922b36..d97e4be6 100644 --- a/src/SFML/Window/Win32/InputImpl.cpp +++ b/src/SFML/Window/Win32/InputImpl.cpp @@ -25,129 +25,584 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include #include - namespace sf::priv { //////////////////////////////////////////////////////////// -bool InputImpl::isKeyPressed(Keyboard::Key key) -{ - int vkey = 0; +Keyboard::Scancode InputImpl::m_keyToScancodeMapping[Keyboard::KeyCount]; ///< Mapping from Key to Scancode +Keyboard::Key InputImpl::m_scancodeToKeyMapping[Keyboard::Scan::ScancodeCount]; ///< Mapping from Scancode to Key +//////////////////////////////////////////////////////////// +Keyboard::Key virtualKeyToSfKey(UINT virtualKey) +{ // clang-format off - switch (key) + switch (virtualKey) { - default: vkey = 0; break; - case Keyboard::A: vkey = 'A'; break; - case Keyboard::B: vkey = 'B'; break; - case Keyboard::C: vkey = 'C'; break; - case Keyboard::D: vkey = 'D'; break; - case Keyboard::E: vkey = 'E'; break; - case Keyboard::F: vkey = 'F'; break; - case Keyboard::G: vkey = 'G'; break; - case Keyboard::H: vkey = 'H'; break; - case Keyboard::I: vkey = 'I'; break; - case Keyboard::J: vkey = 'J'; break; - case Keyboard::K: vkey = 'K'; break; - case Keyboard::L: vkey = 'L'; break; - case Keyboard::M: vkey = 'M'; break; - case Keyboard::N: vkey = 'N'; break; - case Keyboard::O: vkey = 'O'; break; - case Keyboard::P: vkey = 'P'; break; - case Keyboard::Q: vkey = 'Q'; break; - case Keyboard::R: vkey = 'R'; break; - case Keyboard::S: vkey = 'S'; break; - case Keyboard::T: vkey = 'T'; break; - case Keyboard::U: vkey = 'U'; break; - case Keyboard::V: vkey = 'V'; break; - case Keyboard::W: vkey = 'W'; break; - case Keyboard::X: vkey = 'X'; break; - case Keyboard::Y: vkey = 'Y'; break; - case Keyboard::Z: vkey = 'Z'; break; - case Keyboard::Num0: vkey = '0'; break; - case Keyboard::Num1: vkey = '1'; break; - case Keyboard::Num2: vkey = '2'; break; - case Keyboard::Num3: vkey = '3'; break; - case Keyboard::Num4: vkey = '4'; break; - case Keyboard::Num5: vkey = '5'; break; - case Keyboard::Num6: vkey = '6'; break; - case Keyboard::Num7: vkey = '7'; break; - case Keyboard::Num8: vkey = '8'; break; - case Keyboard::Num9: vkey = '9'; break; - case Keyboard::Escape: vkey = VK_ESCAPE; break; - case Keyboard::LControl: vkey = VK_LCONTROL; break; - case Keyboard::LShift: vkey = VK_LSHIFT; break; - case Keyboard::LAlt: vkey = VK_LMENU; break; - case Keyboard::LSystem: vkey = VK_LWIN; break; - case Keyboard::RControl: vkey = VK_RCONTROL; break; - case Keyboard::RShift: vkey = VK_RSHIFT; break; - case Keyboard::RAlt: vkey = VK_RMENU; break; - case Keyboard::RSystem: vkey = VK_RWIN; break; - case Keyboard::Menu: vkey = VK_APPS; break; - case Keyboard::LBracket: vkey = VK_OEM_4; break; - case Keyboard::RBracket: vkey = VK_OEM_6; break; - case Keyboard::Semicolon: vkey = VK_OEM_1; break; - case Keyboard::Comma: vkey = VK_OEM_COMMA; break; - case Keyboard::Period: vkey = VK_OEM_PERIOD; break; - case Keyboard::Quote: vkey = VK_OEM_7; break; - case Keyboard::Slash: vkey = VK_OEM_2; break; - case Keyboard::Backslash: vkey = VK_OEM_5; break; - case Keyboard::Tilde: vkey = VK_OEM_3; break; - case Keyboard::Equal: vkey = VK_OEM_PLUS; break; - case Keyboard::Hyphen: vkey = VK_OEM_MINUS; break; - case Keyboard::Space: vkey = VK_SPACE; break; - case Keyboard::Enter: vkey = VK_RETURN; break; - case Keyboard::Backspace: vkey = VK_BACK; break; - case Keyboard::Tab: vkey = VK_TAB; break; - case Keyboard::PageUp: vkey = VK_PRIOR; break; - case Keyboard::PageDown: vkey = VK_NEXT; break; - case Keyboard::End: vkey = VK_END; break; - case Keyboard::Home: vkey = VK_HOME; break; - case Keyboard::Insert: vkey = VK_INSERT; break; - case Keyboard::Delete: vkey = VK_DELETE; break; - case Keyboard::Add: vkey = VK_ADD; break; - case Keyboard::Subtract: vkey = VK_SUBTRACT; break; - case Keyboard::Multiply: vkey = VK_MULTIPLY; break; - case Keyboard::Divide: vkey = VK_DIVIDE; break; - case Keyboard::Left: vkey = VK_LEFT; break; - case Keyboard::Right: vkey = VK_RIGHT; break; - case Keyboard::Up: vkey = VK_UP; break; - case Keyboard::Down: vkey = VK_DOWN; break; - case Keyboard::Numpad0: vkey = VK_NUMPAD0; break; - case Keyboard::Numpad1: vkey = VK_NUMPAD1; break; - case Keyboard::Numpad2: vkey = VK_NUMPAD2; break; - case Keyboard::Numpad3: vkey = VK_NUMPAD3; break; - case Keyboard::Numpad4: vkey = VK_NUMPAD4; break; - case Keyboard::Numpad5: vkey = VK_NUMPAD5; break; - case Keyboard::Numpad6: vkey = VK_NUMPAD6; break; - case Keyboard::Numpad7: vkey = VK_NUMPAD7; break; - case Keyboard::Numpad8: vkey = VK_NUMPAD8; break; - case Keyboard::Numpad9: vkey = VK_NUMPAD9; break; - case Keyboard::F1: vkey = VK_F1; break; - case Keyboard::F2: vkey = VK_F2; break; - case Keyboard::F3: vkey = VK_F3; break; - case Keyboard::F4: vkey = VK_F4; break; - case Keyboard::F5: vkey = VK_F5; break; - case Keyboard::F6: vkey = VK_F6; break; - case Keyboard::F7: vkey = VK_F7; break; - case Keyboard::F8: vkey = VK_F8; break; - case Keyboard::F9: vkey = VK_F9; break; - case Keyboard::F10: vkey = VK_F10; break; - case Keyboard::F11: vkey = VK_F11; break; - case Keyboard::F12: vkey = VK_F12; break; - case Keyboard::F13: vkey = VK_F13; break; - case Keyboard::F14: vkey = VK_F14; break; - case Keyboard::F15: vkey = VK_F15; break; - case Keyboard::Pause: vkey = VK_PAUSE; break; + case 'A': return Keyboard::A; + case 'B': return Keyboard::B; + case 'C': return Keyboard::C; + case 'D': return Keyboard::D; + case 'E': return Keyboard::E; + case 'F': return Keyboard::F; + case 'G': return Keyboard::G; + case 'H': return Keyboard::H; + case 'I': return Keyboard::I; + case 'J': return Keyboard::J; + case 'K': return Keyboard::K; + case 'L': return Keyboard::L; + case 'M': return Keyboard::M; + case 'N': return Keyboard::N; + case 'O': return Keyboard::O; + case 'P': return Keyboard::P; + case 'Q': return Keyboard::Q; + case 'R': return Keyboard::R; + case 'S': return Keyboard::S; + case 'T': return Keyboard::T; + case 'U': return Keyboard::U; + case 'V': return Keyboard::V; + case 'W': return Keyboard::W; + case 'X': return Keyboard::X; + case 'Y': return Keyboard::Y; + case 'Z': return Keyboard::Z; + case '0': return Keyboard::Num0; + case '1': return Keyboard::Num1; + case '2': return Keyboard::Num2; + case '3': return Keyboard::Num3; + case '4': return Keyboard::Num4; + case '5': return Keyboard::Num5; + case '6': return Keyboard::Num6; + case '7': return Keyboard::Num7; + case '8': return Keyboard::Num8; + case '9': return Keyboard::Num9; + case VK_ESCAPE: return Keyboard::Escape; + case VK_LCONTROL: return Keyboard::LControl; + case VK_LSHIFT: return Keyboard::LShift; + case VK_LMENU: return Keyboard::LAlt; + case VK_LWIN: return Keyboard::LSystem; + case VK_RCONTROL: return Keyboard::RControl; + case VK_RSHIFT: return Keyboard::RShift; + case VK_RMENU: return Keyboard::RAlt; + case VK_RWIN: return Keyboard::RSystem; + case VK_APPS: return Keyboard::Menu; + case VK_OEM_4: return Keyboard::LBracket; + case VK_OEM_6: return Keyboard::RBracket; + case VK_OEM_1: return Keyboard::Semicolon; + case VK_OEM_COMMA: return Keyboard::Comma; + case VK_OEM_PERIOD: return Keyboard::Period; + case VK_OEM_7: return Keyboard::Apostrophe; + case VK_OEM_2: return Keyboard::Slash; + case VK_OEM_5: return Keyboard::Backslash; + case VK_OEM_3: return Keyboard::Grave; + case VK_OEM_PLUS: return Keyboard::Equal; + case VK_OEM_MINUS: return Keyboard::Hyphen; + case VK_SPACE: return Keyboard::Space; + case VK_RETURN: return Keyboard::Enter; + case VK_BACK: return Keyboard::Backspace; + case VK_TAB: return Keyboard::Tab; + case VK_PRIOR: return Keyboard::PageUp; + case VK_NEXT: return Keyboard::PageDown; + case VK_END: return Keyboard::End; + case VK_HOME: return Keyboard::Home; + case VK_INSERT: return Keyboard::Insert; + case VK_DELETE: return Keyboard::Delete; + case VK_ADD: return Keyboard::Add; + case VK_SUBTRACT: return Keyboard::Subtract; + case VK_MULTIPLY: return Keyboard::Multiply; + case VK_DIVIDE: return Keyboard::Divide; + case VK_LEFT: return Keyboard::Left; + case VK_RIGHT: return Keyboard::Right; + case VK_UP: return Keyboard::Up; + case VK_DOWN: return Keyboard::Down; + case VK_NUMPAD0: return Keyboard::Numpad0; + case VK_NUMPAD1: return Keyboard::Numpad1; + case VK_NUMPAD2: return Keyboard::Numpad2; + case VK_NUMPAD3: return Keyboard::Numpad3; + case VK_NUMPAD4: return Keyboard::Numpad4; + case VK_NUMPAD5: return Keyboard::Numpad5; + case VK_NUMPAD6: return Keyboard::Numpad6; + case VK_NUMPAD7: return Keyboard::Numpad7; + case VK_NUMPAD8: return Keyboard::Numpad8; + case VK_NUMPAD9: return Keyboard::Numpad9; + case VK_F1: return Keyboard::F1; + case VK_F2: return Keyboard::F2; + case VK_F3: return Keyboard::F3; + case VK_F4: return Keyboard::F4; + case VK_F5: return Keyboard::F5; + case VK_F6: return Keyboard::F6; + case VK_F7: return Keyboard::F7; + case VK_F8: return Keyboard::F8; + case VK_F9: return Keyboard::F9; + case VK_F10: return Keyboard::F10; + case VK_F11: return Keyboard::F11; + case VK_F12: return Keyboard::F12; + case VK_F13: return Keyboard::F13; + case VK_F14: return Keyboard::F14; + case VK_F15: return Keyboard::F15; + case VK_PAUSE: return Keyboard::Pause; + default: return Keyboard::Unknown; } // clang-format on - - return (GetAsyncKeyState(vkey) & 0x8000) != 0; } +//////////////////////////////////////////////////////////// +int sfKeyToVirtualKey(Keyboard::Key key) +{ + // clang-format off + switch (key) + { + case Keyboard::A: return 'A'; + case Keyboard::B: return 'B'; + case Keyboard::C: return 'C'; + case Keyboard::D: return 'D'; + case Keyboard::E: return 'E'; + case Keyboard::F: return 'F'; + case Keyboard::G: return 'G'; + case Keyboard::H: return 'H'; + case Keyboard::I: return 'I'; + case Keyboard::J: return 'J'; + case Keyboard::K: return 'K'; + case Keyboard::L: return 'L'; + case Keyboard::M: return 'M'; + case Keyboard::N: return 'N'; + case Keyboard::O: return 'O'; + case Keyboard::P: return 'P'; + case Keyboard::Q: return 'Q'; + case Keyboard::R: return 'R'; + case Keyboard::S: return 'S'; + case Keyboard::T: return 'T'; + case Keyboard::U: return 'U'; + case Keyboard::V: return 'V'; + case Keyboard::W: return 'W'; + case Keyboard::X: return 'X'; + case Keyboard::Y: return 'Y'; + case Keyboard::Z: return 'Z'; + case Keyboard::Num0: return '0'; + case Keyboard::Num1: return '1'; + case Keyboard::Num2: return '2'; + case Keyboard::Num3: return '3'; + case Keyboard::Num4: return '4'; + case Keyboard::Num5: return '5'; + case Keyboard::Num6: return '6'; + case Keyboard::Num7: return '7'; + case Keyboard::Num8: return '8'; + case Keyboard::Num9: return '9'; + case Keyboard::Escape: return VK_ESCAPE; + case Keyboard::LControl: return VK_LCONTROL; + case Keyboard::LShift: return VK_LSHIFT; + case Keyboard::LAlt: return VK_LMENU; + case Keyboard::LSystem: return VK_LWIN; + case Keyboard::RControl: return VK_RCONTROL; + case Keyboard::RShift: return VK_RSHIFT; + case Keyboard::RAlt: return VK_RMENU; + case Keyboard::RSystem: return VK_RWIN; + case Keyboard::Menu: return VK_APPS; + case Keyboard::LBracket: return VK_OEM_4; + case Keyboard::RBracket: return VK_OEM_6; + case Keyboard::Semicolon: return VK_OEM_1; + case Keyboard::Comma: return VK_OEM_COMMA; + case Keyboard::Period: return VK_OEM_PERIOD; + case Keyboard::Apostrophe: return VK_OEM_7; + case Keyboard::Slash: return VK_OEM_2; + case Keyboard::Backslash: return VK_OEM_5; + case Keyboard::Grave: return VK_OEM_3; + case Keyboard::Equal: return VK_OEM_PLUS; + case Keyboard::Hyphen: return VK_OEM_MINUS; + case Keyboard::Space: return VK_SPACE; + case Keyboard::Enter: return VK_RETURN; + case Keyboard::Backspace: return VK_BACK; + case Keyboard::Tab: return VK_TAB; + case Keyboard::PageUp: return VK_PRIOR; + case Keyboard::PageDown: return VK_NEXT; + case Keyboard::End: return VK_END; + case Keyboard::Home: return VK_HOME; + case Keyboard::Insert: return VK_INSERT; + case Keyboard::Delete: return VK_DELETE; + case Keyboard::Add: return VK_ADD; + case Keyboard::Subtract: return VK_SUBTRACT; + case Keyboard::Multiply: return VK_MULTIPLY; + case Keyboard::Divide: return VK_DIVIDE; + case Keyboard::Left: return VK_LEFT; + case Keyboard::Right: return VK_RIGHT; + case Keyboard::Up: return VK_UP; + case Keyboard::Down: return VK_DOWN; + case Keyboard::Numpad0: return VK_NUMPAD0; + case Keyboard::Numpad1: return VK_NUMPAD1; + case Keyboard::Numpad2: return VK_NUMPAD2; + case Keyboard::Numpad3: return VK_NUMPAD3; + case Keyboard::Numpad4: return VK_NUMPAD4; + case Keyboard::Numpad5: return VK_NUMPAD5; + case Keyboard::Numpad6: return VK_NUMPAD6; + case Keyboard::Numpad7: return VK_NUMPAD7; + case Keyboard::Numpad8: return VK_NUMPAD8; + case Keyboard::Numpad9: return VK_NUMPAD9; + case Keyboard::F1: return VK_F1; + case Keyboard::F2: return VK_F2; + case Keyboard::F3: return VK_F3; + case Keyboard::F4: return VK_F4; + case Keyboard::F5: return VK_F5; + case Keyboard::F6: return VK_F6; + case Keyboard::F7: return VK_F7; + case Keyboard::F8: return VK_F8; + case Keyboard::F9: return VK_F9; + case Keyboard::F10: return VK_F10; + case Keyboard::F11: return VK_F11; + case Keyboard::F12: return VK_F12; + case Keyboard::F13: return VK_F13; + case Keyboard::F14: return VK_F14; + case Keyboard::F15: return VK_F15; + case Keyboard::Pause: return VK_PAUSE; + default: return 0; + } + // clang-format on +} + +//////////////////////////////////////////////////////////// +WORD sfScanToWinScan(Keyboard::Scancode code) +{ + // Convert an SFML scancode to a Windows scancode + // Reference: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx + // clang-format off + switch (code) + { + case Keyboard::Scan::A: return 0x1E; + case Keyboard::Scan::B: return 0x30; + case Keyboard::Scan::C: return 0x2E; + case Keyboard::Scan::D: return 0x20; + case Keyboard::Scan::E: return 0x12; + case Keyboard::Scan::F: return 0x21; + case Keyboard::Scan::G: return 0x22; + case Keyboard::Scan::H: return 0x23; + case Keyboard::Scan::I: return 0x17; + case Keyboard::Scan::J: return 0x24; + case Keyboard::Scan::K: return 0x25; + case Keyboard::Scan::L: return 0x26; + case Keyboard::Scan::M: return 0x32; + case Keyboard::Scan::N: return 0x31; + case Keyboard::Scan::O: return 0x18; + case Keyboard::Scan::P: return 0x19; + case Keyboard::Scan::Q: return 0x10; + case Keyboard::Scan::R: return 0x13; + case Keyboard::Scan::S: return 0x1F; + case Keyboard::Scan::T: return 0x14; + case Keyboard::Scan::U: return 0x16; + case Keyboard::Scan::V: return 0x2F; + case Keyboard::Scan::W: return 0x11; + case Keyboard::Scan::X: return 0x2D; + case Keyboard::Scan::Y: return 0x15; + case Keyboard::Scan::Z: return 0x2C; + + case Keyboard::Scan::Num1: return 0x02; + case Keyboard::Scan::Num2: return 0x03; + case Keyboard::Scan::Num3: return 0x04; + case Keyboard::Scan::Num4: return 0x05; + case Keyboard::Scan::Num5: return 0x06; + case Keyboard::Scan::Num6: return 0x07; + case Keyboard::Scan::Num7: return 0x08; + case Keyboard::Scan::Num8: return 0x09; + case Keyboard::Scan::Num9: return 0x0A; + case Keyboard::Scan::Num0: return 0x0B; + + case Keyboard::Scan::Enter: return 0x1C; + case Keyboard::Scan::Escape: return 0x01; + case Keyboard::Scan::Backspace: return 0x0E; + case Keyboard::Scan::Tab: return 0x0F; + case Keyboard::Scan::Space: return 0x39; + case Keyboard::Scan::Hyphen: return 0x0C; + case Keyboard::Scan::Equal: return 0x0D; + case Keyboard::Scan::LBracket: return 0x1A; + case Keyboard::Scan::RBracket: return 0x1B; + case Keyboard::Scan::Backslash: return 0x2B; + case Keyboard::Scan::Semicolon: return 0x27; + case Keyboard::Scan::Apostrophe: return 0x28; + case Keyboard::Scan::Grave: return 0x29; + case Keyboard::Scan::Comma: return 0x33; + case Keyboard::Scan::Period: return 0x34; + case Keyboard::Scan::Slash: return 0x35; + + case Keyboard::Scan::F1: return 0x3B; + case Keyboard::Scan::F2: return 0x3C; + case Keyboard::Scan::F3: return 0x3D; + case Keyboard::Scan::F4: return 0x3E; + case Keyboard::Scan::F5: return 0x3F; + case Keyboard::Scan::F6: return 0x40; + case Keyboard::Scan::F7: return 0x41; + case Keyboard::Scan::F8: return 0x42; + case Keyboard::Scan::F9: return 0x43; + case Keyboard::Scan::F10: return 0x44; + case Keyboard::Scan::F11: return 0x57; + case Keyboard::Scan::F12: return 0x58; + case Keyboard::Scan::F13: return 0x64; + case Keyboard::Scan::F14: return 0x65; + case Keyboard::Scan::F15: return 0x66; + case Keyboard::Scan::F16: return 0x67; + case Keyboard::Scan::F17: return 0x68; + case Keyboard::Scan::F18: return 0x69; + case Keyboard::Scan::F19: return 0x6A; + case Keyboard::Scan::F20: return 0x6B; + case Keyboard::Scan::F21: return 0x6C; + case Keyboard::Scan::F22: return 0x6D; + case Keyboard::Scan::F23: return 0x6E; + case Keyboard::Scan::F24: return 0x76; + + case Keyboard::Scan::CapsLock: return 0x3A; + case Keyboard::Scan::PrintScreen: return 0xE037; + case Keyboard::Scan::ScrollLock: return 0x46; + case Keyboard::Scan::Pause: return 0x45; + case Keyboard::Scan::Insert: return 0xE052; + case Keyboard::Scan::Home: return 0xE047; + case Keyboard::Scan::PageUp: return 0xE049; + case Keyboard::Scan::Delete: return 0xE053; + case Keyboard::Scan::End: return 0xE04F; + case Keyboard::Scan::PageDown: return 0xE051; + case Keyboard::Scan::Right: return 0xE04D; + case Keyboard::Scan::Left: return 0xE04B; + case Keyboard::Scan::Down: return 0xE050; + case Keyboard::Scan::Up: return 0xE048; + case Keyboard::Scan::NumLock: return 0xE045; + + case Keyboard::Scan::NumpadDivide: return 0xE035; + case Keyboard::Scan::NumpadMultiply: return 0x37; + case Keyboard::Scan::NumpadMinus: return 0x4A; + case Keyboard::Scan::NumpadPlus: return 0x4E; + case Keyboard::Scan::NumpadEqual: return 0x7E; + case Keyboard::Scan::NumpadEnter: return 0xE01C; + case Keyboard::Scan::NumpadDecimal: return 0x53; + + case Keyboard::Scan::Numpad1: return 0x4F; + case Keyboard::Scan::Numpad2: return 0x50; + case Keyboard::Scan::Numpad3: return 0x51; + case Keyboard::Scan::Numpad4: return 0x4B; + case Keyboard::Scan::Numpad5: return 0x4C; + case Keyboard::Scan::Numpad6: return 0x4D; + case Keyboard::Scan::Numpad7: return 0x47; + case Keyboard::Scan::Numpad8: return 0x48; + case Keyboard::Scan::Numpad9: return 0x49; + case Keyboard::Scan::Numpad0: return 0x52; + + case Keyboard::Scan::NonUsBackslash: return 0x56; + // No known scancode for Keyboard::Scan::Application + // No known scancode for Keyboard::Scan::Execute + // No known scancode for Keyboard::Scan::ModeChange + case Keyboard::Scan::Help: return 0xE061; + case Keyboard::Scan::Menu: return 0xE05D; + case Keyboard::Scan::Select: return 0xE01E; + // No known scancode for Keyboard::Scan::Redo + // No known scancode for Keyboard::Scan::Undo + // No known scancode for Keyboard::Scan::Cut + // No known scancode for Keyboard::Scan::Copy + // No known scancode for Keyboard::Scan::Paste + + case Keyboard::Scan::VolumeMute: return 0xE020; + case Keyboard::Scan::VolumeUp: return 0xE02E; + case Keyboard::Scan::VolumeDown: return 0xE02C; + case Keyboard::Scan::MediaPlayPause: return 0xE022; + case Keyboard::Scan::MediaStop: return 0xE024; + case Keyboard::Scan::MediaNextTrack: return 0xE019; + case Keyboard::Scan::MediaPreviousTrack: return 0xE010; + + case Keyboard::Scan::LControl: return 0x1D; + case Keyboard::Scan::LShift: return 0x2A; + case Keyboard::Scan::LAlt: return 0x38; + case Keyboard::Scan::LSystem: return 0xE05B; + case Keyboard::Scan::RControl: return 0xE01D; + case Keyboard::Scan::RShift: return 0x36; + case Keyboard::Scan::RAlt: return 0xE038; + case Keyboard::Scan::RSystem: return 0xE05C; + + case Keyboard::Scan::Back: return 0xE06A; + case Keyboard::Scan::Forward: return 0xE069; + case Keyboard::Scan::Refresh: return 0xE067; + case Keyboard::Scan::Stop: return 0xE068; + case Keyboard::Scan::Search: return 0xE065; + case Keyboard::Scan::Favorites: return 0xE066; + case Keyboard::Scan::HomePage: return 0xE030; + + case Keyboard::Scan::LaunchApplication1: return 0xE06B; + case Keyboard::Scan::LaunchApplication2: return 0xE021; + case Keyboard::Scan::LaunchMail: return 0xE06C; + case Keyboard::Scan::LaunchMediaSelect: return 0xE06D; + + // Unable to map to a scancode + default: return 0x0; + } + // clang-format on +} + +//////////////////////////////////////////////////////////// +WORD sfScanToWinScanExtended(Keyboard::Scancode code) +{ + // Convert an SFML scancode to a Windows scancode + // Reference: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx + // clang-format off + switch (code) + { + case Keyboard::Scan::PrintScreen: return 55 | 0xE100; + case Keyboard::Scan::Insert: return 82 | 0xE100; + case Keyboard::Scan::Home: return 71 | 0xE100; + case Keyboard::Scan::PageUp: return 73 | 0xE100; + case Keyboard::Scan::Delete: return 83 | 0xE100; + case Keyboard::Scan::End: return 79 | 0xE100; + case Keyboard::Scan::PageDown: return 81 | 0xE100; + case Keyboard::Scan::Right: return 77 | 0xE100; + case Keyboard::Scan::Left: return 75 | 0xE100; + case Keyboard::Scan::Down: return 80 | 0xE100; + case Keyboard::Scan::Up: return 72 | 0xE100; + case Keyboard::Scan::NumLock: return 69 | 0xE100; + case Keyboard::Scan::NumpadEnter: return 28 | 0xE100; + case Keyboard::Scan::NumpadDivide: return 53 | 0xE100; + case Keyboard::Scan::Help: return 97 | 0xE100; + case Keyboard::Scan::Menu: return 93 | 0xE100; + case Keyboard::Scan::Select: return 30 | 0xE100; + case Keyboard::Scan::VolumeMute: return 32 | 0xE100; + case Keyboard::Scan::VolumeUp: return 46 | 0xE100; + case Keyboard::Scan::VolumeDown: return 44 | 0xE100; + case Keyboard::Scan::MediaPlayPause: return 34 | 0xE100; + case Keyboard::Scan::MediaStop: return 36 | 0xE100; + case Keyboard::Scan::MediaNextTrack: return 25 | 0xE100; + case Keyboard::Scan::MediaPreviousTrack: return 16 | 0xE100; + case Keyboard::Scan::LSystem: return 91 | 0xE100; + case Keyboard::Scan::RControl: return 29 | 0xE100; + case Keyboard::Scan::RAlt: return 56 | 0xE100; + case Keyboard::Scan::RSystem: return 92 | 0xE100; + case Keyboard::Scan::Back: return 106 | 0xE100; + case Keyboard::Scan::Forward: return 105 | 0xE100; + case Keyboard::Scan::Refresh: return 103 | 0xE100; + case Keyboard::Scan::Stop: return 104 | 0xE100; + case Keyboard::Scan::Search: return 101 | 0xE100; + case Keyboard::Scan::Favorites: return 102 | 0xE100; + case Keyboard::Scan::HomePage: return 48 | 0xE100; + case Keyboard::Scan::LaunchApplication1: return 107 | 0xE100; + case Keyboard::Scan::LaunchApplication2: return 33 | 0xE100; + case Keyboard::Scan::LaunchMail: return 108 | 0xE100; + case Keyboard::Scan::LaunchMediaSelect: return 109 | 0xE100; + + // Use non-extended mapping + default: return sfScanToWinScan(code); + } + // clang-format on +} + +UINT sfScanToVirtualKey(Keyboard::Scancode code) +{ + WORD winScancode = sfScanToWinScan(code); + + // Manually map non-extended key codes + // clang-format off + switch (code) + { + case Keyboard::Scan::Numpad0: return VK_NUMPAD0; + case Keyboard::Scan::Numpad1: return VK_NUMPAD1; + case Keyboard::Scan::Numpad2: return VK_NUMPAD2; + case Keyboard::Scan::Numpad3: return VK_NUMPAD3; + case Keyboard::Scan::Numpad4: return VK_NUMPAD4; + case Keyboard::Scan::Numpad5: return VK_NUMPAD5; + case Keyboard::Scan::Numpad6: return VK_NUMPAD6; + case Keyboard::Scan::Numpad7: return VK_NUMPAD7; + case Keyboard::Scan::Numpad8: return VK_NUMPAD8; + case Keyboard::Scan::Numpad9: return VK_NUMPAD9; + case Keyboard::Scan::NumpadMinus: return VK_SUBTRACT; + case Keyboard::Scan::NumpadDecimal: return VK_DECIMAL; + case Keyboard::Scan::NumpadDivide: return VK_DIVIDE; + case Keyboard::Scan::Pause: return VK_PAUSE; + case Keyboard::Scan::RControl: return VK_RCONTROL; + case Keyboard::Scan::RAlt: return VK_RMENU; + default: return MapVirtualKey(winScancode, MAPVK_VSC_TO_VK_EX); + } + // clang-format on +} + +bool isValidScancode(Keyboard::Scancode code) +{ + return code > Keyboard::Scan::Unknown && code < Keyboard::Scan::ScancodeCount; +} + +bool isValidKey(Keyboard::Key key) +{ + return key > Keyboard::Unknown && key < Keyboard::KeyCount; +} + +//////////////////////////////////////////////////////////// +void InputImpl::ensureMappings() +{ + static bool isMappingInitialized = false; + + if (isMappingInitialized) + return; + + // Phase 1: Initialize mappings with default values + for (auto& scancode : m_keyToScancodeMapping) + scancode = Keyboard::Scan::Unknown; + + for (auto& key : m_scancodeToKeyMapping) + key = Keyboard::Unknown; + + // Phase 2: Translate scancode to virtual code to key names + for (int i = 0; i < Keyboard::Scan::ScancodeCount; ++i) + { + Keyboard::Scancode scan = static_cast(i); + UINT virtualKey = sfScanToVirtualKey(scan); + Keyboard::Key key = virtualKeyToSfKey(virtualKey); + if (key != Keyboard::Unknown && m_keyToScancodeMapping[key] == Keyboard::Scan::Unknown) + m_keyToScancodeMapping[key] = scan; + m_scancodeToKeyMapping[scan] = key; + } + + isMappingInitialized = true; +} + +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Key key) +{ + int virtualKey = sfKeyToVirtualKey(key); + return (GetAsyncKeyState(virtualKey) & 0x8000) != 0; +} + +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Scancode code) +{ + UINT virtualKey = sfScanToVirtualKey(code); + return (GetAsyncKeyState(static_cast(virtualKey)) & KF_UP) != 0; +} + +//////////////////////////////////////////////////////////// +Keyboard::Key InputImpl::localize(Keyboard::Scancode code) +{ + if (!isValidScancode(code)) + return Keyboard::Unknown; + + ensureMappings(); + + return m_scancodeToKeyMapping[code]; +} + +//////////////////////////////////////////////////////////// +Keyboard::Scancode InputImpl::delocalize(Keyboard::Key key) +{ + if (!isValidKey(key)) + return Keyboard::Scan::Unknown; + + ensureMappings(); + + return m_keyToScancodeMapping[key]; +} + +//////////////////////////////////////////////////////////// +String InputImpl::getDescription(Keyboard::Scancode code) +{ + WORD winCode = sfScanToWinScanExtended(code); + const int bufSize = 1024; + WCHAR name[bufSize]; + int result = GetKeyNameText(winCode << 16, name, bufSize); + if (result > 0) + { + return name; + } + return "Unknown"; +} //////////////////////////////////////////////////////////// void InputImpl::setVirtualKeyboardVisible(bool /*visible*/) @@ -159,30 +614,30 @@ void InputImpl::setVirtualKeyboardVisible(bool /*visible*/) //////////////////////////////////////////////////////////// bool InputImpl::isMouseButtonPressed(Mouse::Button button) { - int vkey = 0; + int virtualKey = 0; switch (button) { case Mouse::Left: - vkey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON; + virtualKey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON; break; case Mouse::Right: - vkey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON; + virtualKey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_LBUTTON : VK_RBUTTON; break; case Mouse::Middle: - vkey = VK_MBUTTON; + virtualKey = VK_MBUTTON; break; case Mouse::XButton1: - vkey = VK_XBUTTON1; + virtualKey = VK_XBUTTON1; break; case Mouse::XButton2: - vkey = VK_XBUTTON2; + virtualKey = VK_XBUTTON2; break; default: - vkey = 0; + virtualKey = 0; break; } - return (GetAsyncKeyState(vkey) & 0x8000) != 0; + return (GetAsyncKeyState(virtualKey) & 0x8000) != 0; } diff --git a/src/SFML/Window/Win32/InputImpl.hpp b/src/SFML/Window/Win32/InputImpl.hpp index 9342833a..b37da8c2 100644 --- a/src/SFML/Window/Win32/InputImpl.hpp +++ b/src/SFML/Window/Win32/InputImpl.hpp @@ -41,19 +41,37 @@ class InputImpl { public: //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise + /// \copydoc sf::Keyboard::isKeyPressed(Key) /// //////////////////////////////////////////////////////////// static bool isKeyPressed(Keyboard::Key key); //////////////////////////////////////////////////////////// - /// \brief Show or hide the virtual keyboard + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) /// - /// \param visible True to show, false to hide + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::setVirtualKeyboardVisible /// //////////////////////////////////////////////////////////// static void setVirtualKeyboardVisible(bool visible); @@ -154,6 +172,21 @@ public: /// //////////////////////////////////////////////////////////// static Vector2i getTouchPosition(unsigned int finger, const WindowBase& relativeTo); + +private: + //////////////////////////////////////////////////////////// + /// Ensure the mappings are generated from/to Key and Scancode. + /// + //////////////////////////////////////////////////////////// + static void ensureMappings(); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + // NOLINTBEGIN(readability-identifier-naming) + static Keyboard::Scancode m_keyToScancodeMapping[Keyboard::KeyCount]; ///< Mapping from Key to Scancode + static Keyboard::Key m_scancodeToKeyMapping[Keyboard::Scan::ScancodeCount]; ///< Mapping from Scancode to Key + // NOLINTEND(readability-identifier-naming) }; } // namespace sf::priv diff --git a/src/SFML/Window/Win32/WindowImplWin32.cpp b/src/SFML/Window/Win32/WindowImplWin32.cpp index 753e6d83..a35c67cf 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.cpp +++ b/src/SFML/Window/Win32/WindowImplWin32.cpp @@ -560,6 +560,133 @@ void WindowImplWin32::grabCursor(bool grabbed) } } +//////////////////////////////////////////////////////////// +Keyboard::Scancode WindowImplWin32::toScancode(WPARAM wParam, LPARAM lParam) +{ + int code = (lParam & (0xFF << 16)) >> 16; + + // Retrieve the scancode from the VirtualKey for synthetic key messages + if (code == 0) + { + code = static_cast(MapVirtualKey(static_cast(wParam), MAPVK_VK_TO_VSC)); + } + + // Windows scancodes + // Reference: https://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx + // clang-format off + switch (code) + { + case 1: return Keyboard::Scan::Escape; + case 2: return Keyboard::Scan::Num1; + case 3: return Keyboard::Scan::Num2; + case 4: return Keyboard::Scan::Num3; + case 5: return Keyboard::Scan::Num4; + case 6: return Keyboard::Scan::Num5; + case 7: return Keyboard::Scan::Num6; + case 8: return Keyboard::Scan::Num7; + case 9: return Keyboard::Scan::Num8; + case 10: return Keyboard::Scan::Num9; + case 11: return Keyboard::Scan::Num0; + case 12: return Keyboard::Scan::Hyphen; + case 13: return Keyboard::Scan::Equal; + case 14: return Keyboard::Scan::Backspace; + case 15: return Keyboard::Scan::Tab; + case 16: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::MediaPreviousTrack : Keyboard::Scan::Q; + case 17: return Keyboard::Scan::W; + case 18: return Keyboard::Scan::E; + case 19: return Keyboard::Scan::R; + case 20: return Keyboard::Scan::T; + case 21: return Keyboard::Scan::Y; + case 22: return Keyboard::Scan::U; + case 23: return Keyboard::Scan::I; + case 24: return Keyboard::Scan::O; + case 25: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::MediaNextTrack : Keyboard::Scan::P; + case 26: return Keyboard::Scan::LBracket; + case 27: return Keyboard::Scan::RBracket; + case 28: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::NumpadEnter : Keyboard::Scan::Enter; + case 29: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::RControl : Keyboard::Scan::LControl; + case 30: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Select : Keyboard::Scan::A; + case 31: return Keyboard::Scan::S; + case 32: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::VolumeMute : Keyboard::Scan::D; + case 33: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::LaunchApplication1 : Keyboard::Scan::F; + case 34: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::MediaPlayPause : Keyboard::Scan::G; + case 35: return Keyboard::Scan::H; + case 36: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::MediaStop : Keyboard::Scan::J; + case 37: return Keyboard::Scan::K; + case 38: return Keyboard::Scan::L; + case 39: return Keyboard::Scan::Semicolon; + case 40: return Keyboard::Scan::Apostrophe; + case 41: return Keyboard::Scan::Grave; + case 42: return Keyboard::Scan::LShift; + case 43: return Keyboard::Scan::Backslash; + case 44: return Keyboard::Scan::Z; + case 45: return Keyboard::Scan::X; + case 46: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::VolumeDown : Keyboard::Scan::C; + case 47: return Keyboard::Scan::V; + case 48: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::VolumeUp : Keyboard::Scan::B; + case 49: return Keyboard::Scan::N; + case 50: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::HomePage : Keyboard::Scan::M; + case 51: return Keyboard::Scan::Comma; + case 52: return Keyboard::Scan::Period; + case 53: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::NumpadDivide : Keyboard::Scan::Slash; + case 54: return Keyboard::Scan::RShift; + case 55: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::PrintScreen : Keyboard::Scan::NumpadMultiply; + case 56: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::RAlt : Keyboard::Scan::LAlt; + case 57: return Keyboard::Scan::Space; + case 58: return Keyboard::Scan::CapsLock; + case 59: return Keyboard::Scan::F1; + case 60: return Keyboard::Scan::F2; + case 61: return Keyboard::Scan::F3; + case 62: return Keyboard::Scan::F4; + case 63: return Keyboard::Scan::F5; + case 64: return Keyboard::Scan::F6; + case 65: return Keyboard::Scan::F7; + case 66: return Keyboard::Scan::F8; + case 67: return Keyboard::Scan::F9; + case 68: return Keyboard::Scan::F10; + case 69: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::NumLock : Keyboard::Scan::Pause; + case 70: return Keyboard::Scan::ScrollLock; + case 71: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Home : Keyboard::Scan::Numpad7; + case 72: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Up : Keyboard::Scan::Numpad8; + case 73: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::PageUp : Keyboard::Scan::Numpad9; + case 74: return Keyboard::Scan::NumpadMinus; + case 75: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Left : Keyboard::Scan::Numpad4; + case 76: return Keyboard::Scan::Numpad5; + case 77: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Right : Keyboard::Scan::Numpad6; + case 78: return Keyboard::Scan::NumpadPlus; + case 79: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::End : Keyboard::Scan::Numpad1; + case 80: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Down : Keyboard::Scan::Numpad2; + case 81: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::PageDown : Keyboard::Scan::Numpad3; + case 82: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Insert : Keyboard::Scan::Numpad0; + case 83: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Delete : Keyboard::Scan::NumpadDecimal; + + case 86: return Keyboard::Scan::NonUsBackslash; + case 87: return Keyboard::Scan::F11; + case 88: return Keyboard::Scan::F12; + + case 91: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::LSystem : Keyboard::Scan::Unknown; + case 92: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::RSystem : Keyboard::Scan::Unknown; + case 93: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Menu : Keyboard::Scan::Unknown; + + case 99: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Help : Keyboard::Scan::Unknown; + case 100: return Keyboard::Scan::F13; + case 101: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Search : Keyboard::Scan::F14; + case 102: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Favorites : Keyboard::Scan::F15; + case 103: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Refresh : Keyboard::Scan::F16; + case 104: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Stop : Keyboard::Scan::F17; + case 105: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Forward : Keyboard::Scan::F18; + case 106: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::Back : Keyboard::Scan::F19; + case 107: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::LaunchApplication1 : Keyboard::Scan::F20; + case 108: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::LaunchMail : Keyboard::Scan::F21; + case 109: return (HIWORD(lParam) & KF_EXTENDED) ? Keyboard::Scan::LaunchMediaSelect : Keyboard::Scan::F22; + case 110: return Keyboard::Scan::F23; + + case 118: return Keyboard::Scan::F24; + + default: return Keyboard::Scan::Unknown; + } + // clang-format on +} //////////////////////////////////////////////////////////// void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) @@ -730,12 +857,13 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) if (m_keyRepeatEnabled || ((HIWORD(lParam) & KF_REPEAT) == 0)) { Event event; - event.type = Event::KeyPressed; - event.key.alt = HIWORD(GetKeyState(VK_MENU)) != 0; - event.key.control = HIWORD(GetKeyState(VK_CONTROL)) != 0; - event.key.shift = HIWORD(GetKeyState(VK_SHIFT)) != 0; - event.key.system = HIWORD(GetKeyState(VK_LWIN)) || HIWORD(GetKeyState(VK_RWIN)); - event.key.code = virtualKeyCodeToSF(wParam, lParam); + event.type = Event::KeyPressed; + event.key.alt = HIWORD(GetKeyState(VK_MENU)) != 0; + event.key.control = HIWORD(GetKeyState(VK_CONTROL)) != 0; + event.key.shift = HIWORD(GetKeyState(VK_SHIFT)) != 0; + event.key.system = HIWORD(GetKeyState(VK_LWIN)) || HIWORD(GetKeyState(VK_RWIN)); + event.key.code = virtualKeyCodeToSF(wParam, lParam); + event.key.scancode = toScancode(wParam, lParam); pushEvent(event); } break; @@ -746,12 +874,13 @@ void WindowImplWin32::processEvent(UINT message, WPARAM wParam, LPARAM lParam) case WM_SYSKEYUP: { Event event; - event.type = Event::KeyReleased; - event.key.alt = HIWORD(GetKeyState(VK_MENU)) != 0; - event.key.control = HIWORD(GetKeyState(VK_CONTROL)) != 0; - event.key.shift = HIWORD(GetKeyState(VK_SHIFT)) != 0; - event.key.system = HIWORD(GetKeyState(VK_LWIN)) || HIWORD(GetKeyState(VK_RWIN)); - event.key.code = virtualKeyCodeToSF(wParam, lParam); + event.type = Event::KeyReleased; + event.key.alt = HIWORD(GetKeyState(VK_MENU)) != 0; + event.key.control = HIWORD(GetKeyState(VK_CONTROL)) != 0; + event.key.shift = HIWORD(GetKeyState(VK_SHIFT)) != 0; + event.key.system = HIWORD(GetKeyState(VK_LWIN)) || HIWORD(GetKeyState(VK_RWIN)); + event.key.code = virtualKeyCodeToSF(wParam, lParam); + event.key.scancode = toScancode(wParam, lParam); pushEvent(event); break; } @@ -1029,9 +1158,9 @@ Keyboard::Key WindowImplWin32::virtualKeyCodeToSF(WPARAM key, LPARAM flags) case VK_OEM_6: return Keyboard::RBracket; case VK_OEM_COMMA: return Keyboard::Comma; case VK_OEM_PERIOD: return Keyboard::Period; - case VK_OEM_7: return Keyboard::Quote; + case VK_OEM_7: return Keyboard::Apostrophe; case VK_OEM_5: return Keyboard::Backslash; - case VK_OEM_3: return Keyboard::Tilde; + case VK_OEM_3: return Keyboard::Grave; case VK_ESCAPE: return Keyboard::Escape; case VK_SPACE: return Keyboard::Space; case VK_RETURN: return Keyboard::Enter; diff --git a/src/SFML/Window/Win32/WindowImplWin32.hpp b/src/SFML/Window/Win32/WindowImplWin32.hpp index 93b1eb62..66cc37e6 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.hpp +++ b/src/SFML/Window/Win32/WindowImplWin32.hpp @@ -265,6 +265,16 @@ private: //////////////////////////////////////////////////////////// static LRESULT CALLBACK globalOnEvent(HWND handle, UINT message, WPARAM wParam, LPARAM lParam); + //////////////////////////////////////////////////////////// + /// \brief Convert a Win32 scancode to an sfml scancode + /// + /// \param flags input flags + /// + /// \return SFML scancode corresponding to the key + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode toScancode(WPARAM wParam, LPARAM lParam); + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/iOS/InputImpl.hpp b/src/SFML/Window/iOS/InputImpl.hpp index 4f75fcde..44def623 100644 --- a/src/SFML/Window/iOS/InputImpl.hpp +++ b/src/SFML/Window/iOS/InputImpl.hpp @@ -41,19 +41,37 @@ class InputImpl { public: //////////////////////////////////////////////////////////// - /// \brief Check if a key is pressed - /// - /// \param key Key to check - /// - /// \return True if the key is pressed, false otherwise + /// \copydoc sf::Keyboard::isKeyPressed(Key) /// //////////////////////////////////////////////////////////// static bool isKeyPressed(Keyboard::Key key); //////////////////////////////////////////////////////////// - /// \brief Show or hide the virtual keyboard + /// \copydoc sf::Keyboard::isKeyPressed(Scancode) /// - /// \param visible True to show, false to hide + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::localize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Key localize(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::delocalize + /// + //////////////////////////////////////////////////////////// + static Keyboard::Scancode delocalize(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::getDescription + /// + //////////////////////////////////////////////////////////// + static String getDescription(Keyboard::Scancode code); + + //////////////////////////////////////////////////////////// + /// \copydoc sf::Keyboard::setVirtualKeyboardVisible /// //////////////////////////////////////////////////////////// static void setVirtualKeyboardVisible(bool visible); diff --git a/src/SFML/Window/iOS/InputImpl.mm b/src/SFML/Window/iOS/InputImpl.mm index 470077e8..c722b582 100644 --- a/src/SFML/Window/iOS/InputImpl.mm +++ b/src/SFML/Window/iOS/InputImpl.mm @@ -41,6 +41,29 @@ bool InputImpl::isKeyPressed(Keyboard::Key /* key */) return false; } +bool InputImpl::isKeyPressed(Keyboard::Scancode /* codes */) +{ + // Not applicable + return false; +} + +Keyboard::Key InputImpl::localize(Keyboard::Scancode /* code */) +{ + // Not applicable + return Keyboard::Unknown; +} + +Keyboard::Scancode InputImpl::delocalize(Keyboard::Key /* key */) +{ + // Not applicable + return Keyboard::Scan::Unknown; +} + +String InputImpl::getDescription(Keyboard::Scancode /* code */) +{ + // Not applicable + return ""; +} //////////////////////////////////////////////////////////// void InputImpl::setVirtualKeyboardVisible(bool visible)