web-dev-qa-db-ja.com

M4 / M5の代わりにOSXのユニバーサル戻る/進むマウスボタン?

LogitechMXマスターマウスの2つのサイドボタンの動作に困惑しています。他のすべてのマウスでは、サイドボタンは一般的な「ボタン4」および「ボタン5」として検出されます。 (Xcodeを使用してこれを確認しました。)OS Xは、Windowsとは対照的に、これらのコマンドをミドルクリックとして処理するようです。したがって、期待する前後の動作を得るには、USBオーバードライブなどのツールを使用してマップする必要があります。それらへ +[ そして +]。残念ながら、この回避策はすべてのアプリで機能するとは限らず、トリガーするとメニューバーが点滅します。

一方、マスターのサイドボタンは、Xcodeのマウスタップではまったく検出されませんが、ナビゲーションバーを備えた実質的にすべてのアプリで機能します。 Finder、Safari、システム環境設定、Xcodeでテストしました。メニューの点滅はなく、マウスカーソルは、ナビゲーションバーによって制御されるウィンドウの領域上にある必要があります。これは、(通常のM4/M5とは対照的に)何らかのユニバーサルな戻る/進むイベントが送信されることを意味します。ただし、OSXに存在するそのようなイベントのドキュメントが見つかりません。ほとんどのM4/M5修正には、これらのボタンのマッピングが含まれます。 +[ そして +]

では、マスターはこれらのサイドボタンで何をしているのでしょうか。また、他のすべてのマウスで同じ動作を再現するにはどうすればよいでしょうか。

6
Archagon

NSWindowイベントallにタップを追加しました。結局...マスターはスワイプイベントをシミュレートしています!

NSEvent: type=Swipe loc=(252,60) time=5443.9 flags=0x100 win=0x100b091f0 winNum=2014 ctxt=0x0 phase: 1 axis:0 amount=0.000 velocity={0, 0}
NSEvent: type=Swipe loc=(252,60) time=5443.9 flags=0x100 win=0x100b091f0 winNum=2014 ctxt=0x0 phase: 8 axis:0 amount=0.000 velocity={0, 0}
NSEvent: type=Swipe loc=(252,60) time=5445.7 flags=0x100 win=0x100b091f0 winNum=2014 ctxt=0x0 phase: 1 axis:0 amount=0.000 velocity={0, 0}
NSEvent: type=Swipe loc=(252,60) time=5445.7 flags=0x100 win=0x100b091f0 winNum=2014 ctxt=0x0 phase: 8 axis:0 amount=0.000 velocity={0, 0}

OK、それはかなり賢いです。基本的には、swipeWithEvent:セレクターをサポートするすべてのビューで機能することを意味します。これがサイドボタンのデフォルトの動作ではない理由がわかりません。次に、この機能を他のマウスに追加する方法を理解する必要があります。 AppleScriptにジェスチャーをシミュレートする方法がない限り、USBオーバードライブでこのようなことができるとは思いません。

UPDATE:natevw のリバースエンジニアリングされたジェスチャシミュレーション関数を使用して、これらのイベントを複製することができました https ://github.com/calftrail/Touch 。まだ少し修正する必要があるかもしれませんが、それは機能します!最後のステップは、M4およびM5イベントを食べ、これらのジェスチャーを吐き出す、常に実行されるアプリを作成することです。

TLInfoSwipeDirection dir = kTLInfoSwipeLeft;

NSDictionary* swipeInfo1 = [NSDictionary dictionaryWithObjectsAndKeys:
                            @(kTLInfoSubtypeSwipe), kTLInfoKeyGestureSubtype,
                            @(1), kTLInfoKeyGesturePhase,
                            nil];

NSDictionary* swipeInfo2 = [NSDictionary dictionaryWithObjectsAndKeys:
                            @(kTLInfoSubtypeSwipe), kTLInfoKeyGestureSubtype,
                            @(dir), kTLInfoKeySwipeDirection,
                            @(4), kTLInfoKeyGesturePhase,
                            nil];

CGEventRef event1 = tl_CGEventCreateFromGesture((__bridge CFDictionaryRef)(swipeInfo1), (__bridge CFArrayRef)@[]);
CGEventRef event2 = tl_CGEventCreateFromGesture((__bridge CFDictionaryRef)(swipeInfo2), (__bridge CFArrayRef)@[]);

CGEventPost(kCGHIDEventTap, event1);
CGEventPost(kCGHIDEventTap, event2);

// not sure if necessary under ARC
CFRelease(event1);
CFRelease(event2);

UPDATE 2:これは、M4とM5をグローバルにキャプチャし、スワイプを発行するViewControllerの大まかなスケッチです。

static void SBFFakeSwipe(TLInfoSwipeDirection dir) {
        NSDictionary* swipeInfo1 = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @(kTLInfoSubtypeSwipe), kTLInfoKeyGestureSubtype,
                                    @(1), kTLInfoKeyGesturePhase,
                                    nil];

        NSDictionary* swipeInfo2 = [NSDictionary dictionaryWithObjectsAndKeys:
                                    @(kTLInfoSubtypeSwipe), kTLInfoKeyGestureSubtype,
                                    @(dir), kTLInfoKeySwipeDirection,
                                    @(4), kTLInfoKeyGesturePhase,
                                    nil];

        CGEventRef event1 = tl_CGEventCreateFromGesture((__bridge CFDictionaryRef)(swipeInfo1), (__bridge CFArrayRef)@[]);
        CGEventRef event2 = tl_CGEventCreateFromGesture((__bridge CFDictionaryRef)(swipeInfo2), (__bridge CFArrayRef)@[]);

        CGEventPost(kCGHIDEventTap, event1);
        CGEventPost(kCGHIDEventTap, event2);

        CFRelease(event1);
        CFRelease(event2);
}

static CGEventRef KeyDownCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    int64_t number = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber);
    BOOL down = (CGEventGetType(event) == kCGEventOtherMouseDown);

    if (number == 3) {
        if (down) {
            SBFFakeSwipe(kTLInfoSwipeLeft);
        }

        return NULL;
    }
    else if (number == 4) {
        if (down) {
            SBFFakeSwipe(kTLInfoSwipeRight);
        }

        return NULL;
    }
    else {
        return event;
    }
}

@implementation ViewController

-(void) viewDidLoad {
    [super viewDidLoad];

    NSDictionary* options = @{ (__bridge id)kAXTrustedCheckOptionPrompt: @YES };
    BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);

    assert(accessibilityEnabled);

    CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap,
                                              kCGHeadInsertEventTap,
                                              kCGEventTapOptionDefault,
                                              CGEventMaskBit(kCGEventOtherMouseUp)|CGEventMaskBit(kCGEventOtherMouseDown),
                                              &KeyDownCallback,
                                              NULL);

    assert(eventTap != NULL);

    CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(NULL, eventTap, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
    CFRelease(runLoopSource);

    CGEventTapEnable(eventTap, true);

    //CFRelease(eventTap); -- needs to be done on dealloc, I think
}

@end

更新3:すべてのサードパーティマウスのマスターの動作を複製するオープンソースのメニューバーアプリをリリースしました。それは SensibleSideButtons と呼ばれます。技術的な詳細はウェブサイトに記載されています。

7
Archagon