1717 */
1818
1919#include " AutoTypeMac.h"
20+ #include " AutoTypeMac_p.h"
2021#include " core/Tools.h"
2122#include " gui/osutils/macutils/MacUtils.h"
2223#include " gui/MessageBox.h"
2526
2627#define INVALID_KEYCODE 0xFFFF
2728
29+ //
30+ // Resolve macOS virtual key code and modifier flags for a unicode character
31+ // using the current keyboard layout via UCKeyTranslate.
32+ // Returns false if no single-keystroke mapping is found.
33+ //
34+ bool charToNativeKeyCode (const UCKeyboardLayout* layout,
35+ UInt32 keyboardType,
36+ const QChar& ch,
37+ uint16_t & outKeyCode,
38+ CGEventFlags& outFlags)
39+ {
40+ UInt32 deadKeyState = 0 ;
41+ UniCharCount actualLen = 0 ;
42+ UniChar unicode[4 ];
43+
44+ static const UInt32 kUCTShift = shiftKey >> 8 ;
45+ static const UInt32 kUCTOption = optionKey >> 8 ;
46+
47+ static const UInt32 modifierCombinations[] = {
48+ 0 ,
49+ kUCTShift ,
50+ kUCTOption ,
51+ kUCTShift | kUCTOption ,
52+ };
53+
54+ for (uint16_t keyCode = 0 ; keyCode < 128 ; keyCode++) {
55+ for (UInt32 mods : modifierCombinations) {
56+ deadKeyState = 0 ;
57+ OSStatus status = UCKeyTranslate (layout,
58+ keyCode,
59+ kUCKeyActionDown ,
60+ mods,
61+ keyboardType,
62+ kUCKeyTranslateNoDeadKeysBit ,
63+ &deadKeyState,
64+ 4 ,
65+ &actualLen,
66+ unicode);
67+ if (status == noErr && actualLen == 1 && unicode[0 ] == ch.unicode ()) {
68+ outKeyCode = keyCode;
69+ outFlags = 0 ;
70+ if (mods & kUCTShift ) {
71+ outFlags |= kCGEventFlagMaskShift ;
72+ }
73+ if (mods & kUCTOption ) {
74+ outFlags |= kCGEventFlagMaskAlternate ;
75+ }
76+ return true ;
77+ }
78+ }
79+ }
80+
81+ return false ;
82+ }
83+
84+ static bool charToNativeKeyCode (const QChar& ch, uint16_t & outKeyCode, CGEventFlags& outFlags)
85+ {
86+ TISInputSourceRef keyboard = TISCopyCurrentKeyboardLayoutInputSource ();
87+ if (!keyboard) {
88+ keyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource ();
89+ }
90+ if (!keyboard) {
91+ return false ;
92+ }
93+
94+ CFDataRef layoutData =
95+ static_cast <CFDataRef>(TISGetInputSourceProperty (keyboard, kTISPropertyUnicodeKeyLayoutData ));
96+ if (!layoutData) {
97+ CFRelease (keyboard);
98+ return false ;
99+ }
100+
101+ const UCKeyboardLayout* layout = reinterpret_cast <const UCKeyboardLayout*>(CFDataGetBytePtr (layoutData));
102+ bool result = charToNativeKeyCode (layout, LMGetKbdType (), ch, outKeyCode, outFlags);
103+
104+ CFRelease (keyboard);
105+ return result;
106+ }
107+
28108AutoTypePlatformMac::AutoTypePlatformMac ()
29109{
30110 MessageBox::initializeButtonDefs ();
@@ -158,6 +238,18 @@ bool AutoTypePlatformMac::raiseOwnWindow()
158238 return m_macUtils->raiseOwnWindow ();
159239}
160240
241+ //
242+ // Send raw keycode event
243+ //
244+ void AutoTypePlatformMac::sendRawKey (uint16_t keyCode, bool isKeyDown)
245+ {
246+ CGEventRef keyEvent = ::CGEventCreateKeyboardEvent (nullptr , keyCode, isKeyDown);
247+ if (keyEvent != nullptr ) {
248+ ::CGEventPost (kCGSessionEventTap , keyEvent);
249+ ::CFRelease (keyEvent);
250+ }
251+ }
252+
161253//
162254// Send unicode character to active window
163255// see: Quartz Event Services
@@ -173,6 +265,53 @@ void AutoTypePlatformMac::sendChar(const QChar& ch, bool isKeyDown)
173265 }
174266}
175267
268+ //
269+ // Send unicode character as native keycode + modifiers
270+ // see: Quartz Event Services
271+ //
272+ bool AutoTypePlatformMac::sendCharVirtual (const QChar& ch, bool isKeyDown)
273+ {
274+ uint16_t keyCode = 0 ;
275+ CGEventFlags flags = 0 ;
276+
277+ if (!charToNativeKeyCode (ch, keyCode, flags)) {
278+ // Couldn't determine which keys to press to make the character
279+ return false ;
280+ }
281+
282+ // Send physical modifier key events for VNC/Screen Sharing compatibility
283+ if (isKeyDown) {
284+ if (flags & kCGEventFlagMaskShift ) {
285+ sendRawKey (kVK_Shift , true );
286+ }
287+ if (flags & kCGEventFlagMaskAlternate ) {
288+ sendRawKey (kVK_Option , true );
289+ }
290+ }
291+
292+ CGEventRef keyEvent = ::CGEventCreateKeyboardEvent (nullptr , keyCode, isKeyDown);
293+ if (keyEvent != nullptr ) {
294+ if (flags != 0 ) {
295+ ::CGEventSetFlags (keyEvent, flags);
296+ }
297+ UniChar unicode = ch.unicode ();
298+ ::CGEventKeyboardSetUnicodeString (keyEvent, 1 , &unicode);
299+ ::CGEventPost (kCGSessionEventTap , keyEvent);
300+ ::CFRelease (keyEvent);
301+ }
302+
303+ // Release modifier keys
304+ if (!isKeyDown) {
305+ if (flags & kCGEventFlagMaskAlternate ) {
306+ sendRawKey (kVK_Option , false );
307+ }
308+ if (flags & kCGEventFlagMaskShift ) {
309+ sendRawKey (kVK_Shift , false );
310+ }
311+ }
312+ return true ;
313+ }
314+
176315//
177316// Send key code to active window
178317// see: Quartz Event Services
@@ -243,8 +382,6 @@ AutoTypeAction::Result AutoTypeExecutorMac::execBegin(const AutoTypeBegin* actio
243382
244383AutoTypeAction::Result AutoTypeExecutorMac::execType (const AutoTypeKey* action)
245384{
246-
247-
248385 if (action->key != Qt::Key_unknown) {
249386 m_platform->sendKey (action->key , true , action->modifiers );
250387 m_platform->sendKey (action->key , false , action->modifiers );
@@ -256,9 +393,11 @@ AutoTypeAction::Result AutoTypeExecutorMac::execType(const AutoTypeKey* action)
256393 m_platform->sendKey (static_cast <Qt::Key>(ch), true , action->modifiers );
257394 m_platform->sendKey (static_cast <Qt::Key>(ch), false , action->modifiers );
258395 } else if (mode == Mode::VIRTUAL ) {
259- int ch = action->character .toLatin1 ();
260- m_platform->sendKey (static_cast <Qt::Key>(ch), true , action->modifiers );
261- m_platform->sendKey (static_cast <Qt::Key>(ch), false , action->modifiers );
396+ if (!m_platform->sendCharVirtual (action->character , true )) {
397+ return AutoTypeAction::Result::Failed (AutoTypePlatformMac::tr (" Unable to get valid keycode for key: " )
398+ + action->character );
399+ }
400+ m_platform->sendCharVirtual (action->character , false );
262401 } else {
263402 m_platform->sendChar (action->character , true );
264403 m_platform->sendChar (action->character , false );
0 commit comments