Skip to content

Instantly share code, notes, and snippets.

@jw910731
Created June 23, 2023 10:19
Show Gist options
  • Save jw910731/428898356bf25c7ebb1c3600cf365cb3 to your computer and use it in GitHub Desktop.
Save jw910731/428898356bf25c7ebb1c3600cf365cb3 to your computer and use it in GitHub Desktop.
Numlock Enabler CLI
import Foundation
import IOKit
import IOKit.hid
class KeyboardCapture {
var manager: IOHIDManager
init() {
manager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
let matchingDict = [
kIOHIDVendorIDKey: 0xc45,
kIOHIDProductIDKey: 0x7811,
kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
kIOHIDDeviceUsageKey: kHIDUsage_GD_Keyboard,
] as CFDictionary
IOHIDManagerSetDeviceMatching(manager, matchingDict)
IOHIDManagerRegisterInputValueCallback(manager, {context, result, sender, value in
let element = IOHIDValueGetElement(value)
if (IOHIDElementGetUsagePage(element) == 0x07) {
let scancode = IOHIDElementGetUsage(element);
if (scancode >= 4 && scancode <= 231) {
let pressed = IOHIDValueGetIntegerValue(value);
print("Scancode: 0x\(String(format: "%X", scancode)) -> ", terminator: "")
switch (pressed) {
case 0:
print("Key Down")
break;
case 1:
print("Key Up")
break;
default:
break
}
}
}
}, nil)
IOHIDManagerRegisterInputReportCallback(manager, {context, result, sender, type, reportID, report, reportLength in
var typeStr = ""
switch(type) {
case kIOHIDReportTypeInput:
typeStr = "Input"
break
case kIOHIDReportTypeOutput:
typeStr = "Output"
break
case kIOHIDReportTypeCount:
typeStr = "Count"
break
case kIOHIDReportTypeFeature:
typeStr = "Feature"
break
default:
break
}
let buf = UnsafeBufferPointer(start: report, count: reportLength - 4)
let bufHex = buf.reduce(into: "") { $0 += String(format: "%02x ", $1) }
print("type: \(typeStr), report: \(bufHex)")
}, nil)
IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
}
func open() -> IOReturn {
return IOHIDManagerOpen(manager, IOOptionBits(kIOHIDOptionsTypeNone))
}
func startCapture() {
CFRunLoopRun()
}
func stopCapture() {
IOHIDManagerClose(manager, IOOptionBits(kIOHIDOptionsTypeNone))
CFRunLoopStop(CFRunLoopGetCurrent())
}
deinit {
IOHIDManagerClose(manager, IOOptionBits(kIOHIDOptionsTypeNone))
}
}
let capture = KeyboardCapture()
let result = capture.open()
print(stringifyIOReturn(result))
if result != kIOReturnSuccess {
print("Failed, exiting.")
} else {
if let devices = IOHIDManagerCopyDevices(capture.manager) as? Set<IOHIDDevice> {
if !devices.isEmpty {
for device in devices {
print(device)
let elements = IOHIDDeviceCopyMatchingElements(device, nil, IOOptionBits(kIOHIDOptionsTypeNone))
if elements != nil {
for element in elements! as NSArray as [AnyObject] as! [IOHIDElement?] {
if element != nil && IOHIDElementGetUsagePage(element!) == kHIDPage_LEDs {
let element = element!
let led = IOHIDElementGetUsage(element);
if led == kHIDUsage_LED_NumLock {
let retValPtr = UnsafeMutablePointer<Unmanaged<IOHIDValue>>.allocate(capacity: 1)
if IOHIDDeviceGetValue(device, element, retValPtr) == kIOReturnSuccess {
let state = retValPtr.pointee.takeUnretainedValue() as IOHIDValue
let current = IOHIDValueGetIntegerValue(state);
retValPtr.deinitialize(count: 1)
print("State: \(current)")
let newState = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, element, 0, 1)
IOHIDDeviceSetValue(device, element, newState)
}
else {
print("Op failed")
}
retValPtr.deallocate()
}
}
}
}
}
} else {
print("no devices")
}
} else {
print("devices not available")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment