node-mac-recorder 2.18.1 → 2.18.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.
Files changed (2) hide show
  1. package/index.js +126 -33
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -252,6 +252,12 @@ class MacRecorder extends EventEmitter {
252
252
 
253
253
  // Recording için display bilgisini sakla (cursor capture için)
254
254
  const targetDisplay = displays.find(d => d.id === targetDisplayId);
255
+
256
+ // CRITICAL FIX: Get actual physical dimensions for proper cursor scaling
257
+ // This fixes cursor coordinate issues on Retina displays during AVFoundation recording
258
+ const cursorPosition = nativeBinding.getCursorPosition();
259
+ const displayScalingInfo = cursorPosition.displayInfo;
260
+
255
261
  this.recordingDisplayInfo = {
256
262
  displayId: targetDisplayId,
257
263
  x: targetDisplay.x,
@@ -261,6 +267,10 @@ class MacRecorder extends EventEmitter {
261
267
  // Add scaling information for cursor coordinate transformation
262
268
  logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
263
269
  logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
270
+ // CRITICAL: Add physical dimensions for proper cursor scaling
271
+ physicalWidth: displayScalingInfo ? displayScalingInfo.physicalWidth : parseInt(targetDisplay.resolution.split("x")[0]),
272
+ physicalHeight: displayScalingInfo ? displayScalingInfo.physicalHeight : parseInt(targetDisplay.resolution.split("x")[1]),
273
+ scaleFactor: displayScalingInfo ? displayScalingInfo.scaleFactor : 1.0,
264
274
  };
265
275
  }
266
276
 
@@ -298,6 +308,11 @@ class MacRecorder extends EventEmitter {
298
308
  }
299
309
 
300
310
  if (targetDisplay) {
311
+ // CRITICAL FIX: Get actual physical dimensions for proper cursor scaling
312
+ // This fixes cursor coordinate issues on Retina displays during AVFoundation recording
313
+ const cursorPosition = nativeBinding.getCursorPosition();
314
+ const displayScalingInfo = cursorPosition.displayInfo;
315
+
301
316
  this.recordingDisplayInfo = {
302
317
  displayId: targetDisplay.id,
303
318
  x: targetDisplay.x || 0,
@@ -307,6 +322,10 @@ class MacRecorder extends EventEmitter {
307
322
  // Add scaling information for cursor coordinate transformation
308
323
  logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
309
324
  logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
325
+ // CRITICAL: Add physical dimensions for proper cursor scaling
326
+ physicalWidth: displayScalingInfo ? displayScalingInfo.physicalWidth : parseInt(targetDisplay.resolution.split("x")[0]),
327
+ physicalHeight: displayScalingInfo ? displayScalingInfo.physicalHeight : parseInt(targetDisplay.resolution.split("x")[1]),
328
+ scaleFactor: displayScalingInfo ? displayScalingInfo.scaleFactor : 1.0,
310
329
  };
311
330
  }
312
331
  } catch (error) {
@@ -365,10 +384,10 @@ class MacRecorder extends EventEmitter {
365
384
  this.isRecording = true;
366
385
  this.recordingStartTime = Date.now();
367
386
 
368
- // Start unified cursor tracking for all recording types
369
- // Use the same standard cursor tracking logic that works best (display-relative)
387
+ // Start unified cursor tracking with video-relative coordinates
388
+ // This ensures cursor positions match exactly with video frames
370
389
  const standardCursorOptions = {
371
- displayRelative: true,
390
+ videoRelative: true,
372
391
  displayInfo: this.recordingDisplayInfo,
373
392
  recordingType: this.options.windowId ? 'window' :
374
393
  this.options.captureArea ? 'area' : 'display',
@@ -652,12 +671,14 @@ class MacRecorder extends EventEmitter {
652
671
  }
653
672
 
654
673
  /**
655
- * Unified cursor capture for all recording types - uses standardized display-relative coordinates
674
+ * Unified cursor capture for all recording types - uses video-relative coordinates
656
675
  * @param {string|number} intervalOrFilepath - Cursor data JSON dosya yolu veya interval
657
676
  * @param {Object} options - Cursor capture seçenekleri
658
- * @param {boolean} options.displayRelative - Use display-relative coordinates (recommended)
677
+ * @param {boolean} options.videoRelative - Use video-relative coordinates (recommended)
659
678
  * @param {Object} options.displayInfo - Display information for coordinate transformation
660
679
  * @param {string} options.recordingType - Type of recording: 'display', 'window', 'area'
680
+ * @param {Object} options.captureArea - Capture area for area recording coordinate transformation
681
+ * @param {number} options.windowId - Window ID for window recording coordinate transformation
661
682
  */
662
683
  async startCursorCapture(intervalOrFilepath = 100, options = {}) {
663
684
  let filepath;
@@ -679,16 +700,46 @@ class MacRecorder extends EventEmitter {
679
700
  throw new Error("Cursor capture is already running");
680
701
  }
681
702
 
682
- // Use standardized display-relative coordinate system for all recording types
683
- if (options.displayRelative && options.displayInfo) {
684
- // Standardized display-relative coordinates for all recording types
703
+ // Use video-relative coordinate system for all recording types
704
+ if (options.videoRelative && options.displayInfo) {
705
+ // Calculate video offset based on recording type
706
+ let videoOffsetX = 0;
707
+ let videoOffsetY = 0;
708
+ let videoWidth = options.displayInfo.width || options.displayInfo.logicalWidth;
709
+ let videoHeight = options.displayInfo.height || options.displayInfo.logicalHeight;
710
+
711
+ if (options.recordingType === 'window' && options.windowId) {
712
+ // For window recording: offset = window position in display
713
+ if (options.captureArea) {
714
+ videoOffsetX = options.captureArea.x;
715
+ videoOffsetY = options.captureArea.y;
716
+ videoWidth = options.captureArea.width;
717
+ videoHeight = options.captureArea.height;
718
+ }
719
+ } else if (options.recordingType === 'area' && options.captureArea) {
720
+ // For area recording: offset = area position in display
721
+ videoOffsetX = options.captureArea.x;
722
+ videoOffsetY = options.captureArea.y;
723
+ videoWidth = options.captureArea.width;
724
+ videoHeight = options.captureArea.height;
725
+ }
726
+ // For display recording: offset remains 0,0
727
+
685
728
  this.cursorDisplayInfo = {
686
729
  displayId: options.displayInfo.displayId || options.displayInfo.id,
687
- x: options.displayInfo.x || 0,
688
- y: options.displayInfo.y || 0,
689
- width: options.displayInfo.width || options.displayInfo.logicalWidth,
690
- height: options.displayInfo.height || options.displayInfo.logicalHeight,
691
- displayRelative: true,
730
+ displayX: options.displayInfo.x || 0,
731
+ displayY: options.displayInfo.y || 0,
732
+ displayWidth: options.displayInfo.width || options.displayInfo.logicalWidth,
733
+ displayHeight: options.displayInfo.height || options.displayInfo.logicalHeight,
734
+ // CRITICAL: Add physical dimensions for proper cursor scaling
735
+ physicalWidth: options.displayInfo.physicalWidth || options.displayInfo.width || options.displayInfo.logicalWidth,
736
+ physicalHeight: options.displayInfo.physicalHeight || options.displayInfo.height || options.displayInfo.logicalHeight,
737
+ scaleFactor: options.displayInfo.scaleFactor || 1.0,
738
+ videoOffsetX: videoOffsetX,
739
+ videoOffsetY: videoOffsetY,
740
+ videoWidth: videoWidth,
741
+ videoHeight: videoHeight,
742
+ videoRelative: true,
692
743
  recordingType: options.recordingType || 'display',
693
744
  // Store additional context for debugging
694
745
  captureArea: options.captureArea,
@@ -698,7 +749,19 @@ class MacRecorder extends EventEmitter {
698
749
  // Fallback: Use recording display info if available
699
750
  this.cursorDisplayInfo = {
700
751
  ...this.recordingDisplayInfo,
701
- displayRelative: true,
752
+ displayX: this.recordingDisplayInfo.x || 0,
753
+ displayY: this.recordingDisplayInfo.y || 0,
754
+ displayWidth: this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
755
+ displayHeight: this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
756
+ // CRITICAL: Include physical dimensions from recording display info
757
+ physicalWidth: this.recordingDisplayInfo.physicalWidth || this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
758
+ physicalHeight: this.recordingDisplayInfo.physicalHeight || this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
759
+ scaleFactor: this.recordingDisplayInfo.scaleFactor || 1.0,
760
+ videoOffsetX: 0,
761
+ videoOffsetY: 0,
762
+ videoWidth: this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
763
+ videoHeight: this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
764
+ videoRelative: true,
702
765
  recordingType: options.recordingType || 'display'
703
766
  };
704
767
  } else {
@@ -738,26 +801,43 @@ class MacRecorder extends EventEmitter {
738
801
  const position = nativeBinding.getCursorPosition();
739
802
  const timestamp = Date.now() - this.cursorCaptureStartTime;
740
803
 
741
- // Standardized coordinate transformation for all recording types
804
+ // Video-relative coordinate transformation for all recording types
742
805
  let x = position.x;
743
806
  let y = position.y;
744
807
  let coordinateSystem = "global";
745
808
 
746
- // Apply display-relative transformation for all recording types
747
- if (this.cursorDisplayInfo && this.cursorDisplayInfo.displayRelative) {
748
- // Transform global → display-relative coordinates
749
- x = position.x - this.cursorDisplayInfo.x;
750
- y = position.y - this.cursorDisplayInfo.y;
751
- coordinateSystem = "display-relative";
752
-
753
- // Optional bounds check for display (don't skip, just note if outside)
754
- const outsideDisplay = x < 0 || y < 0 ||
755
- x >= this.cursorDisplayInfo.width ||
756
- y >= this.cursorDisplayInfo.height;
757
-
758
- // For debugging - add metadata if cursor is outside recording area
759
- if (outsideDisplay) {
760
- coordinateSystem = "display-relative-outside";
809
+ // Apply video-relative transformation for all recording types
810
+ if (this.cursorDisplayInfo && this.cursorDisplayInfo.videoRelative) {
811
+ // Step 1: Transform global → display-relative coordinates
812
+ const displayRelativeX = position.x - this.cursorDisplayInfo.displayX;
813
+ const displayRelativeY = position.y - this.cursorDisplayInfo.displayY;
814
+
815
+ // CRITICAL FIX: Apply physical scaling for AVFoundation recording
816
+ // AVFoundation records at physical resolution while cursor coordinates are logical
817
+ let scaledX = displayRelativeX;
818
+ let scaledY = displayRelativeY;
819
+
820
+ if (this.cursorDisplayInfo.scaleFactor && this.cursorDisplayInfo.scaleFactor > 1.0) {
821
+ // Scale logical cursor coordinates to match physical video dimensions
822
+ scaledX = displayRelativeX * this.cursorDisplayInfo.scaleFactor;
823
+ scaledY = displayRelativeY * this.cursorDisplayInfo.scaleFactor;
824
+ coordinateSystem = "video-relative-scaled";
825
+ }
826
+
827
+ // Step 2: Transform display-relative → video-relative coordinates
828
+ x = scaledX - this.cursorDisplayInfo.videoOffsetX;
829
+ y = scaledY - this.cursorDisplayInfo.videoOffsetY;
830
+ coordinateSystem = this.cursorDisplayInfo.scaleFactor > 1.0 ? "video-relative-scaled" : "video-relative";
831
+
832
+ // Bounds check for video area (use physical video dimensions for AVFoundation)
833
+ const videoWidth = this.cursorDisplayInfo.physicalWidth || this.cursorDisplayInfo.videoWidth;
834
+ const videoHeight = this.cursorDisplayInfo.physicalHeight || this.cursorDisplayInfo.videoHeight;
835
+
836
+ const outsideVideo = x < 0 || y < 0 || x >= videoWidth || y >= videoHeight;
837
+
838
+ // For debugging - add metadata if cursor is outside video area
839
+ if (outsideVideo) {
840
+ coordinateSystem = coordinateSystem + "-outside";
761
841
  }
762
842
  }
763
843
 
@@ -769,12 +849,25 @@ class MacRecorder extends EventEmitter {
769
849
  cursorType: position.cursorType,
770
850
  type: position.eventType || "move",
771
851
  coordinateSystem: coordinateSystem,
772
- // Standardized metadata for all recording types
852
+ // Video-relative metadata for all recording types
773
853
  recordingType: this.cursorDisplayInfo?.recordingType || "display",
854
+ videoInfo: this.cursorDisplayInfo ? {
855
+ width: this.cursorDisplayInfo.videoWidth,
856
+ height: this.cursorDisplayInfo.videoHeight,
857
+ offsetX: this.cursorDisplayInfo.videoOffsetX,
858
+ offsetY: this.cursorDisplayInfo.videoOffsetY,
859
+ // CRITICAL: Add physical dimensions for debugging
860
+ physicalWidth: this.cursorDisplayInfo.physicalWidth,
861
+ physicalHeight: this.cursorDisplayInfo.physicalHeight
862
+ } : null,
774
863
  displayInfo: this.cursorDisplayInfo ? {
775
864
  displayId: this.cursorDisplayInfo.displayId,
776
- width: this.cursorDisplayInfo.width,
777
- height: this.cursorDisplayInfo.height
865
+ width: this.cursorDisplayInfo.displayWidth,
866
+ height: this.cursorDisplayInfo.displayHeight,
867
+ // CRITICAL: Add scaling info for debugging AVFoundation cursor issues
868
+ physicalWidth: this.cursorDisplayInfo.physicalWidth,
869
+ physicalHeight: this.cursorDisplayInfo.physicalHeight,
870
+ scaleFactor: this.cursorDisplayInfo.scaleFactor
778
871
  } : null
779
872
  };
780
873
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.18.1",
3
+ "version": "2.18.3",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [