【问题标题】:Modify keyDown output修改keyDown输出
【发布时间】:2013-11-07 21:25:59
【问题描述】:

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

所以如果我写“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 Event的输出。

例如,我在 chrome、gmail 中输入电子邮件...我的键盘设置为英文,但我输入的是俄文字符。

像这样:translit.ru

有没有办法修改输出?

【问题讨论】:

  • 我不清楚你在问什么——也许你可以提供更多的上下文,或者一个例子来说明你在找什么?请务必检查question checklist。谢谢!
  • @ChristianTernus 我需要的是这样的本机应用程序:translit.ru 但在后台工作。每次我编写并且应用程序正在运行时,我都会在我的所有应用程序(safari、chrome 等)中获得俄语输出
  • 看起来这个问题的答案会对你有所帮助:stackoverflow.com/questions/6421718/…
  • @NickolayOlshevsky 我不这么认为,他和我完全一样......
  • 他可能想要什么:一个程序(守护程序或任何其他后台应用程序)将按钮映射到不同的键(符号、基里尔文......),可能是这样的工具:macupdate.com/app/mac/25141/keyremap4macbook跨度>

标签: objective-c xcode macos


【解决方案1】:

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

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

请注意,您可以在 Xcode 中通过转到 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;
}

【讨论】:

  • 您好,感谢您发布代码,我不太适合目标 C。我创建了一个命令行工具并将此代码粘贴到 main.m。试图建立一堆错误。 ndefined symbols for architecture x86_64: "_CGEventGetType", 引用自:-[KeyChanger processEvent:] in main.o "_CGEventTapCreate",引用自:-[KeyChanger tapEvents] in main.o "_CGEventTapEnable",引用自:
  • @Fincha 您为命令行工具选择了哪些设置?您应该选择“Foundation”作为类型,并且您还需要将 AppKit 框架添加到“构建阶段”的“与库的链接”部分中的目标
  • 非常感谢,Appкit 不见了
  • @Fincha 没问题。如果您不想使用 AppKit,您可以使用 CGEvent 系列 C 函数。我认为NSEvent 更方便:)
  • @Fincha 我用一个已知问题更新了我的帖子。明天我会更新解决方案。
猜你喜欢
  • 2015-06-07
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2022-08-11
  • 2015-02-15
  • 1970-01-01
相关资源
最近更新 更多