Skip to content

Commit 0d4bb0a

Browse files
committed
NSEvent 대신 CGEvent로 마우스 클릭 감지
Sublime Merge 등 조합 중 클릭하면 입력하던 글자가 유실되는 문제 해결 c93d638 3450c50 참고
1 parent 978932f commit 0d4bb0a

File tree

2 files changed

+90
-5
lines changed

2 files changed

+90
-5
lines changed

SokIM/AppDelegate.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2222

2323
let statusBar = StatusBar()
2424
let inputMonitor = InputMonitor()
25+
let clickMonitor = ClickMonitor()
2526

2627
private var eventHandlerRef: EventHandlerRef?
2728
private var eventHotKeyRef: EventHotKeyRef?
@@ -42,11 +43,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
4243
object: nil
4344
)
4445

45-
// 사용자가 마우스 클릭하는 시점에 초기화
46-
NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDown.union(.rightMouseDown).union(.otherMouseDown)) { _ in
47-
self.reset()
48-
}
49-
5046
// 사용자의 한/A 전환키 조합을 시스템에 등록, 더미 함수 호출
5147
var eventSpec = EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed))
5248
InstallEventHandler(GetApplicationEventTarget(), eventHotKeyHandler, 1, &eventSpec, nil, &eventHandlerRef)
@@ -105,6 +101,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
105101
do {
106102
notice("모니터 시작 중...")
107103
try inputMonitor.start()
104+
try clickMonitor.start()
108105
statusBar.setStatus("⌨️")
109106
statusBar.removeMessage()
110107
} catch {
@@ -122,6 +119,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
122119
notice("모니터 재시작 중...")
123120
inputMonitor.stop()
124121
try inputMonitor.start()
122+
clickMonitor.stop()
123+
try clickMonitor.start()
125124
} catch {
126125
warning("\(error)")
127126
self.perform(#selector(restartMonitorSilently), with: aNotification, afterDelay: 1)

SokIM/ClickMonitor.swift

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import QuartzCore
2+
3+
enum ClickMonitorError: Error, CustomStringConvertible {
4+
case failedToCreateTap
5+
case failedToCreateSource
6+
7+
var description: String {
8+
switch self {
9+
case .failedToCreateTap:
10+
"알 수 없는 오류가 발생했습니다. (tap)"
11+
case .failedToCreateSource:
12+
"알 수 없는 오류가 발생했습니다. (source)"
13+
}
14+
}
15+
}
16+
17+
/**
18+
마우스 클릭 모니터링
19+
@see https://github.com/pqrs-org/Karabiner-Elements/blob/main/DEVELOPMENT.md
20+
@see https://github.com/pqrs-org/Karabiner-Elements/blob/main/src/share/monitor/event_tap_monitor.hpp
21+
*/
22+
class ClickMonitor {
23+
private var tap: CFMachPort?
24+
private var source: CFRunLoopSource?
25+
26+
func start() throws {
27+
debug()
28+
29+
if tap != nil || source != nil {
30+
warning("초기화된 tap 또는 source가 이미 있음")
31+
return
32+
}
33+
34+
let tap = CGEvent.tapCreate(
35+
tap: .cghidEventTap,
36+
place: .headInsertEventTap,
37+
options: .listenOnly,
38+
eventsOfInterest: CGEventMask(
39+
1 << CGEventType.leftMouseDown.rawValue
40+
| 1 << CGEventType.rightMouseDown.rawValue
41+
| 1 << CGEventType.otherMouseDown.rawValue),
42+
callback: { _, _, event, _ in
43+
// 사용자가 마우스 클릭하는 시점에 초기화
44+
AppDelegate.shared().reset()
45+
return Unmanaged.passUnretained(event)
46+
},
47+
userInfo: nil
48+
)
49+
guard let tap else {
50+
throw ClickMonitorError.failedToCreateTap
51+
}
52+
53+
let source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0)
54+
guard let source else {
55+
throw ClickMonitorError.failedToCreateSource
56+
}
57+
58+
CFRunLoopAddSource(CFRunLoopGetMain(), source, .defaultMode)
59+
CGEvent.tapEnable(tap: tap, enable: true)
60+
61+
self.tap = tap
62+
self.source = source
63+
debug("ClickMonitor 시작 성공")
64+
}
65+
66+
func stop() {
67+
debug()
68+
69+
if let tap {
70+
CGEvent.tapEnable(tap: tap, enable: false)
71+
CFMachPortInvalidate(tap)
72+
} else {
73+
warning("초기화된 tap이 없음")
74+
}
75+
76+
if let source {
77+
CFRunLoopSourceInvalidate(source)
78+
} else {
79+
warning("초기화된 source가 없음")
80+
}
81+
82+
self.tap = nil
83+
self.source = nil
84+
debug("ClickMonitor 중단 성공")
85+
}
86+
}

0 commit comments

Comments
 (0)