node-mac-recorder 2.16.31 β†’ 2.16.32

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.
@@ -0,0 +1,60 @@
1
+ const MacRecorder = require('./index.js');
2
+
3
+ console.log('πŸ” Debugging cursor coordinate scaling issue...\n');
4
+
5
+ const recorder = new MacRecorder();
6
+
7
+ async function testCursorScaling() {
8
+ console.log('Getting display info...');
9
+ const displays = await recorder.getDisplays();
10
+
11
+ displays.forEach((display, index) => {
12
+ console.log(`Display ${index}:`);
13
+ console.log(` Resolution: ${display.resolution}`);
14
+ console.log(` Position: (${display.x}, ${display.y})`);
15
+ console.log(` Primary: ${display.isPrimary}`);
16
+ console.log(` ID: ${display.id}`);
17
+ });
18
+
19
+ console.log('\n🎯 Please move your mouse to different corners and press Enter...');
20
+ console.log('This will help us understand the coordinate mapping issue.\n');
21
+
22
+ const readline = require('readline');
23
+ const rl = readline.createInterface({
24
+ input: process.stdin,
25
+ output: process.stdout
26
+ });
27
+
28
+ let testCount = 0;
29
+ const testPositions = [
30
+ 'Top-Left corner of the display',
31
+ 'Top-Right corner of the display',
32
+ 'Bottom-Left corner of the display',
33
+ 'Bottom-Right corner of the display',
34
+ 'Center of the display'
35
+ ];
36
+
37
+ function nextTest() {
38
+ if (testCount >= testPositions.length) {
39
+ console.log('\nβœ… Test completed!');
40
+ rl.close();
41
+ process.exit(0);
42
+ return;
43
+ }
44
+
45
+ console.log(`\nπŸ“ Test ${testCount + 1}: Move mouse to ${testPositions[testCount]} and press Enter:`);
46
+ rl.question('', () => {
47
+ const position = recorder.getCursorPosition();
48
+ console.log(` Raw cursor position: (${position.rawX || 'N/A'}, ${position.rawY || 'N/A'})`);
49
+ console.log(` Logical cursor position: (${position.x}, ${position.y})`);
50
+ console.log(` Scale factor: ${position.scaleFactor || 'N/A'}x`);
51
+
52
+ testCount++;
53
+ nextTest();
54
+ });
55
+ }
56
+
57
+ nextTest();
58
+ }
59
+
60
+ testCursorScaling().catch(console.error);
@@ -0,0 +1,46 @@
1
+ const MacRecorder = require('./index.js');
2
+
3
+ async function testCursorPermissions() {
4
+ console.log('πŸ”’ Testing cursor tracking permissions...\n');
5
+
6
+ const recorder = new MacRecorder();
7
+
8
+ // Check permissions first
9
+ console.log('1. Checking permissions:');
10
+ const permissions = await recorder.checkPermissions();
11
+ console.log(' Screen Recording:', permissions.screenRecording ? 'βœ…' : '❌');
12
+ console.log(' Accessibility:', permissions.accessibility ? 'βœ…' : '❌');
13
+ console.log(' Microphone:', permissions.microphone ? 'βœ…' : '❌');
14
+
15
+ if (permissions.error) {
16
+ console.log(' Error:', permissions.error);
17
+ }
18
+
19
+ console.log('\n2. Testing direct cursor position (no capture):');
20
+ for (let i = 0; i < 5; i++) {
21
+ try {
22
+ const position = recorder.getCursorPosition();
23
+ console.log(` Position ${i+1}: (${position.x}, ${position.y}) - ${position.cursorType}`);
24
+
25
+ if (position.scaleFactor) {
26
+ console.log(` Scale: ${position.scaleFactor}x, Display: (${position.displayInfo?.displayX}, ${position.displayInfo?.displayY})`);
27
+ console.log(` Logical: ${position.displayInfo?.logicalWidth}x${position.displayInfo?.logicalHeight}`);
28
+ console.log(` Physical: ${position.displayInfo?.physicalWidth}x${position.displayInfo?.physicalHeight}`);
29
+ }
30
+
31
+ await new Promise(resolve => setTimeout(resolve, 500));
32
+ } catch (error) {
33
+ console.error(` Error getting position ${i+1}:`, error.message);
34
+ }
35
+ }
36
+
37
+ console.log('\n3. Testing cursor capture status:');
38
+ const status = recorder.getCursorCaptureStatus();
39
+ console.log(' Is Capturing:', status.isCapturing);
40
+ console.log(' Output File:', status.outputFile);
41
+ console.log(' Display Info:', status.displayInfo);
42
+
43
+ process.exit(0);
44
+ }
45
+
46
+ testCursorPermissions().catch(console.error);
@@ -0,0 +1,53 @@
1
+ const MacRecorder = require('./index.js');
2
+
3
+ async function debugCursorScaling() {
4
+ console.log('πŸ” Debugging cursor scaling detection...\n');
5
+
6
+ const recorder = new MacRecorder();
7
+
8
+ // Get all displays
9
+ const displays = await recorder.getDisplays();
10
+ console.log('πŸ“Ί Available displays:');
11
+ displays.forEach((display, index) => {
12
+ console.log(` Display ${index}: ID=${display.id}, ${display.resolution} at (${display.x}, ${display.y}), Primary: ${display.isPrimary}`);
13
+ });
14
+
15
+ console.log('\n🎯 Current cursor position analysis:');
16
+ const position = recorder.getCursorPosition();
17
+
18
+ console.log(` Logical position: (${position.x}, ${position.y})`);
19
+ console.log(` Cursor type: ${position.cursorType}`);
20
+ console.log(` Event type: ${position.eventType}`);
21
+
22
+ if (position.scaleFactor) {
23
+ console.log(` Scale factor detected: ${position.scaleFactor}x`);
24
+ if (position.displayInfo) {
25
+ const info = position.displayInfo;
26
+ console.log(` Display bounds: (${info.displayX}, ${info.displayY})`);
27
+ console.log(` Logical size: ${info.logicalWidth}x${info.logicalHeight}`);
28
+ console.log(` Physical size: ${info.physicalWidth}x${info.physicalHeight}`);
29
+ console.log(` Raw position: (${position.rawX}, ${position.rawY})`);
30
+ }
31
+ } else {
32
+ console.log(` ❌ No scale factor detected`);
33
+ console.log(` This could mean:`);
34
+ console.log(` - getDisplayScalingInfo() didn't find the correct display`);
35
+ console.log(` - Display detection logic needs debugging`);
36
+ }
37
+
38
+ // Find which display the cursor is on
39
+ console.log('\nπŸ” Manual display detection:');
40
+ displays.forEach((display, index) => {
41
+ const inX = position.x >= display.x && position.x < display.x + parseInt(display.resolution.split('x')[0]);
42
+ const inY = position.y >= display.y && position.y < display.y + parseInt(display.resolution.split('x')[1]);
43
+ const isInside = inX && inY;
44
+
45
+ console.log(` Display ${index} (${display.resolution}): ${isInside ? 'βœ… CURSOR IS HERE' : '❌'}`);
46
+ console.log(` Bounds: X(${display.x} - ${display.x + parseInt(display.resolution.split('x')[0])}), Y(${display.y} - ${display.y + parseInt(display.resolution.split('x')[1])})`);
47
+ console.log(` Cursor: X(${position.x}), Y(${position.y})`);
48
+ });
49
+
50
+ process.exit(0);
51
+ }
52
+
53
+ debugCursorScaling().catch(console.error);
package/index.js CHANGED
@@ -258,6 +258,9 @@ class MacRecorder extends EventEmitter {
258
258
  y: targetDisplay.y,
259
259
  width: parseInt(targetDisplay.resolution.split("x")[0]),
260
260
  height: parseInt(targetDisplay.resolution.split("x")[1]),
261
+ // Add scaling information for cursor coordinate transformation
262
+ logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
263
+ logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
261
264
  };
262
265
  }
263
266
 
@@ -292,6 +295,9 @@ class MacRecorder extends EventEmitter {
292
295
  y: targetDisplay.y,
293
296
  width: parseInt(targetDisplay.resolution.split("x")[0]),
294
297
  height: parseInt(targetDisplay.resolution.split("x")[1]),
298
+ // Add scaling information for cursor coordinate transformation
299
+ logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
300
+ logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
295
301
  };
296
302
  }
297
303
  } catch (error) {
@@ -696,10 +702,32 @@ class MacRecorder extends EventEmitter {
696
702
  let coordinateSystem = "global";
697
703
 
698
704
  if (this.cursorDisplayInfo) {
699
- // Offset'leri Γ§Δ±kar (display veya window)
700
- // Y koordinat dânüşümü başlangıçta yapıldı
701
- x = position.x - this.cursorDisplayInfo.x;
702
- y = position.y - this.cursorDisplayInfo.y;
705
+ // CRITICAL FIX: Handle DPR scaling for cursor coordinates
706
+ // Get scaling information from native cursor position
707
+ const scaleFactor = position.scaleFactor || 1;
708
+ const displayInfo = position.displayInfo;
709
+
710
+ // Convert logical cursor position to physical (if recording uses physical coordinates)
711
+ let physicalX = position.x;
712
+ let physicalY = position.y;
713
+
714
+ if (scaleFactor > 1.1 && displayInfo) {
715
+ // Convert to display-relative logical, then to physical
716
+ const displayRelativeX = position.x - displayInfo.displayX;
717
+ const displayRelativeY = position.y - displayInfo.displayY;
718
+
719
+ // Scale to physical coordinates
720
+ const physicalRelativeX = displayRelativeX * scaleFactor;
721
+ const physicalRelativeY = displayRelativeY * scaleFactor;
722
+
723
+ // Convert back to global physical
724
+ physicalX = displayInfo.displayX + physicalRelativeX;
725
+ physicalY = displayInfo.displayY + physicalRelativeY;
726
+ }
727
+
728
+ // Offset'leri Γ§Δ±kar (display veya window) - use physical coordinates
729
+ x = physicalX - this.cursorDisplayInfo.x;
730
+ y = physicalY - this.cursorDisplayInfo.y;
703
731
 
704
732
  if (this.cursorDisplayInfo.windowRelative) {
705
733
  // Window-relative koordinatlar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.16.31",
3
+ "version": "2.16.32",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -209,17 +209,8 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
209
209
  CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
210
210
  NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
211
211
 
212
- if (scaleFactor > 1.1) {
213
- // Convert to logical coordinates
214
- CGFloat displayRelativeX = rawLocation.x - displayBounds.origin.x;
215
- CGFloat displayRelativeY = rawLocation.y - displayBounds.origin.y;
216
-
217
- CGFloat logicalRelativeX = displayRelativeX / scaleFactor;
218
- CGFloat logicalRelativeY = displayRelativeY / scaleFactor;
219
-
220
- location.x = displayBounds.origin.x + logicalRelativeX;
221
- location.y = displayBounds.origin.y + logicalRelativeY;
222
- }
212
+ // Keep logical coordinates - no scaling needed here
213
+ location = rawLocation;
223
214
  }
224
215
  NSDate *currentDate = [NSDate date];
225
216
  NSTimeInterval timestamp = [currentDate timeIntervalSinceDate:g_trackingStartTime] * 1000; // milliseconds
@@ -291,17 +282,8 @@ void cursorTimerCallback() {
291
282
  CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
292
283
  NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
293
284
 
294
- if (scaleFactor > 1.1) {
295
- // Convert to logical coordinates
296
- CGFloat displayRelativeX = rawLocation.x - displayBounds.origin.x;
297
- CGFloat displayRelativeY = rawLocation.y - displayBounds.origin.y;
298
-
299
- CGFloat logicalRelativeX = displayRelativeX / scaleFactor;
300
- CGFloat logicalRelativeY = displayRelativeY / scaleFactor;
301
-
302
- location.x = displayBounds.origin.x + logicalRelativeX;
303
- location.y = displayBounds.origin.y + logicalRelativeY;
304
- }
285
+ // Keep logical coordinates - no scaling needed here
286
+ location = rawLocation;
305
287
  }
306
288
 
307
289
  NSDate *currentDate = [NSDate date];
@@ -491,16 +473,34 @@ NSDictionary* getDisplayScalingInfo(CGPoint globalPoint) {
491
473
  CGDirectDisplayID displayID = displayIDs[i];
492
474
  CGRect displayBounds = CGDisplayBounds(displayID);
493
475
 
476
+ NSLog(@"πŸ” Display %u: bounds(%.0f,%.0f %.0fx%.0f), cursor(%.0f,%.0f)",
477
+ displayID, displayBounds.origin.x, displayBounds.origin.y,
478
+ displayBounds.size.width, displayBounds.size.height,
479
+ globalPoint.x, globalPoint.y);
480
+
481
+ // CRITICAL FIX: Manual bounds check for better coordinate system compatibility
482
+ BOOL isInBounds = (globalPoint.x >= displayBounds.origin.x &&
483
+ globalPoint.x < displayBounds.origin.x + displayBounds.size.width &&
484
+ globalPoint.y >= displayBounds.origin.y &&
485
+ globalPoint.y < displayBounds.origin.y + displayBounds.size.height);
486
+
487
+ NSLog(@"πŸ” Manual bounds check: %s", isInBounds ? "INSIDE" : "OUTSIDE");
488
+
494
489
  // Check if point is within this display
495
- if (CGRectContainsPoint(displayBounds, globalPoint)) {
490
+ if (isInBounds) {
496
491
  // Get display scaling info
497
492
  CGSize logicalSize = displayBounds.size;
498
493
  CGSize physicalSize = CGSizeMake(CGDisplayPixelsWide(displayID), CGDisplayPixelsHigh(displayID));
499
494
 
495
+ NSLog(@"πŸ” Scaling info: logical(%.0fx%.0f) physical(%.0fx%.0f)",
496
+ logicalSize.width, logicalSize.height, physicalSize.width, physicalSize.height);
497
+
500
498
  CGFloat scaleX = physicalSize.width / logicalSize.width;
501
499
  CGFloat scaleY = physicalSize.height / logicalSize.height;
502
500
  CGFloat scaleFactor = MAX(scaleX, scaleY);
503
501
 
502
+ NSLog(@"πŸ” Scale factors: X=%.2f, Y=%.2f, Final=%.2f", scaleX, scaleY, scaleFactor);
503
+
504
504
  return @{
505
505
  @"displayID": @(displayID),
506
506
  @"logicalSize": [NSValue valueWithSize:NSMakeSize(logicalSize.width, logicalSize.height)],
@@ -549,21 +549,9 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
549
549
  CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
550
550
  NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
551
551
 
552
- // CRITICAL FIX: Convert physical coordinates to logical coordinates
553
- // CGEventGetLocation returns physical coordinates on Retina displays
554
- if (scaleFactor > 1.1) {
555
- // Convert to display-relative, then scale down to logical, then back to global logical
556
- CGFloat displayRelativeX = rawLocation.x - displayBounds.origin.x;
557
- CGFloat displayRelativeY = rawLocation.y - displayBounds.origin.y;
558
-
559
- // Scale down to logical coordinates
560
- CGFloat logicalRelativeX = displayRelativeX / scaleFactor;
561
- CGFloat logicalRelativeY = displayRelativeY / scaleFactor;
562
-
563
- // Convert back to global logical coordinates
564
- logicalLocation.x = displayBounds.origin.x + logicalRelativeX;
565
- logicalLocation.y = displayBounds.origin.y + logicalRelativeY;
566
- }
552
+ // CGEventGetLocation returns LOGICAL coordinates (correct for JS layer)
553
+ // Keep logical coordinates - transformation happens in JS layer
554
+ logicalLocation = rawLocation;
567
555
  }
568
556
 
569
557
  NSString *cursorType = getCursorType();
@@ -602,12 +590,27 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
602
590
  result.Set("cursorType", Napi::String::New(env, [cursorType UTF8String]));
603
591
  result.Set("eventType", Napi::String::New(env, [eventType UTF8String]));
604
592
 
605
- // Add scaling debug info
593
+ // Add scaling info for coordinate transformation
606
594
  if (scalingInfo) {
607
595
  CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
596
+ NSSize logicalSize = [[scalingInfo objectForKey:@"logicalSize"] sizeValue];
597
+ NSSize physicalSize = [[scalingInfo objectForKey:@"physicalSize"] sizeValue];
598
+ NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
599
+
608
600
  result.Set("scaleFactor", Napi::Number::New(env, scaleFactor));
609
601
  result.Set("rawX", Napi::Number::New(env, (int)rawLocation.x));
610
602
  result.Set("rawY", Napi::Number::New(env, (int)rawLocation.y));
603
+
604
+ // Add display dimension info for JS coordinate transformation
605
+ Napi::Object displayInfo = Napi::Object::New(env);
606
+ displayInfo.Set("logicalWidth", Napi::Number::New(env, logicalSize.width));
607
+ displayInfo.Set("logicalHeight", Napi::Number::New(env, logicalSize.height));
608
+ displayInfo.Set("physicalWidth", Napi::Number::New(env, physicalSize.width));
609
+ displayInfo.Set("physicalHeight", Napi::Number::New(env, physicalSize.height));
610
+ displayInfo.Set("displayX", Napi::Number::New(env, displayBounds.origin.x));
611
+ displayInfo.Set("displayY", Napi::Number::New(env, displayBounds.origin.y));
612
+
613
+ result.Set("displayInfo", displayInfo);
611
614
  }
612
615
 
613
616
  return result;