Seema Kadavan
Seema Kadavan

Reputation: 2648

Simulate/Toggle CAPS LOCK programatically in OS X

I have seen many post on this topic. But haven't found a clear answer anywhere.

Is there a way to toggle CAPS LOCK in Objective-C or C code? I am not looking for a solution using X11 libs. I am not bothered about the LED on/off status. But just the functionality of CAPS LOCK (changing the case of letters and printing the special characters on number keys).

Why is CGEvent not supporting this the way it does for other keys?

Upvotes: 3

Views: 1755

Answers (4)

HangarRash
HangarRash

Reputation: 15049

The following command line program in Objective-C will toggle the current caps lock state:

#import <IOKit/IOKitLib.h> // or @import IOKit;
#import <IOKit/hid/IOHIDBase.h> // needed for kIOHIDSystemClass

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Use kIOMasterPortDefault instead of kIOMainPortDefault for macOS 11.x or earlier
        io_service_t ioService = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching(kIOHIDSystemClass));
        io_connect_t ioConnect = 0;
        IOServiceOpen(ioService, mach_task_self_, kIOHIDParamConnectType, &ioConnect);

        bool state = false;
        IOHIDGetModifierLockState(ioConnect, kIOHIDCapsLockState, &state);

        // Toggle state
        state = !state; // 1 = CapsLock on, 0 = CapsLock off
        IOHIDSetModifierLockState(ioConnect, kIOHIDCapsLockState, state);

        IOServiceClose(ioConnect);
    }
    return 0;
}

Upvotes: 3

Byteboon
Byteboon

Reputation: 140

The following method also works when you want CapsLock to toggle the current keyboard language (if you have the CapsLock key configured to do that).

CFMutableDictionaryRef mdict = IOServiceMatching(kIOHIDSystemClass);
io_service_t ios = IOServiceGetMatchingService(kIOMasterPortDefault, (CFDictionaryRef)mdict);
if (ios) {
    io_connect_t ioc = 0;
    IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, &ioc);
    if (ioc) {
        NXEventData event{};
        IOGPoint loc{};

        // press CapsLock key
        UInt32 evtInfo = NX_KEYTYPE_CAPS_LOCK << 16 | NX_KEYDOWN << 8;
        event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
        event.compound.misc.L[0] = evtInfo;
        IOHIDPostEvent(ioc, NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE);

        // release CapsLock key
        evtInfo = NX_KEYTYPE_CAPS_LOCK << 16 | NX_KEYUP << 8;
        event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
        event.compound.misc.L[0] = evtInfo;
        IOHIDPostEvent(ioc, NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE);

        IOServiceClose(ioc);
    }
}

Upvotes: 0

Roman Kerimov
Roman Kerimov

Reputation: 151

var ioConnect: io_connect_t = .init(0)
let ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass))
IOServiceOpen(ioService, mach_task_self_, UInt32(kIOHIDParamConnectType), &ioConnect)

var modifierLockState = false
IOHIDGetModifierLockState(ioConnect, Int32(kIOHIDCapsLockState), &modifierLockState) 

modifierLockState.toggle()
IOHIDSetModifierLockState(ioConnect, Int32(kIOHIDCapsLockState), modifierLockState)

IOServiceClose(ioConnect)

Upvotes: 5

Seema Kadavan
Seema Kadavan

Reputation: 2648

I got this working, after a long struggle.

Invoke the method given below twice. Once for up event and another for down event. For example for simulating CAPS A, we need to do the following.

[self handleKeyEventWithCapsOn:0 andKeyDown:NO];
[self handleKeyEventWithCapsOn:0 andKeyDown:YES];

0 is the keycode for 'a'.

- (void) handleKeyEventWithCapsOn:(int) keyCode andKeyDown:(BOOL)keyDown
{
    if(keyDown)
    {
        CGEventRef eventDown;
        eventDown = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)keyCode, true);
        CGEventSetFlags(eventDown, kCGEventFlagMaskShift);
        CGEventPost(kCGSessionEventTap, eventDown);
        CFRelease(eventDown);
    }
    else
    {
        CGEventRef eventUp;
        eventUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)keyCode, false);
        CGEventSetFlags(eventUp, kCGEventFlagMaskShift);
        CGEventPost(kCGSessionEventTap, eventUp);

        // SHIFT Up Event
        CGEventRef eShiftUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
        CGEventPost(kCGSessionEventTap, eShiftUp);
        CFRelease(eventUp);
        CFRelease(eShiftUp);
    }
}

Upvotes: -1

Related Questions