node-mac-recorder 2.20.7 โ†’ 2.20.9

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.
@@ -2,7 +2,8 @@
2
2
  "permissions": {
3
3
  "allow": [
4
4
  "Bash(node-gyp:*)",
5
- "Bash(node:*)"
5
+ "Bash(node:*)",
6
+ "Bash(timeout:*)"
6
7
  ],
7
8
  "deny": [],
8
9
  "ask": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.20.7",
3
+ "version": "2.20.9",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -5,6 +5,7 @@
5
5
  #import <QuartzCore/QuartzCore.h>
6
6
  #import <AppKit/AppKit.h>
7
7
  #include <string>
8
+ #import "logging.h"
8
9
 
9
10
  static AVAssetWriter *g_avWriter = nil;
10
11
  static AVAssetWriterInput *g_avVideoInput = nil;
@@ -32,7 +33,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
32
33
  }
33
34
 
34
35
  @try {
35
- NSLog(@"๐ŸŽฌ AVFoundation: Starting recording initialization");
36
+ MRLog(@"๐ŸŽฌ AVFoundation: Starting recording initialization");
36
37
 
37
38
  // Create output URL
38
39
  NSString *outputPathStr = [NSString stringWithUTF8String:outputPath.c_str()];
@@ -42,7 +43,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
42
43
  NSError *removeError = nil;
43
44
  [[NSFileManager defaultManager] removeItemAtURL:outputURL error:&removeError];
44
45
  if (removeError && removeError.code != NSFileNoSuchFileError) {
45
- NSLog(@"โš ๏ธ AVFoundation: Warning removing existing file: %@", removeError);
46
+ MRLog(@"โš ๏ธ AVFoundation: Warning removing existing file: %@", removeError);
46
47
  }
47
48
 
48
49
  // Create asset writer
@@ -85,21 +86,21 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
85
86
  recordingSize = actualImageSize;
86
87
  }
87
88
 
88
- NSLog(@"๐ŸŽฏ CRITICAL: Logical %.0fx%.0f โ†’ Actual image %.0fx%.0f",
89
+ MRLog(@"๐ŸŽฏ CRITICAL: Logical %.0fx%.0f โ†’ Actual image %.0fx%.0f",
89
90
  logicalSize.width, logicalSize.height, actualImageSize.width, actualImageSize.height);
90
91
 
91
- NSLog(@"๐Ÿ–ฅ๏ธ Display bounds (logical): %.0fx%.0f", logicalSize.width, logicalSize.height);
92
- NSLog(@"๐Ÿ–ฅ๏ธ Display pixels (physical): %.0fx%.0f", physicalSize.width, physicalSize.height);
92
+ MRLog(@"๐Ÿ–ฅ๏ธ Display bounds (logical): %.0fx%.0f", logicalSize.width, logicalSize.height);
93
+ MRLog(@"๐Ÿ–ฅ๏ธ Display pixels (physical): %.0fx%.0f", physicalSize.width, physicalSize.height);
93
94
 
94
95
  if (scaleFactor > 1.5) {
95
- NSLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Retina display detected (macOS 14/13 scaling fix applied)", scaleFactor);
96
+ MRLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Retina display detected (macOS 14/13 scaling fix applied)", scaleFactor);
96
97
  } else if (scaleFactor > 1.1) {
97
- NSLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Non-standard scaling detected", scaleFactor);
98
+ MRLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Non-standard scaling detected", scaleFactor);
98
99
  } else {
99
- NSLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Standard display", scaleFactor);
100
+ MRLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Standard display", scaleFactor);
100
101
  }
101
102
 
102
- NSLog(@"๐ŸŽฏ Recording size: %.0fx%.0f (using actual physical dimensions for Retina fix)", recordingSize.width, recordingSize.height);
103
+ MRLog(@"๐ŸŽฏ Recording size: %.0fx%.0f (using actual physical dimensions for Retina fix)", recordingSize.width, recordingSize.height);
103
104
 
104
105
  // Video settings with macOS compatibility
105
106
  NSString *codecKey;
@@ -142,7 +143,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
142
143
  (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey: @YES
143
144
  };
144
145
 
145
- NSLog(@"๐Ÿ”ง Using pixel format: %u", pixelFormat);
146
+ MRLog(@"๐Ÿ”ง Using pixel format: %u", pixelFormat);
146
147
 
147
148
  g_avPixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:g_avVideoInput sourcePixelBufferAttributes:pixelBufferAttributes];
148
149
 
@@ -253,11 +254,11 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
253
254
  size_t imageWidth = CGImageGetWidth(screenImage);
254
255
  size_t imageHeight = CGImageGetHeight(screenImage);
255
256
 
256
- NSLog(@"๐Ÿ” Debug: Buffer %zux%zu, Image %zux%zu", bufferWidth, bufferHeight, imageWidth, imageHeight);
257
+ MRLog(@"๐Ÿ” Debug: Buffer %zux%zu, Image %zux%zu", bufferWidth, bufferHeight, imageWidth, imageHeight);
257
258
 
258
259
  if (bufferWidth != imageWidth || bufferHeight != imageHeight) {
259
- NSLog(@"๐Ÿ”ง EXPECTED SIZE DIFFERENCE: Buffer %zux%zu (logical) vs Image %zux%zu (physical)", bufferWidth, bufferHeight, imageWidth, imageHeight);
260
- NSLog(@" This is normal on Retina displays - scaling handled correctly now");
260
+ MRLog(@"๐Ÿ”ง EXPECTED SIZE DIFFERENCE: Buffer %zux%zu (logical) vs Image %zux%zu (physical)", bufferWidth, bufferHeight, imageWidth, imageHeight);
261
+ MRLog(@" This is normal on Retina displays - scaling handled correctly now");
261
262
  }
262
263
 
263
264
  CVPixelBufferLockBaseAddress(pixelBuffer, 0);
@@ -332,7 +333,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
332
333
  dispatch_resume(g_avTimer);
333
334
  g_avIsRecording = true;
334
335
 
335
- NSLog(@"๐ŸŽฅ AVFoundation recording started: %dx%d @ 10fps",
336
+ MRLog(@"๐ŸŽฅ AVFoundation recording started: %dx%d @ 10fps",
336
337
  (int)recordingSize.width, (int)recordingSize.height);
337
338
 
338
339
  return true;
@@ -365,7 +366,7 @@ extern "C" bool stopAVFoundationRecording() {
365
366
  });
366
367
 
367
368
  g_avTimer = nil;
368
- NSLog(@"โœ… AVFoundation timer stopped safely");
369
+ MRLog(@"โœ… AVFoundation timer stopped safely");
369
370
  }
370
371
 
371
372
  // Finish writing with null checks
@@ -391,7 +392,7 @@ extern "C" bool stopAVFoundationRecording() {
391
392
  g_avPixelBufferAdaptor = nil;
392
393
  g_avFrameNumber = 0;
393
394
 
394
- NSLog(@"โœ… AVFoundation recording stopped");
395
+ MRLog(@"โœ… AVFoundation recording stopped");
395
396
  return true;
396
397
 
397
398
  } @catch (NSException *exception) {
@@ -402,4 +403,4 @@ extern "C" bool stopAVFoundationRecording() {
402
403
 
403
404
  extern "C" bool isAVFoundationRecording() {
404
405
  return g_avIsRecording;
405
- }
406
+ }
@@ -6,6 +6,7 @@
6
6
  #import <Carbon/Carbon.h>
7
7
  #import <Accessibility/Accessibility.h>
8
8
  #import <dispatch/dispatch.h>
9
+ #import "logging.h"
9
10
 
10
11
  #ifndef kAXHitTestParameterizedAttribute
11
12
  #define kAXHitTestParameterizedAttribute CFSTR("AXHitTest")
@@ -391,56 +392,100 @@ static NSString* cursorTypeFromCursorName(NSString *value) {
391
392
 
392
393
  NSString *normalized = [[value stringByReplacingOccurrencesOfString:@"_" withString:@"-"] lowercaseString];
393
394
 
394
- if ([normalized containsString:@"arrow"]) {
395
+ // Arrow cursor patterns
396
+ if ([normalized containsString:@"arrow"] || [normalized containsString:@"default"]) {
395
397
  return @"default";
396
398
  }
399
+
400
+ // Text cursor patterns
397
401
  if ([normalized containsString:@"ibeam"] ||
398
402
  [normalized containsString:@"insertion"] ||
399
- [normalized containsString:@"text"]) {
403
+ [normalized containsString:@"text"] ||
404
+ [normalized containsString:@"edit"]) {
400
405
  return @"text";
401
406
  }
402
- if ([normalized containsString:@"openhand"]) {
407
+
408
+ // Hand cursors
409
+ if ([normalized containsString:@"openhand"] || [normalized containsString:@"open-hand"]) {
403
410
  return @"grab";
404
411
  }
405
- if ([normalized containsString:@"closedhand"]) {
412
+ if ([normalized containsString:@"closedhand"] || [normalized containsString:@"closed-hand"]) {
406
413
  return @"grabbing";
407
414
  }
415
+
416
+ // Pointer cursor patterns
408
417
  if ([normalized containsString:@"pointing"] ||
409
- ([normalized containsString:@"hand"] && ![normalized containsString:@"closed"])) {
418
+ [normalized containsString:@"pointinghand"] ||
419
+ ([normalized containsString:@"hand"] && ![normalized containsString:@"closed"] && ![normalized containsString:@"open"]) ||
420
+ [normalized containsString:@"link"] ||
421
+ [normalized containsString:@"button"]) {
410
422
  return @"pointer";
411
423
  }
412
- if ([normalized containsString:@"crosshair"]) {
424
+
425
+ // Crosshair patterns
426
+ if ([normalized containsString:@"crosshair"] || [normalized containsString:@"cross-hair"]) {
413
427
  return @"crosshair";
414
428
  }
429
+
430
+ // Not allowed patterns
415
431
  if ([normalized containsString:@"not-allowed"] ||
416
432
  [normalized containsString:@"notallowed"] ||
417
- [normalized containsString:@"forbidden"]) {
433
+ [normalized containsString:@"forbidden"] ||
434
+ [normalized containsString:@"operation-not-allowed"]) {
418
435
  return @"not-allowed";
419
436
  }
420
- if ([normalized containsString:@"dragcopy"] || [normalized containsString:@"copy"]) {
437
+
438
+ // Copy cursor patterns
439
+ if ([normalized containsString:@"dragcopy"] ||
440
+ [normalized containsString:@"drag-copy"] ||
441
+ [normalized containsString:@"copy"]) {
421
442
  return @"copy";
422
443
  }
423
- if ([normalized containsString:@"draglink"] || [normalized containsString:@"alias"]) {
444
+
445
+ // Alias cursor patterns
446
+ if ([normalized containsString:@"draglink"] ||
447
+ [normalized containsString:@"drag-link"] ||
448
+ [normalized containsString:@"alias"]) {
424
449
  return @"alias";
425
450
  }
426
- if ([normalized containsString:@"context"] && [normalized containsString:@"menu"]) {
451
+
452
+ // Context menu patterns
453
+ if (([normalized containsString:@"context"] && [normalized containsString:@"menu"]) ||
454
+ [normalized containsString:@"contextual-menu"]) {
427
455
  return @"context-menu";
428
456
  }
457
+
458
+ // Zoom patterns
429
459
  if ([normalized containsString:@"zoom"]) {
430
460
  if ([normalized containsString:@"out"]) {
431
461
  return @"zoom-out";
432
462
  }
433
463
  return @"zoom-in";
434
464
  }
465
+
466
+ // Resize cursor patterns - more comprehensive
435
467
  if ([normalized containsString:@"resize"] || [normalized containsString:@"size"]) {
436
- BOOL diagonalUp = [normalized containsString:@"diagonalup"] || [normalized containsString:@"nesw"];
437
- BOOL diagonalDown = [normalized containsString:@"diagonaldown"] || [normalized containsString:@"nwse"];
468
+ // Diagonal resize cursors
469
+ BOOL diagonalUp = [normalized containsString:@"diagonalup"] ||
470
+ [normalized containsString:@"diagonal-up"] ||
471
+ [normalized containsString:@"nesw"];
472
+ BOOL diagonalDown = [normalized containsString:@"diagonaldown"] ||
473
+ [normalized containsString:@"diagonal-down"] ||
474
+ [normalized containsString:@"nwse"];
475
+
476
+ // Horizontal and vertical resize
438
477
  BOOL horizontal = [normalized containsString:@"leftright"] ||
478
+ [normalized containsString:@"left-right"] ||
439
479
  [normalized containsString:@"horizontal"] ||
440
- ([normalized containsString:@"left"] && [normalized containsString:@"right"]);
480
+ ([normalized containsString:@"left"] && [normalized containsString:@"right"]) ||
481
+ [normalized containsString:@"col"] ||
482
+ [normalized containsString:@"column"];
483
+
441
484
  BOOL vertical = [normalized containsString:@"updown"] ||
485
+ [normalized containsString:@"up-down"] ||
442
486
  [normalized containsString:@"vertical"] ||
443
- ([normalized containsString:@"up"] && [normalized containsString:@"down"]);
487
+ ([normalized containsString:@"up"] && [normalized containsString:@"down"]) ||
488
+ [normalized containsString:@"row"];
444
489
 
445
490
  if (diagonalUp) {
446
491
  return @"nesw-resize";
@@ -454,6 +499,22 @@ static NSString* cursorTypeFromCursorName(NSString *value) {
454
499
  if (horizontal) {
455
500
  return @"col-resize";
456
501
  }
502
+
503
+ // Generic resize fallback
504
+ return @"default"; // or could be a generic resize
505
+ }
506
+
507
+ // Additional specific patterns
508
+ if ([normalized containsString:@"wait"] || [normalized containsString:@"busy"]) {
509
+ return @"wait";
510
+ }
511
+
512
+ if ([normalized containsString:@"help"] || [normalized containsString:@"question"]) {
513
+ return @"help";
514
+ }
515
+
516
+ if ([normalized containsString:@"progress"]) {
517
+ return @"progress";
457
518
  }
458
519
 
459
520
  return nil;
@@ -461,9 +522,10 @@ static NSString* cursorTypeFromCursorName(NSString *value) {
461
522
 
462
523
  static NSString* cursorTypeFromNSCursor(NSCursor *cursor) {
463
524
  if (!cursor) {
464
- return nil;
525
+ return @"default";
465
526
  }
466
527
 
528
+ // Standard macOS cursors
467
529
  if (cursor == [NSCursor arrowCursor]) {
468
530
  return @"default";
469
531
  }
@@ -498,34 +560,66 @@ static NSString* cursorTypeFromNSCursor(NSCursor *cursor) {
498
560
  if (cursor == [NSCursor contextualMenuCursor]) {
499
561
  return @"context-menu";
500
562
  }
501
- if ([NSCursor respondsToSelector:@selector(resizeLeftRightCursor)] &&
502
- (cursor == [NSCursor resizeLeftRightCursor] ||
503
- cursor == [NSCursor resizeLeftCursor] ||
504
- cursor == [NSCursor resizeRightCursor])) {
505
- return @"col-resize";
563
+
564
+ // Resize cursors - improved detection
565
+ if ([NSCursor respondsToSelector:@selector(resizeLeftRightCursor)]) {
566
+ if (cursor == [NSCursor resizeLeftRightCursor]) {
567
+ return @"col-resize";
568
+ }
506
569
  }
507
- if ([NSCursor respondsToSelector:@selector(resizeUpDownCursor)] &&
508
- (cursor == [NSCursor resizeUpDownCursor] ||
509
- cursor == [NSCursor resizeUpCursor] ||
510
- cursor == [NSCursor resizeDownCursor])) {
511
- return @"ns-resize";
570
+ if ([NSCursor respondsToSelector:@selector(resizeLeftCursor)]) {
571
+ if (cursor == [NSCursor resizeLeftCursor]) {
572
+ return @"col-resize";
573
+ }
574
+ }
575
+ if ([NSCursor respondsToSelector:@selector(resizeRightCursor)]) {
576
+ if (cursor == [NSCursor resizeRightCursor]) {
577
+ return @"col-resize";
578
+ }
512
579
  }
580
+ if ([NSCursor respondsToSelector:@selector(resizeUpDownCursor)]) {
581
+ if (cursor == [NSCursor resizeUpDownCursor]) {
582
+ return @"ns-resize";
583
+ }
584
+ }
585
+ if ([NSCursor respondsToSelector:@selector(resizeUpCursor)]) {
586
+ if (cursor == [NSCursor resizeUpCursor]) {
587
+ return @"ns-resize";
588
+ }
589
+ }
590
+ if ([NSCursor respondsToSelector:@selector(resizeDownCursor)]) {
591
+ if (cursor == [NSCursor resizeDownCursor]) {
592
+ return @"ns-resize";
593
+ }
594
+ }
595
+
513
596
  if ([NSCursor respondsToSelector:@selector(disappearingItemCursor)] &&
514
597
  cursor == [NSCursor disappearingItemCursor]) {
515
598
  return @"default";
516
599
  }
517
600
 
518
- NSString *derived = cursorTypeFromCursorName(NSStringFromClass([cursor class]));
601
+ // Try to get class name and description for debugging
602
+ NSString *className = NSStringFromClass([cursor class]);
603
+ NSString *description = [cursor description];
604
+
605
+ MRLog(@"๐Ÿ” Unknown cursor class: %@, description: %@", className ?: @"nil", description ?: @"nil");
606
+
607
+ // Try name-based detection
608
+ NSString *derived = cursorTypeFromCursorName(className);
519
609
  if (derived) {
610
+ MRLog(@"๐ŸŽฏ Cursor type from class name: %@", derived);
520
611
  return derived;
521
612
  }
522
613
 
523
- derived = cursorTypeFromCursorName([cursor description]);
614
+ derived = cursorTypeFromCursorName(description);
524
615
  if (derived) {
616
+ MRLog(@"๐ŸŽฏ Cursor type from description: %@", derived);
525
617
  return derived;
526
618
  }
527
619
 
528
- return nil;
620
+ // Default fallback
621
+ MRLog(@"โš ๏ธ Unknown cursor type, defaulting to 'default'");
622
+ return @"default";
529
623
  }
530
624
 
531
625
  static NSString* detectSystemCursorType(void) {
@@ -533,21 +627,59 @@ static NSString* detectSystemCursorType(void) {
533
627
 
534
628
  void (^fetchCursorBlock)(void) = ^{
535
629
  NSCursor *currentCursor = nil;
630
+
631
+ // Try different methods to get current cursor
536
632
  if ([NSCursor respondsToSelector:@selector(currentSystemCursor)]) {
537
633
  currentCursor = [NSCursor currentSystemCursor];
634
+ NSLog(@"๐Ÿ–ฑ๏ธ currentSystemCursor: %@", currentCursor ?: @"nil");
538
635
  }
636
+
539
637
  if (!currentCursor) {
540
638
  currentCursor = [NSCursor currentCursor];
639
+ NSLog(@"๐Ÿ–ฑ๏ธ currentCursor: %@", currentCursor ?: @"nil");
541
640
  }
542
641
 
543
- NSString *derivedType = cursorTypeFromNSCursor(currentCursor);
544
- if (derivedType) {
545
- cursorType = derivedType;
546
- } else if (currentCursor) {
642
+ if (currentCursor) {
643
+ NSString *className = NSStringFromClass([currentCursor class]);
644
+ NSString *description = [currentCursor description];
645
+ NSLog(@"๐Ÿ–ฑ๏ธ Cursor class: %@, description: %@", className ?: @"nil", description ?: @"nil");
646
+
647
+ // Try to identify cursor by hotspot and size (more reliable than pointer equality)
648
+ NSPoint hotspot = [currentCursor hotSpot];
649
+ NSSize cursorSize = [[currentCursor image] size];
650
+
651
+ NSLog(@"๐ŸŽฏ Cursor hotspot: (%.1f, %.1f), size: %.1fx%.1f",
652
+ hotspot.x, hotspot.y, cursorSize.width, cursorSize.height);
653
+
654
+ // Common cursor patterns by hotspot (adjusted for modern macOS cursor sizes)
655
+ if ((hotspot.x >= 0.0 && hotspot.x <= 3.0) && (hotspot.y >= 0.0 && hotspot.y <= 3.0)) {
656
+ // Arrow cursor typically has hotspot at top-left corner
657
+ cursorType = @"default";
658
+ NSLog(@"๐ŸŽฏ ARROW CURSOR (by hotspot) -> default");
659
+ } else if ((hotspot.x >= 12.0 && hotspot.x <= 16.0) && (hotspot.y >= 6.0 && hotspot.y <= 10.0)) {
660
+ // I-beam cursor typically has hotspot around middle
661
+ cursorType = @"text";
662
+ NSLog(@"๐ŸŽฏ IBEAM CURSOR (by hotspot) -> text");
663
+ } else if ((hotspot.x >= 5.0 && hotspot.x <= 10.0) && (hotspot.y >= 0.0 && hotspot.y <= 4.0)) {
664
+ // Pointing hand cursor typically has hotspot at finger tip
665
+ cursorType = @"pointer";
666
+ NSLog(@"๐ŸŽฏ POINTING HAND CURSOR (by hotspot) -> pointer");
667
+ } else {
668
+ // Fallback to more detailed analysis
669
+ NSString *derivedType = cursorTypeFromNSCursor(currentCursor);
670
+ if (derivedType && ![derivedType isEqualToString:@"default"]) {
671
+ cursorType = derivedType;
672
+ NSLog(@"๐ŸŽฏ SYSTEM CURSOR TYPE (derived): %@", cursorType);
673
+ } else {
674
+ // Use accessibility detection if system cursor is generic
675
+ cursorType = @"default";
676
+ NSLog(@"๐ŸŽฏ SYSTEM CURSOR TYPE -> default (will try AX)");
677
+ }
678
+ }
679
+ } else {
680
+ NSLog(@"๐Ÿ–ฑ๏ธ No current cursor found");
547
681
  cursorType = @"default";
548
682
  }
549
-
550
- NSLog(@"๐ŸŽฏ SYSTEM CURSOR TYPE: %@", cursorType ? cursorType : @"(nil)");
551
683
  };
552
684
 
553
685
  if ([NSThread isMainThread]) {
@@ -563,7 +695,7 @@ NSString* getCursorType() {
563
695
  @autoreleasepool {
564
696
  g_cursorTypeCounter++;
565
697
 
566
- // Hybrid: AX (strict) first, then system cursor fallback.
698
+ // Get cursor position first
567
699
  BOOL hasCursorPosition = NO;
568
700
  CGPoint cursorPos = CGPointZero;
569
701
 
@@ -588,22 +720,32 @@ NSString* getCursorType() {
588
720
  }
589
721
  }
590
722
 
723
+ // Try multiple detection methods
724
+ NSString *systemCursorType = detectSystemCursorType();
591
725
  NSString *axCursorType = nil;
726
+
592
727
  if (hasCursorPosition) {
593
728
  axCursorType = detectCursorTypeUsingAccessibility(cursorPos);
594
729
  }
595
730
 
596
- NSString *systemCursorType = detectSystemCursorType();
731
+ NSString *finalType = @"default";
597
732
 
598
- NSString *finalType = nil;
599
- if (axCursorType && ![axCursorType isEqualToString:@"default"]) {
600
- finalType = axCursorType;
601
- } else if (systemCursorType && [systemCursorType length] > 0) {
733
+ // System cursor has priority since it's more accurate for visual state
734
+ NSLog(@"๐ŸŽฏ CURSOR DECISION: system='%@', ax='%@'", systemCursorType ?: @"nil", axCursorType ?: @"nil");
735
+
736
+ // Use system cursor if available (it's more accurate for visual appearance)
737
+ if (systemCursorType && [systemCursorType length] > 0) {
602
738
  finalType = systemCursorType;
603
- } else if (axCursorType && [axCursorType length] > 0) {
739
+ NSLog(@"๐ŸŽฏ Using SYSTEM cursor: %@", finalType);
740
+ }
741
+ // Fallback to AX cursor if system cursor is not available
742
+ else if (axCursorType && [axCursorType length] > 0) {
604
743
  finalType = axCursorType;
605
- } else {
606
- finalType = @"default";
744
+ NSLog(@"๐ŸŽฏ Using AX cursor (fallback): %@", finalType);
745
+ }
746
+ // Final fallback
747
+ else {
748
+ NSLog(@"๐ŸŽฏ Fallback to default cursor");
607
749
  }
608
750
 
609
751
  NSLog(@"๐ŸŽฏ FINAL CURSOR TYPE: %@", finalType);
@@ -1,6 +1,7 @@
1
1
  #import <napi.h>
2
2
  #import <CoreGraphics/CoreGraphics.h>
3
3
  #import <AppKit/AppKit.h>
4
+ #import "../logging.h"
4
5
 
5
6
  // Thread-safe cursor tracking for Electron
6
7
  static dispatch_queue_t g_cursorQueue = nil;
@@ -12,47 +13,65 @@ static void initializeCursorQueue() {
12
13
  });
13
14
  }
14
15
 
16
+ static NSString* MapCursorToType(NSCursor *cursor) {
17
+ if (!cursor) return @"default";
18
+
19
+ if (cursor == [NSCursor arrowCursor]) return @"default";
20
+ if (cursor == [NSCursor IBeamCursor]) return @"text";
21
+ if ([NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)] &&
22
+ cursor == [NSCursor IBeamCursorForVerticalLayout]) return @"text";
23
+ if (cursor == [NSCursor pointingHandCursor]) return @"pointer";
24
+ if ([NSCursor respondsToSelector:@selector(resizeLeftRightCursor)]) {
25
+ if (cursor == [NSCursor resizeLeftRightCursor] ||
26
+ [NSCursor instancesRespondToSelector:@selector(resizeLeftCursor)] && (cursor == [NSCursor resizeLeftCursor]) ||
27
+ [NSCursor instancesRespondToSelector:@selector(resizeRightCursor)] && (cursor == [NSCursor resizeRightCursor])) {
28
+ return @"col-resize";
29
+ }
30
+ }
31
+ if ([NSCursor respondsToSelector:@selector(resizeUpDownCursor)]) {
32
+ if (cursor == [NSCursor resizeUpDownCursor] ||
33
+ [NSCursor instancesRespondToSelector:@selector(resizeUpCursor)] && (cursor == [NSCursor resizeUpCursor]) ||
34
+ [NSCursor instancesRespondToSelector:@selector(resizeDownCursor)] && (cursor == [NSCursor resizeDownCursor])) {
35
+ return @"ns-resize";
36
+ }
37
+ }
38
+ if ([NSCursor respondsToSelector:@selector(openHandCursor)] && cursor == [NSCursor openHandCursor]) return @"grab";
39
+ if ([NSCursor respondsToSelector:@selector(closedHandCursor)] && cursor == [NSCursor closedHandCursor]) return @"grabbing";
40
+ if ([NSCursor respondsToSelector:@selector(crosshairCursor)] && cursor == [NSCursor crosshairCursor]) return @"crosshair";
41
+ if ([NSCursor respondsToSelector:@selector(operationNotAllowedCursor)] && cursor == [NSCursor operationNotAllowedCursor]) return @"not-allowed";
42
+ if ([NSCursor respondsToSelector:@selector(dragCopyCursor)] && cursor == [NSCursor dragCopyCursor]) return @"copy";
43
+ if ([NSCursor respondsToSelector:@selector(dragLinkCursor)] && cursor == [NSCursor dragLinkCursor]) return @"alias";
44
+ if ([NSCursor respondsToSelector:@selector(contextualMenuCursor)] && cursor == [NSCursor contextualMenuCursor]) return @"context-menu";
45
+
46
+ return @"default";
47
+ }
48
+
15
49
  // NAPI Function: Get Cursor Position (Electron-safe)
16
50
  Napi::Value GetCursorPositionElectronSafe(const Napi::CallbackInfo& info) {
17
51
  Napi::Env env = info.Env();
18
52
 
19
53
  @try {
20
54
  initializeCursorQueue();
21
-
22
55
  __block CGPoint mouseLocation = CGPointZero;
23
- __block NSString *cursorType = @"arrow";
24
-
56
+ __block NSString *cursorType = @"default";
57
+
25
58
  dispatch_sync(g_cursorQueue, ^{
26
59
  @try {
27
- // Get mouse location
28
- mouseLocation = [NSEvent mouseLocation];
29
-
30
- // Convert to screen coordinates (flip Y)
31
- NSArray *screens = [NSScreen screens];
32
- if (screens.count > 0) {
33
- NSScreen *mainScreen = screens[0];
34
- CGFloat screenHeight = mainScreen.frame.size.height;
35
- mouseLocation.y = screenHeight - mouseLocation.y;
60
+ // Use CGEventGetLocation to match global logical coordinates used elsewhere
61
+ CGEventRef event = CGEventCreate(NULL);
62
+ mouseLocation = CGEventGetLocation(event);
63
+ if (event) CFRelease(event);
64
+
65
+ // Prefer currentSystemCursor when available for accurate system-wide state
66
+ NSCursor *cursor = nil;
67
+ if ([NSCursor respondsToSelector:@selector(currentSystemCursor)]) {
68
+ cursor = [NSCursor performSelector:@selector(currentSystemCursor)];
36
69
  }
37
-
38
- // Get cursor type safely
39
- NSCursor *currentCursor = [NSCursor currentCursor];
40
- if (currentCursor) {
41
- if (currentCursor == [NSCursor arrowCursor]) {
42
- cursorType = @"arrow";
43
- } else if (currentCursor == [NSCursor IBeamCursor]) {
44
- cursorType = @"ibeam";
45
- } else if (currentCursor == [NSCursor pointingHandCursor]) {
46
- cursorType = @"hand";
47
- } else if (currentCursor == [NSCursor resizeLeftRightCursor]) {
48
- cursorType = @"resize-horizontal";
49
- } else if (currentCursor == [NSCursor resizeUpDownCursor]) {
50
- cursorType = @"resize-vertical";
51
- } else {
52
- cursorType = @"default";
53
- }
70
+ if (!cursor) {
71
+ cursor = [NSCursor currentCursor];
54
72
  }
55
-
73
+ cursorType = MapCursorToType(cursor);
74
+ MRLog(@"Electron-safe cursor: %@ at (%.0f,%.0f)", cursorType, mouseLocation.x, mouseLocation.y);
56
75
  } @catch (NSException *e) {
57
76
  NSLog(@"โŒ Exception getting cursor position: %@", e.reason);
58
77
  }
@@ -80,7 +99,7 @@ Napi::Object InitCursorTrackerElectron(Napi::Env env, Napi::Object exports) {
80
99
 
81
100
  exports.Set("getCursorPosition", Napi::Function::New(env, GetCursorPositionElectronSafe));
82
101
 
83
- NSLog(@"โœ… Electron-safe cursor tracker initialized");
102
+ MRLog(@"โœ… Electron-safe cursor tracker initialized");
84
103
  return exports;
85
104
 
86
105
  } @catch (NSException *e) {
package/src/logging.h ADDED
@@ -0,0 +1,17 @@
1
+ // Lightweight runtime-controlled logging helpers
2
+ #import <Foundation/Foundation.h>
3
+
4
+ static inline BOOL MRShouldVerboseLog(void) {
5
+ static dispatch_once_t onceToken;
6
+ static BOOL enabled = NO;
7
+ dispatch_once(&onceToken, ^{
8
+ const char *env = getenv("MAC_RECORDER_DEBUG");
9
+ if (env && (*env == '1' || *env == 't' || *env == 'T' || *env == 'y' || *env == 'Y')) {
10
+ enabled = YES;
11
+ }
12
+ });
13
+ return enabled;
14
+ }
15
+
16
+ #define MRLog(fmt, ...) do { if (MRShouldVerboseLog()) { NSLog((fmt), ##__VA_ARGS__); } } while(0)
17
+
@@ -4,6 +4,7 @@
4
4
  #import <CoreGraphics/CoreGraphics.h>
5
5
  #import <ImageIO/ImageIO.h>
6
6
  #import <CoreAudio/CoreAudio.h>
7
+ #import "logging.h"
7
8
 
8
9
  // Import screen capture (ScreenCaptureKit only)
9
10
  #import "screen_capture_kit.h"
@@ -70,11 +71,11 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
70
71
 
71
72
  // IMPORTANT: Clean up any stale recording state before starting
72
73
  // This fixes the issue where macOS 14/13 users get "recording already in progress"
73
- NSLog(@"๐Ÿงน Cleaning up any previous recording state...");
74
+ MRLog(@"๐Ÿงน Cleaning up any previous recording state...");
74
75
  cleanupRecording();
75
76
 
76
77
  if (g_isRecording) {
77
- NSLog(@"โš ๏ธ Still recording after cleanup - forcing stop");
78
+ MRLog(@"โš ๏ธ Still recording after cleanup - forcing stop");
78
79
  return Napi::Boolean::New(env, false);
79
80
  }
80
81
 
@@ -168,13 +169,13 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
168
169
  // Window ID support
169
170
  if (options.Has("windowId") && !options.Get("windowId").IsNull()) {
170
171
  windowID = options.Get("windowId").As<Napi::Number>().Uint32Value();
171
- NSLog(@"๐ŸชŸ Window ID specified: %u", windowID);
172
+ MRLog(@"๐ŸชŸ Window ID specified: %u", windowID);
172
173
  }
173
174
  }
174
175
 
175
176
  @try {
176
177
  // Smart Recording Selection: ScreenCaptureKit vs Alternative
177
- NSLog(@"๐ŸŽฏ Smart Recording Engine Selection");
178
+ MRLog(@"๐ŸŽฏ Smart Recording Engine Selection");
178
179
 
179
180
  // Electron environment detection (removed disable logic)
180
181
  BOOL isElectron = (NSBundle.mainBundle.bundleIdentifier &&
@@ -186,8 +187,8 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
186
187
  [NSBundle.mainBundle.bundlePath containsString:@"Electron"]);
187
188
 
188
189
  if (isElectron) {
189
- NSLog(@"โšก Electron environment detected - continuing with ScreenCaptureKit");
190
- NSLog(@"โš ๏ธ Warning: ScreenCaptureKit in Electron may require additional stability measures");
190
+ MRLog(@"โšก Electron environment detected - continuing with ScreenCaptureKit");
191
+ MRLog(@"โš ๏ธ Warning: ScreenCaptureKit in Electron may require additional stability measures");
191
192
  }
192
193
 
193
194
  // Check macOS version for ScreenCaptureKit compatibility
@@ -196,13 +197,13 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
196
197
  BOOL isM14Plus = (osVersion.majorVersion >= 14);
197
198
  BOOL isM13Plus = (osVersion.majorVersion >= 13);
198
199
 
199
- NSLog(@"๐Ÿ–ฅ๏ธ macOS Version: %ld.%ld.%ld",
200
+ MRLog(@"๐Ÿ–ฅ๏ธ macOS Version: %ld.%ld.%ld",
200
201
  (long)osVersion.majorVersion, (long)osVersion.minorVersion, (long)osVersion.patchVersion);
201
202
 
202
203
  // Force AVFoundation for debugging/testing
203
204
  BOOL forceAVFoundation = (getenv("FORCE_AVFOUNDATION") != NULL);
204
205
  if (forceAVFoundation) {
205
- NSLog(@"๐Ÿ”ง FORCE_AVFOUNDATION environment variable detected - skipping ScreenCaptureKit");
206
+ MRLog(@"๐Ÿ”ง FORCE_AVFOUNDATION environment variable detected - skipping ScreenCaptureKit");
206
207
  }
207
208
 
208
209
  // Electron-first priority: This application is built for Electron.js
@@ -210,16 +211,16 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
210
211
  // macOS 14/13 โ†’ AVFoundation (including Electron)
211
212
  if (isM15Plus && !forceAVFoundation) {
212
213
  if (isElectron) {
213
- NSLog(@"โšก ELECTRON PRIORITY: macOS 15+ Electron โ†’ ScreenCaptureKit with full support");
214
+ MRLog(@"โšก ELECTRON PRIORITY: macOS 15+ Electron โ†’ ScreenCaptureKit with full support");
214
215
  } else {
215
- NSLog(@"โœ… macOS 15+ Node.js โ†’ ScreenCaptureKit available with full compatibility");
216
+ MRLog(@"โœ… macOS 15+ Node.js โ†’ ScreenCaptureKit available with full compatibility");
216
217
  }
217
218
 
218
219
  // Try ScreenCaptureKit with extensive safety measures
219
220
  @try {
220
221
  if ([ScreenCaptureKitRecorder isScreenCaptureKitAvailable]) {
221
- NSLog(@"โœ… ScreenCaptureKit availability check passed");
222
- NSLog(@"๐ŸŽฏ Using ScreenCaptureKit - overlay windows will be automatically excluded");
222
+ MRLog(@"โœ… ScreenCaptureKit availability check passed");
223
+ MRLog(@"๐ŸŽฏ Using ScreenCaptureKit - overlay windows will be automatically excluded");
223
224
 
224
225
  // Create configuration for ScreenCaptureKit
225
226
  NSMutableDictionary *sckConfig = [NSMutableDictionary dictionary];
@@ -251,7 +252,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
251
252
  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
252
253
  if (!sckStarted && !g_isRecording) {
253
254
  sckTimedOut = YES;
254
- NSLog(@"โฐ ScreenCaptureKit initialization timeout (3s)");
255
+ MRLog(@"โฐ ScreenCaptureKit initialization timeout (3s)");
255
256
  }
256
257
  });
257
258
 
@@ -263,8 +264,8 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
263
264
 
264
265
  // ScreenCaptureKit baลŸlatma baลŸarฤฑlฤฑ - validation yapmฤฑyoruz
265
266
  sckStarted = YES;
266
- NSLog(@"๐ŸŽฌ RECORDING METHOD: ScreenCaptureKit");
267
- NSLog(@"โœ… ScreenCaptureKit recording started successfully");
267
+ MRLog(@"๐ŸŽฌ RECORDING METHOD: ScreenCaptureKit");
268
+ MRLog(@"โœ… ScreenCaptureKit recording started successfully");
268
269
  g_isRecording = true;
269
270
  return Napi::Boolean::New(env, true);
270
271
  } else {
@@ -274,7 +275,6 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
274
275
  } @catch (NSException *sckException) {
275
276
  NSLog(@"โŒ Exception during ScreenCaptureKit startup: %@", sckException.reason);
276
277
  }
277
-
278
278
  NSLog(@"โŒ ScreenCaptureKit failed or unsafe - will fallback to AVFoundation");
279
279
 
280
280
  } else {
@@ -285,22 +285,22 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
285
285
  }
286
286
 
287
287
  // If we reach here, ScreenCaptureKit failed, so fall through to AVFoundation
288
- NSLog(@"โญ๏ธ ScreenCaptureKit failed - falling back to AVFoundation");
288
+ MRLog(@"โญ๏ธ ScreenCaptureKit failed - falling back to AVFoundation");
289
289
  } else {
290
290
  // macOS 14/13 or forced AVFoundation โ†’ ALWAYS use AVFoundation (Electron supported!)
291
291
  if (isElectron) {
292
292
  if (isM14Plus) {
293
- NSLog(@"โšก ELECTRON PRIORITY: macOS 14/13 Electron โ†’ AVFoundation with full support");
293
+ MRLog(@"โšก ELECTRON PRIORITY: macOS 14/13 Electron โ†’ AVFoundation with full support");
294
294
  } else if (isM13Plus) {
295
- NSLog(@"โšก ELECTRON PRIORITY: macOS 13 Electron โ†’ AVFoundation with limited features");
295
+ MRLog(@"โšก ELECTRON PRIORITY: macOS 13 Electron โ†’ AVFoundation with limited features");
296
296
  }
297
297
  } else {
298
298
  if (isM15Plus) {
299
- NSLog(@"๐ŸŽฏ macOS 15+ Node.js with FORCE_AVFOUNDATION โ†’ using AVFoundation");
299
+ MRLog(@"๐ŸŽฏ macOS 15+ Node.js with FORCE_AVFOUNDATION โ†’ using AVFoundation");
300
300
  } else if (isM14Plus) {
301
- NSLog(@"๐ŸŽฏ macOS 14 Node.js โ†’ using AVFoundation (primary method)");
301
+ MRLog(@"๐ŸŽฏ macOS 14 Node.js โ†’ using AVFoundation (primary method)");
302
302
  } else if (isM13Plus) {
303
- NSLog(@"๐ŸŽฏ macOS 13 Node.js โ†’ using AVFoundation (limited features)");
303
+ MRLog(@"๐ŸŽฏ macOS 13 Node.js โ†’ using AVFoundation (limited features)");
304
304
  }
305
305
  }
306
306
 
@@ -310,11 +310,11 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
310
310
  }
311
311
 
312
312
  // DIRECT AVFoundation for all environments (Node.js + Electron)
313
- NSLog(@"โญ๏ธ Using AVFoundation directly - supports both Node.js and Electron");
313
+ MRLog(@"โญ๏ธ Using AVFoundation directly - supports both Node.js and Electron");
314
314
  }
315
315
 
316
316
  // AVFoundation recording (either fallback from ScreenCaptureKit or direct)
317
- NSLog(@"๐ŸŽฅ Starting AVFoundation recording...");
317
+ MRLog(@"๐ŸŽฅ Starting AVFoundation recording...");
318
318
 
319
319
  @try {
320
320
  // Import AVFoundation recording functions (if available)
@@ -331,8 +331,8 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
331
331
  captureCursor, includeMicrophone, includeSystemAudio, audioDeviceId);
332
332
 
333
333
  if (avResult) {
334
- NSLog(@"๐ŸŽฅ RECORDING METHOD: AVFoundation");
335
- NSLog(@"โœ… AVFoundation recording started successfully");
334
+ MRLog(@"๐ŸŽฅ RECORDING METHOD: AVFoundation");
335
+ MRLog(@"โœ… AVFoundation recording started successfully");
336
336
  g_isRecording = true;
337
337
  return Napi::Boolean::New(env, true);
338
338
  } else {
@@ -358,12 +358,12 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
358
358
  Napi::Value StopRecording(const Napi::CallbackInfo& info) {
359
359
  Napi::Env env = info.Env();
360
360
 
361
- NSLog(@"๐Ÿ“ž StopRecording native method called");
361
+ MRLog(@"๐Ÿ“ž StopRecording native method called");
362
362
 
363
363
  // Try ScreenCaptureKit first
364
364
  if (@available(macOS 12.3, *)) {
365
365
  if ([ScreenCaptureKitRecorder isRecording]) {
366
- NSLog(@"๐Ÿ›‘ Stopping ScreenCaptureKit recording");
366
+ MRLog(@"๐Ÿ›‘ Stopping ScreenCaptureKit recording");
367
367
  [ScreenCaptureKitRecorder stopRecording];
368
368
  g_isRecording = false;
369
369
  return Napi::Boolean::New(env, true);
@@ -376,7 +376,7 @@ Napi::Value StopRecording(const Napi::CallbackInfo& info) {
376
376
 
377
377
  @try {
378
378
  if (isAVFoundationRecording()) {
379
- NSLog(@"๐Ÿ›‘ Stopping AVFoundation recording");
379
+ MRLog(@"๐Ÿ›‘ Stopping AVFoundation recording");
380
380
  if (stopAVFoundationRecording()) {
381
381
  g_isRecording = false;
382
382
  return Napi::Boolean::New(env, true);
@@ -392,7 +392,7 @@ Napi::Value StopRecording(const Napi::CallbackInfo& info) {
392
392
  return Napi::Boolean::New(env, false);
393
393
  }
394
394
 
395
- NSLog(@"โš ๏ธ No active recording found to stop");
395
+ MRLog(@"โš ๏ธ No active recording found to stop");
396
396
  g_isRecording = false;
397
397
  return Napi::Boolean::New(env, true);
398
398
  }
@@ -997,4 +997,4 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
997
997
  return exports;
998
998
  }
999
999
 
1000
- NODE_API_MODULE(mac_recorder, Init)
1000
+ NODE_API_MODULE(mac_recorder, Init)
@@ -1,4 +1,5 @@
1
1
  #import "screen_capture_kit.h"
2
+ #import "logging.h"
2
3
 
3
4
  // Pure ScreenCaptureKit implementation - NO AVFoundation
4
5
  static SCStream * API_AVAILABLE(macos(12.3)) g_stream = nil;
@@ -13,11 +14,11 @@ static NSString *g_outputPath = nil;
13
14
 
14
15
  @implementation PureScreenCaptureDelegate
15
16
  - (void)stream:(SCStream * API_AVAILABLE(macos(12.3)))stream didStopWithError:(NSError *)error API_AVAILABLE(macos(12.3)) {
16
- NSLog(@"๐Ÿ›‘ Pure ScreenCapture stream stopped");
17
+ MRLog(@"๐Ÿ›‘ Pure ScreenCapture stream stopped");
17
18
 
18
19
  // Prevent recursive calls during cleanup
19
20
  if (g_isCleaningUp) {
20
- NSLog(@"โš ๏ธ Already cleaning up, ignoring delegate callback");
21
+ MRLog(@"โš ๏ธ Already cleaning up, ignoring delegate callback");
21
22
  return;
22
23
  }
23
24
 
@@ -26,7 +27,7 @@ static NSString *g_outputPath = nil;
26
27
  if (error) {
27
28
  NSLog(@"โŒ Stream error: %@", error);
28
29
  } else {
29
- NSLog(@"โœ… Stream stopped cleanly");
30
+ MRLog(@"โœ… Stream stopped cleanly");
30
31
  }
31
32
 
32
33
  // Use dispatch_async to prevent potential deadlocks in Electron
@@ -50,7 +51,7 @@ static NSString *g_outputPath = nil;
50
51
  + (BOOL)startRecordingWithConfiguration:(NSDictionary *)config delegate:(id)delegate error:(NSError **)error {
51
52
  @synchronized([ScreenCaptureKitRecorder class]) {
52
53
  if (g_isRecording || g_isCleaningUp) {
53
- NSLog(@"โš ๏ธ Already recording or cleaning up (recording:%d cleaning:%d)", g_isRecording, g_isCleaningUp);
54
+ MRLog(@"โš ๏ธ Already recording or cleaning up (recording:%d cleaning:%d)", g_isRecording, g_isCleaningUp);
54
55
  return NO;
55
56
  }
56
57
 
@@ -73,13 +74,13 @@ static NSString *g_outputPath = nil;
73
74
  NSNumber *includeMicrophone = config[@"includeMicrophone"];
74
75
  NSNumber *includeSystemAudio = config[@"includeSystemAudio"];
75
76
 
76
- NSLog(@"๐ŸŽฌ Starting PURE ScreenCaptureKit recording (NO AVFoundation)");
77
- NSLog(@"๐Ÿ”ง Config: cursor=%@ mic=%@ system=%@ display=%@ window=%@ crop=%@",
77
+ MRLog(@"๐ŸŽฌ Starting PURE ScreenCaptureKit recording (NO AVFoundation)");
78
+ MRLog(@"๐Ÿ”ง Config: cursor=%@ mic=%@ system=%@ display=%@ window=%@ crop=%@",
78
79
  captureCursor, includeMicrophone, includeSystemAudio, displayId, windowId, captureRect);
79
80
 
80
81
  // CRITICAL DEBUG: Log EXACT audio parameter values
81
- NSLog(@"๐Ÿ” AUDIO DEBUG: includeMicrophone type=%@ value=%d", [includeMicrophone class], [includeMicrophone boolValue]);
82
- NSLog(@"๐Ÿ” AUDIO DEBUG: includeSystemAudio type=%@ value=%d", [includeSystemAudio class], [includeSystemAudio boolValue]);
82
+ MRLog(@"๐Ÿ” AUDIO DEBUG: includeMicrophone type=%@ value=%d", [includeMicrophone class], [includeMicrophone boolValue]);
83
+ MRLog(@"๐Ÿ” AUDIO DEBUG: includeSystemAudio type=%@ value=%d", [includeSystemAudio class], [includeSystemAudio boolValue]);
83
84
 
84
85
  // Get shareable content
85
86
  [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent *content, NSError *contentError) {
@@ -88,13 +89,13 @@ static NSString *g_outputPath = nil;
88
89
  return;
89
90
  }
90
91
 
91
- NSLog(@"โœ… Got %lu displays, %lu windows for pure recording",
92
+ MRLog(@"โœ… Got %lu displays, %lu windows for pure recording",
92
93
  content.displays.count, content.windows.count);
93
94
 
94
95
  // CRITICAL DEBUG: List all available displays in ScreenCaptureKit
95
- NSLog(@"๐Ÿ” ScreenCaptureKit available displays:");
96
+ MRLog(@"๐Ÿ” ScreenCaptureKit available displays:");
96
97
  for (SCDisplay *display in content.displays) {
97
- NSLog(@" Display ID=%u, Size=%dx%d, Frame=(%.0f,%.0f,%.0fx%.0f)",
98
+ MRLog(@" Display ID=%u, Size=%dx%d, Frame=(%.0f,%.0f,%.0fx%.0f)",
98
99
  display.displayID, (int)display.width, (int)display.height,
99
100
  display.frame.origin.x, display.frame.origin.y,
100
101
  display.frame.size.width, display.frame.size.height);
@@ -119,7 +120,7 @@ static NSString *g_outputPath = nil;
119
120
  }
120
121
 
121
122
  if (targetWindow && targetApp) {
122
- NSLog(@"๐ŸชŸ Recording window: %@ (%ux%u)",
123
+ MRLog(@"๐ŸชŸ Recording window: %@ (%ux%u)",
123
124
  targetWindow.title, (unsigned)targetWindow.frame.size.width, (unsigned)targetWindow.frame.size.height);
124
125
  filter = [[SCContentFilter alloc] initWithDesktopIndependentWindow:targetWindow];
125
126
  recordingWidth = (NSInteger)targetWindow.frame.size.width;
@@ -134,12 +135,12 @@ static NSString *g_outputPath = nil;
134
135
 
135
136
  if (displayId && [displayId integerValue] != 0) {
136
137
  // Find specific display
137
- NSLog(@"๐ŸŽฏ Looking for display ID=%@ in ScreenCaptureKit list", displayId);
138
+ MRLog(@"๐ŸŽฏ Looking for display ID=%@ in ScreenCaptureKit list", displayId);
138
139
  for (SCDisplay *display in content.displays) {
139
- NSLog(@" Checking display ID=%u vs requested=%u", display.displayID, [displayId unsignedIntValue]);
140
+ MRLog(@" Checking display ID=%u vs requested=%u", display.displayID, [displayId unsignedIntValue]);
140
141
  if (display.displayID == [displayId unsignedIntValue]) {
141
142
  targetDisplay = display;
142
- NSLog(@"โœ… FOUND matching display ID=%u", display.displayID);
143
+ MRLog(@"โœ… FOUND matching display ID=%u", display.displayID);
143
144
  break;
144
145
  }
145
146
  }
@@ -158,7 +159,7 @@ static NSString *g_outputPath = nil;
158
159
  return;
159
160
  }
160
161
 
161
- NSLog(@"๐Ÿ–ฅ๏ธ Recording display %u (%dx%d)",
162
+ MRLog(@"๐Ÿ–ฅ๏ธ Recording display %u (%dx%d)",
162
163
  targetDisplay.displayID, (int)targetDisplay.width, (int)targetDisplay.height);
163
164
  filter = [[SCContentFilter alloc] initWithDisplay:targetDisplay excludingWindows:@[]];
164
165
  recordingWidth = targetDisplay.width;
@@ -171,7 +172,7 @@ static NSString *g_outputPath = nil;
171
172
  CGFloat cropHeight = [captureRect[@"height"] doubleValue];
172
173
 
173
174
  if (cropWidth > 0 && cropHeight > 0) {
174
- NSLog(@"๐Ÿ”ฒ Crop area specified: %.0fx%.0f at (%.0f,%.0f)",
175
+ MRLog(@"๐Ÿ”ฒ Crop area specified: %.0fx%.0f at (%.0f,%.0f)",
175
176
  cropWidth, cropHeight,
176
177
  [captureRect[@"x"] doubleValue], [captureRect[@"y"] doubleValue]);
177
178
  recordingWidth = (NSInteger)cropWidth;
@@ -200,11 +201,11 @@ static NSString *g_outputPath = nil;
200
201
  CGFloat displayRelativeX = globalX - displayBounds.origin.x;
201
202
  CGFloat displayRelativeY = globalY - displayBounds.origin.y;
202
203
 
203
- NSLog(@"๐ŸŒ Global coords: (%.0f,%.0f) on Display ID=%u", globalX, globalY, targetDisplay.displayID);
204
- NSLog(@"๐Ÿ–ฅ๏ธ Display bounds: (%.0f,%.0f,%.0fx%.0f)",
204
+ MRLog(@"๐ŸŒ Global coords: (%.0f,%.0f) on Display ID=%u", globalX, globalY, targetDisplay.displayID);
205
+ MRLog(@"๐Ÿ–ฅ๏ธ Display bounds: (%.0f,%.0f,%.0fx%.0f)",
205
206
  displayBounds.origin.x, displayBounds.origin.y,
206
207
  displayBounds.size.width, displayBounds.size.height);
207
- NSLog(@"๐Ÿ“ Display-relative: (%.0f,%.0f) -> SourceRect", displayRelativeX, displayRelativeY);
208
+ MRLog(@"๐Ÿ“ Display-relative: (%.0f,%.0f) -> SourceRect", displayRelativeX, displayRelativeY);
208
209
 
209
210
  // Validate coordinates are within display bounds
210
211
  if (displayRelativeX >= 0 && displayRelativeY >= 0 &&
@@ -213,11 +214,11 @@ static NSString *g_outputPath = nil;
213
214
 
214
215
  CGRect sourceRect = CGRectMake(displayRelativeX, displayRelativeY, cropWidth, cropHeight);
215
216
  streamConfig.sourceRect = sourceRect;
216
- NSLog(@"โœ‚๏ธ Crop sourceRect applied: (%.0f,%.0f) %.0fx%.0f (display-relative)",
217
+ MRLog(@"โœ‚๏ธ Crop sourceRect applied: (%.0f,%.0f) %.0fx%.0f (display-relative)",
217
218
  displayRelativeX, displayRelativeY, cropWidth, cropHeight);
218
219
  } else {
219
220
  NSLog(@"โŒ Crop coordinates out of display bounds - skipping crop");
220
- NSLog(@" Relative: (%.0f,%.0f) size:(%.0fx%.0f) vs display:(%.0fx%.0f)",
221
+ MRLog(@" Relative: (%.0f,%.0f) size:(%.0fx%.0f) vs display:(%.0fx%.0f)",
221
222
  displayRelativeX, displayRelativeY, cropWidth, cropHeight,
222
223
  displayBounds.size.width, displayBounds.size.height);
223
224
  }
@@ -228,14 +229,14 @@ static NSString *g_outputPath = nil;
228
229
  BOOL shouldShowCursor = captureCursor ? [captureCursor boolValue] : YES;
229
230
  streamConfig.showsCursor = shouldShowCursor;
230
231
 
231
- NSLog(@"๐ŸŽฅ Pure ScreenCapture config: %ldx%ld @ 30fps, cursor=%d",
232
+ MRLog(@"๐ŸŽฅ Pure ScreenCapture config: %ldx%ld @ 30fps, cursor=%d",
232
233
  recordingWidth, recordingHeight, shouldShowCursor);
233
234
 
234
235
  // AUDIO SUPPORT - Enable both microphone and system audio
235
- NSLog(@"๐Ÿ” AUDIO PROCESSING: includeMicrophone=%@ includeSystemAudio=%@", includeMicrophone, includeSystemAudio);
236
+ MRLog(@"๐Ÿ” AUDIO PROCESSING: includeMicrophone=%@ includeSystemAudio=%@", includeMicrophone, includeSystemAudio);
236
237
  BOOL shouldCaptureMic = includeMicrophone ? [includeMicrophone boolValue] : NO;
237
238
  BOOL shouldCaptureSystemAudio = includeSystemAudio ? [includeSystemAudio boolValue] : NO;
238
- NSLog(@"๐Ÿ” AUDIO COMPUTED: shouldCaptureMic=%d shouldCaptureSystemAudio=%d", shouldCaptureMic, shouldCaptureSystemAudio);
239
+ MRLog(@"๐Ÿ” AUDIO COMPUTED: shouldCaptureMic=%d shouldCaptureSystemAudio=%d", shouldCaptureMic, shouldCaptureSystemAudio);
239
240
 
240
241
  // Enable audio if either microphone or system audio is requested
241
242
  if (@available(macOS 13.0, *)) {
@@ -245,19 +246,19 @@ static NSString *g_outputPath = nil;
245
246
  streamConfig.channelCount = 2;
246
247
 
247
248
  if (shouldCaptureMic && shouldCaptureSystemAudio) {
248
- NSLog(@"๐ŸŽต Both microphone and system audio enabled");
249
+ MRLog(@"๐ŸŽต Both microphone and system audio enabled");
249
250
  } else if (shouldCaptureMic) {
250
- NSLog(@"๐ŸŽค Microphone audio enabled");
251
+ MRLog(@"๐ŸŽค Microphone audio enabled");
251
252
  } else {
252
- NSLog(@"๐Ÿ”Š System audio enabled");
253
+ MRLog(@"๐Ÿ”Š System audio enabled");
253
254
  }
254
255
  } else {
255
256
  streamConfig.capturesAudio = NO;
256
- NSLog(@"๐Ÿ”‡ Audio disabled");
257
+ MRLog(@"๐Ÿ”‡ Audio disabled");
257
258
  }
258
259
  } else {
259
260
  streamConfig.capturesAudio = NO;
260
- NSLog(@"๐Ÿ”‡ Audio disabled (macOS < 13.0)");
261
+ MRLog(@"๐Ÿ”‡ Audio disabled (macOS < 13.0)");
261
262
  }
262
263
 
263
264
  // Create pure ScreenCaptureKit recording output
@@ -328,7 +329,7 @@ static NSString *g_outputPath = nil;
328
329
  return;
329
330
  }
330
331
 
331
- NSLog(@"โœ… Pure recording output added to stream");
332
+ MRLog(@"โœ… Pure recording output added to stream");
332
333
 
333
334
  // Start capture with recording
334
335
  [g_stream startCaptureWithCompletionHandler:^(NSError *startError) {
@@ -336,7 +337,7 @@ static NSString *g_outputPath = nil;
336
337
  NSLog(@"โŒ Failed to start pure capture: %@", startError);
337
338
  g_isRecording = NO;
338
339
  } else {
339
- NSLog(@"๐ŸŽ‰ PURE ScreenCaptureKit recording started successfully!");
340
+ MRLog(@"๐ŸŽ‰ PURE ScreenCaptureKit recording started successfully!");
340
341
  g_isRecording = YES;
341
342
  }
342
343
  }];
@@ -351,7 +352,7 @@ static NSString *g_outputPath = nil;
351
352
  return;
352
353
  }
353
354
 
354
- NSLog(@"๐Ÿ›‘ Stopping pure ScreenCaptureKit recording");
355
+ MRLog(@"๐Ÿ›‘ Stopping pure ScreenCaptureKit recording");
355
356
 
356
357
  // Store stream reference to prevent it from being deallocated
357
358
  SCStream *streamToStop = g_stream;
@@ -360,7 +361,7 @@ static NSString *g_outputPath = nil;
360
361
  if (error) {
361
362
  NSLog(@"โŒ Stop error: %@", error);
362
363
  }
363
- NSLog(@"โœ… Pure stream stopped");
364
+ MRLog(@"โœ… Pure stream stopped");
364
365
 
365
366
  // Immediately reset recording state to allow new recordings
366
367
  g_isRecording = NO;
@@ -383,7 +384,7 @@ static NSString *g_outputPath = nil;
383
384
 
384
385
  + (void)finalizeRecording {
385
386
  @synchronized([ScreenCaptureKitRecorder class]) {
386
- NSLog(@"๐ŸŽฌ Finalizing pure ScreenCaptureKit recording");
387
+ MRLog(@"๐ŸŽฌ Finalizing pure ScreenCaptureKit recording");
387
388
 
388
389
  // Set cleanup flag now that we're actually cleaning up
389
390
  g_isCleaningUp = YES;
@@ -391,7 +392,7 @@ static NSString *g_outputPath = nil;
391
392
 
392
393
  if (g_recordingOutput) {
393
394
  // SCRecordingOutput finalizes automatically
394
- NSLog(@"โœ… Pure recording output finalized");
395
+ MRLog(@"โœ… Pure recording output finalized");
395
396
  }
396
397
 
397
398
  [ScreenCaptureKitRecorder cleanupVideoWriter];
@@ -405,30 +406,30 @@ static NSString *g_outputPath = nil;
405
406
 
406
407
  + (void)cleanupVideoWriter {
407
408
  @synchronized([ScreenCaptureKitRecorder class]) {
408
- NSLog(@"๐Ÿงน Starting ScreenCaptureKit cleanup");
409
+ MRLog(@"๐Ÿงน Starting ScreenCaptureKit cleanup");
409
410
 
410
411
  // Clean up in proper order to prevent crashes
411
412
  if (g_stream) {
412
413
  g_stream = nil;
413
- NSLog(@"โœ… Stream reference cleared");
414
+ MRLog(@"โœ… Stream reference cleared");
414
415
  }
415
416
 
416
417
  if (g_recordingOutput) {
417
418
  g_recordingOutput = nil;
418
- NSLog(@"โœ… Recording output reference cleared");
419
+ MRLog(@"โœ… Recording output reference cleared");
419
420
  }
420
421
 
421
422
  if (g_streamDelegate) {
422
423
  g_streamDelegate = nil;
423
- NSLog(@"โœ… Stream delegate reference cleared");
424
+ MRLog(@"โœ… Stream delegate reference cleared");
424
425
  }
425
426
 
426
427
  g_isRecording = NO;
427
428
  g_isCleaningUp = NO; // Reset cleanup flag
428
429
  g_outputPath = nil;
429
430
 
430
- NSLog(@"๐Ÿงน Pure ScreenCaptureKit cleanup complete");
431
+ MRLog(@"๐Ÿงน Pure ScreenCaptureKit cleanup complete");
431
432
  }
432
433
  }
433
434
 
434
- @end
435
+ @end