From b2e6f67784d656f87c05597c56005297b1c941e2 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 2 Oct 2024 13:49:50 +0200 Subject: [PATCH] TEMP: Basic sync of KeyboardMacOS.cxx --- vncviewer/CMakeLists.txt | 2 +- vncviewer/KeyboardMacOS.cxx | 135 --------- vncviewer/KeyboardMacOS.h | 57 +++- vncviewer/KeyboardMacOS.mm | 530 ++++++++++++++++++++++++++++++++++++ vncviewer/cocoa.h | 13 - vncviewer/cocoa.mm | 374 ------------------------- 6 files changed, 576 insertions(+), 535 deletions(-) delete mode 100644 vncviewer/KeyboardMacOS.cxx create mode 100644 vncviewer/KeyboardMacOS.mm diff --git a/vncviewer/CMakeLists.txt b/vncviewer/CMakeLists.txt index 5899cad2dc..e6d9aec524 100644 --- a/vncviewer/CMakeLists.txt +++ b/vncviewer/CMakeLists.txt @@ -48,7 +48,7 @@ elseif(APPLE) set(OS_DEP_SOURCES vncmacview.cxx vncmacview.h - KeyboardMacOS.cxx + KeyboardMacOS.mm KeyboardMacOS.h cocoa.h cocoa.mm diff --git a/vncviewer/KeyboardMacOS.cxx b/vncviewer/KeyboardMacOS.cxx deleted file mode 100644 index f25e2dedf9..0000000000 --- a/vncviewer/KeyboardMacOS.cxx +++ /dev/null @@ -1,135 +0,0 @@ -#include "KeyboardMacOS.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "PlatformPixelBuffer.h" -#include "appmanager.h" -#include "cocoa.h" -#include "i18n.h" -#include "parameters.h" -#include "rdr/Exception.h" -#include "rfb/LogWriter.h" -#include "rfb/ServerParams.h" -#include "rfb/ledStates.h" -#include "vncconnection.h" -#include "DesktopWindow.h" - -#include -extern const unsigned short code_map_osx_to_qnum[]; -extern const unsigned int code_map_osx_to_qnum_len; - -#ifndef XK_VoidSymbol -#define XK_LATIN1 -#define XK_MISCELLANY -#define XK_XKB_KEYS -#include -#endif - -#ifndef NoSymbol -#define NoSymbol 0 -#endif - -static rfb::LogWriter vlog("KeyboardMacOS"); - -KeyboardMacOS::KeyboardMacOS(KeyboardHandler* handler_) - : Keyboard(handler_) -{ -} - -bool KeyboardMacOS::handleEvent(const char* eventType, void* message) -{ - if (strcmp(eventType, "mac_generic_NSEvent") == 0) { - if (cocoa_is_keyboard_event(message)) { - int systemKeyCode = cocoa_event_keycode(message); - uint32_t keyCode; - if ((unsigned)systemKeyCode >= code_map_osx_to_qnum_len) { - keyCode = 0; - } else { - keyCode = code_map_osx_to_qnum[systemKeyCode]; - } - if (cocoa_is_key_press(message)) { - uint32_t keySym = cocoa_event_keysym(message); - if (keySym == NoSymbol) { - vlog.error(_("No symbol for key code 0x%02x (in the current state)"), (int)keyCode); - } - - handler->handleKeyPress(systemKeyCode, keyCode, keySym); - - // We don't get any release events for CapsLock, so we have to - // send the release right away. - if (keySym == XK_Caps_Lock) { - handler->handleKeyRelease(systemKeyCode); - } - } else { - handler->handleKeyRelease(systemKeyCode); - } - return true; - } - } - return false; -} - -bool KeyboardMacOS::isKeyboardSync(QByteArray const& eventType, void* message) -{ - if (strcmp(eventType, "mac_generic_NSEvent") == 0) { - if (cocoa_is_keyboard_sync(message)) { - return true; - } - } - return false; -} - -unsigned KeyboardMacOS::getLEDState() -{ - bool on; - int ret = cocoa_get_caps_lock_state(&on); - if (ret != 0) { - vlog.error(_("Failed to get keyboard LED state: %d"), ret); - return rfb::ledUnknown; - } - unsigned int state = 0; - if (on) { - state |= rfb::ledCapsLock; - } - ret = cocoa_get_num_lock_state(&on); - if (ret != 0) { - vlog.error(_("Failed to get keyboard LED state: %d"), ret); - return rfb::ledUnknown; - } - if (on) { - state |= rfb::ledNumLock; - } - return state; -} - -void KeyboardMacOS::setLEDState(unsigned state) -{ - vlog.debug("Got server LED state: 0x%08x", state); - - int ret = cocoa_set_caps_lock_state(state & rfb::ledCapsLock); - if (ret == 0) { - ret = cocoa_set_num_lock_state(state & rfb::ledNumLock); - } - - if (ret != 0) { - vlog.error(_("Failed to update keyboard LED state: %d"), ret); - } -} - -void KeyboardMacOS::grabKeyboard() -{ - int ret = cocoa_capture_displays(AppManager::instance()->getWindow()->fullscreenScreens()); - if (ret == 1) { - vlog.error(_("Failure grabbing keyboard")); - return; - } - Keyboard::grabKeyboard(); -} - -void KeyboardMacOS::ungrabKeyboard() -{ - cocoa_release_displays(); - Keyboard::ungrabKeyboard(); -} diff --git a/vncviewer/KeyboardMacOS.h b/vncviewer/KeyboardMacOS.h index 14571e848e..15d9530ec9 100644 --- a/vncviewer/KeyboardMacOS.h +++ b/vncviewer/KeyboardMacOS.h @@ -1,28 +1,61 @@ -#ifndef X11KEYBOARDHANDLER_H -#define X11KEYBOARDHANDLER_H +/* Copyright 2011-2021 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __KEYBOARDMACOS_H__ +#define __KEYBOARDMACOS_H__ #include "Keyboard.h" -class NSView; -class NSCursor; +#ifdef __OBJC__ +@class NSEvent; +@class NSString; +#else +class NSEvent; +class NSString; +#endif -class KeyboardMacOS : public Keyboard -{ +class KeyboardMacOS : public Keyboard { public: KeyboardMacOS(KeyboardHandler* handler); + virtual ~KeyboardMacOS(); bool handleEvent(const char* eventType, void* message) override; - static bool isKeyboardSync(QByteArray const& eventType, void* message); - unsigned getLEDState() override; void setLEDState(unsigned state) override; void grabKeyboard() override; void ungrabKeyboard() override; -private: - NSView* view; - NSCursor* cursor; + // Special helper on macOS + static bool isKeyboardSync(const char* eventType, void* message); + +protected: + bool isKeyboardEvent(const NSEvent* nsevent); + bool isKeyPress(const NSEvent* nsevent); + uint32_t translateSystemKeyCode(int systemKeyCode); + unsigned getSystemKeyCode(const NSEvent* nsevent); + + NSString* keyTranslate(unsigned keyCode, unsigned modifierFlags); + uint32_t translateEventKeysym(const NSEvent* nsevent); + + int openHID(unsigned int* ioc); + int getModifierLockState(int modifier, bool* on); + int setModifierLockState(int modifier, bool on); }; -#endif // VNCMACVIEW_H +#endif diff --git a/vncviewer/KeyboardMacOS.mm b/vncviewer/KeyboardMacOS.mm new file mode 100644 index 0000000000..0c8b832c68 --- /dev/null +++ b/vncviewer/KeyboardMacOS.mm @@ -0,0 +1,530 @@ +/* Copyright 2011-2021 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#import +#import + +#include +#include + +// This wasn't added until 10.12 +#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 +const int kVK_RightCommand = 0x36; +#endif +// And this is still missing +const int kVK_Menu = 0x6E; + +#define XK_LATIN1 +#define XK_MISCELLANY +#include +#include +#include +#include + +#define NoSymbol 0 + +#include "appmanager.h" +#include "cocoa.h" +#include "i18n.h" +#include "keysym2ucs.h" +#include "KeyboardMacOS.h" + +extern const unsigned short code_map_osx_to_qnum[]; +extern const unsigned int code_map_osx_to_qnum_len; + +static rfb::LogWriter vlog("KeyboardMacOS"); + +static const int kvk_map[][2] = { + { kVK_Return, XK_Return }, + { kVK_Tab, XK_Tab }, + { kVK_Space, XK_space }, + { kVK_Delete, XK_BackSpace }, + { kVK_Escape, XK_Escape }, + { kVK_RightCommand, XK_Super_R }, + { kVK_Command, XK_Super_L }, + { kVK_Shift, XK_Shift_L }, + { kVK_CapsLock, XK_Caps_Lock }, + { kVK_Option, XK_Alt_L }, + { kVK_Control, XK_Control_L }, + { kVK_RightShift, XK_Shift_R }, + { kVK_RightOption, XK_Alt_R }, + { kVK_RightControl, XK_Control_R }, + { kVK_F17, XK_F17 }, + { kVK_VolumeUp, XF86XK_AudioRaiseVolume }, + { kVK_VolumeDown, XF86XK_AudioLowerVolume }, + { kVK_Mute, XF86XK_AudioMute }, + { kVK_F18, XK_F18 }, + { kVK_F19, XK_F19 }, + { kVK_F20, XK_F20 }, + { kVK_F5, XK_F5 }, + { kVK_F6, XK_F6 }, + { kVK_F7, XK_F7 }, + { kVK_F3, XK_F3 }, + { kVK_F8, XK_F8 }, + { kVK_F9, XK_F9 }, + { kVK_F11, XK_F11 }, + { kVK_F13, XK_F13 }, + { kVK_F16, XK_F16 }, + { kVK_F14, XK_F14 }, + { kVK_F10, XK_F10 }, + { kVK_Menu, XK_Menu }, + { kVK_F12, XK_F12 }, + { kVK_F15, XK_F15 }, + // Should we send Insert here? + { kVK_Help, XK_Help }, + { kVK_Home, XK_Home }, + { kVK_PageUp, XK_Page_Up }, + { kVK_ForwardDelete, XK_Delete }, + { kVK_F4, XK_F4 }, + { kVK_End, XK_End }, + { kVK_F2, XK_F2 }, + { kVK_PageDown, XK_Page_Down }, + { kVK_F1, XK_F1 }, + { kVK_LeftArrow, XK_Left }, + { kVK_RightArrow, XK_Right }, + { kVK_DownArrow, XK_Down }, + { kVK_UpArrow, XK_Up }, + + // The OS X headers claim these keys are not layout independent. + // Could it be because of the state of the decimal key? + /* { kVK_ANSI_KeypadDecimal, XK_KP_Decimal }, */ // see below + { kVK_ANSI_KeypadMultiply, XK_KP_Multiply }, + { kVK_ANSI_KeypadPlus, XK_KP_Add }, + // OS X doesn't have NumLock, so is this really correct? + { kVK_ANSI_KeypadClear, XK_Num_Lock }, + { kVK_ANSI_KeypadDivide, XK_KP_Divide }, + { kVK_ANSI_KeypadEnter, XK_KP_Enter }, + { kVK_ANSI_KeypadMinus, XK_KP_Subtract }, + { kVK_ANSI_KeypadEquals, XK_KP_Equal }, + { kVK_ANSI_Keypad0, XK_KP_0 }, + { kVK_ANSI_Keypad1, XK_KP_1 }, + { kVK_ANSI_Keypad2, XK_KP_2 }, + { kVK_ANSI_Keypad3, XK_KP_3 }, + { kVK_ANSI_Keypad4, XK_KP_4 }, + { kVK_ANSI_Keypad5, XK_KP_5 }, + { kVK_ANSI_Keypad6, XK_KP_6 }, + { kVK_ANSI_Keypad7, XK_KP_7 }, + { kVK_ANSI_Keypad8, XK_KP_8 }, + { kVK_ANSI_Keypad9, XK_KP_9 }, + // Japanese Keyboard Support + { kVK_JIS_Eisu, XK_Eisu_toggle }, + { kVK_JIS_Kana, XK_Hiragana_Katakana }, +}; + +KeyboardMacOS::KeyboardMacOS(KeyboardHandler* handler_) + : Keyboard(handler_) +{ +} + +KeyboardMacOS::~KeyboardMacOS() +{ +} + +bool KeyboardMacOS::handleEvent(const char* eventType, void* message) +{ + const NSEvent* nsevent = (NSEvent*)message; + unsigned systemKeyCode; + + assert(eventType); + assert(message); + + if (strcmp(eventType, "mac_generic_NSEvent") != 0) + return false; + + if (!isKeyboardEvent(nsevent)) + return false; + + systemKeyCode = getSystemKeyCode(nsevent); + + if (isKeyPress(nsevent)) { + uint32_t keyCode; + uint32_t keySym; + + keyCode = translateSystemKeyCode(systemKeyCode); + + keySym = translateEventKeysym(nsevent); + if (keySym == NoSymbol) { + vlog.error(_("No symbol for key code 0x%02x (in the current state)"), + systemKeyCode); + } + + handler->handleKeyPress(systemKeyCode, keyCode, keySym); + + // We don't get any release events for CapsLock, so we have to + // send the release right away. + if (keySym == XK_Caps_Lock) + handler->handleKeyRelease(systemKeyCode); + } else { + handler->handleKeyRelease(systemKeyCode); + } + + return true; +} + +unsigned KeyboardMacOS::getLEDState() +{ + unsigned state; + int ret; + bool on; + + state = 0; + + ret = getModifierLockState(kIOHIDCapsLockState, &on); + if (ret != 0) { + vlog.error(_("Failed to get keyboard LED state: %d"), ret); + return rfb::ledUnknown; + } + if (on) + state |= rfb::ledCapsLock; + + ret = getModifierLockState(kIOHIDNumLockState, &on); + if (ret != 0) { + vlog.error(_("Failed to get keyboard LED state: %d"), ret); + return rfb::ledUnknown; + } + if (on) + state |= rfb::ledNumLock; + + // No support for Scroll Lock // + + return state; +} + +void KeyboardMacOS::setLEDState(unsigned state) +{ + int ret; + + ret = setModifierLockState(kIOHIDCapsLockState, state & rfb::ledCapsLock); + if (ret != 0) { + vlog.error(_("Failed to update keyboard LED state: %d"), ret); + return; + } + + ret = setModifierLockState(kIOHIDNumLockState, state & rfb::ledNumLock); + if (ret != 0) { + vlog.error(_("Failed to update keyboard LED state: %d"), ret); + return; + } + + // No support for Scroll Lock // +} + +void KeyboardMacOS::grabKeyboard() +{ + int ret = cocoa_capture_displays(AppManager::instance()->getWindow()->fullscreenScreens()); + if (ret == 1) { + vlog.error(_("Failure grabbing keyboard")); + return; + } + Keyboard::grabKeyboard(); +} + +void KeyboardMacOS::ungrabKeyboard() +{ + cocoa_release_displays(); + Keyboard::ungrabKeyboard(); +} + +bool KeyboardMacOS::isKeyboardSync(const char* eventType, void* message) +{ + const NSEvent* nsevent = (const NSEvent*)event; + + assert(eventType); + assert(message); + + if (strcmp(eventType, "mac_generic_NSEvent") != 0) + return false; + + // If we get a NSFlagsChanged event with key code 0 then this isn't + // an actual keyboard event but rather the system trying to sync up + // modifier state after it has stolen input for some reason (e.g. + // Cmd+Tab) + + if ([nsevent type] != NSFlagsChanged) + return false; + if ([nsevent keyCode] != 0) + return false; + + return true; +} + +bool KeyboardMacOS::isKeyboardEvent(const NSEvent* nsevent) +{ + switch ([nsevent type]) { + case NSKeyDown: + case NSKeyUp: + return true; + case NSFlagsChanged: + if (isKeyboardSync(nsevent)) + return false; + return true; + default: + return false; + } +} + +bool KeyboardMacOS::isKeyPress(const NSEvent* nsevent) +{ + if ([nsevent type] == NSKeyDown) + return true; + + if ([nsevent type] == NSFlagsChanged) { + UInt32 mask; + + // We don't see any event on release of CapsLock + if ([nsevent keyCode] == kVK_CapsLock) + return true; + + // These are entirely undocumented, but I cannot find any other way + // of differentiating between left and right keys + switch ([nsevent keyCode]) { + case kVK_RightCommand: + mask = 0x0010; + break; + case kVK_Command: + mask = 0x0008; + break; + case kVK_Shift: + mask = 0x0002; + break; + case kVK_CapsLock: + // We don't see any event on release of CapsLock + return 1; + case kVK_Option: + mask = 0x0020; + break; + case kVK_Control: + mask = 0x0001; + break; + case kVK_RightShift: + mask = 0x0004; + break; + case kVK_RightOption: + mask = 0x0040; + break; + case kVK_RightControl: + mask = 0x2000; + break; + default: + return false; + } + + if ([nsevent modifierFlags] & mask) + return true; + else + return false; + } + + return false; +} + +unsigned KeyboardMacOS::getSystemKeyCode(const NSEvent* nsevent) +{ + unsigned keycode; + + keycode = [nsevent keyCode]; + + // macOS swaps these two keys for unknown reasons for ISO layouts + if (KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) { + if (keycode == kVK_ANSI_Grave) + return kVK_ISO_Section; + if (keycode == kVK_ISO_Section) + return kVK_ANSI_Grave; + } + + return keycode; +} + +uint32_t KeyboardMacOS::translateSystemKeyCode(int systemKeyCode) +{ + if ((unsigned)systemKeyCode >= code_map_osx_to_qnum_len) + return 0; + + return code_map_osx_to_qnum[systemKeyCode]; +} + +NSString* KeyboardMacOS::keyTranslate(unsigned keyCode, + unsigned modifierFlags) +{ + const UCKeyboardLayout *layout; + OSStatus err; + + layout = nullptr; + + TISInputSourceRef keyboard; + CFDataRef uchr; + + keyboard = TISCopyCurrentKeyboardLayoutInputSource(); + uchr = (CFDataRef)TISGetInputSourceProperty(keyboard, + kTISPropertyUnicodeKeyLayoutData); + if (uchr == nullptr) + return nil; + + layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); + if (layout == nullptr) + return nil; + + UInt32 dead_state; + UniCharCount max_len, actual_len; + UniChar string[255]; + + dead_state = 0; + max_len = sizeof(string)/sizeof(*string); + + modifierFlags = (modifierFlags >> 8) & 0xff; + + err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags, + LMGetKbdType(), 0, &dead_state, max_len, &actual_len, + string); + if (err != noErr) + return nil; + + // Dead key? + if (dead_state != 0) { + // We have no fool proof way of asking what dead key this is. + // Assume we get a spacing equivalent if we press the + // same key again, and try to deduce something from that. + err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags, + LMGetKbdType(), 0, &dead_state, max_len, &actual_len, + string); + if (err != noErr) + return nil; + } + + return [NSString stringWithCharacters:string length:actual_len]; +} + +uint32_t KeyboardMacOS::translateEventKeysym(const NSEvent* nsevent) +{ + UInt16 key_code; + size_t i; + + NSString *chars; + UInt32 modifiers; + + key_code = [nsevent keyCode]; + + // Start with keys that either don't generate a symbol, or + // generate the same symbol as some other key. + for (i = 0;i < sizeof(kvk_map)/sizeof(kvk_map[0]);i++) { + if (key_code == kvk_map[i][0]) + return kvk_map[i][1]; + } + + // OS X always sends the same key code for the decimal key on the + // num pad, but X11 wants different keysyms depending on if it should + // be a comma or full stop. + if (key_code == 0x41) { + switch ([[nsevent charactersIgnoringModifiers] UTF8String][0]) { + case ',': + return XK_KP_Separator; + case '.': + return XK_KP_Decimal; + default: + return NoSymbol; + } + } + + // We want a "normal" symbol out of the event, which basically means + // we only respect the shift and alt/altgr modifiers. Cocoa can help + // us if we only wanted shift, but as we also want alt/altgr, we'll + // have to do some lookup ourselves. This matches our behaviour on + // other platforms. + + modifiers = 0; + if ([nsevent modifierFlags] & NSAlphaShiftKeyMask) + modifiers |= alphaLock; + if ([nsevent modifierFlags] & NSShiftKeyMask) + modifiers |= shiftKey; + if ([nsevent modifierFlags] & NSAlternateKeyMask) + modifiers |= optionKey; + + chars = keyTranslate(key_code, modifiers); + if (chars == nil) + return NoSymbol; + + // FIXME: Some dead keys are given as NBSP + combining character + if ([chars length] != 1) + return NoSymbol; + + // Dead key? + if ([[nsevent characters] length] == 0) + return ucs2keysym(ucs2combining([chars characterAtIndex:0])); + + return ucs2keysym([chars characterAtIndex:0]); +} + +int KeyboardMacOS::openHID(unsigned int* ioc) +{ + kern_return_t ret; + io_service_t ios; + CFMutableDictionaryRef mdict; + + mdict = IOServiceMatching(kIOHIDSystemClass); + ios = IOServiceGetMatchingService(kIOMasterPortDefault, + (CFDictionaryRef) mdict); + if (!ios) + return KERN_FAILURE; + + ret = IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, ioc); + IOObjectRelease(ios); + if (ret != KERN_SUCCESS) + return ret; + + return KERN_SUCCESS; +} + +int KeyboardMacOS::getModifierLockState(int modifier, bool* on) +{ + kern_return_t ret; + io_connect_t ioc; + + ret = openHID(&ioc); + if (ret != KERN_SUCCESS) + return ret; + + ret = IOHIDGetModifierLockState(ioc, modifier, on); + IOServiceClose(ioc); + if (ret != KERN_SUCCESS) + return ret; + + return KERN_SUCCESS; +} + +int KeyboardMacOS::setModifierLockState(int modifier, bool on) +{ + kern_return_t ret; + io_connect_t ioc; + + ret = openHID(&ioc); + if (ret != KERN_SUCCESS) + return ret; + + ret = IOHIDSetModifierLockState(ioc, modifier, on); + IOServiceClose(ioc); + if (ret != KERN_SUCCESS) + return ret; + + return KERN_SUCCESS; +} diff --git a/vncviewer/cocoa.h b/vncviewer/cocoa.h index edb4fc4df8..a89fb65a74 100644 --- a/vncviewer/cocoa.h +++ b/vncviewer/cocoa.h @@ -43,23 +43,10 @@ void cocoa_release_displays(); void cocoa_update_window_level(QWidget *widget, bool enabled, bool shielding = false); -int cocoa_is_keyboard_sync(const void *event); -int cocoa_is_keyboard_event(const void *event); - -int cocoa_is_key_press(const void *event); bool cocoa_is_mouse_entered(const void *event); bool cocoa_is_mouse_exited(const void *event); bool cocoa_is_mouse_moved(const void *event); -int cocoa_event_keycode(const void *event); -int cocoa_event_keysym(const void *event); - -int cocoa_set_caps_lock_state(bool on); -int cocoa_set_num_lock_state(bool on); - -int cocoa_get_caps_lock_state(bool *on); -int cocoa_get_num_lock_state(bool *on); - void cocoa_get_mouse_properties(const void *event, int *x, int *y, int *buttonMask); bool cocoa_displays_have_separate_spaces(); void cocoa_set_overlay_property(WId winid); diff --git a/vncviewer/cocoa.mm b/vncviewer/cocoa.mm index 047d0ad56c..116df9dde6 100644 --- a/vncviewer/cocoa.mm +++ b/vncviewer/cocoa.mm @@ -30,27 +30,8 @@ #include #include -#include -#include - -#define XK_LATIN1 -#define XK_MISCELLANY -#define XK_XKB_KEYS -#include -#include #include -#include "keysym2ucs.h" - -#define NoSymbol 0 - -// This wasn't added until 10.12 -#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -const int kVK_RightCommand = 0x36; -#endif -// And this is still missing -const int kVK_Menu = 0x6E; - static bool captured = false; //----------------------------------------------------------------------- @@ -364,102 +345,6 @@ void cocoa_update_window_level(QWidget *widget, bool enabled, bool shielding) } } -int cocoa_is_keyboard_sync(const void *event) -{ - assert(event); - const NSEvent *nsevent = (const NSEvent*)event; - - // If we get a NSFlagsChanged event with key code 0 then this isn't - // an actual keyboard event but rather the system trying to sync up - // modifier state after it has stolen input for some reason (e.g. - // Cmd+Tab) - - if ([nsevent type] != NSFlagsChanged) { - return 0; - } - if ([nsevent keyCode] != 0) { - return 0; - } - return 1; -} - -int cocoa_is_keyboard_event(const void *event) -{ - NSEvent *nsevent = (NSEvent*)event; - switch ([nsevent type]) { - case NSKeyDown: - case NSKeyUp: - case NSFlagsChanged: - if (cocoa_is_keyboard_sync(event)) { - return 0; - } - return 1; - default: - return 0; - } - - return 0; -} - -int cocoa_is_key_press(const void *event) -{ - NSEvent *nsevent = (NSEvent*)event; - - if ([nsevent type] == NSKeyDown) { - return 1; - } - if ([nsevent type] == NSFlagsChanged) { - UInt32 mask; - - // We don't see any event on release of CapsLock - if ([nsevent keyCode] == kVK_CapsLock) { - return 1; - } - // These are entirely undocumented, but I cannot find any other way - // of differentiating between left and right keys - switch ([nsevent keyCode]) { - case kVK_RightCommand: - mask = 0x0010; - break; - case kVK_Command: - mask = 0x0008; - break; - case kVK_Shift: - mask = 0x0002; - break; - case kVK_CapsLock: - // We don't see any event on release of CapsLock - return 1; - case kVK_Option: - mask = 0x0020; - break; - case kVK_Control: - mask = 0x0001; - break; - case kVK_RightShift: - mask = 0x0004; - break; - case kVK_RightOption: - mask = 0x0040; - break; - case kVK_RightControl: - mask = 0x2000; - break; - default: - return 0; - } - - if ([nsevent modifierFlags] & mask) { - return 1; - } - else { - return 0; - } - } - - return 0; -} - bool cocoa_is_mouse_entered(const void *event) { NSEvent *nsevent = (NSEvent*)event; @@ -484,265 +369,6 @@ bool cocoa_is_mouse_moved(const void *event) return det; } -int cocoa_event_keycode(const void *event) -{ - NSEvent *nsevent = (NSEvent*)event; - int keycode = [nsevent keyCode]; - - // macOS swaps these two keys for unknown reasons for ISO layouts - if (KBGetLayoutType(LMGetKbdType()) == kKeyboardISO) { - if (keycode == kVK_ANSI_Grave) { - return kVK_ISO_Section; - } - if (keycode == kVK_ISO_Section) { - return kVK_ANSI_Grave; - } - } - - return keycode; -} - -static NSString *key_translate(UInt16 keyCode, UInt32 modifierFlags) -{ - TISInputSourceRef keyboard = TISCopyCurrentKeyboardLayoutInputSource(); - CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); - if (uchr == nullptr) { - return nil; - } - const UCKeyboardLayout *layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); - if (layout == nullptr) { - return nil; - } - - UniCharCount actual_len; - UniChar string[255]; - UInt32 dead_state = 0; - UniCharCount max_len = sizeof(string) / sizeof(*string); - modifierFlags = (modifierFlags >> 8) & 0xff; - OSStatus err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags, - LMGetKbdType(), 0, &dead_state, max_len, &actual_len, - string); - if (err != noErr) { - return nil; - } - // Dead key? - if (dead_state != 0) { - // We have no fool proof way of asking what dead key this is. - // Assume we get a spacing equivalent if we press the - // same key again, and try to deduce something from that. - err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags, - LMGetKbdType(), 0, &dead_state, max_len, &actual_len, - string); - if (err != noErr) { - return nil; - } - } - - return [NSString stringWithCharacters:string length:actual_len]; -} - -static const int kvk_map[][2] = { - { kVK_Return, XK_Return }, - { kVK_Tab, XK_Tab }, - { kVK_Space, XK_space }, - { kVK_Delete, XK_BackSpace }, - { kVK_Escape, XK_Escape }, - { kVK_RightCommand, XK_Super_R }, - { kVK_Command, XK_Super_L }, - { kVK_Shift, XK_Shift_L }, - { kVK_CapsLock, XK_Caps_Lock }, - { kVK_Option, XK_Alt_L }, - { kVK_Control, XK_Control_L }, - { kVK_RightShift, XK_Shift_R }, - { kVK_RightOption, XK_Alt_R }, - { kVK_RightControl, XK_Control_R }, - { kVK_F17, XK_F17 }, - { kVK_VolumeUp, XF86XK_AudioRaiseVolume }, - { kVK_VolumeDown, XF86XK_AudioLowerVolume }, - { kVK_Mute, XF86XK_AudioMute }, - { kVK_F18, XK_F18 }, - { kVK_F19, XK_F19 }, - { kVK_F20, XK_F20 }, - { kVK_F5, XK_F5 }, - { kVK_F6, XK_F6 }, - { kVK_F7, XK_F7 }, - { kVK_F3, XK_F3 }, - { kVK_F8, XK_F8 }, - { kVK_F9, XK_F9 }, - { kVK_F11, XK_F11 }, - { kVK_F13, XK_F13 }, - { kVK_F16, XK_F16 }, - { kVK_F14, XK_F14 }, - { kVK_F10, XK_F10 }, - { kVK_Menu, XK_Menu }, - { kVK_F12, XK_F12 }, - { kVK_F15, XK_F15 }, - // Should we send Insert here? - { kVK_Help, XK_Help }, - { kVK_Home, XK_Home }, - { kVK_PageUp, XK_Page_Up }, - { kVK_ForwardDelete, XK_Delete }, - { kVK_F4, XK_F4 }, - { kVK_End, XK_End }, - { kVK_F2, XK_F2 }, - { kVK_PageDown, XK_Page_Down }, - { kVK_F1, XK_F1 }, - { kVK_LeftArrow, XK_Left }, - { kVK_RightArrow, XK_Right }, - { kVK_DownArrow, XK_Down }, - { kVK_UpArrow, XK_Up }, - - // The OS X headers claim these keys are not layout independent. - // Could it be because of the state of the decimal key? - /* { kVK_ANSI_KeypadDecimal, XK_KP_Decimal }, */ // see below - { kVK_ANSI_KeypadMultiply, XK_KP_Multiply }, - { kVK_ANSI_KeypadPlus, XK_KP_Add }, - // OS X doesn't have NumLock, so is this really correct? - { kVK_ANSI_KeypadClear, XK_Num_Lock }, - { kVK_ANSI_KeypadDivide, XK_KP_Divide }, - { kVK_ANSI_KeypadEnter, XK_KP_Enter }, - { kVK_ANSI_KeypadMinus, XK_KP_Subtract }, - { kVK_ANSI_KeypadEquals, XK_KP_Equal }, - { kVK_ANSI_Keypad0, XK_KP_0 }, - { kVK_ANSI_Keypad1, XK_KP_1 }, - { kVK_ANSI_Keypad2, XK_KP_2 }, - { kVK_ANSI_Keypad3, XK_KP_3 }, - { kVK_ANSI_Keypad4, XK_KP_4 }, - { kVK_ANSI_Keypad5, XK_KP_5 }, - { kVK_ANSI_Keypad6, XK_KP_6 }, - { kVK_ANSI_Keypad7, XK_KP_7 }, - { kVK_ANSI_Keypad8, XK_KP_8 }, - { kVK_ANSI_Keypad9, XK_KP_9 }, - // Japanese Keyboard Support - { kVK_JIS_Eisu, XK_Eisu_toggle }, - { kVK_JIS_Kana, XK_Hiragana_Katakana }, -}; - -int cocoa_event_keysym(const void *event) -{ - NSEvent *nsevent = (NSEvent*)event; - UInt16 key_code = [nsevent keyCode]; - - // Start with keys that either don't generate a symbol, or - // generate the same symbol as some other key. - for (size_t i = 0; i < sizeof(kvk_map) / sizeof(kvk_map[0]); i++) { - if (key_code == kvk_map[i][0]) { - return kvk_map[i][1]; - } - } - - // OS X always sends the same key code for the decimal key on the - // num pad, but X11 wants different keysyms depending on if it should - // be a comma or full stop. - if (key_code == 0x41) { - switch ([[nsevent charactersIgnoringModifiers] UTF8String][0]) { - case ',': - return XK_KP_Separator; - case '.': - return XK_KP_Decimal; - default: - return NoSymbol; - } - } - - // We want a "normal" symbol out of the event, which basically means - // we only respect the shift and alt/altgr modifiers. Cocoa can help - // us if we only wanted shift, but as we also want alt/altgr, we'll - // have to do some lookup ourselves. This matches our behaviour on - // other platforms. - - UInt32 modifiers = 0; - if ([nsevent modifierFlags] & NSAlphaShiftKeyMask) { - modifiers |= alphaLock; - } - if ([nsevent modifierFlags] & NSShiftKeyMask) { - modifiers |= shiftKey; - } - if ([nsevent modifierFlags] & NSAlternateKeyMask) { - modifiers |= optionKey; - } - - NSString *chars = key_translate(key_code, modifiers); - if (chars == nil) { - return NoSymbol; - } - - // FIXME: Some dead keys are given as NBSP + combining character - if ([chars length] != 1) { - return NoSymbol; - } - // Dead key? - if ([[nsevent characters] length] == 0) { - return ucs2keysym(ucs2combining([chars characterAtIndex:0])); - } - return ucs2keysym([chars characterAtIndex:0]); -} - -static int cocoa_open_hid(io_connect_t *ioc) -{ - CFMutableDictionaryRef mdict = IOServiceMatching(kIOHIDSystemClass); - io_service_t ios = IOServiceGetMatchingService(kIOMasterPortDefault, (CFDictionaryRef) mdict); - if (!ios) { - return KERN_FAILURE; - } - kern_return_t ret = IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, ioc); - IOObjectRelease(ios); - if (ret != KERN_SUCCESS) { - return ret; - } - return KERN_SUCCESS; -} - -static int cocoa_set_modifier_lock_state(int modifier, bool on) -{ - io_connect_t ioc; - kern_return_t ret = cocoa_open_hid(&ioc); - if (ret != KERN_SUCCESS) { - return ret; - } - ret = IOHIDSetModifierLockState(ioc, modifier, on); - IOServiceClose(ioc); - if (ret != KERN_SUCCESS) { - return ret; - } - return KERN_SUCCESS; -} - -static int cocoa_get_modifier_lock_state(int modifier, bool *on) -{ - io_connect_t ioc; - kern_return_t ret = cocoa_open_hid(&ioc); - if (ret != KERN_SUCCESS) { - return ret; - } - ret = IOHIDGetModifierLockState(ioc, modifier, on); - IOServiceClose(ioc); - if (ret != KERN_SUCCESS) { - return ret; - } - return KERN_SUCCESS; -} - -int cocoa_set_caps_lock_state(bool on) -{ - return cocoa_set_modifier_lock_state(kIOHIDCapsLockState, on); -} - -int cocoa_set_num_lock_state(bool on) -{ - return cocoa_set_modifier_lock_state(kIOHIDNumLockState, on); -} - -int cocoa_get_caps_lock_state(bool *on) -{ - return cocoa_get_modifier_lock_state(kIOHIDCapsLockState, on); -} - -int cocoa_get_num_lock_state(bool *on) -{ - return cocoa_get_modifier_lock_state(kIOHIDNumLockState, on); -} - void cocoa_get_mouse_properties(const void *event, int *x, int *y, int *buttonMask) { NSEvent *nsevent = (NSEvent*)event;