Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting if Mac has a backlit keyboard

It’s quite easy to detect if Mac has an illuminated keyboard with ioreg at the command line:

ioreg -c IOResources -d 3 | grep '"KeyboardBacklight" =' | sed 's/^.*= //g'

But how can I programmatically get this IOKit boolean property using the latest Swift? I’m looking for some sample code.

like image 494
Tzar Avatar asked Dec 15 '21 08:12

Tzar


2 Answers

I figured out the following with some trial and error:

  • Get the "IOResources" node from the IO registry.
  • Get the "KeyboardBacklight" property from that node.
  • (Conditionally) convert the property value to a boolean.

I have tested this on an MacBook Air (with keyboard backlight) and on an iMac (without keyboard backlight), and it produced the correct result in both cases.

import Foundation
import IOKit

func keyboardHasBacklight() -> Bool {
    let port: mach_port_t
    if #available(macOS 12.0, *) {
        port = kIOMainPortDefault // New name as of macOS 12
    } else {
        port = kIOMasterPortDefault // Old name up to macOS 11
    }
    let service = IOServiceGetMatchingService(port, IOServiceMatching(kIOResourcesClass))
    guard service != IO_OBJECT_NULL else {
        // Could not read IO registry node. You have to decide whether
        // to treat this as a fatal error or not.
        return false
    }
    guard let cfProp = IORegistryEntryCreateCFProperty(service, "KeyboardBacklight" as CFString,
                                                       kCFAllocatorDefault, 0)?.takeRetainedValue(),
          let hasBacklight = cfProp as? Bool
    else {
        // "KeyboardBacklight" property not present, or not a boolean.
        // This happens on Macs without keyboard backlight.
        return false
    }
    // Successfully read boolean "KeyboardBacklight" property:
    return hasBacklight
}
like image 109
Martin R Avatar answered Oct 02 '22 13:10

Martin R


As an addendum to Martin’s excellent answer, here are the notes I got from Apple’s Developer Technical Support:

Calling I/O Kit from Swift is somewhat challenging. You have two strategies here:

  • You can wrap the I/O Kit API in a Swift-friendly wrapper and then use that to accomplish your task.
  • You can just go straight to your task, resulting in lots of ugly low-level Swift.

Pulling random properties out of the I/O Registry is not the best path to long-term binary compatibility. Our general policy here is that we only support properties that have symbolic constants defined in the headers (most notably IOKitKeys.h, but there are a bunch of others). KeyboardBacklight has no symbolic constant and thus isn’t supported.


Make sure you code defensively. This property might go away, change meaning, change its type, and so on. Your code must behave reasonable in all such scenarios.


Please make sure you file an enhancement request for a proper API to get this info, making sure to include a high-level description of your overall goal.

like image 38
Tzar Avatar answered Oct 02 '22 13:10

Tzar