Имитация нажатия клавиш с помощью Swift

Я ищу способ имитировать нажатия клавиш в OSX. Я нашел другое решение ( имитация нажатия клавиш для системных клавиш) с использованием Objective-C, но мне нужно сделать это с помощью Swift. Как я могу адаптировать CGEventCreateKeyboardEvent?

4 ответа

Решение

Код этого связанного ответа довольно легко конвертируется в код Swift, однако есть несколько ошибок, о которых вам нужно позаботиться в процессе:

CGEventSourceCreate занимает CGEventSourceStateID, который является typealiase для UInt32, но такие постоянные, как kCGEventSourceStateHIDSystemState определяются как Intтак что вам придется их разыгрывать, т.е. CGEventSourceStateID(kCGEventSourceStateHIDSystemState), Аналогично с CGEventFlags,

CGEventSourceCreate а также CGEventCreateKeyboardEvent вернуть Unmanaged<CGEventSource> (или же Unmanaged<CGEvent>). Сгенерированный автоматически Swift API для Core Graphics не знает, нужно ли вам возвращать возвращаемые объекты или нет, поэтому вам нужно проверить документы API для этих вызовов, а затем использовать takeRetainedValue() или же takeUnretainedValue() в соответствии с возвращаемым значением, чтобы преобразовать их в базовый тип, с которым вы хотите работать.

Наконец, они возвращают неявно развернутые необязательные параметры, поэтому вам нужно решить, хотите ли вы проверить nils или просто жить с волнением взрывов во время выполнения, если они когда-нибудь вернутся.

Учитывая все, что довольно просто включить Objective-C в этом ответе, демонстрируя нажатие клавиши Cmd-Space для Swift, я просто попытался вставить это в пустое приложение, и оно работало нормально:

(хотя я не проверял документацию по API на предмет правильности или нет сохранения)

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)

Работа со 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)

Свифт 3

Для меня значения шестнадцатеричного ключа, такие как: 0x124, не работали, но простой UInt 124 сделал свое дело!

Хорошую коллекцию кодов клавиш можно найти здесь! Этот фрагмент кода копирования-вставки имитирует нажатие клавиши со стрелкой вправо. Измените номер ключа для того, что вы хотите имитировать:

    // 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)

Обновление: не работает под macOS Mojave.

Я сделал это для проекта Swift 4, не забывайте, что песочница в приложении не позволяет приложению отправлять нажатия клавиш, как это, поэтому его нужно отключить. Это означает, что ваше приложение будет запрещено в 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)
    }
}

Комбинация ответов для стимулирования сочетания клавиш / горячих клавиш. 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)

}

Написанное вручную может содержать ошибки.

Другие вопросы по тегам