node-mac-recorder 1.2.1 → 1.2.3

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/README.md CHANGED
@@ -234,61 +234,47 @@ const thumbnail = await recorder.getDisplayThumbnail(0, {
234
234
 
235
235
  ### Cursor Tracking Methods
236
236
 
237
- #### `startCursorTracking(outputPath)`
237
+ #### `startCursorCapture(outputPath)`
238
238
 
239
- Starts tracking cursor movements and saves data to JSON file.
239
+ Starts automatic cursor tracking and saves data to JSON file in real-time.
240
240
 
241
241
  ```javascript
242
- await recorder.startCursorTracking("./cursor-data.json");
243
- // Cursor tracking started - will record position, cursor type, and events
242
+ await recorder.startCursorCapture("./cursor-data.json");
243
+ // Cursor tracking started - automatically writing to file
244
244
  ```
245
245
 
246
- #### `stopCursorTracking()`
246
+ #### `stopCursorCapture()`
247
247
 
248
- Stops cursor tracking and saves collected data.
248
+ Stops cursor tracking and closes the output file.
249
249
 
250
250
  ```javascript
251
- await recorder.stopCursorTracking();
252
- // Data saved to specified JSON file
251
+ await recorder.stopCursorCapture();
252
+ // Tracking stopped, file closed
253
253
  ```
254
254
 
255
- #### `getCursorPosition()`
255
+ **JSON Output Format:**
256
256
 
257
- Gets current cursor position and type.
258
-
259
- ```javascript
260
- const position = recorder.getCursorPosition();
261
- console.log(position);
262
- // {
263
- // x: 1234,
264
- // y: 567,
265
- // cursorType: "default" // "default", "pointer", "grabbing", "text"
266
- // }
267
- ```
268
-
269
- #### `getCursorTrackingStatus()`
270
-
271
- Returns cursor tracking status and data count.
272
-
273
- ```javascript
274
- const status = recorder.getCursorTrackingStatus();
275
- console.log(status);
276
- // {
277
- // isTracking: true,
278
- // dataCount: 1250,
279
- // hasEventTap: true,
280
- // hasRunLoopSource: true
281
- // }
257
+ ```json
258
+ [
259
+ {
260
+ "x": 851,
261
+ "y": 432,
262
+ "timestamp": 201,
263
+ "cursorType": "default",
264
+ "type": "move"
265
+ },
266
+ {
267
+ "x": 851,
268
+ "y": 432,
269
+ "timestamp": 220,
270
+ "cursorType": "pointer",
271
+ "type": "mousedown"
272
+ }
273
+ ]
282
274
  ```
283
275
 
284
- #### `saveCursorData(outputPath)`
285
-
286
- Manually saves current cursor data to file.
287
-
288
- ```javascript
289
- await recorder.saveCursorData("./cursor-backup.json");
290
- // Data saved to file
291
- ```
276
+ **Cursor Types:** `default`, `pointer`, `text`, `grab`, `grabbing`, `ew-resize`, `ns-resize`, `crosshair`
277
+ **Event Types:** `move`, `mousedown`, `mouseup`, `rightmousedown`, `rightmouseup`
292
278
 
293
279
  ## Usage Examples
294
280
 
@@ -466,56 +452,51 @@ async function createDisplaySelector() {
466
452
  ### Cursor Tracking Usage
467
453
 
468
454
  ```javascript
469
- const recorder = new MacRecorder();
455
+ const MacRecorder = require("node-mac-recorder");
470
456
 
471
457
  async function trackUserInteraction() {
472
- // Start cursor tracking
473
- await recorder.startCursorTracking("./user-interactions.json");
474
- console.log("Cursor tracking started...");
475
-
476
- // Monitor real-time cursor position
477
- const monitorInterval = setInterval(() => {
478
- const position = recorder.getCursorPosition();
479
- console.log(
480
- `Cursor: ${position.x}, ${position.y} (${position.cursorType})`
481
- );
458
+ const recorder = new MacRecorder();
482
459
 
483
- const status = recorder.getCursorTrackingStatus();
484
- console.log(`Tracking status: ${status.dataCount} positions recorded`);
485
- }, 100); // Check every 100ms
460
+ try {
461
+ // Start cursor tracking - automatically writes to file
462
+ await recorder.startCursorCapture("./user-interactions.json");
463
+ console.log("✅ Cursor tracking started...");
486
464
 
487
- // Track for 10 seconds
488
- setTimeout(async () => {
489
- clearInterval(monitorInterval);
465
+ // Track for 5 seconds
466
+ console.log("📱 Move mouse and click for 5 seconds...");
467
+ await new Promise((resolve) => setTimeout(resolve, 5000));
490
468
 
491
- // Stop tracking and save data
492
- await recorder.stopCursorTracking();
493
- console.log("Cursor tracking completed!");
469
+ // Stop tracking
470
+ await recorder.stopCursorCapture();
471
+ console.log("Cursor tracking completed!");
494
472
 
495
- // Load and analyze the data
473
+ // Analyze the data
496
474
  const fs = require("fs");
497
475
  const data = JSON.parse(
498
476
  fs.readFileSync("./user-interactions.json", "utf8")
499
477
  );
500
478
 
501
- console.log(`Total interactions recorded: ${data.length}`);
479
+ console.log(`📄 ${data.length} events recorded`);
480
+
481
+ // Count clicks
482
+ const clicks = data.filter((d) => d.type === "mousedown").length;
483
+ if (clicks > 0) {
484
+ console.log(`🖱️ ${clicks} clicks detected`);
485
+ }
502
486
 
503
- // Analyze cursor types
487
+ // Most used cursor type
504
488
  const cursorTypes = {};
505
489
  data.forEach((item) => {
506
490
  cursorTypes[item.cursorType] = (cursorTypes[item.cursorType] || 0) + 1;
507
491
  });
508
492
 
509
- console.log("Cursor types distribution:", cursorTypes);
510
-
511
- // Analyze event types
512
- const eventTypes = {};
513
- data.forEach((item) => {
514
- eventTypes[item.type] = (eventTypes[item.type] || 0) + 1;
515
- });
516
-
517
- console.log("Event types distribution:", eventTypes);
518
- }, 10000);
493
+ const mostUsed = Object.keys(cursorTypes).reduce((a, b) =>
494
+ cursorTypes[a] > cursorTypes[b] ? a : b
495
+ );
496
+ console.log(`🎯 Most used cursor: ${mostUsed}`);
497
+ } catch (error) {
498
+ console.error("❌ Error:", error.message);
499
+ }
519
500
  }
520
501
 
521
502
  trackUserInteraction();
@@ -524,33 +505,37 @@ trackUserInteraction();
524
505
  ### Combined Screen Recording + Cursor Tracking
525
506
 
526
507
  ```javascript
527
- const recorder = new MacRecorder();
508
+ const MacRecorder = require("node-mac-recorder");
528
509
 
529
510
  async function recordWithCursorTracking() {
530
- // Start both screen recording and cursor tracking
531
- await Promise.all([
532
- recorder.startRecording("./screen-recording.mov", {
533
- captureCursor: false, // Don't show cursor in video
534
- includeSystemAudio: true,
535
- quality: "high",
536
- }),
537
- recorder.startCursorTracking("./cursor-data.json"),
538
- ]);
539
-
540
- console.log("Recording screen and tracking cursor...");
541
-
542
- // Record for 30 seconds
543
- setTimeout(async () => {
511
+ const recorder = new MacRecorder();
512
+
513
+ try {
514
+ // Start both screen recording and cursor tracking
544
515
  await Promise.all([
545
- recorder.stopRecording(),
546
- recorder.stopCursorTracking(),
516
+ recorder.startRecording("./screen-recording.mov", {
517
+ captureCursor: false, // Don't show cursor in video
518
+ includeSystemAudio: true,
519
+ quality: "high",
520
+ }),
521
+ recorder.startCursorCapture("./cursor-data.json"),
547
522
  ]);
548
523
 
549
- console.log("Screen recording and cursor tracking completed!");
550
- console.log("Files created:");
551
- console.log("- screen-recording.mov");
552
- console.log("- cursor-data.json");
553
- }, 30000);
524
+ console.log(" Recording screen and tracking cursor...");
525
+
526
+ // Record for 10 seconds
527
+ await new Promise((resolve) => setTimeout(resolve, 10000));
528
+
529
+ // Stop both
530
+ await Promise.all([recorder.stopRecording(), recorder.stopCursorCapture()]);
531
+
532
+ console.log("✅ Recording completed!");
533
+ console.log("📁 Files created:");
534
+ console.log(" - screen-recording.mov");
535
+ console.log(" - cursor-data.json");
536
+ } catch (error) {
537
+ console.error("❌ Error:", error.message);
538
+ }
554
539
  }
555
540
 
556
541
  recordWithCursorTracking();
@@ -651,6 +636,21 @@ The `getWindows()` method automatically filters out:
651
636
  - **Memory Efficient** - Proper memory management in native layer
652
637
  - **Quality Presets** - Balanced quality/performance options
653
638
 
639
+ ## Testing
640
+
641
+ Run the included demo to test cursor tracking:
642
+
643
+ ```bash
644
+ node cursor-test.js
645
+ ```
646
+
647
+ This will:
648
+
649
+ - ✅ Start cursor tracking for 5 seconds
650
+ - 📱 Capture mouse movements and clicks
651
+ - 📄 Save data to `cursor-data.json`
652
+ - 🖱️ Report clicks detected
653
+
654
654
  ## Troubleshooting
655
655
 
656
656
  ### Permission Issues
package/cursor-data.json CHANGED
@@ -1,93 +1,359 @@
1
1
  [
2
2
  {
3
- "x": 1119,
4
- "y": 1090,
5
- "timestamp": 106,
3
+ "x": 1947,
4
+ "y": 780,
5
+ "timestamp": 85,
6
+ "cursorType": "pointer",
7
+ "type": "move"
8
+ },
9
+ {
10
+ "x": 1950,
11
+ "y": 780,
12
+ "timestamp": 1476,
13
+ "cursorType": "pointer",
14
+ "type": "move"
15
+ },
16
+ {
17
+ "x": 1954,
18
+ "y": 778,
19
+ "timestamp": 1500,
20
+ "cursorType": "pointer",
21
+ "type": "move"
22
+ },
23
+ {
24
+ "x": 1958,
25
+ "y": 777,
26
+ "timestamp": 1518,
27
+ "cursorType": "pointer",
28
+ "type": "move"
29
+ },
30
+ {
31
+ "x": 1963,
32
+ "y": 775,
33
+ "timestamp": 1541,
34
+ "cursorType": "pointer",
35
+ "type": "move"
36
+ },
37
+ {
38
+ "x": 1967,
39
+ "y": 775,
40
+ "timestamp": 1595,
6
41
  "cursorType": "default",
7
42
  "type": "move"
8
43
  },
9
44
  {
10
- "x": 1119,
11
- "y": 1080,
12
- "timestamp": 407,
45
+ "x": 1971,
46
+ "y": 775,
47
+ "timestamp": 1596,
13
48
  "cursorType": "default",
14
49
  "type": "move"
15
50
  },
16
51
  {
17
- "x": 1119,
18
- "y": 1066,
19
- "timestamp": 423,
52
+ "x": 1973,
53
+ "y": 775,
54
+ "timestamp": 1617,
20
55
  "cursorType": "default",
21
56
  "type": "move"
22
57
  },
23
58
  {
24
- "x": 1119,
25
- "y": 1042,
26
- "timestamp": 446,
59
+ "x": 1975,
60
+ "y": 775,
61
+ "timestamp": 1659,
27
62
  "cursorType": "default",
28
63
  "type": "move"
29
64
  },
30
65
  {
31
- "x": 1119,
32
- "y": 968,
33
- "timestamp": 466,
66
+ "x": 1977,
67
+ "y": 775,
68
+ "timestamp": 1679,
69
+ "cursorType": "pointer",
70
+ "type": "move"
71
+ },
72
+ {
73
+ "x": 1979,
74
+ "y": 775,
75
+ "timestamp": 1744,
76
+ "cursorType": "pointer",
77
+ "type": "move"
78
+ },
79
+ {
80
+ "x": 1979,
81
+ "y": 777,
82
+ "timestamp": 1807,
83
+ "cursorType": "pointer",
84
+ "type": "move"
85
+ },
86
+ {
87
+ "x": 1973,
88
+ "y": 782,
89
+ "timestamp": 1828,
34
90
  "cursorType": "default",
35
91
  "type": "move"
36
92
  },
37
93
  {
38
- "x": 1129,
39
- "y": 906,
40
- "timestamp": 487,
94
+ "x": 1968,
95
+ "y": 787,
96
+ "timestamp": 1850,
41
97
  "cursorType": "default",
42
98
  "type": "move"
43
99
  },
44
100
  {
45
- "x": 1153,
46
- "y": 820,
47
- "timestamp": 507,
101
+ "x": 1957,
102
+ "y": 793,
103
+ "timestamp": 1868,
104
+ "cursorType": "pointer",
105
+ "type": "move"
106
+ },
107
+ {
108
+ "x": 1948,
109
+ "y": 799,
110
+ "timestamp": 1891,
111
+ "cursorType": "pointer",
112
+ "type": "move"
113
+ },
114
+ {
115
+ "x": 1943,
116
+ "y": 802,
117
+ "timestamp": 1911,
48
118
  "cursorType": "default",
49
119
  "type": "move"
50
120
  },
51
121
  {
52
- "x": 1168,
53
- "y": 782,
54
- "timestamp": 528,
122
+ "x": 1937,
123
+ "y": 804,
124
+ "timestamp": 1933,
125
+ "cursorType": "default",
126
+ "type": "move"
127
+ },
128
+ {
129
+ "x": 1938,
130
+ "y": 800,
131
+ "timestamp": 2309,
132
+ "cursorType": "default",
133
+ "type": "move"
134
+ },
135
+ {
136
+ "x": 1939,
137
+ "y": 799,
138
+ "timestamp": 2329,
139
+ "cursorType": "pointer",
140
+ "type": "move"
141
+ },
142
+ {
143
+ "x": 1940,
144
+ "y": 797,
145
+ "timestamp": 2435,
146
+ "cursorType": "pointer",
147
+ "type": "move"
148
+ },
149
+ {
150
+ "x": 1941,
151
+ "y": 794,
152
+ "timestamp": 2477,
153
+ "cursorType": "pointer",
154
+ "type": "move"
155
+ },
156
+ {
157
+ "x": 1941,
158
+ "y": 792,
159
+ "timestamp": 2500,
160
+ "cursorType": "pointer",
161
+ "type": "move"
162
+ },
163
+ {
164
+ "x": 1942,
165
+ "y": 788,
166
+ "timestamp": 2518,
167
+ "cursorType": "pointer",
168
+ "type": "move"
169
+ },
170
+ {
171
+ "x": 1943,
172
+ "y": 786,
173
+ "timestamp": 2560,
174
+ "cursorType": "pointer",
175
+ "type": "move"
176
+ },
177
+ {
178
+ "x": 1945,
179
+ "y": 784,
180
+ "timestamp": 2707,
181
+ "cursorType": "pointer",
182
+ "type": "move"
183
+ },
184
+ {
185
+ "x": 1945,
186
+ "y": 796,
187
+ "timestamp": 3544,
188
+ "cursorType": "pointer",
189
+ "type": "move"
190
+ },
191
+ {
192
+ "x": 1945,
193
+ "y": 817,
194
+ "timestamp": 3568,
55
195
  "cursorType": "default",
56
196
  "type": "move"
57
197
  },
58
198
  {
59
- "x": 1176,
60
- "y": 764,
61
- "timestamp": 549,
199
+ "x": 1951,
200
+ "y": 863,
201
+ "timestamp": 3589,
62
202
  "cursorType": "default",
63
203
  "type": "move"
64
204
  },
65
205
  {
66
- "x": 1176,
67
- "y": 764,
68
- "timestamp": 2578,
206
+ "x": 1956,
207
+ "y": 925,
208
+ "timestamp": 3609,
69
209
  "cursorType": "default",
70
- "type": "mousedown"
210
+ "type": "move"
71
211
  },
72
212
  {
73
- "x": 1176,
74
- "y": 764,
75
- "timestamp": 2599,
213
+ "x": 1966,
214
+ "y": 983,
215
+ "timestamp": 3630,
76
216
  "cursorType": "default",
77
217
  "type": "move"
78
218
  },
79
219
  {
80
- "x": 1176,
81
- "y": 764,
82
- "timestamp": 2724,
220
+ "x": 1970,
221
+ "y": 1006,
222
+ "timestamp": 3650,
223
+ "cursorType": "text",
224
+ "type": "move"
225
+ },
226
+ {
227
+ "x": 1978,
228
+ "y": 1042,
229
+ "timestamp": 3675,
83
230
  "cursorType": "default",
84
- "type": "mouseup"
231
+ "type": "move"
85
232
  },
86
233
  {
87
- "x": 1176,
88
- "y": 764,
89
- "timestamp": 2744,
234
+ "x": 1983,
235
+ "y": 1061,
236
+ "timestamp": 3693,
90
237
  "cursorType": "default",
91
238
  "type": "move"
239
+ },
240
+ {
241
+ "x": 1986,
242
+ "y": 1072,
243
+ "timestamp": 3713,
244
+ "cursorType": "text",
245
+ "type": "move"
246
+ },
247
+ {
248
+ "x": 1990,
249
+ "y": 1082,
250
+ "timestamp": 3734,
251
+ "cursorType": "text",
252
+ "type": "move"
253
+ },
254
+ {
255
+ "x": 1991,
256
+ "y": 1086,
257
+ "timestamp": 3755,
258
+ "cursorType": "text",
259
+ "type": "move"
260
+ },
261
+ {
262
+ "x": 1993,
263
+ "y": 1091,
264
+ "timestamp": 3776,
265
+ "cursorType": "text",
266
+ "type": "move"
267
+ },
268
+ {
269
+ "x": 1994,
270
+ "y": 1096,
271
+ "timestamp": 3797,
272
+ "cursorType": "text",
273
+ "type": "move"
274
+ },
275
+ {
276
+ "x": 1995,
277
+ "y": 1099,
278
+ "timestamp": 3818,
279
+ "cursorType": "text",
280
+ "type": "move"
281
+ },
282
+ {
283
+ "x": 1997,
284
+ "y": 1106,
285
+ "timestamp": 3841,
286
+ "cursorType": "text",
287
+ "type": "move"
288
+ },
289
+ {
290
+ "x": 1997,
291
+ "y": 1109,
292
+ "timestamp": 3859,
293
+ "cursorType": "text",
294
+ "type": "move"
295
+ },
296
+ {
297
+ "x": 1998,
298
+ "y": 1116,
299
+ "timestamp": 3882,
300
+ "cursorType": "text",
301
+ "type": "move"
302
+ },
303
+ {
304
+ "x": 2000,
305
+ "y": 1120,
306
+ "timestamp": 3902,
307
+ "cursorType": "text",
308
+ "type": "move"
309
+ },
310
+ {
311
+ "x": 2000,
312
+ "y": 1123,
313
+ "timestamp": 3924,
314
+ "cursorType": "text",
315
+ "type": "move"
316
+ },
317
+ {
318
+ "x": 2001,
319
+ "y": 1126,
320
+ "timestamp": 3945,
321
+ "cursorType": "text",
322
+ "type": "move"
323
+ },
324
+ {
325
+ "x": 2001,
326
+ "y": 1129,
327
+ "timestamp": 3967,
328
+ "cursorType": "text",
329
+ "type": "move"
330
+ },
331
+ {
332
+ "x": 2002,
333
+ "y": 1135,
334
+ "timestamp": 3986,
335
+ "cursorType": "pointer",
336
+ "type": "move"
337
+ },
338
+ {
339
+ "x": 2002,
340
+ "y": 1142,
341
+ "timestamp": 4007,
342
+ "cursorType": "pointer",
343
+ "type": "move"
344
+ },
345
+ {
346
+ "x": 2002,
347
+ "y": 1146,
348
+ "timestamp": 4027,
349
+ "cursorType": "pointer",
350
+ "type": "move"
351
+ },
352
+ {
353
+ "x": 2002,
354
+ "y": 1151,
355
+ "timestamp": 4049,
356
+ "cursorType": "pointer",
357
+ "type": "move"
92
358
  }
93
359
  ]
package/cursor-test.js CHANGED
@@ -1,50 +1,22 @@
1
- const MacRecorder = require("./index");
2
- const path = require("path");
3
- const fs = require("fs");
4
-
5
- async function testCursorCapture() {
6
- console.log("🎯 Cursor Capture Demo\n");
7
-
8
- const recorder = new MacRecorder();
9
- const outputPath = path.join(__dirname, "cursor-data.json");
10
-
11
- try {
12
- // Başlat
13
- await recorder.startCursorCapture(outputPath);
14
- console.log("✅ Kayıt başladı...");
15
-
16
- // 5 saniye bekle
17
- console.log("📱 5 saniye hareket ettirin, tıklayın...");
18
-
19
- for (let i = 5; i > 0; i--) {
20
- process.stdout.write(`⏳ ${i}... `);
21
- await new Promise((resolve) => setTimeout(resolve, 1000));
22
- }
23
- console.log("\n");
24
-
25
- // Durdur
26
- await recorder.stopCursorCapture();
27
- console.log("✅ Kayıt tamamlandı!");
28
-
29
- // Sonuç
30
- if (fs.existsSync(outputPath)) {
31
- const data = JSON.parse(fs.readFileSync(outputPath, "utf8"));
32
- console.log(`📄 ${data.length} event kaydedildi -> ${outputPath}`);
33
-
34
- // Basit istatistik
35
- const clicks = data.filter((d) => d.type === "mousedown").length;
36
- if (clicks > 0) {
37
- console.log(`🖱️ ${clicks} click algılandı`);
38
- }
39
- }
40
- } catch (error) {
41
- console.error("❌ Hata:", error.message);
42
- }
1
+ const MacRecorder = require("./index.js");
2
+
3
+ const recorder = new MacRecorder();
4
+
5
+ console.log("Starting cursor tracking test...");
6
+ console.log(
7
+ "Move your cursor around different applications to test cursor type detection"
8
+ );
9
+ console.log("The test will run for 10 seconds");
10
+
11
+ try {
12
+ recorder.startCursorCapture("./cursor-data.json");
13
+
14
+ setTimeout(() => {
15
+ recorder.stopCursorCapture();
16
+ console.log("Test completed. Check cursor-data.json for results");
17
+ process.exit(0);
18
+ }, 10000);
19
+ } catch (error) {
20
+ console.error("Error during cursor tracking:", error);
21
+ process.exit(1);
43
22
  }
44
-
45
- // Direkt çalıştır
46
- if (require.main === module) {
47
- testCursorCapture().catch(console.error);
48
- }
49
-
50
- module.exports = { testCursorCapture };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -43,47 +43,113 @@ static bool g_leftMouseDown = false;
43
43
  static bool g_rightMouseDown = false;
44
44
  static NSString *g_lastEventType = @"move";
45
45
 
46
- // Cursor type detection helper - gerçek cursor type'ı al
46
+ // Event tap callback
47
+ static CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *userInfo) {
48
+ return event;
49
+ }
50
+
51
+ // Cursor type detection helper - sistem genelindeki cursor type'ı al
47
52
  NSString* getCursorType() {
48
53
  @autoreleasepool {
49
54
  g_cursorTypeCounter++;
50
55
 
51
56
  @try {
52
- // NSCursor.currentCursor kullanarak gerçek cursor type'ı al
53
- NSCursor *currentCursor = [NSCursor currentCursor];
57
+ // Get current cursor info
58
+ NSCursor *currentCursor = [NSCursor currentSystemCursor];
59
+ NSString *cursorType = @"default";
60
+
61
+ // Get cursor image info
62
+ NSImage *cursorImage = [currentCursor image];
63
+ NSPoint hotSpot = [currentCursor hotSpot];
64
+ NSSize imageSize = [cursorImage size];
54
65
 
55
- if (currentCursor == [NSCursor arrowCursor]) {
56
- g_lastDetectedCursorType = @"default";
57
- return @"default";
58
- } else if (currentCursor == [NSCursor pointingHandCursor]) {
59
- g_lastDetectedCursorType = @"pointer";
66
+ // Check cursor type by comparing with standard cursors
67
+ if ([currentCursor isEqual:[NSCursor pointingHandCursor]] ||
68
+ (hotSpot.x >= 5 && hotSpot.x <= 7 && hotSpot.y >= 0 && hotSpot.y <= 4) ||
69
+ (hotSpot.x >= 12 && hotSpot.x <= 14 && hotSpot.y >= 7 && hotSpot.y <= 9)) {
60
70
  return @"pointer";
61
- } else if (currentCursor == [NSCursor IBeamCursor]) {
62
- g_lastDetectedCursorType = @"text";
71
+ } else if ([currentCursor isEqual:[NSCursor IBeamCursor]] ||
72
+ (hotSpot.x >= 3 && hotSpot.x <= 5 && hotSpot.y >= 8 && hotSpot.y <= 10 &&
73
+ imageSize.width <= 10 && imageSize.height >= 16)) {
63
74
  return @"text";
64
- } else if (currentCursor == [NSCursor openHandCursor]) {
65
- g_lastDetectedCursorType = @"grab";
66
- return @"grab";
67
- } else if (currentCursor == [NSCursor closedHandCursor]) {
68
- g_lastDetectedCursorType = @"grabbing";
69
- return @"grabbing";
70
- } else if (currentCursor == [NSCursor resizeLeftRightCursor]) {
71
- g_lastDetectedCursorType = @"ew-resize";
75
+ } else if ([currentCursor isEqual:[NSCursor resizeLeftRightCursor]]) {
72
76
  return @"ew-resize";
73
- } else if (currentCursor == [NSCursor resizeUpDownCursor]) {
74
- g_lastDetectedCursorType = @"ns-resize";
77
+ } else if ([currentCursor isEqual:[NSCursor resizeUpDownCursor]]) {
75
78
  return @"ns-resize";
76
- } else if (currentCursor == [NSCursor crosshairCursor]) {
77
- g_lastDetectedCursorType = @"crosshair";
78
- return @"crosshair";
79
- } else {
80
- // Bilinmeyen cursor - default olarak dön
81
- g_lastDetectedCursorType = @"default";
82
- return @"default";
79
+ } else if ([currentCursor isEqual:[NSCursor openHandCursor]] ||
80
+ [currentCursor isEqual:[NSCursor closedHandCursor]]) {
81
+ return @"grabbing";
83
82
  }
83
+
84
+ // Check if we're in a drag operation
85
+ CGEventRef event = CGEventCreate(NULL);
86
+ if (event) {
87
+ CGEventType eventType = (CGEventType)CGEventGetType(event);
88
+ if (eventType == kCGEventLeftMouseDragged ||
89
+ eventType == kCGEventRightMouseDragged) {
90
+ CFRelease(event);
91
+ return @"grabbing";
92
+ }
93
+ CFRelease(event);
94
+ }
95
+
96
+ // Get the window under the cursor
97
+ CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
98
+ AXUIElementRef systemWide = AXUIElementCreateSystemWide();
99
+ AXUIElementRef elementAtPosition = NULL;
100
+ AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
101
+
102
+ if (error == kAXErrorSuccess && elementAtPosition) {
103
+ CFStringRef role = NULL;
104
+ error = AXUIElementCopyAttributeValue(elementAtPosition, kAXRoleAttribute, (CFTypeRef*)&role);
105
+
106
+ if (error == kAXErrorSuccess && role) {
107
+ NSString *elementRole = (__bridge_transfer NSString*)role;
108
+
109
+ // Check for clickable elements that should show pointer cursor
110
+ if ([elementRole isEqualToString:@"AXLink"] ||
111
+ [elementRole isEqualToString:@"AXButton"] ||
112
+ [elementRole isEqualToString:@"AXMenuItem"] ||
113
+ [elementRole isEqualToString:@"AXRadioButton"] ||
114
+ [elementRole isEqualToString:@"AXCheckBox"]) {
115
+ return @"pointer";
116
+ }
117
+
118
+ // Check subrole for additional pointer cursor elements
119
+ CFStringRef subrole = NULL;
120
+ error = AXUIElementCopyAttributeValue(elementAtPosition, kAXSubroleAttribute, (CFTypeRef*)&subrole);
121
+ if (error == kAXErrorSuccess && subrole) {
122
+ NSString *elementSubrole = (__bridge_transfer NSString*)subrole;
123
+
124
+ if ([elementSubrole isEqualToString:@"AXClickable"] ||
125
+ [elementSubrole isEqualToString:@"AXDisclosureTriangle"] ||
126
+ [elementSubrole isEqualToString:@"AXToolbarButton"] ||
127
+ [elementSubrole isEqualToString:@"AXCloseButton"] ||
128
+ [elementSubrole isEqualToString:@"AXMinimizeButton"] ||
129
+ [elementSubrole isEqualToString:@"AXZoomButton"]) {
130
+ return @"pointer";
131
+ }
132
+ }
133
+
134
+ // Check for text elements
135
+ if ([elementRole isEqualToString:@"AXTextField"] ||
136
+ [elementRole isEqualToString:@"AXTextArea"] ||
137
+ [elementRole isEqualToString:@"AXStaticText"]) {
138
+ return @"text";
139
+ }
140
+ }
141
+
142
+ CFRelease(elementAtPosition);
143
+ }
144
+
145
+ if (systemWide) {
146
+ CFRelease(systemWide);
147
+ }
148
+
149
+ return cursorType;
150
+
84
151
  } @catch (NSException *exception) {
85
- // Hata durumunda default dön
86
- g_lastDetectedCursorType = @"default";
152
+ NSLog(@"Error in getCursorType: %@", exception);
87
153
  return @"default";
88
154
  }
89
155
  }