是否可以控制 OS X 上的 CAPS LOCK LED?

是否可以控制 OS X 上的 CAPS LOCK LED?

我不使用 OS X 中的 CAPS LOCK 功能,因此我在系统偏好设置下禁用了该键。现在该键对我来说完全没用了 - 好吧,我可以将 CMD/CTRL/ALT 键映射到该键,但我已经有了该键的键……

因此,我想到的是使用按键上的 LED(我使用最新的 Apple 无线键盘)来通知是否有邮件在等着我……诸如此类。是否可以直接控制 LED?通过 Objective-C Cocoa 应用程序控制它是可以的,我会自己编写该应用程序,但到目前为止,我可以从 Apple 的 Cocoa 文档中找到任何有用的东西。

答案1

您可以用它来操控 Mac 键盘 LED。

/*
 * keyboard_leds.c
 * Manipulate keyboard LEDs (capslock and numlock) programmatically.
 *
 * gcc -Wall -o keyboard_leds keyboard_leds.c -framework IOKit
 *     -framework CoreFoundation
 *
 * Copyright (c) 2007,2008 Amit Singh. All Rights Reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *     
 *  THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 */

#define PROGNAME "keyboard_leds"
#define PROGVERS "0.1"

#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <sysexits.h>
#include <mach/mach_error.h>

#include <IOKit/IOCFPlugIn.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDUsageTables.h>

static IOHIDElementCookie capslock_cookie = (IOHIDElementCookie)0;
static IOHIDElementCookie numlock_cookie  = (IOHIDElementCookie)0;
static int capslock_value = -1;
static int numlock_value  = -1;

void         usage(void);
inline void  print_errmsg_if_io_err(int expr, char* msg);
inline void  print_errmsg_if_err(int expr, char* msg);

io_service_t find_a_keyboard(void);
void         find_led_cookies(IOHIDDeviceInterface122** handle);
void         create_hid_interface(io_object_t hidDevice,
                                  IOHIDDeviceInterface*** hdi);
int          manipulate_led(UInt32 whichLED, UInt32 value);

void
usage(void)
{
    fprintf(stderr, "%s (version %s)\n"
      "Copyright (c) 2007,2008 Amit Singh. All Rights Reserved.\n"
      "Manipulate keyboard LEDs\n\n"
      "Usage: %s [OPTIONS...], where OPTIONS is one of the following\n\n"
      "  -c[1|0], --capslock[=1|=0] get or set (on=1, off=0) caps lock LED\n"
      "  -h,      --help            print this help message and exit\n"
      "  -n[1|0], --numlock[=1|=0]  get or set (on=1, off=0) num lock LED\n",
    PROGNAME, PROGVERS, PROGNAME);
}

inline void
print_errmsg_if_io_err(int expr, char* msg)
{
    IOReturn err = (expr);

    if (err != kIOReturnSuccess) {
        fprintf(stderr, "*** %s - %s(%x, %d).\n", msg, mach_error_string(err),
                err, err & 0xffffff);
        fflush(stderr);
        exit(EX_OSERR);
    }
}

inline void
print_errmsg_if_err(int expr, char* msg)
{
    if (expr) {
        fprintf(stderr, "*** %s.\n", msg);
        fflush(stderr);
        exit(EX_OSERR);
    }
}

io_service_t
find_a_keyboard(void)
{
    io_service_t result = (io_service_t)0;

    CFNumberRef usagePageRef = (CFNumberRef)0;
    CFNumberRef usageRef = (CFNumberRef)0;
    CFMutableDictionaryRef matchingDictRef = (CFMutableDictionaryRef)0;

    if (!(matchingDictRef = IOServiceMatching(kIOHIDDeviceKey))) {
        return result;
    }

    UInt32 usagePage = kHIDPage_GenericDesktop;
    UInt32 usage = kHIDUsage_GD_Keyboard;

    if (!(usagePageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType,
                                        &usagePage))) {
        goto out;
    }

    if (!(usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType,
                                    &usage))) {
        goto out;
    }

    CFDictionarySetValue(matchingDictRef, CFSTR(kIOHIDPrimaryUsagePageKey),
                         usagePageRef);
    CFDictionarySetValue(matchingDictRef, CFSTR(kIOHIDPrimaryUsageKey),
                         usageRef);

    result = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDictRef);

out:
    if (usageRef) {
        CFRelease(usageRef);
    }
    if (usagePageRef) {
        CFRelease(usagePageRef);
    }

    return result;
}

void
find_led_cookies(IOHIDDeviceInterface122** handle)
{
    IOHIDElementCookie cookie;
    CFTypeRef          object;
    long               number;
    long               usage;
    long               usagePage;
    CFArrayRef         elements;
    CFDictionaryRef    element;
    IOReturn           result;

    if (!handle || !(*handle)) {
        return;
    }

    result = (*handle)->copyMatchingElements(handle, NULL, &elements);

    if (result != kIOReturnSuccess) {
        fprintf(stderr, "Failed to copy cookies.\n");
        exit(1);
    }

    CFIndex i;

    for (i = 0; i < CFArrayGetCount(elements); i++) {

        element = CFArrayGetValueAtIndex(elements, i);

        object = (CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey)));
        if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) {
            continue;
        }
        if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
                              &number)) {
            continue;
        }
        cookie = (IOHIDElementCookie)number;

        object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey));
        if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) {
            continue;
        }
        if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType,
                              &number)) {
            continue;
        }
        usage = number;

        object = CFDictionaryGetValue(element,CFSTR(kIOHIDElementUsagePageKey));
        if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) {
            continue;
        }
        if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType,
                              &number)) {
            continue;
        }
        usagePage = number;

        if (usagePage == kHIDPage_LEDs) {
            switch (usage) {

            case kHIDUsage_LED_NumLock:
                numlock_cookie = cookie;
                break;

            case kHIDUsage_LED_CapsLock:
                capslock_cookie = cookie;
                break;

            default:
                break;
            }
        }
    }

    return;
}

void
create_hid_interface(io_object_t hidDevice, IOHIDDeviceInterface*** hdi)
{
    IOCFPlugInInterface** plugInInterface = NULL;

    io_name_t className;
    HRESULT   plugInResult = S_OK;
    SInt32    score = 0;
    IOReturn  ioReturnValue = kIOReturnSuccess;

    ioReturnValue = IOObjectGetClass(hidDevice, className);

    print_errmsg_if_io_err(ioReturnValue, "Failed to get class name.");

    ioReturnValue = IOCreatePlugInInterfaceForService(
                        hidDevice, kIOHIDDeviceUserClientTypeID,
                        kIOCFPlugInInterfaceID, &plugInInterface, &score);
    if (ioReturnValue != kIOReturnSuccess) {
        return;
    }

    plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
                     CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID)hdi);
    print_errmsg_if_err(plugInResult != S_OK,
                        "Failed to create device interface.\n");

    (*plugInInterface)->Release(plugInInterface);
}

int
manipulate_led(UInt32 whichLED, UInt32 value)
{
    io_service_t           hidService = (io_service_t)0;
    io_object_t            hidDevice = (io_object_t)0;
    IOHIDDeviceInterface **hidDeviceInterface = NULL;
    IOReturn               ioReturnValue = kIOReturnError;
    IOHIDElementCookie     theCookie = (IOHIDElementCookie)0;
    IOHIDEventStruct       theEvent;

    if (!(hidService = find_a_keyboard())) {
        fprintf(stderr, "No keyboard found.\n");
        return ioReturnValue;
    }

    hidDevice = (io_object_t)hidService;

    create_hid_interface(hidDevice, &hidDeviceInterface);

    find_led_cookies((IOHIDDeviceInterface122 **)hidDeviceInterface);

    ioReturnValue = IOObjectRelease(hidDevice);
    if (ioReturnValue != kIOReturnSuccess) {
        goto out;
    }

    ioReturnValue = kIOReturnError;

    if (hidDeviceInterface == NULL) {
        fprintf(stderr, "Failed to create HID device interface.\n");
        return ioReturnValue;
    }

    if (whichLED == kHIDUsage_LED_NumLock) {
        theCookie = numlock_cookie;
    } else if (whichLED == kHIDUsage_LED_CapsLock) {
        theCookie = capslock_cookie;
    }

    if (theCookie == 0) {
        fprintf(stderr, "Bad or missing LED cookie.\n");
        goto out;
    }

    ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, 0);
    if (ioReturnValue != kIOReturnSuccess) {
        fprintf(stderr, "Failed to open HID device interface.\n");
        goto out;
    }

    ioReturnValue = (*hidDeviceInterface)->getElementValue(hidDeviceInterface,
                                               theCookie, &theEvent);
    if (ioReturnValue != kIOReturnSuccess) {
        (void)(*hidDeviceInterface)->close(hidDeviceInterface);
        goto out;
    }

    fprintf(stdout, "%s\n", (theEvent.value) ? "on" : "off");
    if (value != -1) {
        if (theEvent.value != value) {
            theEvent.value = value;
            ioReturnValue = (*hidDeviceInterface)->setElementValue(
                                hidDeviceInterface, theCookie,
                                &theEvent, 0, 0, 0, 0);
            if (ioReturnValue == kIOReturnSuccess) {
                fprintf(stdout, "%s\n", (theEvent.value) ? "on" : "off");
            }
        }
    }

    ioReturnValue = (*hidDeviceInterface)->close(hidDeviceInterface);

out:
    (void)(*hidDeviceInterface)->Release(hidDeviceInterface);

    return ioReturnValue;
}

static const char *options = "c::hn::";
static struct option
long_options[] = {
    { "capslock", optional_argument, 0, 'c' },
    { "help",     no_argument,       0, 'h' },
    { "numlock",  optional_argument, 0, 'n' },
    { 0, 0, 0, 0 },
};

int
main (int argc, char **argv)
{
    UInt32 whichLED = (UInt32)0;
    int c, ctr = 0;
    int target_value = -1;

    while ((c = getopt_long(argc, argv, options, long_options, NULL)) != -1) {

        switch (c) {

        case 0:
            break;

        case 'c':
            ctr++;
            whichLED = kHIDUsage_LED_CapsLock;
            if (optarg) {
                capslock_value = atoi(optarg);
                target_value = (capslock_value);
            }
            break;

        case 'h':
            usage();
            exit(0);
            break;

        case 'n':
            ctr++;
            whichLED = kHIDUsage_LED_NumLock;
            if (optarg) {
                numlock_value = atoi(optarg);
                target_value = (numlock_value);
            }
            break;

        default:
            usage();
            exit(1);
            break;
        }
    }

    if (ctr != 1) {
        fprintf(stderr, "Missing options or invalid combination of options. "
                        "Try -h for help.\n");
        exit(1);
    }

    return manipulate_led(whichLED, target_value);
}

编译并使用此脚本

gcc -Wall -o keyboard_leds keyboard_leds.c \
    -framework IOKit -framework CoreFoundation
./keyboard_leds -h
./keyboard_leds -c # query caps lock LED state
./keyboard_leds -c1 # turn caps lock LED on
./keyboard_leds -c0 # turn caps lock LED off

答案2

这是另一个在 OSX 下控制键盘 LED 的工具:https://github.com/busyloop/maclight

MacLight 让您可以控制 Mac 上的键盘 LED(capslock、numlock)。

答案3

一个 Python 示例:http://www.psychicorigami.com/2009/03/01/5k-morse-code-app-using-capslock-led/

这是有可能的。我正在尝试寻找一个几年前发现的应用程序,它可以切换我旧 iBook (10.4) 上的数字锁定和大写锁定 LED。今天查找时发现了它。

找到了。

但需要编译。

哦,还有HID LED 测试工具– Xcode 示例 :D

相关内容