Modify keyDown output

I try to write my own keychanger.

So if I write “k” I get a russian “к”

  • Call variadic Objective-C function from Swift
  • Social action sheet (like on iOS 6)
  • Why do I have to dismiss this UIAlertView three times?
  • Using a custom annotation when adding it to map
  • Sort Numbers in NSArray?
  • Today Extension compact mode height in iOS 10
  • [NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event){
            NSMutableString *buffer = [event.characters mutableCopy];
            CFMutableStringRef bufferRef = (__bridge CFMutableStringRef)buffer;
            CFStringTransform(bufferRef, NULL, kCFStringTransformLatinCyrillic, false);
            NSLog(@"%@", buffer);
        }];
    

    How to modify the output of keyDown Event in other applications.

    For example, I am typing an email in chrome, gmail… my keyboard is set to english, but I get russian characters.

    like this: translit.ru

    Is there a way to modify the output?

    Solutions Collect From Internet About “Modify keyDown output”

    I quickly threw this code together so be sure to vet it for memory leaks etc. You will see that you need to add the other characters you want to handle as well as the case (I added k -> к only).

    This example was build as a “command line” utility so it has no UI and is suitable for running in the background via launchd. Note that you either need to run this as root or enable assistive device support in system preferences and add permission for this app.

    Note that you can run as root while developing in Xcode by going to Product -> Scheme -> Edit and in the “run” section change the radio for “debug process as” to “root”.

    Edit: Update with autorelease pool to release temporary objects.

    // 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;
    }