//! Logic taken from https://stackoverflow.com/a/71046488 import Foundation import Carbon import Quartz import AppKit extension NSEvent.ModifierFlags: Hashable { } extension OptionSet { public func first(_ options: Self.Element ...) -> Self.Element? { for option in options { if contains(option) { return option } } return nil } } struct Keys { static var special = [NSEvent.SpecialKey:CGKeyCode](); static var specialNames = [String:NSEvent.SpecialKey](); static var character = [String:(CGKeyCode, UInt16)](); static var modifierFlag = [NSEvent.ModifierFlags:CGKeyCode](); } @_cdecl("initialize") func initialize() { for keyCode in (0..<128).map({ CGKeyCode($0) }) { for modifiers in (0..<4) { guard let cgevent = CGEvent(keyboardEventSource: nil, virtualKey: CGKeyCode(keyCode), keyDown: true) else { continue }; if modifiers & (1 << 0) != 0 { cgevent.flags.insert(.maskShift); } if modifiers & (1 << 1) != 0 { cgevent.flags.insert(.maskAlternate); } guard let nsevent = NSEvent(cgEvent: cgevent) else { continue }; if nsevent.type == .keyDown { if let specialKey = nsevent.specialKey { Keys.special[specialKey] = keyCode; } else if let characters = nsevent.charactersIgnoringModifiers, !characters.isEmpty && characters != "\u{0010}" { if Keys.character[characters] == nil { Keys.character[characters] = (keyCode, UInt16(modifiers)); } } } else if nsevent.type == .flagsChanged { guard let modifierFlag = nsevent.modifierFlags.first(.capsLock, .shift, .control, .option, .command, .help, .function) else {continue}; Keys.modifierFlag[modifierFlag] = keyCode; } } } // https://developer.apple.com/documentation/appkit/nsevent/specialkey Keys.specialNames["carriageReturn"] = NSEvent.SpecialKey.carriageReturn; Keys.specialNames["backspace"] = NSEvent.SpecialKey.backspace; Keys.specialNames["newline"] = NSEvent.SpecialKey.newline; Keys.specialNames["enter"] = NSEvent.SpecialKey.enter; Keys.specialNames["delete"] = NSEvent.SpecialKey.delete; Keys.specialNames["deleteForward"] = NSEvent.SpecialKey.deleteForward; Keys.specialNames["backTab"] = NSEvent.SpecialKey.backTab; Keys.specialNames["tab"] = NSEvent.SpecialKey.tab; Keys.specialNames["upArrow"] = NSEvent.SpecialKey.upArrow; Keys.specialNames["downArrow"] = NSEvent.SpecialKey.downArrow; Keys.specialNames["leftArrow"] = NSEvent.SpecialKey.leftArrow; Keys.specialNames["rightArrow"] = NSEvent.SpecialKey.rightArrow; Keys.specialNames["pageUp"] = NSEvent.SpecialKey.pageUp; Keys.specialNames["pageDown"] = NSEvent.SpecialKey.pageDown; Keys.specialNames["home"] = NSEvent.SpecialKey.home; Keys.specialNames["end"] = NSEvent.SpecialKey.end; Keys.specialNames["prev"] = NSEvent.SpecialKey.prev; Keys.specialNames["next"] = NSEvent.SpecialKey.next; Keys.specialNames["begin"] = NSEvent.SpecialKey.begin; Keys.specialNames["break"] = NSEvent.SpecialKey.break; Keys.specialNames["clearDisplay"] = NSEvent.SpecialKey.clearDisplay; Keys.specialNames["clearLine"] = NSEvent.SpecialKey.clearLine; Keys.specialNames["deleteCharacter"] = NSEvent.SpecialKey.deleteCharacter; Keys.specialNames["deleteLine"] = NSEvent.SpecialKey.deleteLine; Keys.specialNames["execute"] = NSEvent.SpecialKey.execute; Keys.specialNames["find"] = NSEvent.SpecialKey.find; Keys.specialNames["formFeed"] = NSEvent.SpecialKey.formFeed; Keys.specialNames["help"] = NSEvent.SpecialKey.help; Keys.specialNames["insert"] = NSEvent.SpecialKey.insert; Keys.specialNames["insertCharacter"] = NSEvent.SpecialKey.insertCharacter; Keys.specialNames["insertLine"] = NSEvent.SpecialKey.insertLine; Keys.specialNames["lineSeparator"] = NSEvent.SpecialKey.lineSeparator; Keys.specialNames["menu"] = NSEvent.SpecialKey.menu; Keys.specialNames["modeSwitch"] = NSEvent.SpecialKey.modeSwitch; Keys.specialNames["paragraphSeparator"] = NSEvent.SpecialKey.paragraphSeparator; Keys.specialNames["pause"] = NSEvent.SpecialKey.pause; Keys.specialNames["print"] = NSEvent.SpecialKey.print; Keys.specialNames["printScreen"] = NSEvent.SpecialKey.printScreen; Keys.specialNames["redo"] = NSEvent.SpecialKey.redo; Keys.specialNames["reset"] = NSEvent.SpecialKey.reset; Keys.specialNames["scrollLock"] = NSEvent.SpecialKey.scrollLock; Keys.specialNames["select"] = NSEvent.SpecialKey.select; Keys.specialNames["stop"] = NSEvent.SpecialKey.stop; Keys.specialNames["sysReq"] = NSEvent.SpecialKey.sysReq; Keys.specialNames["system"] = NSEvent.SpecialKey.system; Keys.specialNames["undo"] = NSEvent.SpecialKey.undo; Keys.specialNames["user"] = NSEvent.SpecialKey.user; Keys.specialNames["f1"] = NSEvent.SpecialKey.f1; Keys.specialNames["f2"] = NSEvent.SpecialKey.f2; Keys.specialNames["f3"] = NSEvent.SpecialKey.f3; Keys.specialNames["f4"] = NSEvent.SpecialKey.f4; Keys.specialNames["f5"] = NSEvent.SpecialKey.f5; Keys.specialNames["f6"] = NSEvent.SpecialKey.f6; Keys.specialNames["f7"] = NSEvent.SpecialKey.f7; Keys.specialNames["f8"] = NSEvent.SpecialKey.f8; Keys.specialNames["f9"] = NSEvent.SpecialKey.f9; Keys.specialNames["f10"] = NSEvent.SpecialKey.f10; Keys.specialNames["f11"] = NSEvent.SpecialKey.f11; Keys.specialNames["f12"] = NSEvent.SpecialKey.f12; Keys.specialNames["f13"] = NSEvent.SpecialKey.f13; Keys.specialNames["f14"] = NSEvent.SpecialKey.f14; Keys.specialNames["f15"] = NSEvent.SpecialKey.f15; Keys.specialNames["f16"] = NSEvent.SpecialKey.f16; Keys.specialNames["f17"] = NSEvent.SpecialKey.f17; Keys.specialNames["f18"] = NSEvent.SpecialKey.f18; Keys.specialNames["f19"] = NSEvent.SpecialKey.f19; Keys.specialNames["f20"] = NSEvent.SpecialKey.f20; Keys.specialNames["f21"] = NSEvent.SpecialKey.f21; Keys.specialNames["f22"] = NSEvent.SpecialKey.f22; Keys.specialNames["f23"] = NSEvent.SpecialKey.f23; Keys.specialNames["f24"] = NSEvent.SpecialKey.f24; Keys.specialNames["f25"] = NSEvent.SpecialKey.f25; Keys.specialNames["f26"] = NSEvent.SpecialKey.f26; Keys.specialNames["f27"] = NSEvent.SpecialKey.f27; Keys.specialNames["f28"] = NSEvent.SpecialKey.f28; Keys.specialNames["f29"] = NSEvent.SpecialKey.f29; Keys.specialNames["f30"] = NSEvent.SpecialKey.f30; Keys.specialNames["f31"] = NSEvent.SpecialKey.f31; Keys.specialNames["f32"] = NSEvent.SpecialKey.f32; Keys.specialNames["f33"] = NSEvent.SpecialKey.f33; Keys.specialNames["f34"] = NSEvent.SpecialKey.f34; Keys.specialNames["f35"] = NSEvent.SpecialKey.f35; } @_cdecl("get_special") public func get_special(name: UnsafePointer) -> UInt16 { let name = String(cString: name); guard let specialKey = Keys.specialNames[name] else { return UInt16.max }; guard let keyCode = Keys.special[specialKey] else { return 1 }; return keyCode; } @_cdecl("get_character") public func get_character(character: UInt16) -> UInt32 { guard let (keyCode, modifiers) = Keys.character[String(UnicodeScalar(character)!)] else { return UInt32.max }; return (UInt32(modifiers) << 16) | UInt32(keyCode); }