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();
////////////////////////////////////////////////////////////
/// \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.
///
////////////////////////////////////////////////////////////
};

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
/// we use the HID manager. Mouse position is handled differently.
///
/// NB: we probably could use
/// NSEvent +addGlobalMonitorForEventsMatchingMask:handler: for mouse only.
///
////////////////////////////////////////////////////////////
namespace sf
@ -123,6 +120,7 @@ SFOpenGLView* getSFOpenGLViewFromSFMLWindow(const WindowBase& window)
return view;
}
////////////////////////////////////////////////////////////
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*/)
{

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
///
////////////////////////////////////////////////////////////
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,
BOOL& wasDown, sf::Keyboard::Key key,
sf::Keyboard::Scancode code,
sf::priv::WindowImplCocoa& requester);
@ -113,6 +114,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);
@ -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;
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;
}
@ -166,6 +169,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
);
@ -175,6 +179,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
);
@ -184,6 +189,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
);
@ -193,6 +199,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
);
}
@ -211,10 +218,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);
@ -239,10 +247,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);
}

View File

@ -94,7 +94,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);
}
@ -188,21 +188,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);
}