fix(mac): protect TIS API calls with a mutex (they are not thread safe)

This commit is contained in:
Luiz Sardinha 2026-05-08 13:33:40 +02:00 committed by Nick Bolton
parent 44c5c15082
commit 426b43fc09
4 changed files with 73 additions and 17 deletions

View file

@ -56,11 +56,19 @@ std::vector<std::string> AppUtilUnix::getKeyboardLayoutList()
AutoCFDictionary dict(
CFDictionaryCreate(nullptr, (const void **)keys, (const void **)values, 1, nullptr, nullptr), CFRelease
);
AutoCFArray kbds(TISCreateInputSourceList(dict.get(), false), CFRelease);
AutoCFArray kbds(nullptr, CFRelease);
{
std::lock_guard<std::mutex> lock(g_tisMutex);
kbds = AutoCFArray(TISCreateInputSourceList(dict.get(), false), CFRelease);
}
for (CFIndex i = 0; i < CFArrayGetCount(kbds.get()); ++i) {
TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(kbds.get(), i);
auto layoutLanguages = (CFArrayRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyInputSourceLanguages);
CFArrayRef layoutLanguages = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
layoutLanguages = (CFArrayRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyInputSourceLanguages);
}
char temporaryCString[128] = {0};
for (CFIndex index = 0; index < CFArrayGetCount(layoutLanguages) && layoutLanguages; index++) {
auto languageCode = (CFStringRef)CFArrayGetValueAtIndex(layoutLanguages, index);
@ -135,8 +143,14 @@ std::string AppUtilUnix::getCurrentLanguageCode()
result = X11LayoutsParser::convertLayoutToISO(m_evdev, result);
#elif defined(Q_OS_MAC)
auto layoutLanguages =
(CFArrayRef)TISGetInputSourceProperty(TISCopyCurrentKeyboardInputSource(), kTISPropertyInputSourceLanguages);
AutoTISInputSourceRef source(nullptr, CFRelease);
CFArrayRef layoutLanguages = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
source = AutoTISInputSourceRef(TISCopyCurrentKeyboardInputSource(), CFRelease);
if (source)
layoutLanguages = (CFArrayRef)TISGetInputSourceProperty(source.get(), kTISPropertyInputSourceLanguages);
}
char temporaryCString[128] = {0};
for (CFIndex index = 0; index < CFArrayGetCount(layoutLanguages) && layoutLanguages; index++) {
auto languageCode = (CFStringRef)CFArrayGetValueAtIndex(layoutLanguages, index);

View file

@ -6,6 +6,7 @@
#include "platform/IOSXKeyResource.h"
#include "platform/OSXAutoTypes.h"
#include <Carbon/Carbon.h>
KeyID IOSXKeyResource::getKeyID(uint8_t c)
@ -101,8 +102,14 @@ KeyID IOSXKeyResource::getKeyID(uint8_t c)
str[1] = 0;
// get current keyboard script
TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource();
CFArrayRef langs = (CFArrayRef)TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages);
AutoTISInputSourceRef isref(nullptr, CFRelease);
CFArrayRef langs = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
isref = AutoTISInputSourceRef(TISCopyCurrentKeyboardInputSource(), CFRelease);
if (isref)
langs = (CFArrayRef)TISGetInputSourceProperty(isref.get(), kTISPropertyInputSourceLanguages);
}
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)CFArrayGetValueAtIndex(langs, 0));
// convert to unicode
CFStringRef cfString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, str, encoding, kCFAllocatorNull);

View file

@ -7,8 +7,11 @@
#include <Carbon/Carbon.h>
#include <memory>
#include <mutex>
using CFDeallocator = decltype(&CFRelease);
using AutoCFArray = std::unique_ptr<const __CFArray, CFDeallocator>;
using AutoCFDictionary = std::unique_ptr<const __CFDictionary, CFDeallocator>;
using AutoTISInputSourceRef = std::unique_ptr<__TISInputSource, CFDeallocator>;
inline std::mutex g_tisMutex;

View file

@ -293,7 +293,14 @@ KeyButton OSXKeyState::mapKeyFromEvent(KeyIDs &ids, KeyModifierMask *maskOut, CG
}
// get keyboard info
AutoTISInputSourceRef currentKeyboardLayout(TISCopyCurrentKeyboardLayoutInputSource(), CFRelease);
AutoTISInputSourceRef currentKeyboardLayout(nullptr, CFRelease);
CFDataRef ref = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
currentKeyboardLayout = AutoTISInputSourceRef(TISCopyCurrentKeyboardLayoutInputSource(), CFRelease);
if (currentKeyboardLayout)
ref = (CFDataRef)TISGetInputSourceProperty(currentKeyboardLayout.get(), kTISPropertyUnicodeKeyLayoutData);
}
if (!currentKeyboardLayout) {
return kKeyNone;
@ -325,7 +332,6 @@ KeyButton OSXKeyState::mapKeyFromEvent(KeyIDs &ids, KeyModifierMask *maskOut, CG
}
// translate via uchr resource
CFDataRef ref = (CFDataRef)TISGetInputSourceProperty(currentKeyboardLayout.get(), kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *layout = (const UCKeyboardLayout *)CFDataGetBytePtr(ref);
const bool layoutValid = (layout != nullptr);
@ -427,8 +433,14 @@ KeyModifierMask OSXKeyState::pollActiveModifiers() const
int32_t OSXKeyState::pollActiveGroup() const
{
AutoTISInputSourceRef keyboardLayout(TISCopyCurrentKeyboardLayoutInputSource(), CFRelease);
CFDataRef id = (CFDataRef)TISGetInputSourceProperty(keyboardLayout.get(), kTISPropertyInputSourceID);
AutoTISInputSourceRef keyboardLayout(nullptr, CFRelease);
CFDataRef id = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
keyboardLayout = AutoTISInputSourceRef(TISCopyCurrentKeyboardLayoutInputSource(), CFRelease);
if (keyboardLayout)
id = (CFDataRef)TISGetInputSourceProperty(keyboardLayout.get(), kTISPropertyInputSourceID);
}
GroupMap::const_iterator i = m_groupMap.find(id);
if (i != m_groupMap.end()) {
@ -463,7 +475,11 @@ void OSXKeyState::getKeyMap(deskflow::KeyMap &keyMap)
numGroups = CFArrayGetCount(m_groups.get());
for (int32_t g = 0; g < numGroups; ++g) {
TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(m_groups.get(), g);
CFDataRef id = (CFDataRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyInputSourceID);
CFDataRef id = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
id = (CFDataRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyInputSourceID);
}
m_groupMap[id] = g;
}
}
@ -479,7 +495,11 @@ void OSXKeyState::getKeyMap(deskflow::KeyMap &keyMap)
// add regular keys
// try uchr resource first
TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(m_groups.get(), g);
CFDataRef resourceRef = (CFDataRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyUnicodeKeyLayoutData);
CFDataRef resourceRef = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
resourceRef = (CFDataRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyUnicodeKeyLayoutData);
}
layoutValid = resourceRef != nullptr;
if (layoutValid)
@ -853,7 +873,11 @@ bool OSXKeyState::getGroups(AutoCFArray &groups) const
AutoCFDictionary dict(
CFDictionaryCreate(nullptr, (const void **)keys, (const void **)values, 1, nullptr, nullptr), CFRelease
);
AutoCFArray kbds(TISCreateInputSourceList(dict.get(), false), CFRelease);
AutoCFArray kbds(nullptr, CFRelease);
{
std::lock_guard<std::mutex> lock(g_tisMutex);
kbds = AutoCFArray(TISCreateInputSourceList(dict.get(), false), CFRelease);
}
if (CFArrayGetCount(kbds.get()) > 0) {
groups = std::move(kbds);
@ -872,16 +896,24 @@ void OSXKeyState::setGroup(int32_t group)
LOG_WARN("needed keyboard layout is null");
return;
}
auto canBeSetted = (CFBooleanRef
)TISGetInputSourceProperty(TISCopyCurrentKeyboardInputSource(), kTISPropertyInputSourceIsEnableCapable);
CFBooleanRef canBeSetted = nullptr;
{
std::lock_guard<std::mutex> lock(g_tisMutex);
AutoTISInputSourceRef source(TISCopyCurrentKeyboardInputSource(), CFRelease);
if (source)
canBeSetted = (CFBooleanRef)TISGetInputSourceProperty(source.get(), kTISPropertyInputSourceIsEnableCapable);
}
if (!canBeSetted) {
LOG_WARN("needed keyboard layout is disabled for programmatically selection");
return;
}
{
std::lock_guard<std::mutex> lock(g_tisMutex);
if (TISSelectInputSource(keyboardLayout) != noErr) {
LOG_WARN("failed to set needed keyboard layout");
}
}
LOG_DEBUG1("keyboard layout change to %d", group);