틸트할 때 함께 들어오는 클릭 막기
틸트 비트는 macOS에 4~7번 버튼으로도 전달되어 앱이 휠 클릭처럼 처리하거나, 휠을 옆으로 밀 때 휠 버튼이 같이 눌리는 경우가 있었다. 이벤트 탭에서 4~7번 버튼과 틸트 직후 0.15초 안의 휠 클릭을 버리고, 버린 버튼의 드래그·떼기도 함께 버린다. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ Razer Pro Click V2 (vendor 5426, product 209)의 휠 좌우 틸트를 각각 `Co
|
|||||||
|
|
||||||
마우스 휠의 세로 스크롤 방향도 반전합니다. 트랙패드와 Magic Mouse처럼 제스처로 스크롤하는 장치와 가로 스크롤은 바꾸지 않습니다.
|
마우스 휠의 세로 스크롤 방향도 반전합니다. 트랙패드와 Magic Mouse처럼 제스처로 스크롤하는 장치와 가로 스크롤은 바꾸지 않습니다.
|
||||||
|
|
||||||
|
틸트할 때 함께 들어오는 원래 버튼 이벤트(4~7번 버튼, 틸트 직후의 휠 클릭)는 막습니다. 측면 버튼도 4·5번 버튼이라 원래 동작 대신 뒤로/앞으로 가기로 동작합니다.
|
||||||
|
|
||||||
## 설치 / 업데이트
|
## 설치 / 업데이트
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
+36
-1
@@ -151,6 +151,39 @@ let accessTimer: Timer? = accessGranted() ? nil : {
|
|||||||
// Invert vertical mouse-wheel scrolling. Trackpads and Magic Mouse send gesture phases;
|
// Invert vertical mouse-wheel scrolling. Trackpads and Magic Mouse send gesture phases;
|
||||||
// wheels do not, even when their high-resolution scrolling is marked continuous.
|
// wheels do not, even when their high-resolution scrolling is marked continuous.
|
||||||
var scrollTap: CFMachPort?
|
var scrollTap: CFMachPort?
|
||||||
|
// Mouse buttons whose down event was swallowed, so the matching drag/up is swallowed too.
|
||||||
|
var suppressedButtons = Set<Int64>()
|
||||||
|
// CG button numbers (0-based) of the tilt bits 0x08/0x10/0x20/0x40, i.e. buttons 4-7.
|
||||||
|
let tiltButtonNumbers: ClosedRange<Int64> = 3...6
|
||||||
|
let middleButtonNumber: Int64 = 2
|
||||||
|
// A wheel click this close to a tilt is the wheel being pressed while it is pushed sideways.
|
||||||
|
let tiltClickWindow: TimeInterval = 0.15
|
||||||
|
|
||||||
|
func tiltActive() -> Bool {
|
||||||
|
if state.lastBits.values.contains(where: { $0 != 0 }) { return true }
|
||||||
|
let now = ProcessInfo.processInfo.systemUptime
|
||||||
|
return state.lastFired.values.contains { now - $0 < tiltClickWindow }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swallow the native button events behind a tilt: many apps treat buttons 4-7 like a
|
||||||
|
// middle click (e.g. open link in new tab), and tilting can also press the wheel button.
|
||||||
|
func shouldSuppress(_ type: CGEventType, _ event: CGEvent) -> Bool {
|
||||||
|
let button = event.getIntegerValueField(.mouseEventButtonNumber)
|
||||||
|
switch type {
|
||||||
|
case .otherMouseDown:
|
||||||
|
if tiltButtonNumbers.contains(button) || (button == middleButtonNumber && tiltActive()) {
|
||||||
|
suppressedButtons.insert(button)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
case .otherMouseDragged:
|
||||||
|
return suppressedButtons.contains(button)
|
||||||
|
case .otherMouseUp:
|
||||||
|
return suppressedButtons.remove(button) != nil
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
// Marks the replacement events this app posts so the tap lets them through untouched.
|
// Marks the replacement events this app posts so the tap lets them through untouched.
|
||||||
let invertedScrollMarker: Int64 = 0x52544D31
|
let invertedScrollMarker: Int64 = 0x52544D31
|
||||||
let scrollCallback: CGEventTapCallBack = { proxy, type, event, _ in
|
let scrollCallback: CGEventTapCallBack = { proxy, type, event, _ in
|
||||||
@@ -158,6 +191,7 @@ let scrollCallback: CGEventTapCallBack = { proxy, type, event, _ in
|
|||||||
if let tap = scrollTap { CGEvent.tapEnable(tap: tap, enable: true) }
|
if let tap = scrollTap { CGEvent.tapEnable(tap: tap, enable: true) }
|
||||||
return Unmanaged.passUnretained(event)
|
return Unmanaged.passUnretained(event)
|
||||||
}
|
}
|
||||||
|
if shouldSuppress(type, event) { return nil }
|
||||||
guard type == .scrollWheel,
|
guard type == .scrollWheel,
|
||||||
event.getIntegerValueField(.eventSourceUserData) != invertedScrollMarker
|
event.getIntegerValueField(.eventSourceUserData) != invertedScrollMarker
|
||||||
else { return Unmanaged.passUnretained(event) }
|
else { return Unmanaged.passUnretained(event) }
|
||||||
@@ -189,7 +223,8 @@ let scrollCallback: CGEventTapCallBack = { proxy, type, event, _ in
|
|||||||
}
|
}
|
||||||
if accessGranted() {
|
if accessGranted() {
|
||||||
scrollTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap,
|
scrollTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .headInsertEventTap, options: .defaultTap,
|
||||||
eventsOfInterest: CGEventMask(1 << CGEventType.scrollWheel.rawValue),
|
eventsOfInterest: [CGEventType.scrollWheel, .otherMouseDown, .otherMouseDragged, .otherMouseUp]
|
||||||
|
.reduce(CGEventMask(0)) { $0 | CGEventMask(1 << $1.rawValue) },
|
||||||
callback: scrollCallback, userInfo: nil)
|
callback: scrollCallback, userInfo: nil)
|
||||||
if let tap = scrollTap {
|
if let tap = scrollTap {
|
||||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0), .commonModes)
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0), .commonModes)
|
||||||
|
|||||||
Reference in New Issue
Block a user