Search code examples
swiftmacoscocoaswiftuiappkit

Detect whether keyboard has the Globe key on macOS


Recent Apple products have the Globe (🌐︎) key on the keyboard. At the AppKit level, it is actually just a fn key that behaves as NSEvent.ModifierFlags.function. But is there any way to know whether the keyboard that the user currently uses has the Globe key or just the fn key?

It seems that the system somehow knows it since the shortcut key display in the menu changes depending on the keyboard. The Globe symbol is shown only when the keyboard actually has the Globe key. Otherwise, it shows "fn".

screenshot of menu with Globe symbol screenshot of menu with fn symbol

But, so far, I have not found a way to detect it programmatically in the AppKit/SwiftUI world. I'd like to implement displaying keyboard shortcuts for those menu commands in my own view.


Solution

  • The I/O registry seems to have information about whether a globe key exists ("SupportsGlobeKey" = Yes or No).

    In my environment (MacBook Pro 2023), ioreg -rln AppleHIDKeyboardEventDriverV2 gives the following results. (I don't have a desktop machine, so I can't verify the absence of the Globe key).

    $ ioreg -rln AppleHIDKeyboardEventDriverV2
    +-o AppleHIDKeyboardEventDriverV2  <class AppleHIDKeyboardEventDriverV2, id 0x100000ba5, registered, matched, active, busy 0 (0 ms), retain 10>
      | {
      |   "LocationID" = 49
      |   "IOPersonalityPublisher" = "com.apple.driver.AppleTopCaseDriverV2"
      |   "Keyboard" = {"Elements"=({"VariableSize"=0,"UnitExponent"=0,"IsRelative"=No,"UsagePage"=7,"Max"=1,"IsArray"=No,"Type"=2,"Size"=1,"Min"=0,"Flags"=134217730,"ReportID"=1,"Usage"=224,"ReportCount"=1,"Unit"=0,"Ha$
      |   "IOMatchCategory" = "IODefaultMatchCategory"
      |   "CapsLockLanguageSwitch" = No
      |   "HIDServiceSupport" = Yes
    *snip*
      |   "KeyboardEnabled" = Yes
      |   "PrimaryUsagePage" = 1
      |   "CFBundleIdentifierKernel" = "com.apple.driver.AppleHIDKeyboard"
      |   "SupportsGlobeKey" = Yes
      |   "SensorProperties" = {}
      |   "ProductID" = 0
    *snip*
    

    To get I/O registry information programmatically, use IOKit.

    For example, to get whether a globe key exists, use the following code.

    import IOKit
    
    let entry = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("AppleHIDKeyboardEventDriverV2"))
    if let property = IORegistryEntryCreateCFProperty(entry, "SupportsGlobeKey" as CFString, kCFAllocatorDefault, 0)?.takeRetainedValue() {
      print(property) // => 1
    }
    
    IOObjectRelease(entry)