node-mac-recorder 2.20.1 → 2.20.2
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 +247 -193
package/package.json
CHANGED
package/src/cursor_tracker.mm
CHANGED
|
@@ -40,6 +40,15 @@ static CursorTimerTarget *g_timerTarget = nil;
|
|
|
40
40
|
static NSString *g_lastDetectedCursorType = nil;
|
|
41
41
|
static int g_cursorTypeCounter = 0;
|
|
42
42
|
|
|
43
|
+
static NSString* CopyAndReleaseCFString(CFStringRef value) {
|
|
44
|
+
if (!value) {
|
|
45
|
+
return nil;
|
|
46
|
+
}
|
|
47
|
+
NSString *result = [NSString stringWithString:(NSString *)value];
|
|
48
|
+
CFRelease(value);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
// Mouse button state tracking
|
|
44
53
|
static bool g_leftMouseDown = false;
|
|
45
54
|
static bool g_rightMouseDown = false;
|
|
@@ -64,220 +73,264 @@ static NSString* detectCursorTypeUsingAccessibility(CGPoint cursorPos) {
|
|
|
64
73
|
NSString *cursorType = @"default"; // Default fallback
|
|
65
74
|
|
|
66
75
|
if (error == kAXErrorSuccess && elementAtPosition) {
|
|
76
|
+
NSString *elementRole = nil;
|
|
67
77
|
CFStringRef role = NULL;
|
|
68
78
|
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleAttribute, (CFTypeRef*)&role);
|
|
69
|
-
|
|
70
79
|
if (error == kAXErrorSuccess && role) {
|
|
71
|
-
|
|
80
|
+
elementRole = CopyAndReleaseCFString(role);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (elementRole) {
|
|
72
84
|
NSLog(@"🎯 ELEMENT ROLE: %@", elementRole);
|
|
85
|
+
}
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
// TEXT CURSORS
|
|
88
|
+
if ([elementRole isEqualToString:@"AXTextField"] ||
|
|
89
|
+
[elementRole isEqualToString:@"AXTextArea"] ||
|
|
90
|
+
[elementRole isEqualToString:@"AXStaticText"] ||
|
|
91
|
+
[elementRole isEqualToString:@"AXSearchField"]) {
|
|
92
|
+
cursorType = @"text";
|
|
93
|
+
}
|
|
94
|
+
// POINTER CURSORS (clickable elements)
|
|
95
|
+
else if ([elementRole isEqualToString:@"AXLink"] ||
|
|
96
|
+
[elementRole isEqualToString:@"AXButton"] ||
|
|
97
|
+
[elementRole isEqualToString:@"AXMenuItem"] ||
|
|
98
|
+
[elementRole isEqualToString:@"AXRadioButton"] ||
|
|
99
|
+
[elementRole isEqualToString:@"AXCheckBox"] ||
|
|
100
|
+
[elementRole isEqualToString:@"AXPopUpButton"] ||
|
|
101
|
+
[elementRole isEqualToString:@"AXTab"]) {
|
|
102
|
+
cursorType = @"pointer";
|
|
103
|
+
}
|
|
104
|
+
// GRAB CURSORS (draggable elements)
|
|
105
|
+
else if ([elementRole isEqualToString:@"AXImage"] ||
|
|
106
|
+
[elementRole isEqualToString:@"AXGroup"]) {
|
|
107
|
+
// Check if element is draggable
|
|
108
|
+
CFBooleanRef draggable = NULL;
|
|
109
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXMovable"), (CFTypeRef*)&draggable);
|
|
110
|
+
if (error == kAXErrorSuccess && draggable && CFBooleanGetValue(draggable)) {
|
|
111
|
+
cursorType = @"grab";
|
|
112
|
+
} else {
|
|
113
|
+
cursorType = @"default";
|
|
90
114
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
115
|
+
}
|
|
116
|
+
// PROGRESS CURSORS (loading/busy elements)
|
|
117
|
+
else if ([elementRole isEqualToString:@"AXProgressIndicator"] ||
|
|
118
|
+
[elementRole isEqualToString:@"AXBusyIndicator"]) {
|
|
119
|
+
cursorType = @"progress";
|
|
120
|
+
}
|
|
121
|
+
// HELP CURSORS (help buttons/tooltips)
|
|
122
|
+
else if ([elementRole isEqualToString:@"AXHelpTag"] ||
|
|
123
|
+
[elementRole isEqualToString:@"AXTooltip"]) {
|
|
124
|
+
cursorType = @"help";
|
|
125
|
+
}
|
|
126
|
+
// RESIZE CURSORS - sadece AXSplitter için
|
|
127
|
+
else if ([elementRole isEqualToString:@"AXSplitter"]) {
|
|
128
|
+
// Get splitter orientation to determine resize direction
|
|
129
|
+
CFStringRef orientation = NULL;
|
|
130
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXOrientation"), (CFTypeRef*)&orientation);
|
|
131
|
+
if (error == kAXErrorSuccess && orientation) {
|
|
132
|
+
NSString *orientationStr = CopyAndReleaseCFString(orientation);
|
|
133
|
+
if ([orientationStr isEqualToString:@"AXHorizontalOrientation"]) {
|
|
134
|
+
cursorType = @"ns-resize"; // Yatay splitter -> dikey hareket (north-south)
|
|
135
|
+
} else if ([orientationStr isEqualToString:@"AXVerticalOrientation"]) {
|
|
136
|
+
cursorType = @"col-resize"; // Dikey splitter -> yatay hareket (east-west)
|
|
99
137
|
} else {
|
|
100
|
-
cursorType = @"default";
|
|
138
|
+
cursorType = @"default"; // Bilinmeyen orientation
|
|
101
139
|
}
|
|
140
|
+
} else {
|
|
141
|
+
cursorType = @"default"; // Orientation alınamazsa default
|
|
102
142
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
143
|
+
}
|
|
144
|
+
// SCROLL CURSORS - hep default olsun, all-scroll görünmesin
|
|
145
|
+
else if ([elementRole isEqualToString:@"AXScrollBar"]) {
|
|
146
|
+
cursorType = @"default"; // ScrollBar'lar için de default
|
|
147
|
+
}
|
|
148
|
+
// AXScrollArea - hep default
|
|
149
|
+
else if ([elementRole isEqualToString:@"AXScrollArea"]) {
|
|
150
|
+
cursorType = @"default"; // ScrollArea her zaman 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";
|
|
112
169
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
}
|
|
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, doğru açılar
|
|
200
|
+
if (x <= edge && y <= edge) {
|
|
201
|
+
cursorType = @"nwse-resize"; // Sol üst köşe - northwest-southeast
|
|
202
|
+
isOnBorder = YES;
|
|
203
|
+
}
|
|
204
|
+
else if (x >= w-edge && y <= edge) {
|
|
205
|
+
cursorType = @"nesw-resize"; // Sağ üst köşe - northeast-southwest
|
|
206
|
+
isOnBorder = YES;
|
|
207
|
+
}
|
|
208
|
+
else if (x <= edge && y >= h-edge) {
|
|
209
|
+
cursorType = @"nesw-resize"; // Sol alt köşe - southwest-northeast
|
|
210
|
+
isOnBorder = YES;
|
|
211
|
+
}
|
|
212
|
+
else if (x >= w-edge && y >= h-edge) {
|
|
213
|
+
cursorType = @"nwse-resize"; // Sağ alt köşe - southeast-northwest
|
|
214
|
+
isOnBorder = YES;
|
|
215
|
+
}
|
|
216
|
+
// Edge resize detection - sadece çok kenarlarda
|
|
217
|
+
else if (x <= edge && y > edge && y < h-edge) {
|
|
218
|
+
cursorType = @"col-resize"; // Sol kenar - column resize (yatay)
|
|
219
|
+
isOnBorder = YES;
|
|
220
|
+
}
|
|
221
|
+
else if (x >= w-edge && y > edge && y < h-edge) {
|
|
222
|
+
cursorType = @"col-resize"; // Sağ kenar - column resize (yatay)
|
|
223
|
+
isOnBorder = YES;
|
|
224
|
+
}
|
|
225
|
+
else if (y <= edge && x > edge && x < w-edge) {
|
|
226
|
+
cursorType = @"ns-resize"; // Üst kenar - north-south resize (dikey)
|
|
227
|
+
isOnBorder = YES;
|
|
228
|
+
}
|
|
229
|
+
else if (y >= h-edge && x > edge && x < w-edge) {
|
|
230
|
+
cursorType = @"ns-resize"; // Alt kenar - north-south resize (dikey)
|
|
231
|
+
isOnBorder = YES;
|
|
126
232
|
}
|
|
233
|
+
|
|
234
|
+
// Eğer border'da değilse default
|
|
235
|
+
if (!isOnBorder) {
|
|
236
|
+
cursorType = @"default";
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (position) CFRelease(position);
|
|
240
|
+
if (size) CFRelease(size);
|
|
127
241
|
} else {
|
|
128
|
-
cursorType = @"default";
|
|
242
|
+
cursorType = @"default";
|
|
129
243
|
}
|
|
244
|
+
} else {
|
|
245
|
+
cursorType = @"default";
|
|
130
246
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
247
|
+
}
|
|
248
|
+
// HER DURUM İÇİN DEFAULT FALLBACK
|
|
249
|
+
else {
|
|
250
|
+
// Bilinmeyen elementler için her zaman default
|
|
251
|
+
cursorType = @"default";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Check subroles for additional context
|
|
255
|
+
CFStringRef subrole = NULL;
|
|
256
|
+
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXSubroleAttribute, (CFTypeRef*)&subrole);
|
|
257
|
+
if (error == kAXErrorSuccess && subrole) {
|
|
258
|
+
NSString *elementSubrole = CopyAndReleaseCFString(subrole);
|
|
259
|
+
NSLog(@"🎯 ELEMENT SUBROLE: %@", elementSubrole);
|
|
260
|
+
|
|
261
|
+
// Subrole override'ları - sadece çok spesifik durumlar için
|
|
262
|
+
if ([elementSubrole isEqualToString:@"AXCloseButton"] ||
|
|
263
|
+
[elementSubrole isEqualToString:@"AXMinimizeButton"] ||
|
|
264
|
+
[elementSubrole isEqualToString:@"AXZoomButton"] ||
|
|
265
|
+
[elementSubrole isEqualToString:@"AXToolbarButton"]) {
|
|
266
|
+
cursorType = @"pointer";
|
|
138
267
|
}
|
|
139
|
-
//
|
|
140
|
-
else if ([
|
|
141
|
-
[
|
|
142
|
-
cursorType = @"
|
|
268
|
+
// Copy/alias subroles - sadece bu durumlar için override
|
|
269
|
+
else if ([elementSubrole isEqualToString:@"AXFileDrop"] ||
|
|
270
|
+
[elementSubrole isEqualToString:@"AXDropTarget"]) {
|
|
271
|
+
cursorType = @"copy";
|
|
143
272
|
}
|
|
144
|
-
//
|
|
145
|
-
else if ([
|
|
146
|
-
|
|
273
|
+
// Alias/shortcut subroles
|
|
274
|
+
else if ([elementSubrole isEqualToString:@"AXAlias"] ||
|
|
275
|
+
[elementSubrole isEqualToString:@"AXShortcut"]) {
|
|
276
|
+
cursorType = @"alias";
|
|
147
277
|
}
|
|
148
|
-
//
|
|
149
|
-
else if ([
|
|
150
|
-
[
|
|
151
|
-
|
|
152
|
-
CFBooleanRef enabled = NULL;
|
|
153
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXEnabledAttribute, (CFTypeRef*)&enabled);
|
|
154
|
-
if (error == kAXErrorSuccess && enabled && !CFBooleanGetValue(enabled)) {
|
|
155
|
-
cursorType = @"not-allowed";
|
|
156
|
-
}
|
|
278
|
+
// Grabbing state (being dragged) - sadece gerçek drag sırasında
|
|
279
|
+
else if ([elementSubrole isEqualToString:@"AXDragging"] ||
|
|
280
|
+
[elementSubrole isEqualToString:@"AXMoving"]) {
|
|
281
|
+
cursorType = @"grabbing";
|
|
157
282
|
}
|
|
158
|
-
//
|
|
159
|
-
else if ([
|
|
160
|
-
|
|
161
|
-
CFBooleanRef resizable = NULL;
|
|
162
|
-
AXError resizableError = AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXResizeButton"), (CFTypeRef*)&resizable);
|
|
163
|
-
|
|
164
|
-
// Sadece resize edilebilir pencereler için cursor değişimi
|
|
165
|
-
if (resizableError == kAXErrorSuccess || true) { // AXResizeButton bulunamazsa da devam et
|
|
166
|
-
CFTypeRef position = NULL;
|
|
167
|
-
CFTypeRef size = NULL;
|
|
168
|
-
error = AXUIElementCopyAttributeValue(elementAtPosition, kAXPositionAttribute, &position);
|
|
169
|
-
AXError sizeError = AXUIElementCopyAttributeValue(elementAtPosition, kAXSizeAttribute, &size);
|
|
170
|
-
|
|
171
|
-
if (error == kAXErrorSuccess && sizeError == kAXErrorSuccess && position && size) {
|
|
172
|
-
CGPoint windowPos;
|
|
173
|
-
CGSize windowSize;
|
|
174
|
-
AXValueGetValue((AXValueRef)position, kAXValueTypeCGPoint, &windowPos);
|
|
175
|
-
AXValueGetValue((AXValueRef)size, kAXValueTypeCGSize, &windowSize);
|
|
176
|
-
|
|
177
|
-
CGFloat x = cursorPos.x - windowPos.x;
|
|
178
|
-
CGFloat y = cursorPos.y - windowPos.y;
|
|
179
|
-
CGFloat w = windowSize.width;
|
|
180
|
-
CGFloat h = windowSize.height;
|
|
181
|
-
CGFloat edge = 3.0; // Daha küçük edge detection (3px)
|
|
182
|
-
|
|
183
|
-
// Sadece çok kenar köşelerde resize cursor'ı göster
|
|
184
|
-
BOOL isOnBorder = NO;
|
|
185
|
-
|
|
186
|
-
// Corner resize detection - çok dar alanda, doğru açılar
|
|
187
|
-
if (x <= edge && y <= edge) {
|
|
188
|
-
cursorType = @"nwse-resize"; // Sol üst köşe - northwest-southeast
|
|
189
|
-
isOnBorder = YES;
|
|
190
|
-
}
|
|
191
|
-
else if (x >= w-edge && y <= edge) {
|
|
192
|
-
cursorType = @"nesw-resize"; // Sağ üst köşe - northeast-southwest
|
|
193
|
-
isOnBorder = YES;
|
|
194
|
-
}
|
|
195
|
-
else if (x <= edge && y >= h-edge) {
|
|
196
|
-
cursorType = @"nesw-resize"; // Sol alt köşe - southwest-northeast
|
|
197
|
-
isOnBorder = YES;
|
|
198
|
-
}
|
|
199
|
-
else if (x >= w-edge && y >= h-edge) {
|
|
200
|
-
cursorType = @"nwse-resize"; // Sağ alt köşe - southeast-northwest
|
|
201
|
-
isOnBorder = YES;
|
|
202
|
-
}
|
|
203
|
-
// Edge resize detection - sadece çok kenarlarda
|
|
204
|
-
else if (x <= edge && y > edge && y < h-edge) {
|
|
205
|
-
cursorType = @"col-resize"; // Sol kenar - column resize (yatay)
|
|
206
|
-
isOnBorder = YES;
|
|
207
|
-
}
|
|
208
|
-
else if (x >= w-edge && y > edge && y < h-edge) {
|
|
209
|
-
cursorType = @"col-resize"; // Sağ kenar - column resize (yatay)
|
|
210
|
-
isOnBorder = YES;
|
|
211
|
-
}
|
|
212
|
-
else if (y <= edge && x > edge && x < w-edge) {
|
|
213
|
-
cursorType = @"ns-resize"; // Üst kenar - north-south resize (dikey)
|
|
214
|
-
isOnBorder = YES;
|
|
215
|
-
}
|
|
216
|
-
else if (y >= h-edge && x > edge && x < w-edge) {
|
|
217
|
-
cursorType = @"ns-resize"; // Alt kenar - north-south resize (dikey)
|
|
218
|
-
isOnBorder = YES;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Eğer border'da değilse default
|
|
222
|
-
if (!isOnBorder) {
|
|
223
|
-
cursorType = @"default";
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (position) CFRelease(position);
|
|
227
|
-
if (size) CFRelease(size);
|
|
228
|
-
} else {
|
|
229
|
-
cursorType = @"default";
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
cursorType = @"default";
|
|
233
|
-
}
|
|
283
|
+
// Zoom controls - sadece spesifik zoom butonları için
|
|
284
|
+
else if ([elementSubrole isEqualToString:@"AXZoomIn"]) {
|
|
285
|
+
cursorType = @"zoom-in";
|
|
234
286
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// Bilinmeyen elementler için her zaman default
|
|
238
|
-
cursorType = @"default";
|
|
287
|
+
else if ([elementSubrole isEqualToString:@"AXZoomOut"]) {
|
|
288
|
+
cursorType = @"zoom-out";
|
|
239
289
|
}
|
|
290
|
+
// Subrole'dan bir şey bulamazsa role-based cursor'ı koruyoruz
|
|
291
|
+
}
|
|
240
292
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
[
|
|
251
|
-
|
|
252
|
-
|
|
293
|
+
CFStringRef roleDescriptionRef = NULL;
|
|
294
|
+
AXError descriptionError = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleDescriptionAttribute, (CFTypeRef*)&roleDescriptionRef);
|
|
295
|
+
if (descriptionError == kAXErrorSuccess && roleDescriptionRef) {
|
|
296
|
+
NSString *roleDescription = CopyAndReleaseCFString(roleDescriptionRef);
|
|
297
|
+
if (roleDescription) {
|
|
298
|
+
NSString *roleDescriptionLower = [roleDescription lowercaseString];
|
|
299
|
+
if ([roleDescriptionLower containsString:@"text"] ||
|
|
300
|
+
[roleDescriptionLower containsString:@"editor"] ||
|
|
301
|
+
[roleDescriptionLower containsString:@"code"] ||
|
|
302
|
+
[roleDescriptionLower containsString:@"document"]) {
|
|
303
|
+
cursorType = @"text";
|
|
304
|
+
} else if ([roleDescriptionLower containsString:@"button"] ||
|
|
305
|
+
[roleDescriptionLower containsString:@"link"] ||
|
|
306
|
+
[roleDescriptionLower containsString:@"tab"]) {
|
|
253
307
|
cursorType = @"pointer";
|
|
254
308
|
}
|
|
255
|
-
// Copy/alias subroles - sadece bu durumlar için override
|
|
256
|
-
else if ([elementSubrole isEqualToString:@"AXFileDrop"] ||
|
|
257
|
-
[elementSubrole isEqualToString:@"AXDropTarget"]) {
|
|
258
|
-
cursorType = @"copy";
|
|
259
|
-
}
|
|
260
|
-
// Alias/shortcut subroles
|
|
261
|
-
else if ([elementSubrole isEqualToString:@"AXAlias"] ||
|
|
262
|
-
[elementSubrole isEqualToString:@"AXShortcut"]) {
|
|
263
|
-
cursorType = @"alias";
|
|
264
|
-
}
|
|
265
|
-
// Grabbing state (being dragged) - sadece gerçek drag sırasında
|
|
266
|
-
else if ([elementSubrole isEqualToString:@"AXDragging"] ||
|
|
267
|
-
[elementSubrole isEqualToString:@"AXMoving"]) {
|
|
268
|
-
cursorType = @"grabbing";
|
|
269
|
-
}
|
|
270
|
-
// Zoom controls - sadece spesifik zoom butonları için
|
|
271
|
-
else if ([elementSubrole isEqualToString:@"AXZoomIn"]) {
|
|
272
|
-
cursorType = @"zoom-in";
|
|
273
|
-
}
|
|
274
|
-
else if ([elementSubrole isEqualToString:@"AXZoomOut"]) {
|
|
275
|
-
cursorType = @"zoom-out";
|
|
276
|
-
}
|
|
277
|
-
// Subrole'dan bir şey bulamazsa role-based cursor'ı koruyoruz
|
|
278
309
|
}
|
|
279
310
|
}
|
|
280
311
|
|
|
312
|
+
CFBooleanRef isEditable = NULL;
|
|
313
|
+
if (AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXEditable"), (CFTypeRef*)&isEditable) == kAXErrorSuccess &&
|
|
314
|
+
isEditable && CFBooleanGetValue(isEditable)) {
|
|
315
|
+
cursorType = @"text";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
CFBooleanRef supportsTextSelection = NULL;
|
|
319
|
+
if (AXUIElementCopyAttributeValue(elementAtPosition, CFSTR("AXSupportsTextSelection"), (CFTypeRef*)&supportsTextSelection) == kAXErrorSuccess &&
|
|
320
|
+
supportsTextSelection && CFBooleanGetValue(supportsTextSelection)) {
|
|
321
|
+
cursorType = @"text";
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
CFTypeRef valueAttribute = NULL;
|
|
325
|
+
if (AXUIElementCopyAttributeValue(elementAtPosition, kAXValueAttribute, &valueAttribute) == kAXErrorSuccess && valueAttribute) {
|
|
326
|
+
CFTypeID typeId = CFGetTypeID(valueAttribute);
|
|
327
|
+
if (typeId == CFAttributedStringGetTypeID() ||
|
|
328
|
+
typeId == CFStringGetTypeID()) {
|
|
329
|
+
cursorType = @"text";
|
|
330
|
+
}
|
|
331
|
+
CFRelease(valueAttribute);
|
|
332
|
+
}
|
|
333
|
+
|
|
281
334
|
}
|
|
282
335
|
|
|
283
336
|
if (elementAtPosition) {
|
|
@@ -482,10 +535,6 @@ NSString* getCursorType() {
|
|
|
482
535
|
g_cursorTypeCounter++;
|
|
483
536
|
|
|
484
537
|
NSString *systemCursorType = detectSystemCursorType();
|
|
485
|
-
if (systemCursorType && ![systemCursorType isEqualToString:@"default"]) {
|
|
486
|
-
NSLog(@"🎯 FINAL CURSOR TYPE: %@", systemCursorType);
|
|
487
|
-
return systemCursorType;
|
|
488
|
-
}
|
|
489
538
|
|
|
490
539
|
NSString *axCursorType = nil;
|
|
491
540
|
BOOL hasCursorPosition = NO;
|
|
@@ -520,7 +569,12 @@ NSString* getCursorType() {
|
|
|
520
569
|
if (axCursorType && ![axCursorType isEqualToString:@"default"]) {
|
|
521
570
|
finalType = axCursorType;
|
|
522
571
|
} else if (systemCursorType && [systemCursorType length] > 0) {
|
|
523
|
-
|
|
572
|
+
if ([systemCursorType isEqualToString:@"pointer"] &&
|
|
573
|
+
(!axCursorType || [axCursorType isEqualToString:@"default"])) {
|
|
574
|
+
finalType = @"default";
|
|
575
|
+
} else {
|
|
576
|
+
finalType = systemCursorType;
|
|
577
|
+
}
|
|
524
578
|
} else {
|
|
525
579
|
finalType = @"default";
|
|
526
580
|
}
|