【问题标题】:Simulate keypress for system wide hotkeys模拟系统范围热键的按键
【发布时间】:2012-05-30 20:26:28
【问题描述】:

我需要在 OSX 中模拟击键。这是我的做法:

-(void)execute {
    CGEventSourceRef sourceRef =
    CGEventSourceCreate(kCGEventSourceStateHIDSystemState);

    CGEventRef keyPress = CGEventCreateKeyboardEvent (sourceRef, (CGKeyCode)keyCode, true);
    CGEventRef keyUnpress = CGEventCreateKeyboardEvent (sourceRef, (CGKeyCode)keyCode, false);

    CGEventSetFlags(keyPress, modifierFlags);
    CGEventPost(kCGHIDEventTap, keyPress);

    //unpressing the acualkey
    CGEventPost(kCGHIDEventTap, keyUnpress);

    CFRelease(keyPress);
    CFRelease(keyUnpress);
    CFRelease(sourceRef);
}

它适用于任何应用程序中的每个热键或简单按键,但不适用于系统范围的快捷方式,例如 option + space 启动 Spotlight 或 cmd + shift + 4 截屏或 ctrl + ` 打开 iTerm2 窗口。

我试图更改事件的来源和发布事件的位置,但没有帮助。有什么想法吗?

【问题讨论】:

    标签: macos cocoa


    【解决方案1】:

    来自 CGEventCreateKeyboardEvent 的文档:

    必须输入生成字符所需的所有击键,包括修饰键。例如,要产生“Z”,SHIFT 键必须按下,“z”键必须按下,然后 SHIFT 和“z”键必须松开:

    所以,你不能只用选项修饰符按下和释放空间来触发选项空间;你必须按下选项,按下空间,释放空间,释放选项。

    附带说明,opt-space 默认情况下不执行任何操作; cmd-space 是 Spotlight 搜索热键,cmd-opt-space 是 Spotlight 窗口热键。

    所以,这段代码会弹出 Spotlight 搜索:

    - (void)execute {
      CGEventSourceRef src = 
        CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
    
      CGEventRef cmdd = CGEventCreateKeyboardEvent(src, 0x38, true);
      CGEventRef cmdu = CGEventCreateKeyboardEvent(src, 0x38, false);
      CGEventRef spcd = CGEventCreateKeyboardEvent(src, 0x31, true);
      CGEventRef spcu = CGEventCreateKeyboardEvent(src, 0x31, false);
    
      CGEventSetFlags(spcd, kCGEventFlagMaskCommand);
      CGEventSetFlags(spcu, kCGEventFlagMaskCommand);
    
      CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
      CGEventPost(loc, cmdd);
      CGEventPost(loc, spcd);
      CGEventPost(loc, spcu);
      CGEventPost(loc, cmdu);
    
      CFRelease(cmdd);
      CFRelease(cmdu);
      CFRelease(spcd);
      CFRelease(spcu);
      CFRelease(src);  
    }
    

    【讨论】:

    • 它有效,谢谢。我一开始使用了类似的代码,但由于某种原因它不起作用。
    • P.S:在非美国 Mac 上,默认使用 cmd+space 更改键盘布局。
    • 非美国键盘不再(全部)使用 cmd+space 来更改键盘布局(不再)。我得到了关注。
    • @pkamb 我有 90% 的把握这样做会在文本框中输入正确的字符,但不会执行诸如触发 cmd-space 热键之类的操作,这就是文档说全部的原因需要击键。
    • 我不知道为什么但我需要放置 usleep(10000);在 key down 和 key up 事件之间,否则它什么也不做!
    【解决方案2】:

    对于其他想要 CGKeyCode 列表的人,这里有一个来自 the RUI project 的函数,带有一个部分表。

    虽然this 可能是一个更完整的例子。有人知道更完整的地图吗?

    int keyCodeForKeyString(char * keyString);  // get the Mac keycode for the RUI representation
    
    int keyCodeForKeyString(char * keyString)
    {
        if (strcmp(keyString, "a") == 0) return 0;
        if (strcmp(keyString, "s") == 0) return 1;
        if (strcmp(keyString, "d") == 0) return 2;
        if (strcmp(keyString, "f") == 0) return 3;
        if (strcmp(keyString, "h") == 0) return 4;
        if (strcmp(keyString, "g") == 0) return 5;
        if (strcmp(keyString, "z") == 0) return 6;
        if (strcmp(keyString, "x") == 0) return 7;
        if (strcmp(keyString, "c") == 0) return 8;
        if (strcmp(keyString, "v") == 0) return 9;
        // what is 10?
        if (strcmp(keyString, "b") == 0) return 11;
        if (strcmp(keyString, "q") == 0) return 12;
        if (strcmp(keyString, "w") == 0) return 13;
        if (strcmp(keyString, "e") == 0) return 14;
        if (strcmp(keyString, "r") == 0) return 15;
        if (strcmp(keyString, "y") == 0) return 16;
        if (strcmp(keyString, "t") == 0) return 17;
        if (strcmp(keyString, "1") == 0) return 18;
        if (strcmp(keyString, "2") == 0) return 19;
        if (strcmp(keyString, "3") == 0) return 20;
        if (strcmp(keyString, "4") == 0) return 21;
        if (strcmp(keyString, "6") == 0) return 22;
        if (strcmp(keyString, "5") == 0) return 23;
        if (strcmp(keyString, "=") == 0) return 24;
        if (strcmp(keyString, "9") == 0) return 25;
        if (strcmp(keyString, "7") == 0) return 26;
        if (strcmp(keyString, "-") == 0) return 27;
        if (strcmp(keyString, "8") == 0) return 28;
        if (strcmp(keyString, "0") == 0) return 29;
        if (strcmp(keyString, "]") == 0) return 30;
        if (strcmp(keyString, "o") == 0) return 31;
        if (strcmp(keyString, "u") == 0) return 32;
        if (strcmp(keyString, "[") == 0) return 33;
        if (strcmp(keyString, "i") == 0) return 34;
        if (strcmp(keyString, "p") == 0) return 35;
        if (strcmp(keyString, "RETURN") == 0) return 36;
        if (strcmp(keyString, "l") == 0) return 37;
        if (strcmp(keyString, "j") == 0) return 38;
        if (strcmp(keyString, "'") == 0) return 39;
        if (strcmp(keyString, "k") == 0) return 40;
        if (strcmp(keyString, ";") == 0) return 41;
        if (strcmp(keyString, "\\") == 0) return 42;
        if (strcmp(keyString, ",") == 0) return 43;
        if (strcmp(keyString, "/") == 0) return 44;
        if (strcmp(keyString, "n") == 0) return 45;
        if (strcmp(keyString, "m") == 0) return 46;
        if (strcmp(keyString, ".") == 0) return 47;
        if (strcmp(keyString, "TAB") == 0) return 48;
        if (strcmp(keyString, "SPACE") == 0) return 49;
        if (strcmp(keyString, "`") == 0) return 50;
        if (strcmp(keyString, "DELETE") == 0) return 51;
        if (strcmp(keyString, "ENTER") == 0) return 52;
        if (strcmp(keyString, "ESCAPE") == 0) return 53;
    
        // some more missing codes abound, reserved I presume, but it would
        // have been helpful for Apple to have a document with them all listed
    
        if (strcmp(keyString, ".") == 0) return 65;
    
        if (strcmp(keyString, "*") == 0) return 67;
    
        if (strcmp(keyString, "+") == 0) return 69;
    
        if (strcmp(keyString, "CLEAR") == 0) return 71;
    
        if (strcmp(keyString, "/") == 0) return 75;
        if (strcmp(keyString, "ENTER") == 0) return 76;  // numberpad on full kbd
    
        if (strcmp(keyString, "=") == 0) return 78;
    
        if (strcmp(keyString, "=") == 0) return 81;
        if (strcmp(keyString, "0") == 0) return 82;
        if (strcmp(keyString, "1") == 0) return 83;
        if (strcmp(keyString, "2") == 0) return 84;
        if (strcmp(keyString, "3") == 0) return 85;
        if (strcmp(keyString, "4") == 0) return 86;
        if (strcmp(keyString, "5") == 0) return 87;
        if (strcmp(keyString, "6") == 0) return 88;
        if (strcmp(keyString, "7") == 0) return 89;
    
        if (strcmp(keyString, "8") == 0) return 91;
        if (strcmp(keyString, "9") == 0) return 92;
    
        if (strcmp(keyString, "F5") == 0) return 96;
        if (strcmp(keyString, "F6") == 0) return 97;
        if (strcmp(keyString, "F7") == 0) return 98;
        if (strcmp(keyString, "F3") == 0) return 99;
        if (strcmp(keyString, "F8") == 0) return 100;
        if (strcmp(keyString, "F9") == 0) return 101;
    
        if (strcmp(keyString, "F11") == 0) return 103;
    
        if (strcmp(keyString, "F13") == 0) return 105;
    
        if (strcmp(keyString, "F14") == 0) return 107;
    
        if (strcmp(keyString, "F10") == 0) return 109;
    
        if (strcmp(keyString, "F12") == 0) return 111;
    
        if (strcmp(keyString, "F15") == 0) return 113;
        if (strcmp(keyString, "HELP") == 0) return 114;
        if (strcmp(keyString, "HOME") == 0) return 115;
        if (strcmp(keyString, "PGUP") == 0) return 116;
        if (strcmp(keyString, "DELETE") == 0) return 117;
        if (strcmp(keyString, "F4") == 0) return 118;
        if (strcmp(keyString, "END") == 0) return 119;
        if (strcmp(keyString, "F2") == 0) return 120;
        if (strcmp(keyString, "PGDN") == 0) return 121;
        if (strcmp(keyString, "F1") == 0) return 122;
        if (strcmp(keyString, "LEFT") == 0) return 123;
        if (strcmp(keyString, "RIGHT") == 0) return 124;
        if (strcmp(keyString, "DOWN") == 0) return 125;
        if (strcmp(keyString, "UP") == 0) return 126;
    
        fprintf(stderr, "keyString %s Not Found. Aborting...\n", keyString);
        exit(EXIT_FAILURE);
    }
    

    【讨论】:

    • 不是特定于键盘布局吗?就像美国键盘一样,如果你在 OS X 中设置的德语键盘布局上使用它,它就会不正确?
    • 此表中缺少一些键,例如“CAPSLOCK”,在此处找到缺少的键:gist.github.com/swillits/df648e87016772c7f7e5dbed2b345066
    • 这是特定于键盘布局的,当您尝试调用快捷方式时可能会非常棘手......如果您愿意,您可以通过将 NSTextInputContext 更改为所需的布局然后把它放回去,它会工作,但它是丑陋的黑客......
    【解决方案3】:

    郑重声明,虽然@abarnert 的回答很好,而且您应该根据文档这样做,但我的原始代码也可以使用。我发现我有一个与这个问题无关的不同问题。

    因此,如果您需要将修饰键应用于按键,您可以像 CGEventSetFlags(keyPress, modifierFlags); 这样简单地添加它们,而无需分别按下每个修饰键。这种方法很有效,我还没有发现任何缺点,而且代码的可读性要好得多。

    【讨论】:

      【解决方案4】:

      XCode 7.3 和 Swift 2.2:

      let event1 = CGEventCreateKeyboardEvent(nil, 0x09, true); // cmd-v down
      CGEventSetFlags(event1, CGEventFlags.MaskCommand);
      CGEventPost(CGEventTapLocation.CGHIDEventTap, event1);
      
      let event2 = CGEventCreateKeyboardEvent(nil, 0x09, false); // cmd-v up
      CGEventSetFlags(event2, CGEventFlags.MaskCommand);
      CGEventPost(CGEventTapLocation.CGHIDEventTap, event2);
      

      上面的代码模拟 CMD-V 按下然后释放(AKA:粘贴)。

      猜你喜欢
      • 1970-01-01
      • 2011-04-15
      • 2013-07-12
      • 2016-11-12
      • 2012-04-30
      • 2013-12-07
      • 2012-01-04
      • 2010-10-02
      相关资源
      最近更新 更多