我对我的 Logitech MX Master 鼠标上两个侧键的行为感到困惑。在我所有的其他鼠标上,侧键被检测为通用的“按钮 4”和“按钮 5”。(我使用 Xcode 验证了这一点。)与 Windows 不同,OS X 似乎将这些命令视为鼠标中键单击,因此为了获得您可能期望的后退/前进行为,您需要使用 USB Overdrive 之类的工具将它们映射到⌘+[和⌘+ ]。不幸的是,这种解决方法并不适用于每个应用程序,当您触发它时,菜单栏会闪烁。
同时,我的 Xcode 鼠标点击根本检测不到 Master 上的侧边按钮,但它们在几乎所有带有导航栏的应用程序中都能正常工作。我在 Finder、Safari、系统偏好设置和 Xcode 中测试了它们。没有菜单闪烁,鼠标光标必须位于导航栏控制的窗口区域上方,这意味着发送了某种通用的后退/前进事件(与通常的 M4/M5 相反)。但是,我找不到 OS X 中存在此类事件的文档。大多数 M4/M5 修复都涉及将这些按钮映射到⌘+[和⌘+ ]。
那么 Master 用这些侧面按钮做什么呢?我如何在我所有的其他鼠标上复制相同的行为?
答案1
VS Code 现在支持Go Back
和Go Forward
操作,包括默认键绑定分别为CTRL+-和CTRL++ SHIFT。-
我可以配置 Logi Options 以使用这些快捷方式,而不会破坏其他应用程序,方法是:
All Applications
通过->Add Application
->添加 VS Code 配置文件Other...
,然后搜索“Visual Studio Code”- 将后退和前进按钮重新映射到
Keystroke assignment
,其键序列与 VS Code 键绑定相匹配
选择了配置文件后,后退按钮配置如下所示:
经过这些更改后,前进和后退按钮可以在 VS Code 中使用,并且其他应用(如 Chrome)中的导航不受影响。
答案2
我添加了一个水龙头全部我的NSWindow
事件。结果……Master 正在模拟滑动事件!
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}
好的,这很聪明,因为它基本上意味着它可以在任何支持swipeWithEvent:
选择器的视图中工作。我不知道为什么这不是侧边按钮的默认行为!现在我必须弄清楚如何将此功能添加到我的其他鼠标中。我不认为 USB Overdrive 可以做这样的事情……除非 AppleScript 有办法模拟手势。
更新:我已经设法使用复制这些事件娜泰夫逆向工程的手势模拟功能,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);
更新2:下面是一个视图控制器的粗略工作草图,它可以全局捕获 M4 和 M5 并发出滑动。
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:我发布了一款开源菜单栏应用,它可以为所有第三方鼠标复制 Master 的行为。它叫做SensibleSideButtons。技术细节已在网站上描述。