node-mac-recorder 2.17.5 → 2.17.7
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/package.json +1 -1
- package/src/cursor_tracker.mm +203 -181
package/package.json
CHANGED
package/src/cursor_tracker.mm
CHANGED
|
@@ -55,217 +55,239 @@ NSString* getCursorType() {
|
|
|
55
55
|
g_cursorTypeCounter++;
|
|
56
56
|
|
|
57
57
|
@try {
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
NSString *cursorType = @"default";
|
|
61
|
-
|
|
62
|
-
// Get cursor image info
|
|
63
|
-
NSImage *cursorImage = [currentCursor image];
|
|
64
|
-
NSPoint hotSpot = [currentCursor hotSpot];
|
|
65
|
-
NSSize imageSize = [cursorImage size];
|
|
66
|
-
|
|
67
|
-
// DEBUG: Log cursor details
|
|
68
|
-
NSLog(@"🖱️ CURSOR DEBUG: size=(%.0f,%.0f), hotspot=(%.0f,%.0f)",
|
|
69
|
-
imageSize.width, imageSize.height, hotSpot.x, hotSpot.y);
|
|
70
|
-
|
|
71
|
-
// Check cursor type by comparing with standard cursors
|
|
72
|
-
if ([currentCursor isEqual:[NSCursor pointingHandCursor]] ||
|
|
73
|
-
(hotSpot.x >= 5 && hotSpot.x <= 7 && hotSpot.y >= 0 && hotSpot.y <= 4) ||
|
|
74
|
-
(hotSpot.x >= 12 && hotSpot.x <= 14 && hotSpot.y >= 7 && hotSpot.y <= 9)) {
|
|
75
|
-
return @"pointer";
|
|
76
|
-
} else if ([currentCursor isEqual:[NSCursor IBeamCursor]] ||
|
|
77
|
-
(hotSpot.x >= 3 && hotSpot.x <= 5 && hotSpot.y >= 8 && hotSpot.y <= 10 &&
|
|
78
|
-
imageSize.width <= 10 && imageSize.height >= 16)) {
|
|
79
|
-
return @"text";
|
|
80
|
-
} else if ([currentCursor isEqual:[NSCursor resizeLeftRightCursor]]) {
|
|
81
|
-
return @"col-resize";
|
|
82
|
-
} else if ([currentCursor isEqual:[NSCursor resizeUpDownCursor]]) {
|
|
83
|
-
return @"ns-resize";
|
|
84
|
-
} else if ([currentCursor isEqual:[NSCursor openHandCursor]]) {
|
|
85
|
-
return @"grab";
|
|
86
|
-
} else if ([currentCursor isEqual:[NSCursor closedHandCursor]]) {
|
|
87
|
-
return @"grabbing";
|
|
88
|
-
} else if ([currentCursor isEqual:[NSCursor crosshairCursor]]) {
|
|
89
|
-
return @"crosshair";
|
|
90
|
-
} else if ([currentCursor isEqual:[NSCursor disappearingItemCursor]]) {
|
|
91
|
-
return @"alias";
|
|
92
|
-
} else if ([currentCursor isEqual:[NSCursor dragCopyCursor]]) {
|
|
93
|
-
return @"copy";
|
|
94
|
-
} else if ([currentCursor isEqual:[NSCursor operationNotAllowedCursor]]) {
|
|
95
|
-
return @"not-allowed";
|
|
96
|
-
} else if ([currentCursor isEqual:[NSCursor contextualMenuCursor]]) {
|
|
97
|
-
return @"help";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Additional detection based on image characteristics
|
|
101
|
-
if (imageSize.width > 0 && imageSize.height > 0) {
|
|
102
|
-
NSLog(@"🖱️ Checking cursor: size=(%.0f,%.0f), hotspot=(%.0f,%.0f)",
|
|
103
|
-
imageSize.width, imageSize.height, hotSpot.x, hotSpot.y);
|
|
58
|
+
// ACCESSIBILITY API BASED CURSOR DETECTION
|
|
59
|
+
// Determine cursor type based on the UI element under the cursor
|
|
104
60
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
61
|
+
CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
|
|
62
|
+
AXUIElementRef systemWide = AXUIElementCreateSystemWide();
|
|
63
|
+
AXUIElementRef elementAtPosition = NULL;
|
|
64
|
+
AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
|
|
108
65
|
|
|
109
|
-
|
|
110
|
-
if (imageSize.width <= 12 && imageSize.height >= 16 &&
|
|
111
|
-
hotSpot.x >= imageSize.width/2 - 2 && hotSpot.x <= imageSize.width/2 + 2) {
|
|
112
|
-
isKnownPattern = true;
|
|
113
|
-
}
|
|
66
|
+
NSString *cursorType = @"default"; // Default fallback
|
|
114
67
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
isKnownPattern = true;
|
|
119
|
-
}
|
|
68
|
+
if (error == kAXErrorSuccess && elementAtPosition) {
|
|
69
|
+
CFStringRef role = NULL;
|
|
70
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleAttribute, (CFTypeRef*)&role);
|
|
120
71
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
isKnownPattern = true;
|
|
125
|
-
}
|
|
72
|
+
if (error == kAXErrorSuccess && role) {
|
|
73
|
+
NSString *elementRole = (__bridge_transfer NSString*)role;
|
|
74
|
+
NSLog(@"🎯 ELEMENT ROLE: %@", elementRole);
|
|
126
75
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
hotSpot.y >= imageSize.height/2 - 4 && hotSpot.y <= imageSize.height/2 + 4) {
|
|
134
|
-
NSLog(@"🖱️ DETECTED PROGRESS: size=(%.0f,%.0f), hotspot=(%.0f,%.0f)",
|
|
135
|
-
imageSize.width, imageSize.height, hotSpot.x, hotSpot.y);
|
|
136
|
-
return @"progress";
|
|
76
|
+
// TEXT CURSORS
|
|
77
|
+
if ([elementRole isEqualToString:@"AXTextField"] ||
|
|
78
|
+
[elementRole isEqualToString:@"AXTextArea"] ||
|
|
79
|
+
[elementRole isEqualToString:@"AXStaticText"] ||
|
|
80
|
+
[elementRole isEqualToString:@"AXSearchField"]) {
|
|
81
|
+
cursorType = @"text";
|
|
137
82
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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";
|
|
151
103
|
}
|
|
152
104
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
abs(imageSize.width - imageSize.height) <= 2 &&
|
|
158
|
-
hotSpot.x >= imageSize.width/2 - 2 && hotSpot.x <= imageSize.width/2 + 2 &&
|
|
159
|
-
hotSpot.y >= imageSize.height/2 - 2 && hotSpot.y <= imageSize.height/2 + 2) {
|
|
160
|
-
NSLog(@"🖱️ DETECTED ALL-SCROLL: size=(%.0f,%.0f)", imageSize.width, imageSize.height);
|
|
161
|
-
return @"all-scroll";
|
|
105
|
+
// PROGRESS CURSORS (loading/busy elements)
|
|
106
|
+
else if ([elementRole isEqualToString:@"AXProgressIndicator"] ||
|
|
107
|
+
[elementRole isEqualToString:@"AXBusyIndicator"]) {
|
|
108
|
+
cursorType = @"progress";
|
|
162
109
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
110
|
+
// HELP CURSORS (help buttons/tooltips)
|
|
111
|
+
else if ([elementRole isEqualToString:@"AXHelpTag"] ||
|
|
112
|
+
[elementRole isEqualToString:@"AXTooltip"]) {
|
|
113
|
+
cursorType = @"help";
|
|
114
|
+
}
|
|
115
|
+
// RESIZE CURSORS
|
|
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 = @"row-resize"; // vertical splitter -> row resize
|
|
124
|
+
} else {
|
|
125
|
+
cursorType = @"col-resize"; // horizontal splitter -> col resize
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
cursorType = @"col-resize"; // default
|
|
171
129
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
130
|
+
}
|
|
131
|
+
// SCROLL CURSORS - sadece gerçek scroll kontrollerinde
|
|
132
|
+
else if ([elementRole isEqualToString:@"AXScrollBar"]) {
|
|
133
|
+
// Check orientation for scroll direction
|
|
134
|
+
CFStringRef orientation = NULL;
|
|
135
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXOrientation"), (CFTypeRef*)&orientation);
|
|
136
|
+
if (error == kAXErrorSuccess && orientation) {
|
|
137
|
+
NSString *orientationStr = (__bridge_transfer NSString*)orientation;
|
|
138
|
+
if ([orientationStr isEqualToString:@"AXVerticalOrientation"]) {
|
|
139
|
+
cursorType = @"ns-resize"; // vertical scrollbar
|
|
140
|
+
} else {
|
|
141
|
+
cursorType = @"col-resize"; // horizontal scrollbar
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
cursorType = @"default"; // fallback to default
|
|
176
145
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
146
|
+
}
|
|
147
|
+
// AXScrollArea için sadece belirli durumlarda all-scroll, çoğunlukla default
|
|
148
|
+
else if ([elementRole isEqualToString:@"AXScrollArea"]) {
|
|
149
|
+
// ScrollArea genellikle default olmalı, sadece özel durumlar için all-scroll
|
|
150
|
+
cursorType = @"default";
|
|
151
|
+
}
|
|
152
|
+
// CROSSHAIR CURSORS (drawing/selection tools)
|
|
153
|
+
else if ([elementRole isEqualToString:@"AXCanvas"] ||
|
|
154
|
+
[elementRole isEqualToString:@"AXDrawingArea"]) {
|
|
155
|
+
cursorType = @"crosshair";
|
|
156
|
+
}
|
|
157
|
+
// ZOOM CURSORS (zoom controls)
|
|
158
|
+
else if ([elementRole isEqualToString:@"AXZoomButton"]) {
|
|
159
|
+
cursorType = @"zoom-in";
|
|
160
|
+
}
|
|
161
|
+
// NOT-ALLOWED CURSORS (disabled elements)
|
|
162
|
+
else if ([elementRole isEqualToString:@"AXStaticText"] ||
|
|
163
|
+
[elementRole isEqualToString:@"AXGroup"]) {
|
|
164
|
+
// Check if element is disabled/readonly
|
|
165
|
+
CFBooleanRef enabled = NULL;
|
|
166
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXEnabledAttribute, (CFTypeRef*)&enabled);
|
|
167
|
+
if (error == kAXErrorSuccess && enabled && !CFBooleanGetValue(enabled)) {
|
|
168
|
+
cursorType = @"not-allowed";
|
|
181
169
|
}
|
|
182
170
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
171
|
+
// WINDOW BORDER RESIZE - sadece pencere kenarlarında
|
|
172
|
+
else if ([elementRole isEqualToString:@"AXWindow"]) {
|
|
173
|
+
// Check window attributes to see if it's resizable
|
|
174
|
+
CFBooleanRef resizable = NULL;
|
|
175
|
+
AXError resizableError = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXResizeButton"), (CFTypeRef*)&resizable);
|
|
176
|
+
|
|
177
|
+
// Sadece resize edilebilir pencereler için cursor değişimi
|
|
178
|
+
if (resizableError == kAXErrorSuccess || true) { // AXResizeButton bulunamazsa da devam et
|
|
179
|
+
CFTypeRef position = NULL;
|
|
180
|
+
CFTypeRef size = NULL;
|
|
181
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXPositionAttribute, &position);
|
|
182
|
+
AXError sizeError = AXUIElementCopyAttributeValue(elementAtPosition, kAXSizeAttribute, &size);
|
|
183
|
+
|
|
184
|
+
if (error == kAXErrorSuccess && sizeError == kAXErrorSuccess && position && size) {
|
|
185
|
+
CGPoint windowPos;
|
|
186
|
+
CGSize windowSize;
|
|
187
|
+
AXValueGetValue((AXValueRef)position, kAXValueTypeCGPoint, &windowPos);
|
|
188
|
+
AXValueGetValue((AXValueRef)size, kAXValueTypeCGSize, &windowSize);
|
|
189
|
+
|
|
190
|
+
CGFloat x = cursorPos.x - windowPos.x;
|
|
191
|
+
CGFloat y = cursorPos.y - windowPos.y;
|
|
192
|
+
CGFloat w = windowSize.width;
|
|
193
|
+
CGFloat h = windowSize.height;
|
|
194
|
+
CGFloat edge = 3.0; // Daha küçük edge detection (3px)
|
|
195
|
+
|
|
196
|
+
// Sadece çok kenar köşelerde resize cursor'ı göster
|
|
197
|
+
BOOL isOnBorder = NO;
|
|
198
|
+
|
|
199
|
+
// Corner resize detection - çok dar alanda
|
|
200
|
+
if ((x <= edge && y <= edge) || (x >= w-edge && y >= h-edge)) {
|
|
201
|
+
cursorType = @"nwse-resize";
|
|
202
|
+
isOnBorder = YES;
|
|
203
|
+
}
|
|
204
|
+
else if ((x >= w-edge && y <= edge) || (x <= edge && y >= h-edge)) {
|
|
205
|
+
cursorType = @"nesw-resize";
|
|
206
|
+
isOnBorder = YES;
|
|
207
|
+
}
|
|
208
|
+
// Edge resize detection - sadece çok kenarlarda
|
|
209
|
+
else if ((x <= edge || x >= w-edge) && y > edge && y < h-edge) {
|
|
210
|
+
cursorType = @"col-resize";
|
|
211
|
+
isOnBorder = YES;
|
|
212
|
+
}
|
|
213
|
+
else if ((y <= edge || y >= h-edge) && x > edge && x < w-edge) {
|
|
214
|
+
cursorType = @"row-resize";
|
|
215
|
+
isOnBorder = YES;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Eğer border'da değilse default
|
|
219
|
+
if (!isOnBorder) {
|
|
220
|
+
cursorType = @"default";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (position) CFRelease(position);
|
|
224
|
+
if (size) CFRelease(size);
|
|
225
|
+
} else {
|
|
226
|
+
cursorType = @"default";
|
|
227
|
+
}
|
|
195
228
|
} else {
|
|
196
|
-
|
|
197
|
-
return @"grabbing";
|
|
229
|
+
cursorType = @"default";
|
|
198
230
|
}
|
|
199
231
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// Check if we're in a drag operation
|
|
205
|
-
CGEventRef event = CGEventCreate(NULL);
|
|
206
|
-
if (event) {
|
|
207
|
-
CGEventType eventType = (CGEventType)CGEventGetType(event);
|
|
208
|
-
if (eventType == kCGEventLeftMouseDragged ||
|
|
209
|
-
eventType == kCGEventRightMouseDragged) {
|
|
210
|
-
CFRelease(event);
|
|
211
|
-
return @"grabbing";
|
|
212
|
-
}
|
|
213
|
-
CFRelease(event);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Get the window under the cursor
|
|
217
|
-
CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
|
|
218
|
-
AXUIElementRef systemWide = AXUIElementCreateSystemWide();
|
|
219
|
-
AXUIElementRef elementAtPosition = NULL;
|
|
220
|
-
AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
|
|
221
|
-
|
|
222
|
-
if (error == kAXErrorSuccess && elementAtPosition) {
|
|
223
|
-
CFStringRef role = NULL;
|
|
224
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleAttribute, (CFTypeRef*)&role);
|
|
225
|
-
|
|
226
|
-
if (error == kAXErrorSuccess && role) {
|
|
227
|
-
NSString *elementRole = (__bridge_transfer NSString*)role;
|
|
228
|
-
|
|
229
|
-
// Check for clickable elements that should show pointer cursor
|
|
230
|
-
if ([elementRole isEqualToString:@"AXLink"] ||
|
|
231
|
-
[elementRole isEqualToString:@"AXButton"] ||
|
|
232
|
-
[elementRole isEqualToString:@"AXMenuItem"] ||
|
|
233
|
-
[elementRole isEqualToString:@"AXRadioButton"] ||
|
|
234
|
-
[elementRole isEqualToString:@"AXCheckBox"]) {
|
|
235
|
-
return @"pointer";
|
|
232
|
+
// HER DURUM İÇİN DEFAULT FALLBACK
|
|
233
|
+
else {
|
|
234
|
+
// Bilinmeyen elementler için her zaman default
|
|
235
|
+
cursorType = @"default";
|
|
236
236
|
}
|
|
237
|
-
|
|
238
|
-
// Check
|
|
237
|
+
|
|
238
|
+
// Check subroles for additional context
|
|
239
239
|
CFStringRef subrole = NULL;
|
|
240
240
|
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXSubroleAttribute, (CFTypeRef*)&subrole);
|
|
241
241
|
if (error == kAXErrorSuccess && subrole) {
|
|
242
242
|
NSString *elementSubrole = (__bridge_transfer NSString*)subrole;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
[elementSubrole isEqualToString:@"AXCloseButton"] ||
|
|
243
|
+
NSLog(@"🎯 ELEMENT SUBROLE: %@", elementSubrole);
|
|
244
|
+
|
|
245
|
+
// Subrole override'ları - sadece çok spesifik durumlar için
|
|
246
|
+
if ([elementSubrole isEqualToString:@"AXCloseButton"] ||
|
|
248
247
|
[elementSubrole isEqualToString:@"AXMinimizeButton"] ||
|
|
249
|
-
[elementSubrole isEqualToString:@"AXZoomButton"]
|
|
250
|
-
|
|
248
|
+
[elementSubrole isEqualToString:@"AXZoomButton"] ||
|
|
249
|
+
[elementSubrole isEqualToString:@"AXToolbarButton"]) {
|
|
250
|
+
cursorType = @"pointer";
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
252
|
+
// Copy/alias subroles - sadece bu durumlar için override
|
|
253
|
+
else if ([elementSubrole isEqualToString:@"AXFileDrop"] ||
|
|
254
|
+
[elementSubrole isEqualToString:@"AXDropTarget"]) {
|
|
255
|
+
cursorType = @"copy";
|
|
256
|
+
}
|
|
257
|
+
// Alias/shortcut subroles
|
|
258
|
+
else if ([elementSubrole isEqualToString:@"AXAlias"] ||
|
|
259
|
+
[elementSubrole isEqualToString:@"AXShortcut"]) {
|
|
260
|
+
cursorType = @"alias";
|
|
261
|
+
}
|
|
262
|
+
// Grabbing state (being dragged) - sadece gerçek drag sırasında
|
|
263
|
+
else if ([elementSubrole isEqualToString:@"AXDragging"] ||
|
|
264
|
+
[elementSubrole isEqualToString:@"AXMoving"]) {
|
|
265
|
+
cursorType = @"grabbing";
|
|
266
|
+
}
|
|
267
|
+
// Zoom controls - sadece spesifik zoom butonları için
|
|
268
|
+
else if ([elementSubrole isEqualToString:@"AXZoomIn"]) {
|
|
269
|
+
cursorType = @"zoom-in";
|
|
270
|
+
}
|
|
271
|
+
else if ([elementSubrole isEqualToString:@"AXZoomOut"]) {
|
|
272
|
+
cursorType = @"zoom-out";
|
|
273
|
+
}
|
|
274
|
+
// Subrole'dan bir şey bulamazsa role-based cursor'ı koruyoruz
|
|
259
275
|
}
|
|
260
276
|
}
|
|
261
|
-
|
|
277
|
+
|
|
262
278
|
CFRelease(elementAtPosition);
|
|
263
279
|
}
|
|
264
|
-
|
|
280
|
+
|
|
265
281
|
if (systemWide) {
|
|
266
282
|
CFRelease(systemWide);
|
|
267
283
|
}
|
|
268
|
-
|
|
284
|
+
|
|
285
|
+
// Son güvence - eğer cursorType hala nil veya geçersizse default'a çevir
|
|
286
|
+
if (!cursorType || [cursorType length] == 0) {
|
|
287
|
+
cursorType = @"default";
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
NSLog(@"🎯 FINAL CURSOR TYPE: %@", cursorType);
|
|
269
291
|
return cursorType;
|
|
270
292
|
|
|
271
293
|
} @catch (NSException *exception) {
|