node-mac-recorder 2.17.11 → 2.17.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/package.json +1 -1
- package/src/cursor_tracker.mm +134 -232
package/package.json
CHANGED
package/src/cursor_tracker.mm
CHANGED
|
@@ -35,9 +35,17 @@ NSDictionary* getDisplayScalingInfo(CGPoint globalPoint);
|
|
|
35
35
|
|
|
36
36
|
static CursorTimerTarget *g_timerTarget = nil;
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// Enhanced cursor state tracking with stability
|
|
39
39
|
static NSString *g_lastDetectedCursorType = nil;
|
|
40
|
+
static NSString *g_stableCursorType = @"default";
|
|
40
41
|
static int g_cursorTypeCounter = 0;
|
|
42
|
+
static NSTimeInterval g_lastCursorCheckTime = 0;
|
|
43
|
+
static int g_sameCursorDetectionCount = 0;
|
|
44
|
+
static NSString *g_pendingCursorType = nil;
|
|
45
|
+
|
|
46
|
+
// Cursor stability constants
|
|
47
|
+
static const NSTimeInterval CURSOR_STABILITY_THRESHOLD = 0.1; // 100ms
|
|
48
|
+
static const int CURSOR_CONFIRMATION_COUNT = 2; // Need 2 consecutive detections
|
|
41
49
|
|
|
42
50
|
// Mouse button state tracking
|
|
43
51
|
static bool g_leftMouseDown = false;
|
|
@@ -49,255 +57,145 @@ static CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|
|
49
57
|
return event;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
//
|
|
60
|
+
// Enhanced cursor type detection with multi-layer approach and stability
|
|
61
|
+
NSString* detectCursorTypeFromNSCursor() {
|
|
62
|
+
@try {
|
|
63
|
+
NSCursor *currentCursor = [NSCursor currentSystemCursor];
|
|
64
|
+
if (!currentCursor) {
|
|
65
|
+
return @"default";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Compare with known system cursors using identity comparison
|
|
69
|
+
if (currentCursor == [NSCursor arrowCursor]) {
|
|
70
|
+
return @"default";
|
|
71
|
+
} else if (currentCursor == [NSCursor IBeamCursor]) {
|
|
72
|
+
return @"text";
|
|
73
|
+
} else if (currentCursor == [NSCursor pointingHandCursor]) {
|
|
74
|
+
return @"pointer";
|
|
75
|
+
} else if (currentCursor == [NSCursor resizeLeftRightCursor]) {
|
|
76
|
+
return @"col-resize";
|
|
77
|
+
} else if (currentCursor == [NSCursor resizeUpDownCursor]) {
|
|
78
|
+
return @"ns-resize";
|
|
79
|
+
} else if (currentCursor == [NSCursor crosshairCursor]) {
|
|
80
|
+
return @"crosshair";
|
|
81
|
+
} else if (currentCursor == [NSCursor openHandCursor]) {
|
|
82
|
+
return @"grab";
|
|
83
|
+
} else if (currentCursor == [NSCursor closedHandCursor]) {
|
|
84
|
+
return @"grabbing";
|
|
85
|
+
} else if (currentCursor == [NSCursor operationNotAllowedCursor]) {
|
|
86
|
+
return @"not-allowed";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Fallback to image-based comparison for custom cursors
|
|
90
|
+
NSImage *cursorImage = [currentCursor image];
|
|
91
|
+
if (cursorImage) {
|
|
92
|
+
NSSize imageSize = [cursorImage size];
|
|
93
|
+
|
|
94
|
+
// Text cursors typically have I-beam shape (narrow width)
|
|
95
|
+
if (imageSize.width < 8 && imageSize.height > 15) {
|
|
96
|
+
return @"text";
|
|
97
|
+
}
|
|
98
|
+
// Pointer cursors are typically hand-shaped
|
|
99
|
+
else if (imageSize.width > 15 && imageSize.height > 15) {
|
|
100
|
+
return @"pointer";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return @"default";
|
|
105
|
+
} @catch (NSException *exception) {
|
|
106
|
+
return @"default";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Improved cursor type detection with stability and multi-layer approach
|
|
53
111
|
NSString* getCursorType() {
|
|
54
112
|
@autoreleasepool {
|
|
55
113
|
g_cursorTypeCounter++;
|
|
56
|
-
|
|
57
|
-
@try {
|
|
58
|
-
// ACCESSIBILITY API BASED CURSOR DETECTION
|
|
59
|
-
// Determine cursor type based on the UI element under the cursor
|
|
60
|
-
|
|
61
|
-
CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
|
|
62
|
-
AXUIElementRef systemWide = AXUIElementCreateSystemWide();
|
|
63
|
-
AXUIElementRef elementAtPosition = NULL;
|
|
64
|
-
AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
|
|
65
|
-
|
|
66
|
-
NSString *cursorType = @"default"; // Default fallback
|
|
67
|
-
|
|
68
|
-
if (error == kAXErrorSuccess && elementAtPosition) {
|
|
69
|
-
CFStringRef role = NULL;
|
|
70
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleAttribute, (CFTypeRef*)&role);
|
|
71
|
-
|
|
72
|
-
if (error == kAXErrorSuccess && role) {
|
|
73
|
-
NSString *elementRole = (__bridge_transfer NSString*)role;
|
|
74
|
-
NSLog(@"🎯 ELEMENT ROLE: %@", elementRole);
|
|
75
|
-
|
|
76
|
-
// TEXT CURSORS
|
|
77
|
-
if ([elementRole isEqualToString:@"AXTextField"] ||
|
|
78
|
-
[elementRole isEqualToString:@"AXTextArea"] ||
|
|
79
|
-
[elementRole isEqualToString:@"AXStaticText"] ||
|
|
80
|
-
[elementRole isEqualToString:@"AXSearchField"]) {
|
|
81
|
-
cursorType = @"text";
|
|
82
|
-
}
|
|
83
|
-
// POINTER CURSORS (clickable elements)
|
|
84
|
-
else if ([elementRole isEqualToString:@"AXLink"] ||
|
|
85
|
-
[elementRole isEqualToString:@"AXButton"] ||
|
|
86
|
-
[elementRole isEqualToString:@"AXMenuItem"] ||
|
|
87
|
-
[elementRole isEqualToString:@"AXRadioButton"] ||
|
|
88
|
-
[elementRole isEqualToString:@"AXCheckBox"] ||
|
|
89
|
-
[elementRole isEqualToString:@"AXPopUpButton"] ||
|
|
90
|
-
[elementRole isEqualToString:@"AXTab"]) {
|
|
91
|
-
cursorType = @"pointer";
|
|
92
|
-
}
|
|
93
|
-
// GRAB CURSORS (draggable elements)
|
|
94
|
-
else if ([elementRole isEqualToString:@"AXImage"] ||
|
|
95
|
-
[elementRole isEqualToString:@"AXGroup"]) {
|
|
96
|
-
// Check if element is draggable
|
|
97
|
-
CFBooleanRef draggable = NULL;
|
|
98
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXMovable"), (CFTypeRef*)&draggable);
|
|
99
|
-
if (error == kAXErrorSuccess && draggable && CFBooleanGetValue(draggable)) {
|
|
100
|
-
cursorType = @"grab";
|
|
101
|
-
} else {
|
|
102
|
-
cursorType = @"default";
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// PROGRESS CURSORS (loading/busy elements)
|
|
106
|
-
else if ([elementRole isEqualToString:@"AXProgressIndicator"] ||
|
|
107
|
-
[elementRole isEqualToString:@"AXBusyIndicator"]) {
|
|
108
|
-
cursorType = @"progress";
|
|
109
|
-
}
|
|
110
|
-
// HELP CURSORS (help buttons/tooltips)
|
|
111
|
-
else if ([elementRole isEqualToString:@"AXHelpTag"] ||
|
|
112
|
-
[elementRole isEqualToString:@"AXTooltip"]) {
|
|
113
|
-
cursorType = @"help";
|
|
114
|
-
}
|
|
115
|
-
// RESIZE CURSORS - sadece AXSplitter için
|
|
116
|
-
else if ([elementRole isEqualToString:@"AXSplitter"]) {
|
|
117
|
-
// Get splitter orientation to determine resize direction
|
|
118
|
-
CFStringRef orientation = NULL;
|
|
119
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXOrientation"), (CFTypeRef*)&orientation);
|
|
120
|
-
if (error == kAXErrorSuccess && orientation) {
|
|
121
|
-
NSString *orientationStr = (__bridge_transfer NSString*)orientation;
|
|
122
|
-
if ([orientationStr isEqualToString:@"AXHorizontalOrientation"]) {
|
|
123
|
-
cursorType = @"ns-resize"; // Yatay splitter -> dikey hareket (north-south)
|
|
124
|
-
} else if ([orientationStr isEqualToString:@"AXVerticalOrientation"]) {
|
|
125
|
-
cursorType = @"col-resize"; // Dikey splitter -> yatay hareket (east-west)
|
|
126
|
-
} else {
|
|
127
|
-
cursorType = @"default"; // Bilinmeyen orientation
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
cursorType = @"default"; // Orientation alınamazsa default
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
// SCROLL CURSORS - hep default olsun, all-scroll görünmesin
|
|
134
|
-
else if ([elementRole isEqualToString:@"AXScrollBar"]) {
|
|
135
|
-
cursorType = @"default"; // ScrollBar'lar için de default
|
|
136
|
-
}
|
|
137
|
-
// AXScrollArea - hep default
|
|
138
|
-
else if ([elementRole isEqualToString:@"AXScrollArea"]) {
|
|
139
|
-
cursorType = @"default"; // ScrollArea her zaman default
|
|
140
|
-
}
|
|
141
|
-
// CROSSHAIR CURSORS (drawing/selection tools)
|
|
142
|
-
else if ([elementRole isEqualToString:@"AXCanvas"] ||
|
|
143
|
-
[elementRole isEqualToString:@"AXDrawingArea"]) {
|
|
144
|
-
cursorType = @"crosshair";
|
|
145
|
-
}
|
|
146
|
-
// ZOOM CURSORS (zoom controls)
|
|
147
|
-
else if ([elementRole isEqualToString:@"AXZoomButton"]) {
|
|
148
|
-
cursorType = @"zoom-in";
|
|
149
|
-
}
|
|
150
|
-
// NOT-ALLOWED CURSORS (disabled elements)
|
|
151
|
-
else if ([elementRole isEqualToString:@"AXStaticText"] ||
|
|
152
|
-
[elementRole isEqualToString:@"AXGroup"]) {
|
|
153
|
-
// Check if element is disabled/readonly
|
|
154
|
-
CFBooleanRef enabled = NULL;
|
|
155
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXEnabledAttribute, (CFTypeRef*)&enabled);
|
|
156
|
-
if (error == kAXErrorSuccess && enabled && !CFBooleanGetValue(enabled)) {
|
|
157
|
-
cursorType = @"not-allowed";
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// WINDOW BORDER RESIZE - sadece pencere kenarlarında
|
|
161
|
-
else if ([elementRole isEqualToString:@"AXWindow"]) {
|
|
162
|
-
// Check window attributes to see if it's resizable
|
|
163
|
-
CFBooleanRef resizable = NULL;
|
|
164
|
-
AXError resizableError = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXResizeButton"), (CFTypeRef*)&resizable);
|
|
165
|
-
|
|
166
|
-
// Sadece resize edilebilir pencereler için cursor değişimi
|
|
167
|
-
if (resizableError == kAXErrorSuccess || true) { // AXResizeButton bulunamazsa da devam et
|
|
168
|
-
CFTypeRef position = NULL;
|
|
169
|
-
CFTypeRef size = NULL;
|
|
170
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXPositionAttribute, &position);
|
|
171
|
-
AXError sizeError = AXUIElementCopyAttributeValue(elementAtPosition, kAXSizeAttribute, &size);
|
|
172
|
-
|
|
173
|
-
if (error == kAXErrorSuccess && sizeError == kAXErrorSuccess && position && size) {
|
|
174
|
-
CGPoint windowPos;
|
|
175
|
-
CGSize windowSize;
|
|
176
|
-
AXValueGetValue((AXValueRef)position, kAXValueTypeCGPoint, &windowPos);
|
|
177
|
-
AXValueGetValue((AXValueRef)size, kAXValueTypeCGSize, &windowSize);
|
|
178
|
-
|
|
179
|
-
CGFloat x = cursorPos.x - windowPos.x;
|
|
180
|
-
CGFloat y = cursorPos.y - windowPos.y;
|
|
181
|
-
CGFloat w = windowSize.width;
|
|
182
|
-
CGFloat h = windowSize.height;
|
|
183
|
-
CGFloat edge = 3.0; // Daha küçük edge detection (3px)
|
|
184
|
-
|
|
185
|
-
// Sadece çok kenar köşelerde resize cursor'ı göster
|
|
186
|
-
BOOL isOnBorder = NO;
|
|
187
|
-
|
|
188
|
-
// Corner resize detection - çok dar alanda, doğru açılar
|
|
189
|
-
if (x <= edge && y <= edge) {
|
|
190
|
-
cursorType = @"nwse-resize"; // Sol üst köşe - northwest-southeast
|
|
191
|
-
isOnBorder = YES;
|
|
192
|
-
}
|
|
193
|
-
else if (x >= w-edge && y <= edge) {
|
|
194
|
-
cursorType = @"nesw-resize"; // Sağ üst köşe - northeast-southwest
|
|
195
|
-
isOnBorder = YES;
|
|
196
|
-
}
|
|
197
|
-
else if (x <= edge && y >= h-edge) {
|
|
198
|
-
cursorType = @"nesw-resize"; // Sol alt köşe - southwest-northeast
|
|
199
|
-
isOnBorder = YES;
|
|
200
|
-
}
|
|
201
|
-
else if (x >= w-edge && y >= h-edge) {
|
|
202
|
-
cursorType = @"nwse-resize"; // Sağ alt köşe - southeast-northwest
|
|
203
|
-
isOnBorder = YES;
|
|
204
|
-
}
|
|
205
|
-
// Edge resize detection - sadece çok kenarlarda
|
|
206
|
-
else if (x <= edge && y > edge && y < h-edge) {
|
|
207
|
-
cursorType = @"col-resize"; // Sol kenar - column resize (yatay)
|
|
208
|
-
isOnBorder = YES;
|
|
209
|
-
}
|
|
210
|
-
else if (x >= w-edge && y > edge && y < h-edge) {
|
|
211
|
-
cursorType = @"col-resize"; // Sağ kenar - column resize (yatay)
|
|
212
|
-
isOnBorder = YES;
|
|
213
|
-
}
|
|
214
|
-
else if (y <= edge && x > edge && x < w-edge) {
|
|
215
|
-
cursorType = @"ns-resize"; // Üst kenar - north-south resize (dikey)
|
|
216
|
-
isOnBorder = YES;
|
|
217
|
-
}
|
|
218
|
-
else if (y >= h-edge && x > edge && x < w-edge) {
|
|
219
|
-
cursorType = @"ns-resize"; // Alt kenar - north-south resize (dikey)
|
|
220
|
-
isOnBorder = YES;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Eğer border'da değilse default
|
|
224
|
-
if (!isOnBorder) {
|
|
225
|
-
cursorType = @"default";
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (position) CFRelease(position);
|
|
229
|
-
if (size) CFRelease(size);
|
|
230
|
-
} else {
|
|
231
|
-
cursorType = @"default";
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
cursorType = @"default";
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
// HER DURUM İÇİN DEFAULT FALLBACK
|
|
238
|
-
else {
|
|
239
|
-
// Bilinmeyen elementler için her zaman default
|
|
240
|
-
cursorType = @"default";
|
|
241
|
-
}
|
|
114
|
+
NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
|
|
242
115
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
[elementSubrole isEqualToString:@"AXMoving"]) {
|
|
270
|
-
cursorType = @"grabbing";
|
|
116
|
+
@try {
|
|
117
|
+
// Layer 1: Fast NSCursor detection (most reliable)
|
|
118
|
+
NSString *nsCursorType = detectCursorTypeFromNSCursor();
|
|
119
|
+
|
|
120
|
+
// Layer 2: Accessibility API for context (when NSCursor isn't enough)
|
|
121
|
+
NSString *contextualCursorType = nsCursorType;
|
|
122
|
+
|
|
123
|
+
// Only use expensive Accessibility API if NSCursor gives us "default"
|
|
124
|
+
if ([nsCursorType isEqualToString:@"default"]) {
|
|
125
|
+
CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
|
|
126
|
+
AXUIElementRef systemWide = AXUIElementCreateSystemWide();
|
|
127
|
+
AXUIElementRef elementAtPosition = NULL;
|
|
128
|
+
AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
|
|
129
|
+
|
|
130
|
+
if (error == kAXErrorSuccess && elementAtPosition) {
|
|
131
|
+
CFStringRef role = NULL;
|
|
132
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleAttribute, (CFTypeRef*)&role);
|
|
133
|
+
|
|
134
|
+
if (error == kAXErrorSuccess && role) {
|
|
135
|
+
NSString *elementRole = (__bridge_transfer NSString*)role;
|
|
136
|
+
|
|
137
|
+
// Simplified, high-confidence role mappings only
|
|
138
|
+
if ([elementRole isEqualToString:@"AXTextField"] ||
|
|
139
|
+
[elementRole isEqualToString:@"AXTextArea"] ||
|
|
140
|
+
[elementRole isEqualToString:@"AXSearchField"]) {
|
|
141
|
+
contextualCursorType = @"text";
|
|
271
142
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
143
|
+
else if ([elementRole isEqualToString:@"AXLink"] ||
|
|
144
|
+
[elementRole isEqualToString:@"AXButton"] ||
|
|
145
|
+
[elementRole isEqualToString:@"AXMenuItem"]) {
|
|
146
|
+
contextualCursorType = @"pointer";
|
|
275
147
|
}
|
|
276
|
-
else if ([
|
|
277
|
-
|
|
148
|
+
else if ([elementRole isEqualToString:@"AXProgressIndicator"]) {
|
|
149
|
+
contextualCursorType = @"progress";
|
|
278
150
|
}
|
|
279
|
-
// Subrole'dan bir şey bulamazsa role-based cursor'ı koruyoruz
|
|
280
151
|
}
|
|
152
|
+
CFRelease(elementAtPosition);
|
|
281
153
|
}
|
|
154
|
+
if (systemWide) CFRelease(systemWide);
|
|
155
|
+
}
|
|
282
156
|
|
|
283
|
-
|
|
157
|
+
// Layer 3: Stability filtering to prevent oscillation
|
|
158
|
+
NSString *detectedCursorType = contextualCursorType;
|
|
159
|
+
|
|
160
|
+
// Time-based stability check
|
|
161
|
+
if (currentTime - g_lastCursorCheckTime > CURSOR_STABILITY_THRESHOLD) {
|
|
162
|
+
// Enough time has passed, reset counters
|
|
163
|
+
g_sameCursorDetectionCount = 0;
|
|
164
|
+
g_pendingCursorType = detectedCursorType;
|
|
284
165
|
}
|
|
285
166
|
|
|
286
|
-
if
|
|
287
|
-
|
|
167
|
+
// Check if detected cursor matches pending cursor
|
|
168
|
+
if ([detectedCursorType isEqualToString:g_pendingCursorType]) {
|
|
169
|
+
g_sameCursorDetectionCount++;
|
|
170
|
+
|
|
171
|
+
// If we have enough confirmations, update stable cursor
|
|
172
|
+
if (g_sameCursorDetectionCount >= CURSOR_CONFIRMATION_COUNT) {
|
|
173
|
+
g_stableCursorType = detectedCursorType;
|
|
174
|
+
g_lastDetectedCursorType = detectedCursorType;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
// Different cursor detected, start new pending
|
|
178
|
+
g_pendingCursorType = detectedCursorType;
|
|
179
|
+
g_sameCursorDetectionCount = 1;
|
|
288
180
|
}
|
|
289
181
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
182
|
+
g_lastCursorCheckTime = currentTime;
|
|
183
|
+
|
|
184
|
+
// Final validation
|
|
185
|
+
NSString *finalCursorType = g_stableCursorType;
|
|
186
|
+
if (!finalCursorType || [finalCursorType length] == 0) {
|
|
187
|
+
finalCursorType = @"default";
|
|
293
188
|
}
|
|
294
189
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
190
|
+
// Debug logging for stability tracking
|
|
191
|
+
NSLog(@"🎯 CURSOR DETECTION - NSCursor: %@, Contextual: %@, Stable: %@, Count: %d",
|
|
192
|
+
nsCursorType, contextualCursorType, finalCursorType, g_sameCursorDetectionCount);
|
|
193
|
+
|
|
194
|
+
return finalCursorType;
|
|
195
|
+
|
|
298
196
|
} @catch (NSException *exception) {
|
|
299
197
|
NSLog(@"Error in getCursorType: %@", exception);
|
|
300
|
-
return @"default";
|
|
198
|
+
return g_stableCursorType ?: @"default";
|
|
301
199
|
}
|
|
302
200
|
}
|
|
303
201
|
}
|
|
@@ -501,7 +399,11 @@ void cleanupCursorTracking() {
|
|
|
501
399
|
g_outputPath = nil;
|
|
502
400
|
g_debugCallbackCount = 0;
|
|
503
401
|
g_lastDetectedCursorType = nil;
|
|
402
|
+
g_stableCursorType = @"default";
|
|
504
403
|
g_cursorTypeCounter = 0;
|
|
404
|
+
g_lastCursorCheckTime = 0;
|
|
405
|
+
g_sameCursorDetectionCount = 0;
|
|
406
|
+
g_pendingCursorType = nil;
|
|
505
407
|
g_isFirstWrite = true;
|
|
506
408
|
}
|
|
507
409
|
|