修改按键输出



我尝试编写自己的密钥转换器。

所以如果我写"k",我会得到一个俄语"к"

[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event){
        NSMutableString *buffer = [event.characters mutableCopy];
        CFMutableStringRef bufferRef = (__bridge CFMutableStringRef)buffer;
        CFStringTransform(bufferRef, NULL, kCFStringTransformLatinCyrillic, false);
        NSLog(@"%@", buffer);
    }];

如何在其他应用程序中修改 keyDown 事件的输出。

例如,我正在Chrome,gmail中输入电子邮件...我的键盘设置为英语,但我得到俄语字符。

像这样:translit.ru

有没有办法修改输出?

我很快把这段代码放在一起,所以一定要检查它是否有内存泄漏等。您将看到您需要添加要处理的其他字符以及大小写(我仅添加了 k -> к)。

此示例是作为"命令行"实用程序构建的,因此它没有 UI,适合通过 launchd 在后台运行。请注意,您需要以 root 身份运行此功能,或者在系统偏好设置中启用辅助设备支持并为此应用程序添加权限。

请注意,您可以在 Xcode 中以 root 身份运行,方法是转到 Product -> Scheme -> Edit,并在"运行"部分中将"调试过程为"的无线电更改为"root"。

编辑:使用自动释放池进行更新以释放临时对象。

// compile and run from the commandline with:
//    clang -fobjc-arc -framework Cocoa  ./foo.m  -o foo
//    sudo ./foo 
#import <Foundation/Foundation.h>
#import <AppKit/NSEvent.h>
typedef CFMachPortRef EventTap;
@interface KeyChanger : NSObject
{
@private
    EventTap _eventTap;
    CFRunLoopSourceRef _runLoopSource;
    CGEventRef _lastEvent;
}
@end
CGEventRef _tapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, KeyChanger* listener);
@implementation KeyChanger
- (BOOL)tapEvents
{
    if (!_eventTap) {
        NSLog(@"Initializing an event tap.");
        _eventTap = CGEventTapCreate(kCGSessionEventTap,
                                     kCGTailAppendEventTap,
                                     kCGEventTapOptionDefault,
                                     CGEventMaskBit(kCGEventKeyDown),
                                     (CGEventTapCallBack)_tapCallback,
                                     (__bridge void *)(self));
        if (!_eventTap) {
            NSLog(@"unable to create event tap. must run as root or add privlidges for assistive devices to this app.");
            return NO;
        }
    }
    CGEventTapEnable(_eventTap, TRUE);
    return [self isTapActive];
}
- (BOOL)isTapActive
{
    return CGEventTapIsEnabled(_eventTap);
}
- (void)listen
{
    if (!_runLoopSource) {
        if (_eventTap) {//dont use [self tapActive]
            _runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault,
                                                           _eventTap, 0);
            // Add to the current run loop.
            CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource,
                               kCFRunLoopCommonModes);
            NSLog(@"Registering event tap as run loop source.");
            CFRunLoopRun();
        }else{
            NSLog(@"No Event tap in place! You will need to call listen after tapEvents to get events.");
        }
    }
}
- (CGEventRef)processEvent:(CGEventRef)cgEvent
{
    NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
    // TODO: add other cases and do proper handling of case
    if ([event.characters caseInsensitiveCompare:@"k"] == NSOrderedSame) {
        event = [NSEvent keyEventWithType:event.type
                                 location:NSZeroPoint
                            modifierFlags:event.modifierFlags
                                timestamp:event.timestamp
                             windowNumber:event.windowNumber
                                  context:event.context
                               characters:@"к"
              charactersIgnoringModifiers:@"к"
                                isARepeat:event.isARepeat
                                  keyCode:event.keyCode];
    }
    _lastEvent = [event CGEvent];
    CFRetain(_lastEvent); // must retain the event. will be released by the system
    return _lastEvent;
}
- (void)dealloc
{
    if (_runLoopSource){
        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
        CFRelease(_runLoopSource);
    }
    if (_eventTap){
        //kill the event tap
        CGEventTapEnable(_eventTap, FALSE);
        CFRelease(_eventTap);
    }
}
@end
CGEventRef _tapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, KeyChanger* listener) {
    //Do not make the NSEvent here.
    //NSEvent will throw an exception if we try to make an event from the tap timout type
    @autoreleasepool {
        if(type == kCGEventTapDisabledByTimeout) {
            NSLog(@"event tap has timed out, re-enabling tap");
            [listener tapEvents];
            return nil;
        }
        if (type != kCGEventTapDisabledByUserInput) {
            return [listener processEvent:event];
        }
    }
    return event;
}
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        KeyChanger* keyChanger = [KeyChanger new];
        [keyChanger tapEvents];
        [keyChanger listen];//blocking call.
    }
    return 0;
}

最新更新