2

The purpose of this application is to run in the background 24/7 and lock the mouse in the center of the screen. It's for work with a series of flash programs to simulate joystick-style movement for the mouse. I've already attempted to use other methods built into Cocoa/Quartz in order to accomplish this, and none of them worked for my purpose, so this is the way I have to do it.

I have been trying to figure out why, after a seemingly random amount of time, this program simply stops restricting the mouse. The program doesn't give an error or anything like that, it just stops working. The force-quit screen DOES say "Not Responding", however, many of my mouse modifying scripts, including this one, always read as "not responding" and they keep functioning.

Here's the code:

code removed, check below for updated code.

Final Update

Ken Thomases gave me the right answer, I've updated my code based on his suggestions. Here's the final code that I've gotten to work flawlessly (this ran for 12+ hours without a hitch before I manually stopped it):

#import <Cocoa/Cocoa.h>
#import <CoreMedia/CoreMedia.h>

int screen_width, screen_height;

struct event_tap_data_struct {
    CFMachPortRef event_tap;
    float speed_modifier;    
};

CGEventRef 
mouse_filter(CGEventTapProxy proxy, CGEventType type, CGEventRef event, struct event_tap_data_struct *);



int
screen_res(int);


int
main(int argc, char *argv[]) {



    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


    screen_width = screen_res(0);
    screen_height = screen_res(1);
    CFRunLoopSourceRef runLoopSource;
    CGEventMask event_mask = kCGEventMaskForAllEvents;

    CGSetLocalEventsSuppressionInterval(0);
    CFMachPortRef eventTap;
    struct event_tap_data_struct event_tap_data = {eventTap,0.2};

    eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0, event_mask, mouse_filter, &event_tap_data);
    event_tap_data.event_tap = eventTap;
    if (!eventTap) {
        NSLog(@"Couldn't create event tap!");
        exit(1); 
    }

    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, event_tap_data.event_tap, 0);

    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);

    CGEventTapEnable(event_tap_data.event_tap, true);

    CFRunLoopRun();

    CFRelease(eventTap);
    CFRelease(runLoopSource);
    [pool release];

    exit(0);
}

int
screen_res(int width_or_height) {

    NSRect screenRect;
    NSArray *screenArray = [NSScreen screens];
    unsigned screenCount = (unsigned)[screenArray count];


    for (unsigned index  = 0; index < screenCount; index++)
    {
        NSScreen *screen = [screenArray objectAtIndex: index];
        screenRect = [screen visibleFrame];
    }
    int resolution_array[] = {(int)CGDisplayPixelsWide(CGMainDisplayID()),(int)CGDisplayPixelsHigh(CGMainDisplayID())};

    if(width_or_height==0){
        return resolution_array[0];
    }else {
        return resolution_array[1];

    }

}


CGEventRef 
mouse_filter(CGEventTapProxy proxy, CGEventType type, CGEventRef event, struct event_tap_data_struct *event_tap_data) {


    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    if (type == kCGEventTapDisabledByTimeout || type == kCGEventTapDisabledByUserInput) {

        CGEventTapEnable(event_tap_data->event_tap,true);
        return event;

    } else if (type == kCGEventMouseMoved || type == kCGEventLeftMouseDragged || type == kCGEventRightMouseDragged || type == kCGEventOtherMouseDragged){

    CGPoint point = CGEventGetLocation(event);
    NSPoint old_point;

    CGPoint target; 
    int tX = point.x; 
    int tY = point.y; 
    float oX = screen_width/2;
    float oY = screen_height/2;
    float dX = tX-oX;
    float dY = tY-oY;

    old_point.x = floor(oX); old_point.y = floor(oY);

    dX*=2, dY*=2;

    tX = round(oX + dX);
    tY = round(oY + dY);


    target = CGPointMake(tX, tY);


    CGWarpMouseCursorPosition(old_point);
    CGEventSetLocation(event,target);

    }

    [pool release];

    return event;
}

(first) Update:

The program is still crashing, but I have now run it as an executable and received an error code.

When it terminates, the console logs "Segmentation Fault: 11".

I've been trying to discover what this means, however it appears to be an impressively broad term, and I've yet to hone in on something useful.

Here is the new code I am using:

#import <Cocoa/Cocoa.h>
#import <CoreMedia/CoreMedia.h>

int screen_width, screen_height;

struct event_tap_data_struct {
    CFMachPortRef event_tap;
    float speed_modifier;    
};

CGEventRef 
mouse_filter(CGEventTapProxy proxy, CGEventType type, CGEventRef event, struct event_tap_data_struct *);



int
screen_res(int);


int
main(int argc, char *argv[]) {


    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


    screen_width = screen_res(0);
    screen_height = screen_res(1);
    CFRunLoopSourceRef runLoopSource;
    CGEventMask event_mask;
    event_mask = CGEventMaskBit(kCGEventMouseMoved) | CGEventMaskBit(kCGEventLeftMouseDragged) | CGEventMaskBit(kCGEventRightMouseDragged) | CGEventMaskBit(kCGEventOtherMouseDragged);

    CGSetLocalEventsSuppressionInterval(0);
    CFMachPortRef eventTap;
    CFMachPortRef *eventTapPtr = &eventTap;
    struct event_tap_data_struct event_tap_data = {*eventTapPtr,0.2};

    eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0, event_mask, mouse_filter, &event_tap_data);

    if (!eventTap) {
        NSLog(@"Couldn't create event tap!");
        exit(1);
    }

    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);

    CGEventTapEnable(eventTap, true);

    CFRunLoopRun();

    CFRelease(eventTap);
    CFRelease(runLoopSource);
    [pool release];

    exit(0);
}

int
screen_res(int width_or_height) {

    NSRect screenRect;
    NSArray *screenArray = [NSScreen screens];
    unsigned screenCount = (unsigned)[screenArray count];


    for (unsigned index  = 0; index < screenCount; index++)
    {
        NSScreen *screen = [screenArray objectAtIndex: index];
        screenRect = [screen visibleFrame];
    }
    int resolution_array[] = {(int)CGDisplayPixelsWide(CGMainDisplayID()),(int)CGDisplayPixelsHigh(CGMainDisplayID())};

    if(width_or_height==0){
        return resolution_array[0];
    }else {
        return resolution_array[1];

    }

}


CGEventRef 
mouse_filter(CGEventTapProxy proxy, CGEventType type, CGEventRef event, struct event_tap_data_struct *event_tap_data) {


    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    if (type == kCGEventTapDisabledByTimeout || type == kCGEventTapDisabledByUserInput) {

        CGEventTapEnable(event_tap_data->event_tap,true);

    }

    CGPoint point = CGEventGetLocation(event);
    NSPoint old_point;

    CGPoint target; 
    int tX = point.x; 
    int tY = point.y; 
    float oX = screen_width/2;
    float oY = screen_height/2;
    float dX = tX-oX;
    float dY = tY-oY;

    old_point.x = floor(oX); old_point.y = floor(oY);

    dX*=2, dY*=2;

    tX = round(oX + dX);
    tY = round(oY + dY);


    target = CGPointMake(tX, tY);


    CGWarpMouseCursorPosition(old_point);
    CGEventSetLocation(event,target);


    [pool release];

    return event;
}
BumbleShrimp
  • 2,150
  • 23
  • 42
  • It would help if you ran the program with GDB attached, so you could get a backtrace when you encounter the segmentation fault issue. – Taum Apr 29 '12 at 00:46
  • @Taum I attached GDB to the process and when the process crashed, GDB simply said segmentation fault 11, process complete. Did I do it wrong? – BumbleShrimp Apr 29 '12 at 01:39
  • (I found the process ID number in the process list, opened a terminal window and wrote `$ gdb attach 588`.) – BumbleShrimp Apr 29 '12 at 01:56

1 Answers1

2

You need to re-enable your event tap when it receives kCGEventTapDisabledByTimeout or kCGEventTapDisabledByUserInput.


Update: here are your lines and how they're (failing to) work:

CFMachPortRef eventTap; // uninitialized value
CFMachPortRef *eventTapPtr = &eventTap; // pointer to eventTap
struct event_tap_data_struct event_tap_data = {*eventTapPtr,0.2}; // dereferences pointer, copying uninitialized value into struct

eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0, event_mask, mouse_filter, &event_tap_data); // sets eventTap but has no effect on event_tap_data
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • I will try this, but I've got other mouse modifying applications that utilize this same event tap code, and they don't have any issues. – BumbleShrimp Apr 28 '12 at 17:30
  • I can't seem to figure out how to reference the event tap itself from within the event tap's callback. I have this code: `if (type == kCGEventTapDisabledByTimeout || type == kCGEventTapDisabledByUserInput) { CGEventTapEnable(eventTap, true); return event; }` but eventTap is not defined in the scope of the callback. How do I access it? – BumbleShrimp Apr 28 '12 at 18:36
  • 1
    You can use the `refcon` parameter to pass arbitrary data from the point where the event tap is created to the callback. Now, you don't have the Mach port yet, but you can pass a pointer to a variable that you'll fill in. You're current using it to pass your `speed_modifier`, but you can put both things into a struct and pass the address of that instead. – Ken Thomases Apr 28 '12 at 19:26
  • I thought that might be how I should do it, but I'm very new to this and I kept second guessing myself. Thank you for coming back and answering my second question - even though you shouldn't have had to inform me of a basic technique I should already know. – BumbleShrimp Apr 28 '12 at 19:31
  • I have posted updated code in the question above, utilizing the technique you just described. I've also posted more information about the problem. – BumbleShrimp Apr 29 '12 at 00:43
  • You either need to store the value of `eventTap` into the `event_tap_data` struct after `CGEventTapCreate()` returns or you need to store a pointer to a `CFMachPortRef` in the struct. What you're doing is a little dance that amounts to copying the uninitialized value of `eventTap` into the struct. – Ken Thomases Apr 29 '12 at 01:15