[C++] 369 ローマ字を英数モードで打った時のリカバリー その1 Swiftから移植 AppleScript

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]

Swift版ではアプリ起動時に入力モードが固定されてしまうため、新たに入力モードを設定するコードを追加する必要がありました。しかし、これが切り替え遅延の原因となり最初の入力文字を取りこぼしてしまいます。

Swiftでは限界を感じたのでC++に移植しました。アプリを起動しても入力モードの切り替えは可能です。遅延もありません。

Swiftはアプリ商品の開発にだけ使用するのが無難ですね。非常にセキュアですが、自由度が低すぎます。Javaと似たような感じです。

#include <Carbon/Carbon.h>
#include <iostream>
#include <string>
#include <unordered_map>

// キーボードキーと文字の対応
std::unordered_map<CGKeyCode, std::string> keyCodeMap = {
    {0, "a"}, {11, "b"}, {8, "c"}, {2, "d"}, {14, "e"}, {3, "f"},
    {5, "g"}, {4, "h"}, {34, "i"}, {38, "j"}, {40, "k"}, {37, "l"},
    {46, "m"}, {45, "n"}, {31, "o"}, {35, "p"}, {12, "q"}, {15, "r"},
    {1, "s"}, {17, "t"}, {32, "u"}, {9, "v"}, {13, "w"}, {7, "x"},
    {16, "y"}, {6, "z"}
};

std::string inputBuffer;
int kanaKeyPressCount;

CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    if (type == kCGEventKeyDown) {
        CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);

        // かなキー(104)が2回連続で押された場合の処理
        if (keyCode == 104) {
            std::cout << "かなキーを押しました" << std::endl; 
            kanaKeyPressCount += 1;

            if (kanaKeyPressCount == 2){
                // AppleScriptを実行する
                std::string command = "osascript \"LatinToHiragana.applescript\" " + inputBuffer;
                std::cout << "command: " << command << std::endl; 
                system(command.c_str());
                
                kanaKeyPressCount = 0;
            }
        } else {
            // キーコードに対応する文字がある場合、inputBufferに追加
            auto it = keyCodeMap.find(keyCode);
            if (it != keyCodeMap.end()) {
                inputBuffer += it->second;
                std::cout << "Current Buffer: " << inputBuffer << std::endl;
            } else {
                inputBuffer.clear();
                std::cout << "Current Bufferをリセット" << std::endl;
            }
        }
    }

    return event;
}

int main() {
    // CGEventTapCreateの呼び出しでkCGEventTapOptionDefaultを使用
    CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyDown), eventCallback, nullptr);
    if (!eventTap) {
        std::cerr << "Failed to create event tap" << std::endl;
        return 1;
    }

    CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
    CGEventTapEnable(eventTap, true);
    CFRunLoopRun();

    return 0;
}