0

I'm working on a macOS menubar app that locks any mouse events. What I'm trying to achieve is that after sending mouse events to the CGEventRef based callback, I cannot click anywhere (naturally) but the problem is I cannot quit the loop because of that.

Every time I need to close it, I'm switching to the Xcode app to stop the running app.

This is the main function and the events that I'm sending to the callback function;

void lockTrackpad(void) {
    // For keyboard inputs, I add CGEventMaskBit(kCGEventKeyUp)| CGEventMaskBit(kCGEventKeyDown)| CGEventMaskBit(NX_SYSDEFINED)
    CGEventMask mask = (
                        CGEventMaskBit(kCGEventMouseMoved)
                        | CGEventMaskBit(kCGEventLeftMouseUp)
                        | CGEventMaskBit(kCGEventLeftMouseDown)
                        | CGEventMaskBit(kCGEventRightMouseUp)
                        | CGEventMaskBit(kCGEventRightMouseDown)
                        | CGEventMaskBit(kCGEventScrollWheel)
                        );
    
    eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, mask, CGEventCallback_r, nil);
    
    ...
}

And this is the callback function;

CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
    printf("Event Tap: %d\n", keycode);

    if (keycode == 43) {
        printf("quit the application \n");
        exit(EXIT_SUCCESS);
    }
    
    return nil;
}

What I want is, for example when the loop is running, if press "control + U + L", I want to run some function to exit the loop. I believe in order to get the keyboard input, I should send the keyboard events as well but I couldn't figure out how to detect my keyboard shortcuts.

  • I added the keyboard bits to the mask.
  • The app is sandboxed.

I want to do something like this;

CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);

    // When pressing "control + U + L", call a function
    if (...) {
        printf("pressed control + U + L \n");
        doSomething();
    }

    return nil;
}
Ömürcan Cengiz
  • 2,085
  • 3
  • 22
  • 28
  • Did you add the keyboard bits to the mask? Is `CGEventCallback_r` called for keyboard events? What is the value of `keycode`? Is the app sandboxed? Is the question how to detect the Control key? – Willeke Jan 22 '23 at 10:02
  • I added the keyboard bits to the mask, called the `CGEventCallback_r` for keyboard events and the app is sandboxed. I get the keycodes for the keyboard events. The question is for example, how I detect pressing "control + U + L" on the `CGEventCallback_r`. – Ömürcan Cengiz Jan 22 '23 at 10:26
  • You can check control with `CGEventGetFlags`. What events do you get when you press "control + U + L"? Maybe try some other functions of Quartz Event Services like `CGEventSourceKeyState`? – Willeke Jan 22 '23 at 12:28
  • I get the keycodes for those. But don't know how to detect if 3 of them pressed or not. – Ömürcan Cengiz Jan 22 '23 at 13:38
  • Have you tried setting a flag when a key goes up or down? Have you tried checking the states? – Willeke Jan 23 '23 at 06:10

1 Answers1

1

I figured out like this;

CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    
    if (type == kCGEventKeyDown) {
        CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
        
        if (CGEventGetFlags(event) & kCGEventFlagMaskControl) {
            // control key is pressed
            controlKeyPressed = true;
        }
        if (keyCode == kVK_ANSI_U) {
            // u key is pressed
            uKeyPressed = true;
        }
        if (keyCode == kVK_ANSI_L) {
            // l key is pressed
            lKeyPressed = true;
        }
        if (controlKeyPressed && uKeyPressed && lKeyPressed) {
            // doSomething();
        }
        return event;
    } else if (type == kCGEventKeyUp) {
        CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
        // set the variable to false just like above.
    }
    
    if (keycode == 43) {
        printf("quit the application \n");
        exit(EXIT_SUCCESS);
    }
    
    return nil;
}

Also, don't forget to put your variables on a global scope.

Ömürcan Cengiz
  • 2,085
  • 3
  • 22
  • 28