From 8aa430c9ee65926e2a9276340c978110e91e5ef2 Mon Sep 17 00:00:00 2001 From: Dmitry Avgustis Date: Fri, 12 Sep 2025 14:20:01 +0200 Subject: [PATCH 1/2] fix build --- README.md | 4 ++++ src/CommandShift.pro | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4e9f0bc..b62e216 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,7 @@ Again, if you have found CommandShift useful please consider supporting my endea * **Patreon (monthly):** https://www.patreon.com/Vasyl_Baran And remember, stay Safe and stay Strong! πŸ‡ΊπŸ‡¦ + +# Build +`/opt/homebrew/opt/qt@5/bin/qmake -config release src/CommandShift.pro +make clean && make -j8` \ No newline at end of file diff --git a/src/CommandShift.pro b/src/CommandShift.pro index dcd43ed..1b6429d 100644 --- a/src/CommandShift.pro +++ b/src/CommandShift.pro @@ -3,12 +3,13 @@ QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets macx { -QMAKE_APPLE_DEVICE_ARCHS = x86_64 arm64 +QMAKE_APPLE_DEVICE_ARCHS = arm64 +QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.15 } -CONFIG += c++11 +CONFIG += c++17 -ICON = icons\icon.icns +ICON = icons/icon.icns # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. @@ -36,3 +37,4 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin RESOURCES += \ app_resources.qrc + From fa8dc2eae42995aab6569415d3dbe3f75d70b2c9 Mon Sep 17 00:00:00 2001 From: Dmitry Avgustis Date: Fri, 12 Sep 2025 14:41:25 +0200 Subject: [PATCH 2/2] Make an app independent on default system shortcuts. --- src/CommandShift.pro | 2 +- src/keypresscatcher.cpp | 71 ++++++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/CommandShift.pro b/src/CommandShift.pro index 1b6429d..c61a6d0 100644 --- a/src/CommandShift.pro +++ b/src/CommandShift.pro @@ -23,7 +23,7 @@ HEADERS += \ constants.h \ keypresscatcher.h -LIBS += -framework ApplicationServices +LIBS += -framework ApplicationServices -framework Carbon # Use custom Info.plist macx { diff --git a/src/keypresscatcher.cpp b/src/keypresscatcher.cpp index 3ca12cc..36593f0 100644 --- a/src/keypresscatcher.cpp +++ b/src/keypresscatcher.cpp @@ -5,6 +5,7 @@ #include #include +#include KeyPressCatcher::KeyPressCatcher(std::function showMessageCallback) : m_showMessageCallback{showMessageCallback} @@ -109,24 +110,63 @@ void KeyPressCatcher::loop() void KeyPressCatcher::sendSystemDefaultChangeLanguageShortcut() { - // Creating a 'Shift + Alt' event - CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); - CGEventRef spaceDown = CGEventCreateKeyboardEvent(src, 0x31, true); - CGEventRef spaceUp = CGEventCreateKeyboardEvent(src, 0x31, false); + // Current keyboard input source + TISInputSourceRef current = TISCopyCurrentKeyboardInputSource(); + + // Build a list of enabled, selectable keyboard input sources + const void* keys[] = { + kTISPropertyInputSourceCategory, + kTISPropertyInputSourceIsEnabled, + kTISPropertyInputSourceIsSelectCapable + }; + const void* values[] = { + kTISCategoryKeyboardInputSource, + kCFBooleanTrue, + kCFBooleanTrue + }; + + CFDictionaryRef filter = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + 3, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CFArrayRef sources = TISCreateInputSourceList(filter, false); + if (filter) CFRelease(filter); + + if (!sources) { + if (current) CFRelease(current); + return; + } + + CFIndex count = CFArrayGetCount(sources); + if (count <= 0) { + CFRelease(sources); + if (current) CFRelease(current); + return; + } - CGEventSetFlags(spaceDown, kCGEventFlagMaskAlternate); - CGEventSetFlags(spaceUp, kCGEventFlagMaskAlternate); - CGEventSetFlags(spaceDown, kCGEventFlagMaskControl); - CGEventSetFlags(spaceUp, kCGEventFlagMaskControl); + // Find the current index + CFIndex currentIndex = -1; + for (CFIndex i = 0; i < count; ++i) { + TISInputSourceRef s = (TISInputSourceRef)CFArrayGetValueAtIndex(sources, i); + if (current && CFEqual(s, current)) { + currentIndex = i; + break; + } + } - CGEventTapLocation loc = kCGHIDEventTap; + // Compute next index (wrap-around). If current not found, pick index 0. + CFIndex nextIndex = (currentIndex >= 0) ? ((currentIndex + 1) % count) : 0; + TISInputSourceRef next = (TISInputSourceRef)CFArrayGetValueAtIndex(sources, nextIndex); - CGEventPost(loc, spaceDown); - CGEventPost(loc, spaceUp); + if (next) { + TISSelectInputSource(next); + } - CFRelease(src); - CFRelease(spaceDown); - CFRelease(spaceUp); + CFRelease(sources); + if (current) CFRelease(current); } void KeyPressCatcher::handleModifierKeysStatusChange(bool shift_pressed_down, bool second_key_pressed_down) @@ -158,7 +198,8 @@ bool KeyPressCatcher::init() m_eventTapPtr = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, modifiersPressedMask, [] (CGEventTapProxy, CGEventType type, CGEventRef event, void *keyPressCatcherRawPtr) - { + { + (void)type; // silence unused parameter warning auto catcher = static_cast(keyPressCatcherRawPtr); CGEventFlags flags = CGEventGetFlags(event); auto secondTriggerKey = catcher->getSecondShortcutKey();