Ошибка сегментации 11 | Приложение CGEventTap прекращает обработку событий мыши по истечении произвольного промежутка времени.

Цель этого приложения - работать в фоновом режиме 24/7 и блокировать мышь в центре экрана. Он предназначен для работы с серией флэш-программ для имитации движения мыши в стиле джойстика. Я уже пытался использовать другие методы, встроенные в Cocoa/Quartz, чтобы добиться этого, и ни один из них не работал для моей цели, так что я должен это сделать.

Я пытался понять, почему после, казалось бы, случайного промежутка времени эта программа просто перестает ограничивать мышь. Программа не выдает ошибку или что-то в этом роде, она просто перестает работать. На экране принудительного выхода ДЕЙСТВИТЕЛЬНО написано «Не отвечает», однако многие из моих сценариев изменения мыши, включая этот, всегда читаются как «не отвечает» и продолжают работать.

Вот код:

code removed, check below for updated code.

Окончательное обновление

Кен Томас дал мне правильный ответ, я обновил свой код на основе его предложений. Вот окончательный код, который у меня работает безупречно (он работал более 12 часов без сучка и задоринки, прежде чем я вручную остановил его):

#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;
}

(первое) обновление:

Программа все еще дает сбой, но теперь я запустил ее как исполняемый файл и получил код ошибки.

Когда он завершается, консоль регистрирует «Ошибка сегментации: 11».

Я пытался выяснить, что это значит, однако это, кажется, впечатляюще широкий термин, и мне еще предстоит отточить что-то полезное.

Вот новый код, который я использую:

#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;
}

person BumbleShrimp    schedule 28.04.2012    source источник
comment
Было бы полезно, если бы вы запускали программу с подключенным GDB, чтобы вы могли получить обратную трассировку, когда столкнетесь с проблемой ошибки сегментации.   -  person Taum    schedule 29.04.2012
comment
@Taum Я подключил GDB к процессу, и когда процесс рухнул, GDB просто сказал, что ошибка сегментации 11, процесс завершен. Я сделал это неправильно?   -  person BumbleShrimp    schedule 29.04.2012
comment
(Я нашел идентификатор процесса в списке процессов, открыл окно терминала и написал $ gdb attach 588.)   -  person BumbleShrimp    schedule 29.04.2012


Ответы (1)


Вам нужно повторно активировать касание события, когда оно получает kCGEventTapDisabledByTimeout или kCGEventTapDisabledByUserInput.


Обновление: вот ваши строки и то, как они (не работают):

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
person Ken Thomases    schedule 28.04.2012
comment
Я попробую это, но у меня есть другие приложения для изменения мыши, которые используют тот же код касания события, и у них нет никаких проблем. - person BumbleShrimp; 28.04.2012
comment
Кажется, я не могу понять, как ссылаться на само касание события из обратного вызова касания события. У меня есть этот код: if (type == kCGEventTapDisabledByTimeout || type == kCGEventTapDisabledByUserInput) { CGEventTapEnable(eventTap, true); return event; }, но eventTap не определен в области обратного вызова. Как получить к нему доступ? - person BumbleShrimp; 28.04.2012
comment
Вы можете использовать параметр refcon для передачи произвольных данных из точки, где создается отвод события, в обратный вызов. Теперь у вас еще нет порта Mach, но вы можете передать указатель на переменную, которую вы будете заполнять. В настоящее время вы используете его для передачи вашего speed_modifier, но вы можете поместить обе вещи в структуру и передать вместо этого адрес. - person Ken Thomases; 28.04.2012
comment
Я подумал, что, может быть, я должен это сделать, но я очень новичок в этом, и я продолжал сомневаться в себе. Спасибо, что вернулись и ответили на мой второй вопрос, хотя вам не нужно было информировать меня об основной технике, которую я уже должен был знать. - person BumbleShrimp; 28.04.2012
comment
Я разместил обновленный код в вопросе выше, используя технику, которую вы только что описали. Я также разместил дополнительную информацию о проблеме. - person BumbleShrimp; 29.04.2012
comment
Вам либо нужно сохранить значение eventTap в структуре event_tap_data после возврата CGEventTapCreate(), либо вам нужно сохранить указатель на CFMachPortRef в структуре. То, что вы делаете, это небольшой танец, который сводится к копированию неинициализированного значения eventTap в структуру. - person Ken Thomases; 29.04.2012