在OSx中模拟/发送修改键(Cntrl,Alt,fn,Shift)

时间:2016-02-16 06:25:47

标签: objective-c macos keyboard-events systemevent

我正在发送键盘按键和按键释放事件,适用于所有键盘按键。

但修饰键只有在与应用程序关联的键与应用程序发送时才有效,而不是从真实硬件发送。那就是我送的 从应用程序转移和'a',它打印'A'(资本A,这是预期的)。

但是如果我从应用程序发送'shift'键向下事件,并从物理键盘输入'a',它会打印'a'(换档键似乎不能在不同的设备上工作)。这同样适用于其他修饰键,例如cmd,alt和fn键!。

有没有办法将修改键发送到系统,以便我可以从我的应用程序模拟修改键? 具体来说,我想从应用程序激活修改键,然后从物理键盘输入组合键

以下是我用来发送按键和发布事件的代码。

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

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

我还尝试通过以下代码创建和发送系统事件

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

当从应用程序发送修饰键和组合键时,这两种方法都有效,但是当仅从应用程序设置修饰键时,这两种方法都不起作用。

2 个答案:

答案 0 :(得分:1)

您所要做的就是创建一个事件点按并设置面具。

样品:

@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

答案 1 :(得分:0)

这是我的appDelegate及其运行方式。

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