Simulate keypress using Swift

后端 未结 5 2022
被撕碎了的回忆
被撕碎了的回忆 2020-11-28 10:17

I\'m searching for a way to simulate keystrokes in OSX. I found another solution (Simulate keypress for system wide hotkeys) using Objective-C, but i need to do it with Swif

相关标签:
5条回答
  • 2020-11-28 10:35

    I made this for a Swift 4 project, don't forget that App sandboxing will not allow an app to send keystrokes like this so it'll need to be turned off. This means your app would prohibited from the AppStore.

    import Foundation
    
    class FakeKey {
        static func send(_ keyCode: CGKeyCode, useCommandFlag: Bool) {
            let sourceRef = CGEventSource(stateID: .combinedSessionState)
    
            if sourceRef == nil {
                NSLog("FakeKey: No event source")
                return
            }
    
            let keyDownEvent = CGEvent(keyboardEventSource: sourceRef,
                                       virtualKey: keyCode,
                                       keyDown: true)
            if useCommandFlag {
                keyDownEvent?.flags = .maskCommand
            }
    
            let keyUpEvent = CGEvent(keyboardEventSource: sourceRef,
                                     virtualKey: keyCode,
                                     keyDown: false)
    
            keyDownEvent?.post(tap: .cghidEventTap)
            keyUpEvent?.post(tap: .cghidEventTap)
        }
    }
    
    0 讨论(0)
  • 2020-11-28 10:39

    A combination of the answers to stimulate a shortcut/hotkey. Swift 5.1

    let source = CGEventSource(stateID: CGEventSourceStateID.hidSystemState)
    
    let cmdKey: UInt16 = 0x38
    let numberThreeKey: UInt16 = 0x14
    
    let cmdDown = CGEvent(keyboardEventSource: source, virtualKey: cmdKey, keyDown: true)
    let cmdUp = CGEvent(keyboardEventSource: source, virtualKey: cmdKey, keyDown: false)
    let keyThreeDown = CGEvent(keyboardEventSource: source, virtualKey: numberThreeKey, keyDown: true)
    let keyThreeUp = CGEvent(keyboardEventSource: source, virtualKey: numberThreeKey, keyDown: false)
    
    
    fileprivate func testShortcut() {
    
    let loc = CGEventTapLocation.cghidEventTap
    
    cmdDown?.flags = CGEventFlags.maskCommand
    cmdUp?.flags = CGEventFlags.maskCommand
    keyThreeDown?.flags = CGEventFlags.maskCommand
    keyThreeUp?.flags = CGEVentFlags.maskCommand
    
    cmdDown?.post(tap: loc)
    keyThreeDown?.post(tap: loc)
    cmdUp?.post(tap: loc)
    keyThreeUp?.post(tap: loc)
    
    }
    

    Manually written may contain mistakes.

    0 讨论(0)
  • 2020-11-28 10:51

    The code on that linked answer is fairly readily convertible to Swift code, however there are a handful of gotchas you will need to take care of along the way:

    CGEventSourceCreate takes a CGEventSourceStateID, which is a typealiase for a UInt32, but the constants such as kCGEventSourceStateHIDSystemState are defined as Int, so you’ll have to cast them i.e. CGEventSourceStateID(kCGEventSourceStateHIDSystemState). Likewise with CGEventFlags.

    CGEventSourceCreate and CGEventCreateKeyboardEvent return an Unmanaged<CGEventSource> (or Unmanaged<CGEvent>). The auto-generated Swift API for Core Graphics doesn’t know whether the returned objects need to be released by you or not so you need to check the API docs for these calls and then use takeRetainedValue() or takeUnretainedValue() accordingly on the returned value, to convert them into the underlying type you want to work with.

    Finally, they return implicitly unwrapped optionals, so you’ll need to decide if you want to check for nils or just live with the excitement of runtime explosions if they ever return one.

    Given all that it’s pretty simple to turn the Objective-C in that answer demonstrating pressing Cmd-Space to Swift, I just tried pasting this into a scratch app and it worked fine:

    (though I haven't checked the API docs for whether the retain is the correct thing to do or not)

    let src = CGEventSourceCreate(CGEventSourceStateID(kCGEventSourceStateHIDSystemState)).takeRetainedValue()
    
    let cmdd = CGEventCreateKeyboardEvent(src, 0x38, true).takeRetainedValue()
    let cmdu = CGEventCreateKeyboardEvent(src, 0x38, false).takeRetainedValue()
    let spcd = CGEventCreateKeyboardEvent(src, 0x31, true).takeRetainedValue()
    let spcu = CGEventCreateKeyboardEvent(src, 0x31, false).takeRetainedValue()
    
    CGEventSetFlags(spcd, CGEventFlags(kCGEventFlagMaskCommand));
    CGEventSetFlags(spcd, CGEventFlags(kCGEventFlagMaskCommand));
    
    let loc = CGEventTapLocation(kCGHIDEventTap)
    
    CGEventPost(loc, cmdd)
    CGEventPost(loc, spcd)
    CGEventPost(loc, spcu)
    CGEventPost(loc, cmdu)
    
    0 讨论(0)
  • 2020-11-28 10:54

    Working with Swift 3

    let src = CGEventSource(stateID: CGEventSourceStateID.hidSystemState)
    
    let cmdd = CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: true)
    let cmdu = CGEvent(keyboardEventSource: src, virtualKey: 0x38, keyDown: false)
    let spcd = CGEvent(keyboardEventSource: src, virtualKey: 0x31, keyDown: true)
    let spcu = CGEvent(keyboardEventSource: src, virtualKey: 0x31, keyDown: false)
    
    spcd?.flags = CGEventFlags.maskCommand;
    
    let loc = CGEventTapLocation.cghidEventTap
    
    cmdd?.post(tap: loc)
    spcd?.post(tap: loc)
    spcu?.post(tap: loc)
    cmdu?.post(tap: loc)
    
    0 讨论(0)
  • 2020-11-28 10:57

    Swift 3

    For me the hexadecimal key values like: 0x124 didn't work, but simple UInt 124 did the trick!

    A nice collection of keycodes can be found here! This copy-paste code snippet simulates a right arrow keypress. Change the key number for whatever you want to simulate:

        // Simulate Right Arrow keypress
        let rightArrowKeyCode: UInt16 = 124
    
        let keyDownEvent = CGEvent(keyboardEventSource: nil, virtualKey: rightArrowKeyCode, keyDown: true)
        keyDownEvent?.flags = CGEventFlags.maskCommand
        keyDownEvent?.post(tap: CGEventTapLocation.cghidEventTap)
    
        let keyUpEvent = CGEvent(keyboardEventSource: nil, virtualKey: rightArrowKeyCode, keyDown: false)
        keyUpEvent?.flags = CGEventFlags.maskCommand
        keyUpEvent?.post(tap: CGEventTapLocation.cghidEventTap)
    

    Update: For macOS Mojave and above you should allow your app to control your computer in System Preferences > Security & Privacy > Accessibility

    0 讨论(0)
提交回复
热议问题