Как симулировать перетаскивание мышью на MacOS? - PullRequest
0 голосов
/ 01 июля 2019

Я пытаюсь переназначить свой LeftMouse в CTRL, используя:

CGPoint mouseFlip( CGPoint pt )  {
    return CGPointMake(pt.x, [NSScreen mainScreen].frame.size.height - pt.y);
}

_eventTap = CGEventTapCreate(
                              kCGHIDEventTap,
                              kCGHeadInsertEventTap,
                              kCGEventTapOptionDefault,
                              CGEventMaskBit(NSEventTypeMouseMoved) | CGEventMaskBit(NSEventTypeFlagsChanged),
                              (CGEventTapCallBack)_tapCallback,
                              (__bridge void *)(self)
                              );
:

CGEventRef _tapCallback(
                        CGEventTapProxy proxy,
                        CGEventType     type,
                        CGEventRef      event,
                        Intercept*     listener
                        )
{
    //Do not make the NSEvent here.
    //NSEvent will throw an exception if we try to make an event from the tap timout type
    @autoreleasepool {
        if( type == kCGEventTapDisabledByTimeout ) {
            NSLog(@"event tap has timed out, re-enabling tap");
            [listener tapEvents];
            return nil;
        }
        if( type != kCGEventTapDisabledByUserInput ) {
            return [listener processEvent:event];
        }
    }
    return event;
}

- (CGEventRef)processEvent:(CGEventRef)cgEvent
{
    NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];

    switch( event.type ) {

         case NSEventTypeMouseMoved:

                CGPoint prev_mouse = [NSEvent mouseLocation];

                int64_t dx = CGEventGetIntegerValueField(cgEvent, kCGMouseEventDeltaX);
                int64_t dy = CGEventGetIntegerValueField(cgEvent, kCGMouseEventDeltaY);

                CGPoint new_mouse = {
                    prev_mouse.x + dx,
                    prev_mouse.y - dy

                };

                :
                /* awkward multi-screen bounds checking here */
                :
                CGEventRef dragEvent = CGEventCreateMouseEvent(
                                                               NULL,
                                                               kCGEventLeftMouseDragged,                mouseFlip(new_mouse),
                                                               0
                                                               );
                CGEventPost(kCGHIDEventTap, dragEvent);
                CFRelease(dragEvent);
                break;    
                :

По большей части это работает. Однако часто я замечаю скольжение / скольжение мыши, как хождение по льду. Есть небольшое ухудшение производительности.

Кто-нибудь захочет критиковать этот подход?

Интересно, есть ли небольшая проблема округления до целого в dx, dy. Или если события как-то теряются.

Может быть, небезопасно получить prev_mouse от [NSEvent mouseLocation]?

Это немного раздражает, что я не могу найти текущую позицию мыши. Я предполагаю, что [NSEvent mouseLocation] обновляется только ПОСЛЕ завершения NSEventTypeMouseMoved. Если бы я мог получить доступ только к новому текущему местоположению, я мог бы обойтись без /* awkward multi-screen bounds checking here */.

Альтернативным подходом будет периодический таймер, который делает:

timer = [NSTimer scheduledTimerWithTimeInterval: .01f
                                        repeats: YES
                                          block:
       ^(NSTimer *timer) {
           if( ! self->ctrl_is_down )
               return;

           CGEventRef dragEvent = CGEventCreateMouseEvent(
                                                          NULL,
                                                          kCGEventLeftMouseDragged,
                                                          mouseFlip( [NSEvent mouseLocation] ),
                                                          0
                                                          );
           CGEventPost(kCGHIDEventTap, dragEvent);
           CFRelease(dragEvent);
       }];

Может ли этот подход быть усовершенствован во что-то пригодное для использования?

Какой правильный способ сделать это?

(также размещено на форумах Apple Dev )

...