robot | send keys | | Search

This code simulates mouse events using Core Graphics and Core Foundation frameworks, allowing for functions to set the mouse position, simulate mouse button clicks, and retrieve the current mouse position. It uses a combination of C-style includes and C++ code, suggesting it may be a wrapper around C code, to create a comprehensive mouse simulation tool.

Run example

npm run import -- "mouse control cocoa"

mouse control cocoa

#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>

#if __cplusplus
extern "C" {
#endif

typedef enum {
  kButtonLeft = 0,
  kButtonRight = 1,
} Button;

CGEventType previous_event_;
int event_number_ = 32000;

int main(int argc, char *argv[])
{
    previous_event_ = kCGEventNull;
}

void DispatchMouseEvent(CGMouseButton button, CGEventType type, CGPoint position, uint32_t click_count, uint32_t delay)
{
    if (previous_event_ == kCGEventMouseMoved || previous_event_ == kCGEventLeftMouseDragged)
    {
        usleep(delay);
    }

    event_number_++;
    CGEventRef event = CGEventCreateMouseEvent(NULL, type, position, button);
    CGEventSetType(event, type);
    CGEventSetIntegerValueField(event, kCGMouseEventNumber, event_number_);
    CGEventSetIntegerValueField(event, kCGMouseEventClickState, click_count);
    CGEventPost(kCGHIDEventTap, event);
    CFRelease(event);
    previous_event_ = type;
    usleep(delay);
}

CGPoint GetMousePosition()
{
    usleep(200000);
    CGEventRef event = CGEventCreate(NULL);
    CGPoint cursor = CGEventGetLocation(event);
    CFRelease(event);
    return cursor;
}

void SetMousePosition(double x, double y)
{
    CGPoint position = CGPointMake(x, y);
    DispatchMouseEvent(kCGMouseButtonLeft, kCGEventMouseMoved, position, 1, 0);
    previous_event_ = kCGEventMouseMoved;
}

void SetPosition(CGPoint position)
{
    DispatchMouseEvent(kCGMouseButtonLeft, kCGEventMouseMoved, position, 1, 0);
    previous_event_ = kCGEventMouseMoved;
}

void SetButtonState(Button btn, bool is_pressed, CGPoint position, int click_count)
{
    CGEventType type;
    CGMouseButton button;

    switch (btn)
    {
    case kButtonLeft:
        type = (is_pressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp);
        button = kCGMouseButtonLeft;
        break;

    case kButtonRight:
        type = (is_pressed ? kCGEventRightMouseDown : kCGEventRightMouseUp);
        button = kCGMouseButtonRight;
        break;
    };

    DispatchMouseEvent(button, type, position, click_count, 200000);
}

void MouseClick(Button btn, CGPoint position)
{
    SetButtonState(btn, true, position, 1);  // Press
    SetButtonState(btn, false, position, 1); // Release
}

void Click(Button btn)
{
    CGPoint position = GetMousePosition();
    MouseClick(btn, position);
}

void MouseDoubleClick(Button btn, CGPoint position)
{
    SetButtonState(btn, true, position, 1);
    SetButtonState(btn, false, position, 1);
    SetButtonState(btn, true, position, 2);
    SetButtonState(btn, false, position, 2);
}

void DoubleClick(Button btn)
{
    CGPoint position = GetMousePosition();
    MouseDoubleClick(btn, position);
}

void MouseDrag(CGPoint position)
{
    DispatchMouseEvent(kCGMouseButtonLeft, kCGEventLeftMouseDragged, position, 1, 0);
}

void MouseStartDrag(CGPoint position)
{
    SetButtonState(kButtonLeft, true, position, 1);
    MouseDrag(position);
}

void MouseEndDrag(CGPoint position)
{
    MouseDrag(position);
    SetButtonState(kButtonLeft, false, position, 1);
}


#if __cplusplus
}
#endif

What the code could have been:

objectivec
#import <CoreGraphics/CoreGraphics.h>
#import <CoreFoundation/CoreFoundation.h>

typedef NS_ENUM(NSInteger, Button) {
    kButtonLeft = 0,
    kButtonRight = 1,
};

typedef NS_ENUM(NSInteger, ClickCount) {
    kSingleClick = 1,
    kDoubleClick = 2,
};

typedef NS_ENUM(NSInteger, EventType) {
    kEventNull = 0,
    kEventMouseMoved,
    kEventLeftMouseDragged,
    kEventLeftMouseDown,
    kEventLeftMouseUp,
    kEventRightMouseDown,
    kEventRightMouseUp,
};

@interface LLMAMouseSimulator : NSObject

@property (nonatomic) CGPoint currentPosition;

- (instancetype)init;

- (void)dispatchMouseEvent:(CGMouseButton)button type:(EventType)type position:(CGPoint)position clickCount:(ClickCount)clickCount delay:(NSTimeInterval)delay;

- (CGPoint)getMousePosition;
- (void)setMousePosition:(CGPoint)position;

- (void)setButtonState:(Button)button isPressed:(BOOL)isPressed position:(CGPoint)position clickCount:(ClickCount)clickCount;
- (void)mouseClick:(Button)button position:(CGPoint)position;
- (void)click:(Button)button;
- (void)mouseDoubleClick:(Button)button position:(CGPoint)position;
- (void)doubleClick:(Button)button;
- (void)mouseDrag:(CGPoint)position;
- (void)mouseStartDrag:(CGPoint)position;
- (void)mouseEndDrag:(CGPoint)position;

@end

@implementation LLMAMouseSimulator

- (instancetype)init {
    self = [super init];
    if (self) {
        self.currentPosition = CGPointMake(0, 0);
    }
    return self;
}

- (void)dispatchMouseEvent:(CGMouseButton)button type:(EventType)type position:(CGPoint)position clickCount:(ClickCount)clickCount delay:(NSTimeInterval)delay {
    if (type == kEventMouseMoved || type == kEventLeftMouseDragged) {
        [NSThread sleepForTimeInterval:delay];
    }
    
    CGEventRef event = CGEventCreateMouseEvent(kCFAllocatorDefault, type, position, button);
    CGEventSetType(event, type);
    CGEventSetIntegerValueField(event, kCGMouseEventNumber, [self getEventNumber]);
    CGEventSetIntegerValueField(event, kCGMouseEventClickState, (NSInteger)clickCount);
    CGEventPost(kCGHIDEventTap, event);
    CFRelease(event);
    
    self.currentPosition = position;
}

- (CGPoint)getMousePosition {
    CGEventRef event = CGEventCreate(NULL);
    CGPoint cursor = CGEventGetLocation(event);
    CFRelease(event);
    return cursor;
}

- (void)setMousePosition:(CGPoint)position {
    [self dispatchMouseEvent:kCGMouseButtonLeft type:kEventMouseMoved position:position clickCount:kSingleClick delay:0];
}

- (void)setButtonState:(Button)button isPressed:(BOOL)isPressed position:(CGPoint)position clickCount:(ClickCount)clickCount {
    EventType type = isPressed? (button == kButtonLeft? kEventLeftMouseDown : kEventRightMouseDown) : (button == kButtonLeft? kEventLeftMouseUp : kEventRightMouseUp);
    [self dispatchMouseEvent:kCGMouseButtonLeft type:type position:position clickCount:clickCount delay:0];
}

- (void)mouseClick:(Button)button position:(CGPoint)position {
    [self setButtonState:button isPressed:YES position:position clickCount:kSingleClick];
    [self setButtonState:button isPressed:NO position:position clickCount:kSingleClick];
}

- (void)click:(Button)button {
    CGPoint position = [self getMousePosition];
    [self mouseClick:button position:position];
}

- (void)mouseDoubleClick:(Button)button position:(CGPoint)position {
    [self setButtonState:button isPressed:YES position:position clickCount:kDoubleClick];
    [self setButtonState:button isPressed:NO position:position clickCount:kDoubleClick];
}

- (void)doubleClick:(Button)button {
    CGPoint position = [self getMousePosition];
    [self mouseDoubleClick:button position:position];
}

- (void)mouseDrag:(CGPoint)position {
    [self dispatchMouseEvent:kCGMouseButtonLeft type:kEventLeftMouseDragged position:position clickCount:kSingleClick delay:0];
}

- (void)mouseStartDrag:(CGPoint)position {
    [self setButtonState:kButtonLeft isPressed:YES position:position clickCount:kSingleClick];
    [self mouseDrag:position];
}

- (void)mouseEndDrag:(CGPoint)position {
    [self mouseDrag:position];
    [self setButtonState:kButtonLeft isPressed:NO position:position clickCount:kSingleClick];
}

- (NSInteger)getEventNumber {
    static NSInteger eventNumber = 32000;
    return eventNumber++;
}

@end

// Usage:
LLMAMouseSimulator* simulator = [[LLMAMouseSimulator alloc] init];
[simulator mouseClick:kButtonLeft position:CGPointMake(100, 100)];
[simulator mouseDoubleClick:kButtonRight position:CGPointMake(200, 200)];
[simulator mouseStartDrag:CGPointMake(300, 300)];
[simulator mouseEndDrag:CGPointMake(300, 300)];

Code Breakdown

Includes and Enum Definition

The code starts by including two frameworks:

It then defines an enum Button to represent the two mouse buttons:

Variables and Main Function

The code declares two global variables:

The main function initializes previous_event_ to kCGEventNull.

DispatchMouseEvent Function

This function simulates a mouse event:

  1. If the previous event was a mouse move or drag event, it waits for the specified delay using usleep.
  2. It increments the event number and creates a new mouse event using CGEventCreateMouseEvent.
  3. It sets the event type, button, position, and click count using CGEventSetType, CGEventSetIntegerValueField, and CGEventSetIntegerValueField.
  4. It posts the event using CGEventPost and releases the event using CFRelease.
  5. It updates previous_event_ with the type of the current event and waits for the specified delay using usleep.

GetMousePosition Function

This function returns the current mouse position:

  1. It waits for 200 milliseconds using usleep.
  2. It creates a new event using CGEventCreate and gets the mouse position using CGEventGetLocation.
  3. It releases the event using CFRelease and returns the mouse position.

SetMousePosition and SetPosition Functions

These functions set the mouse position to the specified coordinates:

SetButtonState Function

This function sets the state of a mouse button:

Note that this is a C++ code, but it uses C-style includes and functions, suggesting that it may be a wrapper around C code.