Emulate / send Modifier Key (Cntrl, Alt, fn, Shift) in OSx

南笙酒味 提交于 2019-12-22 19:01:12

问题


I am sending keyboard key press and key release events, which works for all keyboard keys.

But modifier keys works only when the key associated with modifier key is sent from application, and not from real hardware. That is if I send Shift and 'a' from application, it prints 'A' (capital A, which is what is expected).

But if I send 'shift' key down event from application, and enter 'a' from physical keyboard, it prints 'a' (the shift key doesn't seem to be working across different devices). The same applies for other modifier keys such as cmd, alt, and fn keys!.

Is there a way to send modifier keys to system, so that I can emulate modifier keys from my application?. Specifically, I would like to activate modifier key from application, and enter the combination key from physical keyboard.

Here is the code I use to send key press and release events.

- (void)setADBKey:(uint32)key value:(int)value
{
    if (!eventSource)
    {
        eventSource  = CGEventSourceCreate(kCGEventSourceStatePrivate);
    }

    CGEventRef event = CGEventCreateKeyboardEvent(eventSource, key, value!=0);
    CGEventPost(kCGHIDEventTap, event);
    CFRelease(event);
}

I have also tried creating and sending a system event, through the following code

struct 
{

    CGKeyCode keyCode;
    int flag;
    int cgEventFlag;

} modifiers[] = {

    { 56, NX_DEVICELSHIFTKEYMASK, kCGEventFlagMaskShift },
    { 60, NX_DEVICERSHIFTKEYMASK, kCGEventFlagMaskShift },
    { 59, NX_DEVICELCTLKEYMASK, kCGEventFlagMaskControl },
    { 58, NX_DEVICELALTKEYMASK, kCGEventFlagMaskAlternate },
    { 61, NX_DEVICERALTKEYMASK, kCGEventFlagMaskAlternate },
    { 55, NX_DEVICELCMDKEYMASK, kCGEventFlagMaskCommand },
    { 54, NX_DEVICERCMDKEYMASK, kCGEventFlagMaskCommand }

};

- (void)setAdbKey:(uint32)adbkey value:(int)value repeat:(BOOL)repeat
{
    //int adbkey = def_usb_2_adb_keymap[hidkey];

    int modifier = 0;

    for(int i=0; i< ARR_SIZE(modifiers); i++)
    {
        if (adbkey == modifiers[i].keyCode)
        {
            modifier = modifiers[i].cgEventFlag;
            break;
        }
    }

    if (value)
    {
        flags |= modifier;
    }             
    else 
    {
        flags &= ~modifier;
    }

    CGEventRef event = CGEventCreateKeyboardEvent(eventSource, adbkey, value!=0);

    if (repeat)
    {
        CGEventSetIntegerValueField(event, kCGKeyboardEventAutorepeat, (int64_t)1);
    }

    // don't apply modifier flags to a modifier
    if (!modifier)
    {
        CGEventSetFlags(event, flags);
    }

    CGEventPost(kCGHIDEventTap, event);
    CFRelease(event);
}

Both methods work when modifier key and combination key are sent from application, but not when modifier key alone is set from application.


回答1:


All you have to do is create an event tap and set the mask.

Sample:

@interface AppDelegate ()

@property (assign) CFMachPortRef myEventTap;
@property (assign) CFRunLoopSourceRef myRunLoopSource;

@end

@implementation AppDelegate

CGEventRef MyEventTapCallBack(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    CGEventSetFlags(event, kCGEventFlagMaskShift);
    return event;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    self.myEventTap = CGEventTapCreate(kCGHIDEventTap,
                                            kCGHeadInsertEventTap,
                                            kCGEventTapOptionDefault,
                                            CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged),
                                            MyEventTapCallBack,
                                            (__bridge void *)self);
    if (self.myEventTap) {
        self.myRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.myEventTap, 0);
        if (self.myRunLoopSource)
            CFRunLoopAddSource(CFRunLoopGetMain(), self.myRunLoopSource, kCFRunLoopCommonModes);
    }
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    if (self.myRunLoopSource) {
        CFRunLoopSourceInvalidate(self.myRunLoopSource);
        CFRelease(self.myRunLoopSource);
    }
    if (self.myEventTap)
        CFRelease(self.myEventTap);
}

@end



回答2:


This is my appDelegate and how I got it working.

import Cocoa
import CoreGraphics
import Foundation

var shift = false;
var cntrl = false;
var option = false;
var command = false;

func myCGEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {

    var mask: CGEventFlags = []
    if(shift) {
        mask.insert(CGEventFlags.maskShift)
     }
    if(cntrl) {
        mask.insert(CGEventFlags.maskControl)
     }
     if(option) {
         mask.insert(CGEventFlags.maskAlternate)
     }
    if(command) {
        mask.insert(CGEventFlags.maskCommand)
    }
    if(mask.rawValue != 0) {
        event.flags = mask;
    }

    return Unmanaged.passRetained(event)
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

func applicationDidFinishLaunching(_ aNotification: Notification) {

    // Insert code here to initialize your application
    let mask: CGEventMask = 1 << CGEventType.keyDown.rawValue
    let eventTap:CFMachPort = CGEvent.tapCreate(tap: .cghidEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: mask, callback: myCGEventCallback, userInfo: nil)!

    let runLoopSource:CFRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes);
    CGEvent.tapEnable(tap: eventTap, enable: true);

    CFRunLoopRun();
}

func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
}

public func SetShift() {
    shift = !shift;
}
public func SetCtrl() {
    cntrl = !cntrl;
}
public func SetCommand() {
    command = !command;
}
public func SetOption() {
    option = !option;
}
}


来源:https://stackoverflow.com/questions/35425272/emulate-send-modifier-key-cntrl-alt-fn-shift-in-osx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!