Skip to content

Commit

Permalink
macOS: Fix pasting/copying with various keyboard layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
hluk committed Aug 29, 2021
1 parent 5876bd0 commit c7fab6f
Showing 1 changed file with 82 additions and 3 deletions.
85 changes: 82 additions & 3 deletions src/platform/mac/macplatformwindow.mm
Expand Up @@ -26,12 +26,89 @@
#include <AppKit/NSGraphics.h>
#include <Cocoa/Cocoa.h>
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <dispatch/dispatch.h>

#include <QApplication>
#include <QSet>

namespace {
// Original from: https://stackoverflow.com/a/33584460/454171
NSString* keyCodeToString(CGKeyCode keyCode, UCKeyboardLayout *keyboardLayout)
{
UInt32 deadKeyState = 0;
UniCharCount maxStringLength = 255;
UniCharCount actualStringLength = 0;
UniChar unicodeString[maxStringLength];

OSStatus status = UCKeyTranslate(
keyboardLayout,
keyCode, kUCKeyActionDown, 0,
LMGetKbdType(), 0,
&deadKeyState,
maxStringLength,
&actualStringLength, unicodeString);

if (actualStringLength == 0 && deadKeyState) {
status = UCKeyTranslate(
keyboardLayout,
kVK_Space, kUCKeyActionDown, 0,
LMGetKbdType(), 0,
&deadKeyState,
maxStringLength,
&actualStringLength, unicodeString);
}

if (actualStringLength > 0 && status == noErr) {
return [[NSString stringWithCharacters:unicodeString
length:(NSUInteger)actualStringLength] lowercaseString];
}

return nil;
}

NSNumber* charToKeyCode(const char c)
{
static NSMutableDictionary* dict = nil;

if (dict == nil) {
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(
currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *keyboardLayout =
(const UCKeyboardLayout*)CFDataGetBytePtr(uchr);

if (!keyboardLayout) {
CFRelease(currentKeyboard);
return nil;
}

dict = [NSMutableDictionary dictionary];

size_t i;
for (i = 0; i < 128; ++i) {
NSString* str = keyCodeToString((CGKeyCode)i, keyboardLayout);
if (str != nil && ![str isEqualToString:@""]) {
[dict setObject:[NSNumber numberWithInt:i] forKey:str];
}
}

CFRelease(currentKeyboard);
}

NSString * keyChar = [NSString stringWithFormat:@"%c" , c];

return [dict objectForKey:keyChar];
}

CGKeyCode keyCodeFromChar(const char c, CGKeyCode fallback)
{
const auto keyCode = charToKeyCode(c);
return keyCode == nil
? fallback
: (CGKeyCode)[keyCode intValue];
}

template<typename T> inline T* objc_cast(id from)
{
if (from && [from isKindOfClass:[T class]]) {
Expand Down Expand Up @@ -280,6 +357,7 @@ QString getTitleFromWid(long int wid) {

const AppConfig config;

const auto keyCodeV = keyCodeFromChar('v', kVK_ANSI_V);
if (m_window != nullptr) {
// Window MUST be raised, otherwise we can't send events to it
waitMs(config.option<Config::window_wait_before_raise_ms>());
Expand All @@ -288,9 +366,9 @@ QString getTitleFromWid(long int wid) {

// Paste after after a delay, try 5 times
const int keyPressTimeMs = config.option<Config::window_key_press_time_ms>();
delayedSendShortcut(kVK_Command, kVK_ANSI_V, keyPressTimeMs, 5, m_window);
delayedSendShortcut(kVK_Command, keyCodeV, keyPressTimeMs, 5, m_window);
} else {
sendShortcut(kVK_Command, kVK_ANSI_V, m_runningApplication.processIdentifier);
sendShortcut(kVK_Command, keyCodeV, m_runningApplication.processIdentifier);
}
}

Expand All @@ -310,5 +388,6 @@ QString getTitleFromWid(long int wid) {

// Copy after after a delay, try 5 times
const int keyPressTimeMs = config.option<Config::window_key_press_time_ms>();
delayedSendShortcut(kVK_Command, kVK_ANSI_C, keyPressTimeMs, 5, m_window);
const auto keyCodeC = keyCodeFromChar('c', kVK_ANSI_C);
delayedSendShortcut(kVK_Command, keyCodeC, keyPressTimeMs, 5, m_window);
}

0 comments on commit c7fab6f

Please sign in to comment.