Add support of scancodes for macOS

This commit is contained in:
Marco Antognini 2017-03-28 23:37:06 +02:00 committed by Lukas Dürrenberger
parent 75ef99e2ca
commit e806048904
6 changed files with 1167 additions and 630 deletions

View File

@ -65,16 +65,6 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static HIDInputManager& getInstance(); 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: public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -103,21 +93,64 @@ public:
/// ///
/// Return sf::Keyboard::Unknown if it doesn't match any 'localized' keys. /// Return sf::Keyboard::Unknown if it doesn't match any 'localized' keys.
/// ///
/// By 'localized' I mean keys that depend on the keyboard layout /// By 'localized' we mean keys that depend on the keyboard layout
/// and might not be the same as the US keycode in some country /// and might not be the same as the US keycode for some countries
/// (e.g. the keys 'Y' and 'Z' are switched on QWERTZ keyboard and /// (e.g. the keys 'Y' and 'Z' are swapped on QWERTZ keyboard and
/// US keyboard layouts.) /// 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: private:
@ -136,7 +169,13 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Initialize the keyboard part of this class /// \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(); void initializeKeyboard();
@ -145,28 +184,39 @@ private:
/// \brief Load the given keyboard into m_keys /// \brief Load the given keyboard into m_keys
/// ///
/// If the given keyboard has no key this function simply /// 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 /// \param keyboard Keyboard to load
/// ///
/// \see initializeKeyboard
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void loadKeyboard(IOHIDDeviceRef keyboard); void loadKeyboard(IOHIDDeviceRef keyboard);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the given key into m_keys /// \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 /// \param key Key to load
/// ///
/// \see initializeKeyboard
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void loadKey(IOHIDElementRef key); void loadKey(IOHIDElementRef key);
////////////////////////////////////////////////////////////
/// Regenerate the mappings from/to Key and Scancode
///
////////////////////////////////////////////////////////////
void buildMappings();
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Release all resources /// \brief Release all resources
/// ///
/// Close all connections to any devices, if required /// Close all connections to any devices.
/// Set m_isValid to false ///
/// \see initializeKeyboard
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void freeUp(); void freeUp();
@ -174,11 +224,11 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Filter the devices and return them /// \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 page HID page like kHIDPage_GenericDesktop
/// \param usage HID usage page like kHIDUsage_GD_Keyboard or kHIDUsage_GD_Mouse /// \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); CFSetRef copyDevices(UInt32 page, UInt32 usage);
@ -196,36 +246,48 @@ private:
bool isPressed(IOHIDElements& elements); bool isPressed(IOHIDElements& elements);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Convert a HID key usage to its corresponding virtual code /// \brief Convert a HID key usage to its corresponding scancode
///
/// See IOHIDUsageTables.h
/// ///
/// \param usage Any kHIDUsage_Keyboard* usage /// \param usage Any kHIDUsage_Keyboard* usage
/// \return the virtual code associate with the given HID key usage /// \return the scancode associate with the given HID key usage
/// or 0xff if it is associate with no virtual code /// 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: private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool m_isValid; ///< If any error occurs this variable is false IOHIDManagerRef m_manager; ///< Underlying HID Manager
CFDataRef m_layoutData; ///< CFData containing the layout IOHIDElements m_keys[Keyboard::sCodeCount]; ///< All the keys on any connected keyboard
UCKeyboardLayout* m_layout; ///< Current Keyboard Layout Keyboard::Scancode m_mapping[Keyboard::KeyCount]; ///< Mapping from Key to Scancode
IOHIDManagerRef m_manager; ///< HID Manager Keyboard::Key m_gnippam[Keyboard::sCodeCount]; ///< Mapping from Scancode to Key
IOHIDElements m_keys[Keyboard::KeyCount]; ///< All the keys on any connected keyboard
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// m_keys' index corresponds to sf::Keyboard::Key enum. /// m_keys' index corresponds to sf::Keyboard::Scancode enum.
/// if no key is assigned with key XYZ then m_keys[XYZ].size() == 0. /// 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 /// If there are several keyboards connected and several HID keys associated
/// with the same sf::Keyboard::Key then m_keys[XYZ] contains all these /// with the same sf::Keyboard::Key then m_keys[XYZ] contains all these
/// HID keys. /// HID keys.
/// ///
/// The mappings (both directions) get invalidated when the
/// keyboard layout changes. They both default to (s)Unknown.
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
}; };

File diff suppressed because it is too large Load Diff

View File

@ -40,9 +40,6 @@
/// In order to keep track of the keyboard's state and mouse buttons' state /// In order to keep track of the keyboard's state and mouse buttons' state
/// we use the HID manager. Mouse position is handled differently. /// we use the HID manager. Mouse position is handled differently.
/// ///
/// NB: we probably could use
/// NSEvent +addGlobalMonitorForEventsMatchingMask:handler: for mouse only.
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
namespace sf namespace sf
@ -123,6 +120,7 @@ SFOpenGLView* getSFOpenGLViewFromSFMLWindow(const WindowBase& window)
return view; return view;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool InputImpl::isKeyPressed(Keyboard::Key key) bool InputImpl::isKeyPressed(Keyboard::Key key)
{ {
@ -131,6 +129,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*/) void InputImpl::setVirtualKeyboardVisible(bool /*visible*/)
{ {

View File

@ -56,7 +56,8 @@ void initialiseKeyboardHelper(void);
/// \brief Set up a SFML key event based on the given modifiers flags and key code /// \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);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -100,6 +100,7 @@ BOOL isKeyMaskActive(NSUInteger modifiers, NSUInteger mask);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void processOneModifier(NSUInteger modifiers, NSUInteger mask, void processOneModifier(NSUInteger modifiers, NSUInteger mask,
BOOL& wasDown, sf::Keyboard::Key key, BOOL& wasDown, sf::Keyboard::Key key,
sf::Keyboard::Scancode code,
sf::priv::WindowImplCocoa& requester); sf::priv::WindowImplCocoa& requester);
@ -113,6 +114,7 @@ void processLeftRightModifiers(NSUInteger modifiers,
NSUInteger leftMask, NSUInteger rightMask, NSUInteger leftMask, NSUInteger rightMask,
BOOL& leftWasDown, BOOL& rightWasDown, BOOL& leftWasDown, BOOL& rightWasDown,
sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey, sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey,
sf::Keyboard::Scancode leftCode, sf::Keyboard::Scancode rightCode,
sf::priv::WindowImplCocoa& requester); sf::priv::WindowImplCocoa& requester);
@ -144,14 +146,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; sf::Event::KeyEvent event;
event.code = key; event.code = key;
event.alt = modifiers & NSAlternateKeyMask; event.scancode = code;
event.control = modifiers & NSControlKeyMask; event.alt = modifiers & NSAlternateKeyMask;
event.shift = modifiers & NSShiftKeyMask; event.control = modifiers & NSControlKeyMask;
event.system = modifiers & NSCommandKeyMask; event.shift = modifiers & NSShiftKeyMask;
event.system = modifiers & NSCommandKeyMask;
return event; return event;
} }
@ -166,6 +169,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
NSLeftShiftKeyMask, NSRightShiftKeyMask, NSLeftShiftKeyMask, NSRightShiftKeyMask,
state.leftShiftWasDown, state.rightShiftWasDown, state.leftShiftWasDown, state.rightShiftWasDown,
sf::Keyboard::LShift, sf::Keyboard::RShift, sf::Keyboard::LShift, sf::Keyboard::RShift,
sf::Keyboard::sLShift, sf::Keyboard::sRShift,
requester requester
); );
@ -175,6 +179,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
NSLeftCommandKeyMask, NSRightCommandKeyMask, NSLeftCommandKeyMask, NSRightCommandKeyMask,
state.leftCommandWasDown, state.rightCommandWasDown, state.leftCommandWasDown, state.rightCommandWasDown,
sf::Keyboard::LSystem, sf::Keyboard::RSystem, sf::Keyboard::LSystem, sf::Keyboard::RSystem,
sf::Keyboard::sLSystem, sf::Keyboard::sRSystem,
requester requester
); );
@ -184,6 +189,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
NSLeftAlternateKeyMask, NSRightAlternateKeyMask, NSLeftAlternateKeyMask, NSRightAlternateKeyMask,
state.leftAlternateWasDown, state.rightAlternateWasDown, state.leftAlternateWasDown, state.rightAlternateWasDown,
sf::Keyboard::LAlt, sf::Keyboard::RAlt, sf::Keyboard::LAlt, sf::Keyboard::RAlt,
sf::Keyboard::sLAlt, sf::Keyboard::sRAlt,
requester requester
); );
@ -193,6 +199,7 @@ void handleModifiersChanged(NSUInteger modifiers, sf::priv::WindowImplCocoa& req
NSLeftControlKeyMask, NSRightControlKeyMask, NSLeftControlKeyMask, NSRightControlKeyMask,
state.leftControlWasDown, state.rightControlWasDown, state.leftControlWasDown, state.rightControlWasDown,
sf::Keyboard::LControl, sf::Keyboard::RControl, sf::Keyboard::LControl, sf::Keyboard::RControl,
sf::Keyboard::sLControl, sf::Keyboard::sRControl,
requester requester
); );
} }
@ -211,10 +218,11 @@ BOOL isKeyMaskActive(NSUInteger modifiers, NSUInteger mask)
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
void processOneModifier(NSUInteger modifiers, NSUInteger mask, void processOneModifier(NSUInteger modifiers, NSUInteger mask,
BOOL& wasDown, sf::Keyboard::Key key, BOOL& wasDown, sf::Keyboard::Key key,
sf::Keyboard::Scancode code,
sf::priv::WindowImplCocoa& requester) sf::priv::WindowImplCocoa& requester)
{ {
// Setup a potential event key. // Setup a potential event key.
sf::Event::KeyEvent event = keyEventWithModifiers(modifiers, key); sf::Event::KeyEvent event = keyEventWithModifiers(modifiers, key, code);
// State // State
BOOL isDown = isKeyMaskActive(modifiers, mask); BOOL isDown = isKeyMaskActive(modifiers, mask);
@ -239,10 +247,11 @@ void processLeftRightModifiers(NSUInteger modifiers,
NSUInteger leftMask, NSUInteger rightMask, NSUInteger leftMask, NSUInteger rightMask,
BOOL& leftWasDown, BOOL& rightWasDown, BOOL& leftWasDown, BOOL& rightWasDown,
sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey, sf::Keyboard::Key leftKey, sf::Keyboard::Key rightKey,
sf::Keyboard::Scancode leftCode, sf::Keyboard::Scancode rightCode,
sf::priv::WindowImplCocoa& requester) sf::priv::WindowImplCocoa& requester)
{ {
processOneModifier(modifiers, leftMask, leftWasDown, leftKey, requester); processOneModifier(modifiers, leftMask, leftWasDown, leftKey, leftCode, requester);
processOneModifier(modifiers, rightMask, rightWasDown, rightKey, requester); processOneModifier(modifiers, rightMask, rightWasDown, rightKey, rightCode, requester);
} }

View File

@ -94,7 +94,7 @@
{ {
sf::Event::KeyEvent key = [SFOpenGLView convertNSKeyEventToSFMLEvent:theEvent]; 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); m_requester->keyDown(key);
} }
@ -188,21 +188,17 @@
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
+(sf::Event::KeyEvent)convertNSKeyEventToSFMLEvent:(NSEvent*)event +(sf::Event::KeyEvent)convertNSKeyEventToSFMLEvent:(NSEvent*)event
{ {
// Key code // We look for the key in a list of characters that depend on keyboard localization,
sf::Keyboard::Key key = sf::Keyboard::Unknown; // if the key is not "dead".
// First we look if the key down is from a list of characters
// that depend on keyboard localization.
NSString* string = [event charactersIgnoringModifiers]; NSString* string = [event charactersIgnoringModifiers];
if ([string length] > 0) sf::Keyboard::Key key = ([string length] > 0)
key = sf::priv::HIDInputManager::localizedKeys([string characterAtIndex: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 // The scancode always depends on the hardware keyboard, not some OS setting.
// through virtual key code. sf::Keyboard::Scancode code = sf::priv::HIDInputManager::nonLocalizedKey([event keyCode]);
if (key == sf::Keyboard::Unknown)
key = sf::priv::HIDInputManager::nonLocalizedKeys([event keyCode]);
return keyEventWithModifiers([event modifierFlags], key); return keyEventWithModifiers([event modifierFlags], key, code);
} }