[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
前回のコードでは、かなキーを何となく押し、英字を入力して無意識に2回目を押したりすると不要なひらがなが一気に出現します。
これではまずいので1秒以内に2回目を押した時だけひらがな変換するようにしました。
あとはひらがな変換した文字列を範囲選択して再変換するという処理ですが、流石にC++でもハードルは高そうです。
Carbonフレームワークが今も現役なことに少々驚きました。
#include <Carbon/Carbon.h>
#include <iostream>
#include <string>
#include <unordered_map>
#include <chrono>
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 = 0;
std::chrono::steady_clock::time_point firstKanaKeyPressTime;
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
if (type == kCGEventKeyDown) {
CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
if (keyCode == 104) { // かなキー(104)が押された場合
std::cout << "かなキーを押しました" << std::endl;
auto now = std::chrono::steady_clock::now();
if (kanaKeyPressCount == 0) {
firstKanaKeyPressTime = now;
kanaKeyPressCount = 1;
} else if (kanaKeyPressCount == 1) {
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - firstKanaKeyPressTime).count();
if (duration <= 1) { // 1秒以内に2回目が押された場合
std::string command = "osascript \"LatinToHiragana.applescript\" " + inputBuffer;
std::cout << "command: " << command << std::endl;
system(command.c_str()); // コマンドを実行
inputBuffer.clear();
std::cout << "AppleScript実行。Current Bufferをリセット" << std::endl;
} else {
std::cout << "1秒以上経過のため、カウントをリセット" << std::endl;
}
kanaKeyPressCount = 0; // カウントをリセット
}
} else {
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() {
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;
}