From 9dc7f3dc79e0e3a5c0791c2c4c6f0197ba2df82c Mon Sep 17 00:00:00 2001
From: Marco Antognini <antognini.marco@gmail.com>
Date: Tue, 28 Mar 2017 23:37:06 +0200
Subject: [PATCH] Add support of scancodes for macOS

---
 src/SFML/Window/OSX/HIDInputManager.hpp       |  140 +-
 src/SFML/Window/OSX/HIDInputManager.mm        | 1574 +++++++++++------
 src/SFML/Window/OSX/InputImpl.mm              |   32 +-
 .../Window/OSX/SFKeyboardModifiersHelper.h    |    3 +-
 .../Window/OSX/SFKeyboardModifiersHelper.mm   |   27 +-
 src/SFML/Window/OSX/SFOpenGLView+keyboard.mm  |   22 +-
 6 files changed, 1167 insertions(+), 631 deletions(-)

diff --git a/src/SFML/Window/OSX/HIDInputManager.hpp b/src/SFML/Window/OSX/HIDInputManager.hpp
index 3e143c8a..9a82a7b9 100644
--- a/src/SFML/Window/OSX/HIDInputManager.hpp
+++ b/src/SFML/Window/OSX/HIDInputManager.hpp
@@ -65,16 +65,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);
-
 public:
 
     ////////////////////////////////////////////////////////////
@@ -103,21 +93,64 @@ public:
     ///
     /// 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.
+    /// 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);
+
+    ////////////////////////////////////////////////////////////
+    /// Try to convert a virtual keycode (HID level) into a
+    /// SFML scancode.
+    ///
+    /// Return sf::Keyboard::sUnknown if the keycode is unknown.
+    ///
+    ////////////////////////////////////////////////////////////
+    static Keyboard::Scancode nonLocalizedKey(UniChar virtualKeycode);
+
+public:
+
+    ////////////////////////////////////////////////////////////
+    /// \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::unlocalize
+    ///
+    ////////////////////////////////////////////////////////////
+    Keyboard::Scancode unlocalize(Keyboard::Key key);
+
+    ////////////////////////////////////////////////////////////
+    /// \copydoc sf::Keyboard::localizedRepresentation
+    ///
+    ////////////////////////////////////////////////////////////
+    String localizedRepresentation(Keyboard::Scancode code);
 
 private:
 
@@ -136,7 +169,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:
+    ///         memorise k -> scancode mapping
     ///
     ////////////////////////////////////////////////////////////
     void initializeKeyboard();
@@ -145,28 +184,39 @@ 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);
 
+    ////////////////////////////////////////////////////////////
+    /// Regenerate the mappings from/to Key and Scancode
+    ///
+    ////////////////////////////////////////////////////////////
+    void buildMappings();
+
     ////////////////////////////////////////////////////////////
     /// \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();
@@ -174,11 +224,11 @@ 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 NULL
+    /// \return a retained, non-emtpy CFSetRef of IOHIDDeviceRef or NULL
     ///
     ////////////////////////////////////////////////////////////
     CFSetRef copyDevices(UInt32 page, UInt32 usage);
@@ -196,36 +246,48 @@ 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 associate with the given HID key usage
+    ///         or sUnknown if it is associate with no scancode.
     ///
     ////////////////////////////////////////////////////////////
-    static UInt8 usageToVirtualCode(UInt32 usage);
+    static Keyboard::Scancode usageToScancode(UInt32 usage);
+
+    ////////////////////////////////////////////////////////////
+    /// Convert the scancode to the expected virtual code.
+    ///
+    ////////////////////////////////////////////////////////////
+    static UInt8 scanToVirtualCode(Keyboard::Scancode code);
+
+    ////////////////////////////////////////////////////////////
+    /// Fallback convertion for key that aren't expected to be impacted
+    /// by the layout. Can return Unknown.
+    ///
+    ////////////////////////////////////////////////////////////
+    static Keyboard::Key localizedKeyFallback(Keyboard::Scancode code);
 
 private:
 
     ////////////////////////////////////////////////////////////
     // Member data
     ////////////////////////////////////////////////////////////
-    bool              m_isValid;                    ///< 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::sCodeCount];    ///< All the keys on any connected keyboard
+    Keyboard::Scancode m_mapping[Keyboard::KeyCount];   ///< Mapping from Key to Scancode
+    Keyboard::Key      m_gnippam[Keyboard::sCodeCount]; ///< 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 (s)Unknown.
+    ///
     ////////////////////////////////////////////////////////////
 };
 
diff --git a/src/SFML/Window/OSX/HIDInputManager.mm b/src/SFML/Window/OSX/HIDInputManager.mm
index 7fd40ecb..549829ff 100644
--- a/src/SFML/Window/OSX/HIDInputManager.mm
+++ b/src/SFML/Window/OSX/HIDInputManager.mm
@@ -42,13 +42,6 @@ HIDInputManager& HIDInputManager::getInstance()
 }
 
 
-////////////////////////////////////////////////////////////
-bool HIDInputManager::isKeyPressed(Keyboard::Key key)
-{
-    return isPressed(m_keys[key]);
-}
-
-
 ////////////////////////////////////////////////////////////
 long HIDInputManager::getLocationID(IOHIDDeviceRef device)
 {
@@ -91,36 +84,141 @@ CFDictionaryRef HIDInputManager::copyDevicesMask(UInt32 page, UInt32 usage)
 }
 
 
+////////////////////////////////////////////////////////////
+bool HIDInputManager::isKeyPressed(Keyboard::Key key)
+{
+    return isKeyPressed(unlocalize(key));
+}
+
+
+////////////////////////////////////////////////////////////
+bool HIDInputManager::isKeyPressed(Keyboard::Scancode code)
+{
+    return (code != Keyboard::sUnknown) && isPressed(m_keys[code]);
+}
+
+
+////////////////////////////////////////////////////////////
+Keyboard::Key HIDInputManager::localize(Keyboard::Scancode code)
+{
+    // TODO ensure mapping is still valid
+    return m_gnippam[code];
+}
+
+
+////////////////////////////////////////////////////////////
+Keyboard::Scancode HIDInputManager::unlocalize(Keyboard::Key key)
+{
+    // TODO ensure mapping is still valid
+    return m_mapping[key];
+}
+
+
+////////////////////////////////////////////////////////////
+String HIDInputManager::localizedRepresentation(Keyboard::Scancode code)
+{
+    UniChar unicode = toUnicode(localize(code));
+    if (unicode != 0x00)
+        return sf::String(static_cast<Uint32>(unicode));
+
+    // Fallback to our best guess for the keys that are known to be independent of the layout.
+    switch (code)
+    {
+        case sf::Keyboard::sEnter:      return "Enter";
+        case sf::Keyboard::sEscape:     return "Escape";
+        case sf::Keyboard::sBackspace:  return "Backspace";
+        case sf::Keyboard::sTab:        return "Tab";
+        case sf::Keyboard::sSpace:      return "Space";
+
+        case sf::Keyboard::sF1:   return "F1";
+        case sf::Keyboard::sF2:   return "F2";
+        case sf::Keyboard::sF3:   return "F3";
+        case sf::Keyboard::sF4:   return "F4";
+        case sf::Keyboard::sF5:   return "F5";
+        case sf::Keyboard::sF6:   return "F6";
+        case sf::Keyboard::sF7:   return "F7";
+        case sf::Keyboard::sF8:   return "F8";
+        case sf::Keyboard::sF9:   return "F9";
+        case sf::Keyboard::sF10:  return "F10";
+        case sf::Keyboard::sF11:  return "F11";
+        case sf::Keyboard::sF12:  return "F12";
+        case sf::Keyboard::sF13:  return "F13";
+        case sf::Keyboard::sF14:  return "F14";
+        case sf::Keyboard::sF15:  return "F15";
+
+        case sf::Keyboard::sCapsLock:     return "CapsLock";
+        case sf::Keyboard::sPrintScreen:  return "PrintScreen";
+        case sf::Keyboard::sScrollLock:   return "ScrollLock";
+
+        case sf::Keyboard::sPause:    return "Pause";
+        case sf::Keyboard::sInsert:   return "Insert";
+        case sf::Keyboard::sHome:     return "Home";
+        case sf::Keyboard::sPageUp:   return "PageUp";
+        case sf::Keyboard::sDelete:   return "Delete";
+        case sf::Keyboard::sEnd:      return "End";
+        case sf::Keyboard::sPageDown: return "PageDown";
+
+        case sf::Keyboard::sLeft:   return "Left Arrow";
+        case sf::Keyboard::sRight:  return "Right Arrow";
+        case sf::Keyboard::sDown:   return "Down Arrow";
+        case sf::Keyboard::sUp:     return "Up Arrow";
+
+        case sf::Keyboard::sNumLock:    return "NumLock";
+        case sf::Keyboard::sDivide:     return "Divide (Numpad)";
+        case sf::Keyboard::sMultiply:   return "Multiply (Numpad)";
+        case sf::Keyboard::sMinus:      return "Minux (Numpad)";
+        case sf::Keyboard::sPlus:       return "Plus (Numpad)";
+        case sf::Keyboard::sPadEquals:  return "Equals (Numpad)";
+        case sf::Keyboard::sReturn:     return "Return (Numpad)";
+        case sf::Keyboard::sDecimal:    return "Decimal (Numpad)";
+
+        case sf::Keyboard::sNumpad0:  return "0 (Numpad)";
+        case sf::Keyboard::sNumpad1:  return "1 (Numpad)";
+        case sf::Keyboard::sNumpad2:  return "2 (Numpad)";
+        case sf::Keyboard::sNumpad3:  return "3 (Numpad)";
+        case sf::Keyboard::sNumpad4:  return "4 (Numpad)";
+        case sf::Keyboard::sNumpad5:  return "5 (Numpad)";
+        case sf::Keyboard::sNumpad6:  return "6 (Numpad)";
+        case sf::Keyboard::sNumpad7:  return "7 (Numpad)";
+        case sf::Keyboard::sNumpad8:  return "8 (Numpad)";
+        case sf::Keyboard::sNumpad9:  return "9 (Numpad)";
+
+        case sf::Keyboard::sApplication:  return "Application";
+        case sf::Keyboard::sExecute:      return "Execute";
+        case sf::Keyboard::sHelp:         return "Help";
+        case sf::Keyboard::sMenu:         return "Menu";
+        case sf::Keyboard::sSelect:       return "Select";
+        case sf::Keyboard::sStop:         return "Stop";
+        case sf::Keyboard::sAgain:        return "Again";
+        case sf::Keyboard::sUndo:         return "Undo";
+        case sf::Keyboard::sCut:          return "Cut";
+        case sf::Keyboard::sCopy:         return "Copy";
+        case sf::Keyboard::sPaste:        return "Paste";
+        case sf::Keyboard::sFind:         return "Find";
+        case sf::Keyboard::sMute:         return "Mute";
+        case sf::Keyboard::sVolumeUp:     return "Volume Up";
+        case sf::Keyboard::sVolumeDown:   return "Volume Down";
+
+        case sf::Keyboard::sLControl:  return "Control (Left)";
+        case sf::Keyboard::sLShift:    return "Shift (Left)";
+        case sf::Keyboard::sLAlt:      return "Alt (Left)";
+        case sf::Keyboard::sLSystem:   return "Command (Left)";
+        case sf::Keyboard::sRControl:  return "Control (Right)";
+        case sf::Keyboard::sRShift:    return "Shift (Right)";
+        case sf::Keyboard::sRAlt:      return "Alt (Right)";
+        case sf::Keyboard::sRSystem:   return "Command (Right)";
+
+        default: return "Unknown Scancode"; // no guess good enough possible.
+    }
+}
+
+
 ////////////////////////////////////////////////////////////
 HIDInputManager::HIDInputManager() :
-m_isValid(true),
-m_layoutData(0),
-m_layout(0),
 m_manager(0)
 {
-    // Get the current keyboard layout
-    TISInputSourceRef tis = TISCopyCurrentKeyboardLayoutInputSource();
-    m_layoutData = (CFDataRef)TISGetInputSourceProperty(tis,
-                                                        kTISPropertyUnicodeKeyLayoutData);
-
-    if (m_layoutData == 0)
-    {
-        sf::err() << "Cannot get the keyboard layout" << std::endl;
-        freeUp();
-        return;
-    }
-
-    // Keep a reference for ourself
-    CFRetain(m_layoutData);
-    m_layout = (UCKeyboardLayout *)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
     IOReturn openStatus = IOHIDManagerOpen(m_manager, kIOHIDOptionsTypeNone);
 
     if (openStatus != kIOReturnSuccess)
@@ -130,8 +228,8 @@ m_manager(0)
         return;
     }
 
-    // Initialize the keyboard
     initializeKeyboard();
+    buildMappings();
 }
 
 
@@ -146,31 +244,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 == NULL)
+    CFSetRef underlying = copyDevices(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
+    if (underlying == NULL)
     {
         sf::err() << "No keyboard detected by the HID manager!" << std::endl;
         freeUp();
         return;
     }
 
-    CFIndex keyboardCount = CFSetGetCount(keyboards); // >= 1 (asserted by copyDevices)
+    NSSet* keyboards = static_cast<NSSet*>(underlying); // Toll-Free Bridge
+    for (id keyboard in keyboards)
+      loadKeyboard(static_cast<IOHIDDeviceRef>(keyboard));
 
-    // Get an iterable array
-    CFTypeRef devicesArray[keyboardCount];
-    CFSetGetValues(keyboards, devicesArray);
-
-    for (CFIndex i = 0; i < keyboardCount; ++i)
-    {
-        IOHIDDeviceRef keyboard = (IOHIDDeviceRef)devicesArray[i];
-        loadKeyboard(keyboard);
-    }
-
-    // Release unused stuff
-    CFRelease(keyboards);
+    CFRelease(underlying);
 
     ////////////////////////////////////////////////////////////
     // At this point m_keys is filled with as many IOHIDElementRef as possible
@@ -180,142 +270,105 @@ void HIDInputManager::initializeKeyboard()
 ////////////////////////////////////////////////////////////
 void HIDInputManager::loadKeyboard(IOHIDDeviceRef keyboard)
 {
-    CFArrayRef keys = IOHIDDeviceCopyMatchingElements(keyboard,
-                                                      NULL,
-                                                      kIOHIDOptionsTypeNone);
-    if (keys == NULL)
+    CFArrayRef underlying = IOHIDDeviceCopyMatchingElements(keyboard, NULL, kIOHIDOptionsTypeNone);
+    if ((underlying == NULL) || (CFArrayGetCount(underlying) == 0))
     {
-        sf::err() << "We got a keyboard without any keys (1)" << std::endl;
+        sf::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<NSArray*>(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<IOHIDElementRef>(key);
+        if (IOHIDElementGetUsagePage(elem) == kHIDPage_KeyboardOrKeypad)
+          loadKey(elem);
     }
 
-    // Go through all connected elements.
-    for (CFIndex i = 0; i < keysCount; ++i)
-    {
-        IOHIDElementRef aKey = (IOHIDElementRef) 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;
-    UniChar      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);              // what we get
-
-    if (error == noErr)
+    UInt32 usage = IOHIDElementGetUsage(key);
+    sf::Keyboard::Scancode code = usageToScancode(usage);
+    if (code != Keyboard::sUnknown)
     {
-        // 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 (int i = 0; i < Keyboard::KeyCount; ++i)
+      m_mapping[i] = Keyboard::sUnknown;
+    for (int i = 0; i < Keyboard::sCodeCount; ++i)
+      m_gnippam[i] = 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 = (CFDataRef)TISGetInputSourceProperty(tis, kTISPropertyUnicodeKeyLayoutData);
 
-        // A code was found, wonderful!
-        if (code != Keyboard::Unknown)
+    if (layoutData == 0)
+    {
+        sf::err() << "Cannot get the keyboard layout\n";
+        CFRelease(tis);
+        return;
+    }
+
+    UCKeyboardLayout* layout = (UCKeyboardLayout*)CFDataGetBytePtr(layoutData);
+
+    // For each scancode having a IOHIDElement, we translate the corresponding
+    // virtual code to a localized Key.
+    for (int i = 0; i < Keyboard::sCodeCount; ++i)
+    {
+        Keyboard::Scancode scan = static_cast<Keyboard::Scancode>(i);
+        UInt8 virtualCode = scanToVirtualCode(scan);
+
+        if (virtualCode == 0xff) continue;
+
+        // Unicode string length is usually less or equal to 4
+        UniCharCount const MAX_LENGTH = 4;
+        UniChar string[MAX_LENGTH];
+        UniCharCount length = 0;
+        UInt32 deadKeyState = 0; // unused value
+        UInt32 const modifiers = 0x100; // no modifiers
+
+        // Use current layout for translation
+        OSStatus error = UCKeyTranslate(
+          layout, virtualCode, kUCKeyActionDown, modifiers, LMGetKbdType(),
+          kUCKeyTranslateNoDeadKeysBit, &deadKeyState, MAX_LENGTH, &length, string
+        );
+
+        if (error != noErr)
         {
-            // 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());
+            sf::err() << "Cannot translate the virtual key code, error: " << error << "\n";
+            continue;
         }
 
-        ////////////////////////////////////////////////////////////
-        // 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 = (length > 0) ? localizedKey(string[0]) : Keyboard::Unknown;
+        if (code == Keyboard::Unknown) code = localizedKeyFallback(scan);
 
-        //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;
-        //}
+        if (code == Keyboard::Unknown) continue;
 
-    } /* if (error == noErr) */
-    else
-    {
-        sf::err() << "Cannot translate the virtual key code, error: "
-                  << error
-                  << std::endl;
+        // Register the bi-mapping
+        m_mapping[code] = scan;
+        m_gnippam[scan] = code;
     }
+
+    CFRelease(tis);
 }
 
 
 ////////////////////////////////////////////////////////////
 void HIDInputManager::freeUp()
 {
-    m_isValid = false;
-
-    if (m_layoutData != 0)
-        CFRelease(m_layoutData);
-    m_layoutData = 0;
-    // Do not release m_layout! It is owned by m_layoutData.
     if (m_manager != 0)
         CFRelease(m_manager);
     m_manager = 0;
@@ -339,32 +392,28 @@ CFSetRef HIDInputManager::copyDevices(UInt32 page, UInt32 usage)
     IOHIDManagerSetDeviceMatching(m_manager, mask);
 
     CFRelease(mask);
-    mask = 0;
+    mask = NULL;
 
     CFSetRef devices = IOHIDManagerCopyDevices(m_manager);
     if (devices == NULL)
         return NULL;
 
     // Is there at least one device?
-    CFIndex deviceCount = CFSetGetCount(devices);
-    if (deviceCount < 1)
+    if (CFSetGetCount(devices) < 1)
     {
         CFRelease(devices);
         return NULL;
     }
-
+    
     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 (IOHIDElements::iterator it = elements.begin(); it != elements.end(); /* noop */)
+    bool pressed = false;
+    for (IOHIDElements::iterator it = elements.begin(); it != elements.end() && !pressed; /* noop */)
     {
         IOHIDValueRef value = 0;
 
@@ -373,493 +422,886 @@ 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)
+sf::Keyboard::Scancode HIDInputManager::usageToScancode(UInt32 usage)
 {
-    // Some usage key doesn't have any corresponding virtual
-    // code or it was not found (return 0xff).
     switch (usage)
     {
-        case kHIDUsage_KeyboardErrorRollOver:       return 0xff;
-        case kHIDUsage_KeyboardPOSTFail:            return 0xff;
-        case kHIDUsage_KeyboardErrorUndefined:      return 0xff;
+        case kHIDUsage_KeyboardErrorRollOver:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardPOSTFail:        return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardErrorUndefined:  return sf::Keyboard::sUnknown;
 
-        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 sf::Keyboard::sA;
+        case kHIDUsage_KeyboardB:  return sf::Keyboard::sB;
+        case kHIDUsage_KeyboardC:  return sf::Keyboard::sC;
+        case kHIDUsage_KeyboardD:  return sf::Keyboard::sD;
+        case kHIDUsage_KeyboardE:  return sf::Keyboard::sE;
+        case kHIDUsage_KeyboardF:  return sf::Keyboard::sF;
+        case kHIDUsage_KeyboardG:  return sf::Keyboard::sG;
+        case kHIDUsage_KeyboardH:  return sf::Keyboard::sH;
+        case kHIDUsage_KeyboardI:  return sf::Keyboard::sI;
+        case kHIDUsage_KeyboardJ:  return sf::Keyboard::sJ;
+        case kHIDUsage_KeyboardK:  return sf::Keyboard::sK;
+        case kHIDUsage_KeyboardL:  return sf::Keyboard::sL;
+        case kHIDUsage_KeyboardM:  return sf::Keyboard::sM;
+        case kHIDUsage_KeyboardN:  return sf::Keyboard::sN;
+        case kHIDUsage_KeyboardO:  return sf::Keyboard::sO;
+        case kHIDUsage_KeyboardP:  return sf::Keyboard::sP;
+        case kHIDUsage_KeyboardQ:  return sf::Keyboard::sQ;
+        case kHIDUsage_KeyboardR:  return sf::Keyboard::sR;
+        case kHIDUsage_KeyboardS:  return sf::Keyboard::sS;
+        case kHIDUsage_KeyboardT:  return sf::Keyboard::sT;
+        case kHIDUsage_KeyboardU:  return sf::Keyboard::sU;
+        case kHIDUsage_KeyboardV:  return sf::Keyboard::sV;
+        case kHIDUsage_KeyboardW:  return sf::Keyboard::sW;
+        case kHIDUsage_KeyboardX:  return sf::Keyboard::sX;
+        case kHIDUsage_KeyboardY:  return sf::Keyboard::sY;
+        case kHIDUsage_KeyboardZ:  return sf::Keyboard::sZ;
 
-        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 sf::Keyboard::sNum1;
+        case kHIDUsage_Keyboard2:  return sf::Keyboard::sNum2;
+        case kHIDUsage_Keyboard3:  return sf::Keyboard::sNum3;
+        case kHIDUsage_Keyboard4:  return sf::Keyboard::sNum4;
+        case kHIDUsage_Keyboard5:  return sf::Keyboard::sNum5;
+        case kHIDUsage_Keyboard6:  return sf::Keyboard::sNum6;
+        case kHIDUsage_Keyboard7:  return sf::Keyboard::sNum7;
+        case kHIDUsage_Keyboard8:  return sf::Keyboard::sNum8;
+        case kHIDUsage_Keyboard9:  return sf::Keyboard::sNum9;
+        case kHIDUsage_Keyboard0:  return sf::Keyboard::sNum0;
 
-        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 sf::Keyboard::sEnter;
+        case kHIDUsage_KeyboardEscape:              return sf::Keyboard::sEscape;
+        case kHIDUsage_KeyboardDeleteOrBackspace:   return sf::Keyboard::sBackspace;
+        case kHIDUsage_KeyboardTab:                 return sf::Keyboard::sTab;
+        case kHIDUsage_KeyboardSpacebar:            return sf::Keyboard::sSpace;
+        case kHIDUsage_KeyboardHyphen:              return sf::Keyboard::sHyphen;
+        case kHIDUsage_KeyboardEqualSign:           return sf::Keyboard::sEquals;
+        case kHIDUsage_KeyboardOpenBracket:         return sf::Keyboard::sLBracket;
+        case kHIDUsage_KeyboardCloseBracket:        return sf::Keyboard::sRBracket;
+        case kHIDUsage_KeyboardBackslash:           return sf::Keyboard::sBackslash;
+        case kHIDUsage_KeyboardNonUSPound:          return sf::Keyboard::sDash;
+        case kHIDUsage_KeyboardSemicolon:           return sf::Keyboard::sSemicolon;
+        case kHIDUsage_KeyboardQuote:               return sf::Keyboard::sQuote;
+        case kHIDUsage_KeyboardGraveAccentAndTilde: return sf::Keyboard::sGraveAccent;
+        case kHIDUsage_KeyboardComma:               return sf::Keyboard::sComma;
+        case kHIDUsage_KeyboardPeriod:              return sf::Keyboard::sPeriod;
+        case kHIDUsage_KeyboardSlash:               return sf::Keyboard::sForwardSlash;
+        case kHIDUsage_KeyboardCapsLock:            return sf::Keyboard::sCapsLock;
 
-        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 sf::Keyboard::sF1;
+        case kHIDUsage_KeyboardF2:   return sf::Keyboard::sF2;
+        case kHIDUsage_KeyboardF3:   return sf::Keyboard::sF3;
+        case kHIDUsage_KeyboardF4:   return sf::Keyboard::sF4;
+        case kHIDUsage_KeyboardF5:   return sf::Keyboard::sF5;
+        case kHIDUsage_KeyboardF6:   return sf::Keyboard::sF6;
+        case kHIDUsage_KeyboardF7:   return sf::Keyboard::sF7;
+        case kHIDUsage_KeyboardF8:   return sf::Keyboard::sF8;
+        case kHIDUsage_KeyboardF9:   return sf::Keyboard::sF9;
+        case kHIDUsage_KeyboardF10:  return sf::Keyboard::sF10;
+        case kHIDUsage_KeyboardF11:  return sf::Keyboard::sF11;
+        case kHIDUsage_KeyboardF12:  return sf::Keyboard::sF12;
 
-        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 sf::Keyboard::sPrintScreen;
+        case kHIDUsage_KeyboardScrollLock:     return sf::Keyboard::sScrollLock;
+        case kHIDUsage_KeyboardPause:          return sf::Keyboard::sPause;
+        case kHIDUsage_KeyboardInsert:         return sf::Keyboard::sInsert;
+        case kHIDUsage_KeyboardHome:           return sf::Keyboard::sHome;
+        case kHIDUsage_KeyboardPageUp:         return sf::Keyboard::sPageUp;
+        case kHIDUsage_KeyboardDeleteForward:  return sf::Keyboard::sDelete;
+        case kHIDUsage_KeyboardEnd:            return sf::Keyboard::sEnd;
+        case kHIDUsage_KeyboardPageDown:       return sf::Keyboard::sPageDown;
 
-        case kHIDUsage_KeyboardRightArrow:          return 0x7c;
-        case kHIDUsage_KeyboardLeftArrow:           return 0x7b;
-        case kHIDUsage_KeyboardDownArrow:           return 0x7d;
-        case kHIDUsage_KeyboardUpArrow:             return 0x7e;
+        case kHIDUsage_KeyboardRightArrow:  return sf::Keyboard::sRight;
+        case kHIDUsage_KeyboardLeftArrow:   return sf::Keyboard::sLeft;
+        case kHIDUsage_KeyboardDownArrow:   return sf::Keyboard::sDown;
+        case kHIDUsage_KeyboardUpArrow:     return sf::Keyboard::sUp;
 
-        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 sf::Keyboard::sNumLock;
+        case kHIDUsage_KeypadSlash:     return sf::Keyboard::sDivide;
+        case kHIDUsage_KeypadAsterisk:  return sf::Keyboard::sMultiply;
+        case kHIDUsage_KeypadHyphen:    return sf::Keyboard::sMinus;
+        case kHIDUsage_KeypadPlus:      return sf::Keyboard::sPlus;
+        case kHIDUsage_KeypadEnter:     return sf::Keyboard::sReturn;
 
-        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 sf::Keyboard::sNumpad1;
+        case kHIDUsage_Keypad2:  return sf::Keyboard::sNumpad2;
+        case kHIDUsage_Keypad3:  return sf::Keyboard::sNumpad3;
+        case kHIDUsage_Keypad4:  return sf::Keyboard::sNumpad4;
+        case kHIDUsage_Keypad5:  return sf::Keyboard::sNumpad5;
+        case kHIDUsage_Keypad6:  return sf::Keyboard::sNumpad6;
+        case kHIDUsage_Keypad7:  return sf::Keyboard::sNumpad7;
+        case kHIDUsage_Keypad8:  return sf::Keyboard::sNumpad8;
+        case kHIDUsage_Keypad9:  return sf::Keyboard::sNumpad9;
+        case kHIDUsage_Keypad0:  return sf::Keyboard::sNumpad0;
 
-        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_KeypadPeriod:            return sf::Keyboard::sDecimal;
+        case kHIDUsage_KeyboardNonUSBackslash:  return sf::Keyboard::sReverseSolidus;
+        case kHIDUsage_KeyboardApplication:     return sf::Keyboard::sApplication;
+        case kHIDUsage_KeyboardPower:           return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeypadEqualSign:         return sf::Keyboard::sPadEquals;
 
-        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 sf::Keyboard::sF13;
+        case kHIDUsage_KeyboardF14:   return sf::Keyboard::sF14;
+        case kHIDUsage_KeyboardF15:   return sf::Keyboard::sF15;
+        case kHIDUsage_KeyboardF16:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF17:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF18:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF19:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF20:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF21:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF22:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF23:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardF24:   return sf::Keyboard::sUnknown;
 
-        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 sf::Keyboard::sExecute;
+        case kHIDUsage_KeyboardHelp:     return sf::Keyboard::sHelp;
+        case kHIDUsage_KeyboardMenu:     return sf::Keyboard::sMenu;
+        case kHIDUsage_KeyboardSelect:   return sf::Keyboard::sSelect;
+        case kHIDUsage_KeyboardStop:     return sf::Keyboard::sStop;
+        case kHIDUsage_KeyboardAgain:    return sf::Keyboard::sAgain;
+        case kHIDUsage_KeyboardUndo:     return sf::Keyboard::sUndo;
+        case kHIDUsage_KeyboardCut:      return sf::Keyboard::sCut;
+        case kHIDUsage_KeyboardCopy:     return sf::Keyboard::sCopy;
+        case kHIDUsage_KeyboardPaste:    return sf::Keyboard::sPaste;
+        case kHIDUsage_KeyboardFind:     return sf::Keyboard::sFind;
 
-        case kHIDUsage_KeyboardMute:                return 0xff;
-        case kHIDUsage_KeyboardVolumeUp:            return 0xff;
-        case kHIDUsage_KeyboardVolumeDown:          return 0xff;
+        case kHIDUsage_KeyboardMute:       return sf::Keyboard::sMute;
+        case kHIDUsage_KeyboardVolumeUp:   return sf::Keyboard::sVolumeUp;
+        case kHIDUsage_KeyboardVolumeDown: return sf::Keyboard::sVolumeDown;
 
-        case kHIDUsage_KeyboardLockingCapsLock:     return 0xff;
-        case kHIDUsage_KeyboardLockingNumLock:      return 0xff;
-        case kHIDUsage_KeyboardLockingScrollLock:   return 0xff;
+        case kHIDUsage_KeyboardLockingCapsLock:     return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLockingNumLock:      return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLockingScrollLock:   return sf::Keyboard::sUnknown;
 
-        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 sf::Keyboard::sUnknown;
+        case kHIDUsage_KeypadEqualSignAS400:    return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational1:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational2:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational3:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational4:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational5:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational6:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational7:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational8:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardInternational9:  return sf::Keyboard::sUnknown;
 
-        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 sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG2:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG3:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG4:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG5:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG6:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG7:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG8:  return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardLANG9:  return sf::Keyboard::sUnknown;
 
-        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 sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardSysReqOrAttention:   return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardCancel:              return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardClear:               return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardPrior:               return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardReturn:              return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardSeparator:           return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardOut:                 return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardOper:                return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardClearOrAgain:        return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardCrSelOrProps:        return sf::Keyboard::sUnknown;
+        case kHIDUsage_KeyboardExSel:               return sf::Keyboard::sUnknown;
 
             /* 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 sf::Keyboard::sLControl;
+        case kHIDUsage_KeyboardLeftShift:    return sf::Keyboard::sLShift;
+        case kHIDUsage_KeyboardLeftAlt:      return sf::Keyboard::sLAlt;
+        case kHIDUsage_KeyboardLeftGUI:      return sf::Keyboard::sLSystem;
+        case kHIDUsage_KeyboardRightControl: return sf::Keyboard::sRControl;
+        case kHIDUsage_KeyboardRightShift:   return sf::Keyboard::sRShift;
+        case kHIDUsage_KeyboardRightAlt:     return sf::Keyboard::sRAlt;
+        case kHIDUsage_KeyboardRightGUI:     return sf::Keyboard::sRSystem;
 
             /* 0xe8-0xffff Reserved */
 
-        case kHIDUsage_Keyboard_Reserved:           return 0xff;
-        default:                                    return 0xff;
+        case kHIDUsage_Keyboard_Reserved:  return sf::Keyboard::sUnknown;
+        default:                           return sf::Keyboard::sUnknown;
     }
 }
 
 
 ////////////////////////////////////////////////////////
-Keyboard::Key HIDInputManager::localizedKeys(UniChar ch)
+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.
     switch (ch)
     {
-        case 'a':
-        case 'A':                   return sf::Keyboard::A;
+        case 0x41:  return sf::Keyboard::A; // uppercase A
+        case 0x42:  return sf::Keyboard::B;
+        case 0x43:  return sf::Keyboard::C;
+        case 0x44:  return sf::Keyboard::D;
+        case 0x45:  return sf::Keyboard::E;
+        case 0x46:  return sf::Keyboard::F;
+        case 0x47:  return sf::Keyboard::G;
+        case 0x48:  return sf::Keyboard::H;
+        case 0x49:  return sf::Keyboard::I;
+        case 0x4a:  return sf::Keyboard::J;
+        case 0x4b:  return sf::Keyboard::K;
+        case 0x4c:  return sf::Keyboard::L;
+        case 0x4d:  return sf::Keyboard::M;
+        case 0x4e:  return sf::Keyboard::N;
+        case 0x4f:  return sf::Keyboard::O;
+        case 0x50:  return sf::Keyboard::P;
+        case 0x51:  return sf::Keyboard::Q;
+        case 0x52:  return sf::Keyboard::R;
+        case 0x53:  return sf::Keyboard::S;
+        case 0x54:  return sf::Keyboard::T;
+        case 0x55:  return sf::Keyboard::U;
+        case 0x56:  return sf::Keyboard::V;
+        case 0x57:  return sf::Keyboard::W;
+        case 0x58:  return sf::Keyboard::X;
+        case 0x59:  return sf::Keyboard::Y;
 
-        case 'b':
-        case 'B':                   return sf::Keyboard::B;
+        case 0x5a:  return sf::Keyboard::Z; // lowercase A
+        case 0x61:  return sf::Keyboard::A;
+        case 0x62:  return sf::Keyboard::B;
+        case 0x63:  return sf::Keyboard::C;
+        case 0x64:  return sf::Keyboard::D;
+        case 0x65:  return sf::Keyboard::E;
+        case 0x66:  return sf::Keyboard::F;
+        case 0x67:  return sf::Keyboard::G;
+        case 0x68:  return sf::Keyboard::H;
+        case 0x69:  return sf::Keyboard::I;
+        case 0x6a:  return sf::Keyboard::J;
+        case 0x6b:  return sf::Keyboard::K;
+        case 0x6c:  return sf::Keyboard::L;
+        case 0x6d:  return sf::Keyboard::M;
+        case 0x6e:  return sf::Keyboard::N;
+        case 0x6f:  return sf::Keyboard::O;
+        case 0x70:  return sf::Keyboard::P;
+        case 0x71:  return sf::Keyboard::Q;
+        case 0x72:  return sf::Keyboard::R;
+        case 0x73:  return sf::Keyboard::S;
+        case 0x74:  return sf::Keyboard::T;
+        case 0x75:  return sf::Keyboard::U;
+        case 0x76:  return sf::Keyboard::V;
+        case 0x77:  return sf::Keyboard::W;
+        case 0x78:  return sf::Keyboard::X;
+        case 0x79:  return sf::Keyboard::Y;
+        case 0x7a:  return sf::Keyboard::Z;
 
-        case 'c':
-        case 'C':                   return sf::Keyboard::C;
+        case 0x30:  return sf::Keyboard::Num0;
+        case 0x31:  return sf::Keyboard::Num1;
+        case 0x32:  return sf::Keyboard::Num2;
+        case 0x33:  return sf::Keyboard::Num3;
+        case 0x34:  return sf::Keyboard::Num4;
+        case 0x35:  return sf::Keyboard::Num5;
+        case 0x36:  return sf::Keyboard::Num6;
+        case 0x37:  return sf::Keyboard::Num7;
+        case 0x38:  return sf::Keyboard::Num8;
+        case 0x39:  return sf::Keyboard::Num9;
 
-        case 'd':
-        case 'D':                   return sf::Keyboard::D;
+        case 0x1b:  return sf::Keyboard::Escape;
 
-        case 'e':
-        case 'E':                   return sf::Keyboard::E;
+        /* Those are not represented using Unicode.
+         * case 0x:  return sf::Keyboard::LControl;
+         * case 0x:  return sf::Keyboard::LShift;
+         * case 0x:  return sf::Keyboard::LAlt;
+         * case 0x:  return sf::Keyboard::LSystem;
+         * case 0x:  return sf::Keyboard::RControl;
+         * case 0x:  return sf::Keyboard::RShift;
+         * case 0x:  return sf::Keyboard::RAlt;
+         * case 0x:  return sf::Keyboard::RSystem;
+         */
 
-        case 'f':
-        case 'F':                   return sf::Keyboard::F;
+        case NSMenuFunctionKey:  return sf::Keyboard::Menu;
 
-        case 'g':
-        case 'G':                   return sf::Keyboard::G;
+        case 0x5b:  return sf::Keyboard::LBracket;
+        case 0x5d:  return sf::Keyboard::RBracket;
+        case 0x3b:  return sf::Keyboard::SemiColon;
+        case 0x2c:  return sf::Keyboard::Comma;
+        case 0x2e:  return sf::Keyboard::Period;
+        case 0x27:  return sf::Keyboard::Quote;
+        case 0x2044:  return sf::Keyboard::Slash; // 0x2f is Divide
+        case 0x5c:  return sf::Keyboard::BackSlash;
+        case 0x7e:  return sf::Keyboard::Tilde;
+        case 0x3d:  return sf::Keyboard::Equal;
+        case 0x2013:  return sf::Keyboard::Dash; // 0x2d is Subtract
+        case 0x20:  return sf::Keyboard::Space;
+        case 0x0d:  return sf::Keyboard::Return;
+        case 0x7f:  return sf::Keyboard::BackSpace; // NOTE backspace & delete are swapped on Mac.
+        case 0x09:  return sf::Keyboard::Tab;
 
-        case 'h':
-        case 'H':                   return sf::Keyboard::H;
+        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 NSInsertFunctionKey:    return sf::Keyboard::Insert;
+        case NSDeleteFunctionKey:    return sf::Keyboard::Delete;
 
-        case 'i':
-        case 'I':                   return sf::Keyboard::I;
+        case 0x2b:  return sf::Keyboard::Add;
+        case 0x2d:  return sf::Keyboard::Subtract;
+        case 0x2a:  return sf::Keyboard::Multiply;
+        case 0x2f:  return sf::Keyboard::Divide;
 
-        case 'j':
-        case 'J':                   return sf::Keyboard::J;
+        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 'k':
-        case 'K':                   return sf::Keyboard::K;
+        /* Those are not represented using Unicode.
+         * case 0x:  return sf::Keyboard::Numpad0;
+         * case 0x:  return sf::Keyboard::Numpad1;
+         * case 0x:  return sf::Keyboard::Numpad2;
+         * case 0x:  return sf::Keyboard::Numpad3;
+         * case 0x:  return sf::Keyboard::Numpad4;
+         * case 0x:  return sf::Keyboard::Numpad5;
+         * case 0x:  return sf::Keyboard::Numpad6;
+         * case 0x:  return sf::Keyboard::Numpad7;
+         * case 0x:  return sf::Keyboard::Numpad8;
+         * case 0x:  return sf::Keyboard::Numpad9;
+         */
 
-        case 'l':
-        case 'L':                   return sf::Keyboard::L;
+        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 'm':
-        case 'M':                   return sf::Keyboard::M;
+        case NSPauseFunctionKey:  return sf::Keyboard::Pause;
 
-        case 'n':
-        case 'N':                   return sf::Keyboard::N;
-
-        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 sf::Keyboard::Unknown;
     }
 }
 
 
 ////////////////////////////////////////////////////////
-Keyboard::Key HIDInputManager::nonLocalizedKeys(UniChar virtualKeycode)
+UniChar HIDInputManager::toUnicode(Keyboard::Key key)
 {
-    // (Some) 0x code based on https://forums.macrumors.com/showthread.php?t=780577
-    // Some sf::Keyboard::Key are present twice.
+    switch (key)
+    {
+        case sf::Keyboard::A:  return 0x41;
+        case sf::Keyboard::B:  return 0x42;
+        case sf::Keyboard::C:  return 0x43;
+        case sf::Keyboard::D:  return 0x44;
+        case sf::Keyboard::E:  return 0x45;
+        case sf::Keyboard::F:  return 0x46;
+        case sf::Keyboard::G:  return 0x47;
+        case sf::Keyboard::H:  return 0x48;
+        case sf::Keyboard::I:  return 0x49;
+        case sf::Keyboard::J:  return 0x4a;
+        case sf::Keyboard::K:  return 0x4b;
+        case sf::Keyboard::L:  return 0x4c;
+        case sf::Keyboard::M:  return 0x4d;
+        case sf::Keyboard::N:  return 0x4e;
+        case sf::Keyboard::O:  return 0x4f;
+        case sf::Keyboard::P:  return 0x50;
+        case sf::Keyboard::Q:  return 0x51;
+        case sf::Keyboard::R:  return 0x52;
+        case sf::Keyboard::S:  return 0x53;
+        case sf::Keyboard::T:  return 0x54;
+        case sf::Keyboard::U:  return 0x55;
+        case sf::Keyboard::V:  return 0x56;
+        case sf::Keyboard::W:  return 0x57;
+        case sf::Keyboard::X:  return 0x58;
+        case sf::Keyboard::Y:  return 0x59;
+        case sf::Keyboard::Z:  return 0x5a;
+
+        case sf::Keyboard::Num0:  return 0x30;
+        case sf::Keyboard::Num1:  return 0x31;
+        case sf::Keyboard::Num2:  return 0x32;
+        case sf::Keyboard::Num3:  return 0x33;
+        case sf::Keyboard::Num4:  return 0x34;
+        case sf::Keyboard::Num5:  return 0x35;
+        case sf::Keyboard::Num6:  return 0x36;
+        case sf::Keyboard::Num7:  return 0x37;
+        case sf::Keyboard::Num8:  return 0x38;
+        case sf::Keyboard::Num9:  return 0x39;
+
+        case sf::Keyboard::Escape:  return 0x1b;
+
+        /* Not representable as Unicode:
+         * case sf::Keyboard::LControl:  return 0x;
+         * case sf::Keyboard::LShift:    return 0x;
+         * case sf::Keyboard::LAlt:      return 0x;
+         * case sf::Keyboard::LSystem:   return 0x;
+         * case sf::Keyboard::RControl:  return 0x;
+         * case sf::Keyboard::RShift:    return 0x;
+         * case sf::Keyboard::RAlt:      return 0x;
+         * case sf::Keyboard::RSystem:   return 0x;
+         */
+
+        case sf::Keyboard::Menu:  return NSMenuFunctionKey;
+
+        case sf::Keyboard::LBracket:    return 0x5b;
+        case sf::Keyboard::RBracket:    return 0x5d;
+        case sf::Keyboard::SemiColon:   return 0x3b;
+        case sf::Keyboard::Comma:       return 0x2c;
+        case sf::Keyboard::Period:      return 0x2e;
+        case sf::Keyboard::Quote:       return 0x27;
+        case sf::Keyboard::Slash:       return 0x2044; // 0x2f is Divide
+        case sf::Keyboard::BackSlash:   return 0x5c;
+        case sf::Keyboard::Tilde:       return 0x7e;
+        case sf::Keyboard::Equal:       return 0x3d;
+        case sf::Keyboard::Dash:        return 0x2013; // 0x2d is Subtract
+        case sf::Keyboard::Space:       return 0x20;
+        case sf::Keyboard::Return:      return 0x0d;
+        case sf::Keyboard::BackSpace:   return 0x7f; // NOTE backspace & delete are swapped on Mac.
+        case sf::Keyboard::Tab:         return 0x09;
+
+        case sf::Keyboard::PageUp:    return NSPageUpFunctionKey;
+        case sf::Keyboard::PageDown:  return NSPageDownFunctionKey;
+        case sf::Keyboard::End:       return NSEndFunctionKey;
+        case sf::Keyboard::Home:      return NSHomeFunctionKey;
+        case sf::Keyboard::Insert:    return NSInsertFunctionKey;
+        case sf::Keyboard::Delete:    return NSDeleteFunctionKey;
+
+        case sf::Keyboard::Add:       return 0x2b;
+        case sf::Keyboard::Subtract:  return 0x2d;
+        case sf::Keyboard::Multiply:  return 0x2a;
+        case sf::Keyboard::Divide:    return 0x2f;
+
+        case sf::Keyboard::Left:    return NSLeftArrowFunctionKey;
+        case sf::Keyboard::Right:   return NSRightArrowFunctionKey;
+        case sf::Keyboard::Up:      return NSUpArrowFunctionKey;
+        case sf::Keyboard::Down:    return NSDownArrowFunctionKey;
+
+        /* Those are not represented using Unicode.
+         * case sf::Keyboard::Numpad0:  return 0x;
+         * case sf::Keyboard::Numpad1:  return 0x;
+         * case sf::Keyboard::Numpad2:  return 0x;
+         * case sf::Keyboard::Numpad3:  return 0x;
+         * case sf::Keyboard::Numpad4:  return 0x;
+         * case sf::Keyboard::Numpad5:  return 0x;
+         * case sf::Keyboard::Numpad6:  return 0x;
+         * case sf::Keyboard::Numpad7:  return 0x;
+         * case sf::Keyboard::Numpad8:  return 0x;
+         * case sf::Keyboard::Numpad9:  return 0x;
+         */
+
+        case sf::Keyboard::F1:   return NSF1FunctionKey;
+        case sf::Keyboard::F2:   return NSF2FunctionKey;
+        case sf::Keyboard::F3:   return NSF3FunctionKey;
+        case sf::Keyboard::F4:   return NSF4FunctionKey;
+        case sf::Keyboard::F5:   return NSF5FunctionKey;
+        case sf::Keyboard::F6:   return NSF6FunctionKey;
+        case sf::Keyboard::F7:   return NSF7FunctionKey;
+        case sf::Keyboard::F8:   return NSF8FunctionKey;
+        case sf::Keyboard::F9:   return NSF9FunctionKey;
+        case sf::Keyboard::F10:  return NSF10FunctionKey;
+        case sf::Keyboard::F11:  return NSF11FunctionKey;
+        case sf::Keyboard::F12:  return NSF12FunctionKey;
+        case sf::Keyboard::F13:  return NSF13FunctionKey;
+        case sf::Keyboard::F14:  return NSF14FunctionKey;
+        case sf::Keyboard::F15:  return NSF15FunctionKey;
+
+        case sf::Keyboard::Pause:  return NSPauseFunctionKey;
+
+        default:  return 0x00;
+    }
+}
+
+
+////////////////////////////////////////////////////////
+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
     switch (virtualKeycode)
     {
-            // 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 0x00:  return sf::Keyboard::sA;
+        case 0x0b:  return sf::Keyboard::sB;
+        case 0x08:  return sf::Keyboard::sC;
+        case 0x02:  return sf::Keyboard::sD;
+        case 0x0e:  return sf::Keyboard::sE;
+        case 0x03:  return sf::Keyboard::sF;
+        case 0x05:  return sf::Keyboard::sG;
+        case 0x04:  return sf::Keyboard::sH;
+        case 0x22:  return sf::Keyboard::sI;
+        case 0x26:  return sf::Keyboard::sJ;
+        case 0x28:  return sf::Keyboard::sK;
+        case 0x25:  return sf::Keyboard::sL;
+        case 0x2e:  return sf::Keyboard::sM;
+        case 0x2d:  return sf::Keyboard::sN;
+        case 0x1f:  return sf::Keyboard::sO;
+        case 0x23:  return sf::Keyboard::sP;
+        case 0x0c:  return sf::Keyboard::sQ;
+        case 0x0f:  return sf::Keyboard::sR;
+        case 0x01:  return sf::Keyboard::sS;
+        case 0x11:  return sf::Keyboard::sT;
+        case 0x20:  return sf::Keyboard::sU;
+        case 0x09:  return sf::Keyboard::sV;
+        case 0x0d:  return sf::Keyboard::sW;
+        case 0x07:  return sf::Keyboard::sX;
+        case 0x10:  return sf::Keyboard::sY;
+        case 0x06:  return sf::Keyboard::sZ;
 
-            // 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 0x12:  return sf::Keyboard::sNum1;
+        case 0x13:  return sf::Keyboard::sNum2;
+        case 0x14:  return sf::Keyboard::sNum3;
+        case 0x15:  return sf::Keyboard::sNum4;
+        case 0x17:  return sf::Keyboard::sNum5;
+        case 0x16:  return sf::Keyboard::sNum6;
+        case 0x1a:  return sf::Keyboard::sNum7;
+        case 0x1c:  return sf::Keyboard::sNum8;
+        case 0x19:  return sf::Keyboard::sNum9;
+        case 0x1d:  return sf::Keyboard::sNum0;
 
-        case 0x35:                      return sf::Keyboard::Escape;
+        case 0x24:  return sf::Keyboard::sEnter;
+        case 0x35:  return sf::Keyboard::sEscape;
+        case 0x33:  return sf::Keyboard::sBackspace;
+        case 0x30:  return sf::Keyboard::sTab;
+        case 0x31:  return sf::Keyboard::sSpace;
+        // case 0x27:  return sf::Keyboard::sHyphen; // TODO 0x27 is for Quote
+        case 0x18:  return sf::Keyboard::sEquals;
+        case 0x21:  return sf::Keyboard::sLBracket;
+        case 0x1e:  return sf::Keyboard::sRBracket;
+        case 0x2a:  return sf::Keyboard::sBackslash;
+        case 0x1b:  return sf::Keyboard::sDash;
+        case 0x29:  return sf::Keyboard::sSemicolon;
+        case 0x27:  return sf::Keyboard::sQuote;
+        case 0x32:  return sf::Keyboard::sGraveAccent;
+        case 0x2b:  return sf::Keyboard::sComma;
+        case 0x2f:  return sf::Keyboard::sPeriod;
+        case 0x2c:  return sf::Keyboard::sForwardSlash;
 
-            // 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 0x7a:  return sf::Keyboard::sF1;
+        case 0x78:  return sf::Keyboard::sF2;
+        case 0x63:  return sf::Keyboard::sF3;
+        case 0x76:  return sf::Keyboard::sF4;
+        case 0x60:  return sf::Keyboard::sF5;
+        case 0x61:  return sf::Keyboard::sF6;
+        case 0x62:  return sf::Keyboard::sF7;
+        case 0x64:  return sf::Keyboard::sF8;
+        case 0x65:  return sf::Keyboard::sF9;
+        case 0x6d:  return sf::Keyboard::sF10;
+        case 0x67:  return sf::Keyboard::sF11;
+        case 0x6f:  return sf::Keyboard::sF12;
+        case 0x69:  return sf::Keyboard::sF13;
+        case 0x6b:  return sf::Keyboard::sF14;
+        case 0x71:  return sf::Keyboard::sF15;
 
-        case 0x7f:                      return sf::Keyboard::Menu;
-        case NSMenuFunctionKey:         return sf::Keyboard::Menu;
+        case 0x39:  return sf::Keyboard::sCapsLock;
 
-        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;
+        /* TODO Those are missing:
+         * case 0x:  return sf::Keyboard::sPrintScreen;
+         * case 0x:  return sf::Keyboard::sScrollLock;
+         * case 0x:  return sf::Keyboard::sPause;
+         */
 
-            // 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 0x72:  return sf::Keyboard::sInsert;
+        case 0x73:  return sf::Keyboard::sHome;
+        case 0x74:  return sf::Keyboard::sPageUp;
+        case 0x75:  return sf::Keyboard::sDelete;
+        case 0x77:  return sf::Keyboard::sEnd;
+        case 0x79:  return sf::Keyboard::sPageDown;
 
-        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 0x7c:  return sf::Keyboard::sRight;
+        case 0x7b:  return sf::Keyboard::sLeft;
+        case 0x7d:  return sf::Keyboard::sDown;
+        case 0x7e:  return sf::Keyboard::sUp;
 
-            // 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 0x47:  return sf::Keyboard::sNumLock;
+        case 0x4b:  return sf::Keyboard::sDivide;
+        case 0x43:  return sf::Keyboard::sMultiply;
+        case 0x4e:  return sf::Keyboard::sMinus;
+        case 0x45:  return sf::Keyboard::sPlus;
+        case 0x51:  return sf::Keyboard::sPadEquals;
+        case 0x4c:  return sf::Keyboard::sReturn;
+        case 0x41:  return sf::Keyboard::sDecimal;
 
-        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 0x53:  return sf::Keyboard::sNumpad1;
+        case 0x54:  return sf::Keyboard::sNumpad2;
+        case 0x55:  return sf::Keyboard::sNumpad3;
+        case 0x56:  return sf::Keyboard::sNumpad4;
+        case 0x57:  return sf::Keyboard::sNumpad5;
+        case 0x58:  return sf::Keyboard::sNumpad6;
+        case 0x59:  return sf::Keyboard::sNumpad7;
+        case 0x5b:  return sf::Keyboard::sNumpad8;
+        case 0x5c:  return sf::Keyboard::sNumpad9;
+        case 0x52:  return sf::Keyboard::sNumpad0;
 
-        case 0x72:                      return sf::Keyboard::Insert;
-        case NSInsertFunctionKey:       return sf::Keyboard::Insert;
-        case 0x75:                      return sf::Keyboard::Delete;
-        case NSDeleteFunctionKey:       return sf::Keyboard::Delete;
+        /* TODO Those are missing:
+         * case 0x:  return sf::Keyboard::sReverseSolidus;
+         * case 0x:  return sf::Keyboard::sApplication;
+         * case 0x:  return sf::Keyboard::sExecute;
+         * case 0x72:  return sf::Keyboard::sHelp; // 0x72 is for Insert
+         * case 0x:  return sf::Keyboard::sMenu;
+         * case 0x:  return sf::Keyboard::sSelect;
+         * case 0x:  return sf::Keyboard::sStop;
+         * case 0x:  return sf::Keyboard::sAgain;
+         * case 0x:  return sf::Keyboard::sUndo;
+         * case 0x:  return sf::Keyboard::sCut;
+         * case 0x:  return sf::Keyboard::sCopy;
+         * case 0x:  return sf::Keyboard::sPaste;
+         * case 0x:  return sf::Keyboard::sFind;
+         */
 
-        case 0x45:                      return sf::Keyboard::Add;
-        case 0x4e:                      return sf::Keyboard::Subtract;
-        case 0x43:                      return sf::Keyboard::Multiply;
-        case 0x4b:                      return sf::Keyboard::Divide;
+        case 0x4a:  return sf::Keyboard::sMute;
+        case 0x48:  return sf::Keyboard::sVolumeUp;
+        case 0x49:  return sf::Keyboard::sVolumeDown;
 
-            // 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;
+        /* NOTE Those are symmetric so we leave them out.
+         *      Thankfully handled through modifiers and not virtual codes.
+         * case 0x3b:  return sf::Keyboard::sLControl;
+         * case 0x38:  return sf::Keyboard::sLShift;
+         * case 0x3a:  return sf::Keyboard::sLAlt;
+         * case 0x37:  return sf::Keyboard::sLSystem;
+         * case 0x3b:  return sf::Keyboard::sRControl;
+         * case 0x38:  return sf::Keyboard::sRShift;
+         * case 0x3a:  return sf::Keyboard::sRAlt;
+         * case 0x37:  return sf::Keyboard::sRSystem;
+         */
 
-        case NSLeftArrowFunctionKey:    return sf::Keyboard::Left;
-        case NSRightArrowFunctionKey:   return sf::Keyboard::Right;
-        case NSUpArrowFunctionKey:      return sf::Keyboard::Up;
-        case NSDownArrowFunctionKey:    return sf::Keyboard::Down;
+        default:  return sf::Keyboard::sUnknown;
+    }
+}
 
-        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;
+////////////////////////////////////////////////////////
+UInt8 HIDInputManager::scanToVirtualCode(Keyboard::Scancode code)
+{
+    switch (code)
+    {
+        case sf::Keyboard::sA:  return 0x00;
+        case sf::Keyboard::sB:  return 0x0b;
+        case sf::Keyboard::sC:  return 0x08;
+        case sf::Keyboard::sD:  return 0x02;
+        case sf::Keyboard::sE:  return 0x0e;
+        case sf::Keyboard::sF:  return 0x03;
+        case sf::Keyboard::sG:  return 0x05;
+        case sf::Keyboard::sH:  return 0x04;
+        case sf::Keyboard::sI:  return 0x22;
+        case sf::Keyboard::sJ:  return 0x26;
+        case sf::Keyboard::sK:  return 0x28;
+        case sf::Keyboard::sL:  return 0x25;
+        case sf::Keyboard::sM:  return 0x2e;
+        case sf::Keyboard::sN:  return 0x2d;
+        case sf::Keyboard::sO:  return 0x1f;
+        case sf::Keyboard::sP:  return 0x23;
+        case sf::Keyboard::sQ:  return 0x0c;
+        case sf::Keyboard::sR:  return 0x0f;
+        case sf::Keyboard::sS:  return 0x01;
+        case sf::Keyboard::sT:  return 0x11;
+        case sf::Keyboard::sU:  return 0x20;
+        case sf::Keyboard::sV:  return 0x09;
+        case sf::Keyboard::sW:  return 0x0d;
+        case sf::Keyboard::sX:  return 0x07;
+        case sf::Keyboard::sY:  return 0x10;
+        case sf::Keyboard::sZ:  return 0x06;
 
-        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 sf::Keyboard::sNum1:  return 0x12;
+        case sf::Keyboard::sNum2:  return 0x13;
+        case sf::Keyboard::sNum3:  return 0x14;
+        case sf::Keyboard::sNum4:  return 0x15;
+        case sf::Keyboard::sNum5:  return 0x17;
+        case sf::Keyboard::sNum6:  return 0x16;
+        case sf::Keyboard::sNum7:  return 0x1a;
+        case sf::Keyboard::sNum8:  return 0x1c;
+        case sf::Keyboard::sNum9:  return 0x19;
+        case sf::Keyboard::sNum0:  return 0x1d;
 
-        case NSPauseFunctionKey:        return sf::Keyboard::Pause;
+        case sf::Keyboard::sEnter:      return 0x24;
+        case sf::Keyboard::sEscape:     return 0x35;
+        case sf::Keyboard::sBackspace:  return 0x33;
+        case sf::Keyboard::sTab:        return 0x30;
+        case sf::Keyboard::sSpace:      return 0x31;
 
-            // keycode 0x1b is not bound to any key.
-            // This key is ' on CH-FR, ) on FR and - on US layouts.
+        // case sf::Keyboard::sHyphen:       return 0; // 0x27 is for Quote
 
-            // An unknown key.
-        default:                        return sf::Keyboard::Unknown;
+        case sf::Keyboard::sEquals:       return 0x18;
+        case sf::Keyboard::sLBracket:     return 0x21;
+        case sf::Keyboard::sRBracket:     return 0x1e;
+        case sf::Keyboard::sBackslash:    return 0x2a;
+        case sf::Keyboard::sDash:         return 0x1b;
+        case sf::Keyboard::sSemicolon:    return 0x29;
+        case sf::Keyboard::sQuote:        return 0x27;
+        case sf::Keyboard::sGraveAccent:  return 0x32;
+        case sf::Keyboard::sComma:        return 0x2b;
+        case sf::Keyboard::sPeriod:       return 0x2f;
+        case sf::Keyboard::sForwardSlash: return 0x2c;
+
+        case sf::Keyboard::sF1:   return 0x7a;
+        case sf::Keyboard::sF2:   return 0x78;
+        case sf::Keyboard::sF3:   return 0x63;
+        case sf::Keyboard::sF4:   return 0x76;
+        case sf::Keyboard::sF5:   return 0x60;
+        case sf::Keyboard::sF6:   return 0x61;
+        case sf::Keyboard::sF7:   return 0x62;
+        case sf::Keyboard::sF8:   return 0x64;
+        case sf::Keyboard::sF9:   return 0x65;
+        case sf::Keyboard::sF10:  return 0x6d;
+        case sf::Keyboard::sF11:  return 0x67;
+        case sf::Keyboard::sF12:  return 0x6f;
+        case sf::Keyboard::sF13:  return 0x69;
+        case sf::Keyboard::sF14:  return 0x6b;
+        case sf::Keyboard::sF15:  return 0x71;
+
+        case sf::Keyboard::sCapsLock:    return 0x39;
+
+        /* TODO Those are missing:
+         * case sf::Keyboard::sPrintScreen: return 0;
+         * case sf::Keyboard::sScrollLock:  return 0;
+         * case sf::Keyboard::sPause:    return 0;
+         */
+        case sf::Keyboard::sInsert:   return 0x72;
+        case sf::Keyboard::sHome:     return 0x73;
+        case sf::Keyboard::sPageUp:   return 0x74;
+        case sf::Keyboard::sDelete:   return 0x75;
+        case sf::Keyboard::sEnd:      return 0x77;
+        case sf::Keyboard::sPageDown: return 0x79;
+
+        case sf::Keyboard::sRight:  return 0x7c;
+        case sf::Keyboard::sLeft:   return 0x7b;
+        case sf::Keyboard::sDown:   return 0x7d;
+        case sf::Keyboard::sUp:     return 0x7e;
+
+        case sf::Keyboard::sNumLock:   return 0x47;
+        case sf::Keyboard::sDivide:    return 0x4b;
+        case sf::Keyboard::sMultiply:  return 0x43;
+        case sf::Keyboard::sMinus:     return 0x4e;
+        case sf::Keyboard::sPlus:      return 0x45;
+        case sf::Keyboard::sPadEquals: return 0x51;
+        case sf::Keyboard::sReturn:    return 0x4c;
+        case sf::Keyboard::sDecimal:   return 0x41;
+
+        case sf::Keyboard::sNumpad1:  return 0x53;
+        case sf::Keyboard::sNumpad2:  return 0x54;
+        case sf::Keyboard::sNumpad3:  return 0x55;
+        case sf::Keyboard::sNumpad4:  return 0x56;
+        case sf::Keyboard::sNumpad5:  return 0x57;
+        case sf::Keyboard::sNumpad6:  return 0x58;
+        case sf::Keyboard::sNumpad7:  return 0x59;
+        case sf::Keyboard::sNumpad8:  return 0x5b;
+        case sf::Keyboard::sNumpad9:  return 0x5c;
+        case sf::Keyboard::sNumpad0:  return 0x52;
+
+        /* TODO Those are missing:
+         * case sf::Keyboard::sReverseSolidus: return 0;
+         * case sf::Keyboard::sApplication: return 0;
+         * case sf::Keyboard::sExecute:     return 0;
+         * case sf::Keyboard::sHelp:        return 0;
+         * case sf::Keyboard::sMenu:        return 0;
+         * case sf::Keyboard::sSelect:      return 0;
+         * case sf::Keyboard::sStop:        return 0;
+         * case sf::Keyboard::sAgain:       return 0;
+         * case sf::Keyboard::sUndo:        return 0;
+         * case sf::Keyboard::sCut:         return 0;
+         * case sf::Keyboard::sCopy:        return 0;
+         * case sf::Keyboard::sPaste:       return 0;
+         * case sf::Keyboard::sFind:        return 0;
+         */
+
+        case sf::Keyboard::sMute:        return 0x4a;
+        case sf::Keyboard::sVolumeUp:    return 0x48;
+        case sf::Keyboard::sVolumeDown:  return 0x49;
+
+        case sf::Keyboard::sLControl:  return 0x3b;
+        case sf::Keyboard::sLShift:    return 0x38;
+        case sf::Keyboard::sLAlt:      return 0x3a;
+        case sf::Keyboard::sLSystem:   return 0x37;
+        case sf::Keyboard::sRControl:  return 0x3b;
+        case sf::Keyboard::sRShift:    return 0x38;
+        case sf::Keyboard::sRAlt:      return 0x3a;
+        case sf::Keyboard::sRSystem:   return 0x37;
+
+        default: return 0x00;
+    }
+}
+
+
+////////////////////////////////////////////////////////
+Keyboard::Key HIDInputManager::localizedKeyFallback(Keyboard::Scancode code)
+{
+    switch (code)
+    {
+        case sf::Keyboard::sEnter:      return sf::Keyboard::Return;
+        case sf::Keyboard::sEscape:     return sf::Keyboard::Escape;
+        case sf::Keyboard::sBackspace:  return sf::Keyboard::BackSpace;
+        case sf::Keyboard::sTab:        return sf::Keyboard::Tab;
+        case sf::Keyboard::sSpace:      return sf::Keyboard::Space;
+
+        case sf::Keyboard::sF1:   return sf::Keyboard::F1;
+        case sf::Keyboard::sF2:   return sf::Keyboard::F2;
+        case sf::Keyboard::sF3:   return sf::Keyboard::F3;
+        case sf::Keyboard::sF4:   return sf::Keyboard::F4;
+        case sf::Keyboard::sF5:   return sf::Keyboard::F5;
+        case sf::Keyboard::sF6:   return sf::Keyboard::F6;
+        case sf::Keyboard::sF7:   return sf::Keyboard::F7;
+        case sf::Keyboard::sF8:   return sf::Keyboard::F8;
+        case sf::Keyboard::sF9:   return sf::Keyboard::F9;
+        case sf::Keyboard::sF10:  return sf::Keyboard::F10;
+        case sf::Keyboard::sF11:  return sf::Keyboard::F11;
+        case sf::Keyboard::sF12:  return sf::Keyboard::F12;
+        case sf::Keyboard::sF13:  return sf::Keyboard::F13;
+        case sf::Keyboard::sF14:  return sf::Keyboard::F14;
+        case sf::Keyboard::sF15:  return sf::Keyboard::F15;
+
+        case sf::Keyboard::sPause:    return sf::Keyboard::Pause;
+        case sf::Keyboard::sInsert:   return sf::Keyboard::Insert;
+        case sf::Keyboard::sHome:     return sf::Keyboard::Home;
+        case sf::Keyboard::sPageUp:   return sf::Keyboard::PageUp;
+        case sf::Keyboard::sDelete:   return sf::Keyboard::Delete;
+        case sf::Keyboard::sEnd:      return sf::Keyboard::End;
+        case sf::Keyboard::sPageDown: return sf::Keyboard::PageDown;
+
+        case sf::Keyboard::sRight:  return sf::Keyboard::Right;
+        case sf::Keyboard::sLeft:   return sf::Keyboard::Left;
+        case sf::Keyboard::sDown:   return sf::Keyboard::Down;
+        case sf::Keyboard::sUp:     return sf::Keyboard::Up;
+
+        case sf::Keyboard::sDivide:   return sf::Keyboard::Divide;
+        case sf::Keyboard::sMultiply: return sf::Keyboard::Multiply;
+        case sf::Keyboard::sMinus:    return sf::Keyboard::Subtract;
+        case sf::Keyboard::sPlus:     return sf::Keyboard::Add;
+
+        case sf::Keyboard::sNumpad0:  return sf::Keyboard::Numpad1;
+        case sf::Keyboard::sNumpad1:  return sf::Keyboard::Numpad2;
+        case sf::Keyboard::sNumpad2:  return sf::Keyboard::Numpad3;
+        case sf::Keyboard::sNumpad3:  return sf::Keyboard::Numpad4;
+        case sf::Keyboard::sNumpad4:  return sf::Keyboard::Numpad5;
+        case sf::Keyboard::sNumpad5:  return sf::Keyboard::Numpad6;
+        case sf::Keyboard::sNumpad6:  return sf::Keyboard::Numpad7;
+        case sf::Keyboard::sNumpad7:  return sf::Keyboard::Numpad8;
+        case sf::Keyboard::sNumpad8:  return sf::Keyboard::Numpad9;
+        case sf::Keyboard::sNumpad9:  return sf::Keyboard::Numpad0;
+
+        case sf::Keyboard::sLControl:  return sf::Keyboard::LControl;
+        case sf::Keyboard::sLShift:    return sf::Keyboard::LShift;
+        case sf::Keyboard::sLAlt:      return sf::Keyboard::LAlt;
+        case sf::Keyboard::sLSystem:   return sf::Keyboard::LSystem;
+        case sf::Keyboard::sRControl:  return sf::Keyboard::RControl;
+        case sf::Keyboard::sRShift:    return sf::Keyboard::RShift;
+        case sf::Keyboard::sRAlt:      return sf::Keyboard::RAlt;
+        case sf::Keyboard::sRSystem:   return sf::Keyboard::RSystem;
+
+        default: return sf::Keyboard::Unknown;
     }
 }
 
diff --git a/src/SFML/Window/OSX/InputImpl.mm b/src/SFML/Window/OSX/InputImpl.mm
index 0397237a..84533803 100644
--- a/src/SFML/Window/OSX/InputImpl.mm
+++ b/src/SFML/Window/OSX/InputImpl.mm
@@ -39,9 +39,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
@@ -122,6 +119,7 @@ SFOpenGLView* getSFOpenGLViewFromSFMLWindow(const WindowBase& window)
     return view;
 }
 
+
 ////////////////////////////////////////////////////////////
 bool InputImpl::isKeyPressed(Keyboard::Key key)
 {
@@ -129,6 +127,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::unlocalize(Keyboard::Key key)
+{
+    return HIDInputManager::getInstance().unlocalize(key);
+}
+
+
+////////////////////////////////////////////////////////////
+String InputImpl::localizedRepresentation(Keyboard::Scancode code)
+{
+    return HIDInputManager::getInstance().localizedRepresentation(code);
+}
+
+
 ////////////////////////////////////////////////////////////
 void InputImpl::setVirtualKeyboardVisible(bool /*visible*/)
 {
diff --git a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h
index ea2d5a17..03d6c093 100644
--- a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h
+++ b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.h
@@ -56,7 +56,8 @@ void initialiseKeyboardHelper(void);
 /// \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 105e9110..cf9a1940 100644
--- a/src/SFML/Window/OSX/SFKeyboardModifiersHelper.mm
+++ b/src/SFML/Window/OSX/SFKeyboardModifiersHelper.mm
@@ -92,6 +92,7 @@ BOOL isKeyMaskActive(NSUInteger modifiers, NSUInteger mask);
 ////////////////////////////////////////////////////////////
 void processOneModifier(NSUInteger modifiers, NSUInteger mask,
                         BOOL& wasDown, sf::Keyboard::Key key,
+                        sf::Keyboard::Scancode code,
                         sf::priv::WindowImplCocoa& requester);
 
 
@@ -105,6 +106,7 @@ void processLeftRightModifiers(NSUInteger modifiers,
                                NSUInteger leftMask, NSUInteger rightMask,
                                BOOL& leftWasDown, BOOL& rightWasDown,
                                sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey,
+                               sf::Keyboard::Scancode leftCode, sf::Keyboard::Scancode rightCode,
                                sf::priv::WindowImplCocoa& requester);
 
 
@@ -136,14 +138,15 @@ void initialiseKeyboardHelper(void)
 
 
 ////////////////////////////////////////////////////////
-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;
 }
@@ -158,6 +161,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
         NSLeftShiftKeyMask, NSRightShiftKeyMask,
         state.leftShiftWasDown, state.rightShiftWasDown,
         sf::Keyboard::LShift, sf::Keyboard::RShift,
+        sf::Keyboard::sLShift, sf::Keyboard::sRShift,
         requester
     );
 
@@ -167,6 +171,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
         NSLeftCommandKeyMask, NSRightCommandKeyMask,
         state.leftCommandWasDown, state.rightCommandWasDown,
         sf::Keyboard::LSystem, sf::Keyboard::RSystem,
+        sf::Keyboard::sLSystem, sf::Keyboard::sRSystem,
         requester
     );
 
@@ -176,6 +181,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
         NSLeftAlternateKeyMask, NSRightAlternateKeyMask,
         state.leftAlternateWasDown, state.rightAlternateWasDown,
         sf::Keyboard::LAlt, sf::Keyboard::RAlt,
+        sf::Keyboard::sLAlt, sf::Keyboard::sRAlt,
         requester
     );
 
@@ -185,6 +191,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
         NSLeftControlKeyMask, NSRightControlKeyMask,
         state.leftControlWasDown, state.rightControlWasDown,
         sf::Keyboard::LControl, sf::Keyboard::RControl,
+        sf::Keyboard::sLControl, sf::Keyboard::sRControl,
         requester
     );
 }
@@ -203,10 +210,11 @@ BOOL isKeyMaskActive(NSUInteger modifiers, NSUInteger mask)
 ////////////////////////////////////////////////////////
 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);
@@ -231,10 +239,11 @@ void processLeftRightModifiers(NSUInteger modifiers,
                                NSUInteger leftMask, NSUInteger rightMask,
                                BOOL& leftWasDown, 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 eb3e1231..86b4f109 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::sUnknown))
             m_requester->keyDown(key);
     }
 
@@ -180,21 +180,17 @@
 ////////////////////////////////////////////////////////
 +(sf::Event::KeyEvent)convertNSKeyEventToSFMLEvent:(NSEvent*)event
 {
-    // Key code
-    sf::Keyboard::Key key = sf::Keyboard::Unknown;
-
-    // First we look if the key down is from a list of characters
-    // that depend on keyboard localization.
+    // We look for the key in a list of characters that depend on keyboard localization,
+    // if the key is not "dead".
     NSString* string = [event charactersIgnoringModifiers];
-    if ([string length] > 0)
-        key = sf::priv::HIDInputManager::localizedKeys([string characterAtIndex:0]);
+    sf::Keyboard::Key key = ([string length] > 0)
+        ? sf::priv::HIDInputManager::localizedKey([string characterAtIndex:0])
+        : sf::Keyboard::Unknown;
 
-    // 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]);
+    // The scancode always depends on the hardware keyboard, not some OS setting.
+    sf::Keyboard::Scancode code = sf::priv::HIDInputManager::nonLocalizedKey([event keyCode]);
 
-    return keyEventWithModifiers([event modifierFlags], key);
+    return keyEventWithModifiers([event modifierFlags], key, code);
 }