Home > OS >  Simple key remapping using IOKit with Swift
Simple key remapping using IOKit with Swift

Time:11-24

In macOS, the IOKit HID APIs can be used for key remapping. In the example below, A key is remapped to B key, and vice versa, using Objective-C.

Is it possible to do the same remapping programmatically with the latest Swift 5.5? If it can be done, how does the example below look rewritten in Swift?

Or is there a new, more modern API that can be used with Swift to accomplish the same task?


#import <Foundation/Foundation.h>
#import <IOKit/hidsystem/IOHIDEventSystemClient.h>
#import <IOKit/hidsystem/IOHIDServiceClient.h>
#import <IOKit/hid/IOHIDUsageTables.h>
 
int main(int argc, char *argv[])
{
    IOHIDEventSystemClientRef system;
    CFArrayRef services;
 
    uint64_t aKey = 0x700000004;
    uint64_t bKey = 0x700000005;
 
    NSArray *map = @[
                     @{@kIOHIDKeyboardModifierMappingSrcKey:@(aKey),
                        @kIOHIDKeyboardModifierMappingDstKey:@(bKey)},
                     @{@kIOHIDKeyboardModifierMappingSrcKey:@(bKey),
                        @kIOHIDKeyboardModifierMappingDstKey:@(aKey)},
                     ];
 
    system = IOHIDEventSystemClientCreateSimpleClient(kCFAllocatorDefault);
    services = IOHIDEventSystemClientCopyServices(system);
    for(CFIndex i = 0; i < CFArrayGetCount(services); i  ) {
        IOHIDServiceClientRef service = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(services, i);
        if(IOHIDServiceClientConformsTo(service, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) {
            IOHIDServiceClientSetProperty(service, CFSTR(kIOHIDUserKeyUsageMapKey), (CFArrayRef)map);
        }
    }
 
    CFRelease(services);
    CFRelease(system);
 
    return 0;
}

CodePudding user response:

You can rewrite your cursed code in Swift 5.5 like this:

import Cocoa

class ViewController: NSViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    
     remapKeys()
  }

  func remapKeys() {
    let aKey: UInt64 = 0x700000004
    let bKey: UInt64 = 0x700000005
    
    let map: [[String: UInt64]] = [
      [kIOHIDKeyboardModifierMappingSrcKey:aKey,
       kIOHIDKeyboardModifierMappingDstKey:bKey],
      [kIOHIDKeyboardModifierMappingSrcKey:bKey,
       kIOHIDKeyboardModifierMappingDstKey:aKey],
    ]
    
    let system = IOHIDEventSystemClientCreateSimpleClient(kCFAllocatorDefault)
    let services = IOHIDEventSystemClientCopyServices(system)
    
    for service in services as! [IOHIDServiceClient] {
      if((IOHIDServiceClientConformsTo(service, UInt32((kHIDPage_GenericDesktop)), UInt32(kHIDUsage_GD_Keyboard))) != 0) {
        IOHIDServiceClientSetProperty(service, kIOHIDUserKeyUsageMapKey as CFString, map as CFArray)
      }
    }
  }
}

providing you create a bridging header containing these imports:

#import <IOKit/hidsystem/IOHIDEventSystemClient.h>
#import <IOKit/hidsystem/IOHIDServiceClient.h>
#import <IOKit/hid/IOHIDUsageTables.h>
  • Related