node-mac-recorder 2.4.10 → 2.4.12
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/binding.gyp +5 -12
- package/index.js +25 -104
- package/install.js +2 -19
- package/package.json +2 -6
- package/src/audio_capture.mm +40 -96
- package/src/cursor_tracker.mm +4 -3
- package/src/mac_recorder.mm +673 -753
- package/src/screen_capture.h +0 -5
- package/src/screen_capture.mm +60 -139
- package/src/window_selector.mm +48 -226
- package/window-selector.js +34 -112
- package/ELECTRON-INTEGRATION.md +0 -342
- package/WINDOW_SELECTOR_USAGE.md +0 -447
- package/backup/binding.gyp +0 -44
- package/backup/src/audio_capture.mm +0 -116
- package/backup/src/cursor_tracker.mm +0 -518
- package/backup/src/mac_recorder.mm +0 -829
- package/backup/src/screen_capture.h +0 -19
- package/backup/src/screen_capture.mm +0 -162
- package/backup/src/screen_capture_kit.h +0 -15
- package/backup/src/window_selector.mm +0 -1457
- package/electron-window-selector.js +0 -698
- package/node-mac-recorder-2.4.2.tgz +0 -0
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/test-api-compatibility.js +0 -92
- package/test-audio.js +0 -94
- package/test-comprehensive.js +0 -164
- package/test-electron-window-selector.js +0 -119
- package/test-recording.js +0 -142
- package/test-sck-availability.js +0 -26
- package/test-sck-simple.js +0 -37
- package/test-sck.js +0 -56
- package/test-screencapture-overlay.js +0 -54
- package/test-simple-windows.js +0 -29
- package/test-sync.js +0 -52
- package/test-window-details.js +0 -34
- package/test-windows.js +0 -57
package/src/window_selector.mm
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
#import <ApplicationServices/ApplicationServices.h>
|
|
6
6
|
#import <Carbon/Carbon.h>
|
|
7
7
|
#import <Accessibility/Accessibility.h>
|
|
8
|
-
#import <ScreenCaptureKit/ScreenCaptureKit.h>
|
|
9
8
|
|
|
10
9
|
// Global state for window selection
|
|
11
10
|
static bool g_isWindowSelecting = false;
|
|
@@ -36,7 +35,6 @@ void cleanupWindowSelector();
|
|
|
36
35
|
void updateOverlay();
|
|
37
36
|
NSDictionary* getWindowUnderCursor(CGPoint point);
|
|
38
37
|
NSArray* getAllSelectableWindows();
|
|
39
|
-
NSArray* getAllSelectableWindowsLegacy();
|
|
40
38
|
bool bringWindowToFront(int windowId);
|
|
41
39
|
void cleanupRecordingPreview();
|
|
42
40
|
bool showRecordingPreview(NSDictionary *windowInfo);
|
|
@@ -223,10 +221,8 @@ bool hideScreenRecordingPreview();
|
|
|
223
221
|
@implementation WindowSelectorDelegate
|
|
224
222
|
- (void)selectButtonClicked:(id)sender {
|
|
225
223
|
if (g_currentWindowUnderCursor) {
|
|
226
|
-
g_selectedWindowInfo = [g_currentWindowUnderCursor
|
|
227
|
-
|
|
228
|
-
cleanupWindowSelector();
|
|
229
|
-
});
|
|
224
|
+
g_selectedWindowInfo = [g_currentWindowUnderCursor retain];
|
|
225
|
+
cleanupWindowSelector();
|
|
230
226
|
}
|
|
231
227
|
}
|
|
232
228
|
|
|
@@ -235,24 +231,25 @@ bool hideScreenRecordingPreview();
|
|
|
235
231
|
NSInteger screenIndex = [button tag];
|
|
236
232
|
|
|
237
233
|
// Get screen info from global array using button tag
|
|
238
|
-
if (g_allScreens && screenIndex >= 0 && screenIndex <
|
|
234
|
+
if (g_allScreens && screenIndex >= 0 && screenIndex < [g_allScreens count]) {
|
|
239
235
|
NSDictionary *screenInfo = [g_allScreens objectAtIndex:screenIndex];
|
|
240
|
-
g_selectedScreenInfo = [screenInfo
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
236
|
+
g_selectedScreenInfo = [screenInfo retain];
|
|
237
|
+
|
|
238
|
+
NSLog(@"🖥️ SCREEN BUTTON CLICKED: %@ (%@)",
|
|
239
|
+
[screenInfo objectForKey:@"name"],
|
|
240
|
+
[screenInfo objectForKey:@"resolution"]);
|
|
241
|
+
|
|
242
|
+
cleanupScreenSelector();
|
|
244
243
|
}
|
|
245
244
|
}
|
|
246
245
|
|
|
247
246
|
- (void)cancelButtonClicked:(id)sender {
|
|
247
|
+
NSLog(@"🚫 CANCEL BUTTON CLICKED: Selection cancelled");
|
|
248
|
+
// Clean up without selecting anything
|
|
248
249
|
if (g_isScreenSelecting) {
|
|
249
|
-
|
|
250
|
-
cleanupScreenSelector();
|
|
251
|
-
});
|
|
250
|
+
cleanupScreenSelector();
|
|
252
251
|
} else {
|
|
253
|
-
|
|
254
|
-
cleanupWindowSelector();
|
|
255
|
-
});
|
|
252
|
+
cleanupWindowSelector();
|
|
256
253
|
}
|
|
257
254
|
}
|
|
258
255
|
|
|
@@ -369,83 +366,8 @@ bool bringWindowToFront(int windowId) {
|
|
|
369
366
|
}
|
|
370
367
|
}
|
|
371
368
|
|
|
372
|
-
// Get all selectable windows
|
|
369
|
+
// Get all selectable windows
|
|
373
370
|
NSArray* getAllSelectableWindows() {
|
|
374
|
-
@autoreleasepool {
|
|
375
|
-
NSMutableArray *windows = [NSMutableArray array];
|
|
376
|
-
|
|
377
|
-
// Check if ScreenCaptureKit is available (requires macOS 12.3+)
|
|
378
|
-
if (@available(macOS 12.3, *)) {
|
|
379
|
-
// Use dispatch_semaphore for synchronous SCShareableContent retrieval
|
|
380
|
-
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
381
|
-
__block SCShareableContent *content = nil;
|
|
382
|
-
__block NSError *error = nil;
|
|
383
|
-
|
|
384
|
-
[SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent *shareableContent, NSError *contentError) {
|
|
385
|
-
content = shareableContent;
|
|
386
|
-
error = contentError;
|
|
387
|
-
dispatch_semaphore_signal(semaphore);
|
|
388
|
-
}];
|
|
389
|
-
|
|
390
|
-
// Wait for completion (timeout after 5 seconds)
|
|
391
|
-
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
|
|
392
|
-
if (dispatch_semaphore_wait(semaphore, timeout) == 0 && content) {
|
|
393
|
-
NSLog(@"✅ ScreenCaptureKit: Found %lu windows", (unsigned long)content.windows.count);
|
|
394
|
-
|
|
395
|
-
for (SCWindow *scWindow in content.windows) {
|
|
396
|
-
// Skip windows without proper frame or title
|
|
397
|
-
if (scWindow.frame.size.width < 50 || scWindow.frame.size.height < 50) continue;
|
|
398
|
-
if (!scWindow.owningApplication) continue;
|
|
399
|
-
|
|
400
|
-
// Skip system applications
|
|
401
|
-
NSString *bundleId = scWindow.owningApplication.bundleIdentifier;
|
|
402
|
-
NSString *appName = scWindow.owningApplication.applicationName;
|
|
403
|
-
if ([bundleId hasPrefix:@"com.apple.dock"] ||
|
|
404
|
-
[bundleId hasPrefix:@"com.apple.WindowServer"] ||
|
|
405
|
-
[bundleId hasPrefix:@"com.apple.controlcenter"] ||
|
|
406
|
-
[bundleId hasPrefix:@"com.apple.notificationcenterui"]) {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Convert SCWindow coordinates to match CGWindow coordinate system
|
|
411
|
-
CGRect frame = scWindow.frame;
|
|
412
|
-
int x = (int)frame.origin.x;
|
|
413
|
-
int y = (int)frame.origin.y;
|
|
414
|
-
int width = (int)frame.size.width;
|
|
415
|
-
int height = (int)frame.size.height;
|
|
416
|
-
|
|
417
|
-
NSString *windowTitle = scWindow.title ?: @"Untitled";
|
|
418
|
-
if ([windowTitle length] == 0) windowTitle = @"Untitled";
|
|
419
|
-
|
|
420
|
-
NSDictionary *window = @{
|
|
421
|
-
@"id": @(scWindow.windowID),
|
|
422
|
-
@"title": windowTitle,
|
|
423
|
-
@"appName": appName ?: @"Unknown App",
|
|
424
|
-
@"x": @(x),
|
|
425
|
-
@"y": @(y),
|
|
426
|
-
@"width": @(width),
|
|
427
|
-
@"height": @(height),
|
|
428
|
-
@"bundleId": bundleId ?: @""
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
[windows addObject:window];
|
|
432
|
-
}
|
|
433
|
-
} else {
|
|
434
|
-
NSLog(@"❌ ScreenCaptureKit: Failed to get shareable content or timeout occurred");
|
|
435
|
-
// Fall back to CGWindowList as backup
|
|
436
|
-
return getAllSelectableWindowsLegacy();
|
|
437
|
-
}
|
|
438
|
-
} else {
|
|
439
|
-
NSLog(@"⚠️ ScreenCaptureKit not available, falling back to CGWindowList");
|
|
440
|
-
return getAllSelectableWindowsLegacy();
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return [windows copy];
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Legacy window enumeration using CGWindowList (fallback for older macOS)
|
|
448
|
-
NSArray* getAllSelectableWindowsLegacy() {
|
|
449
371
|
@autoreleasepool {
|
|
450
372
|
NSMutableArray *windows = [NSMutableArray array];
|
|
451
373
|
|
|
@@ -521,11 +443,6 @@ NSDictionary* getWindowUnderCursor(CGPoint point) {
|
|
|
521
443
|
|
|
522
444
|
// Update overlay to highlight window under cursor
|
|
523
445
|
void updateOverlay() {
|
|
524
|
-
// Ensure AppKit usage on main thread
|
|
525
|
-
if (![NSThread isMainThread]) {
|
|
526
|
-
dispatch_async(dispatch_get_main_queue(), ^{ updateOverlay(); });
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
446
|
@autoreleasepool {
|
|
530
447
|
if (!g_isWindowSelecting || !g_overlayWindow) return;
|
|
531
448
|
|
|
@@ -541,7 +458,8 @@ void updateOverlay() {
|
|
|
541
458
|
|
|
542
459
|
if (windowUnderCursor && ![windowUnderCursor isEqualToDictionary:g_currentWindowUnderCursor]) {
|
|
543
460
|
// Update current window
|
|
544
|
-
g_currentWindowUnderCursor
|
|
461
|
+
[g_currentWindowUnderCursor release];
|
|
462
|
+
g_currentWindowUnderCursor = [windowUnderCursor retain];
|
|
545
463
|
|
|
546
464
|
// Update overlay position and size
|
|
547
465
|
int x = [[windowUnderCursor objectForKey:@"x"] intValue];
|
|
@@ -632,6 +550,7 @@ void updateOverlay() {
|
|
|
632
550
|
NSLog(@"🚪 WINDOW LEFT: %@ - \"%@\"", leftAppName, leftWindowTitle);
|
|
633
551
|
|
|
634
552
|
[g_overlayWindow orderOut:nil];
|
|
553
|
+
[g_currentWindowUnderCursor release];
|
|
635
554
|
g_currentWindowUnderCursor = nil;
|
|
636
555
|
}
|
|
637
556
|
}
|
|
@@ -639,12 +558,6 @@ void updateOverlay() {
|
|
|
639
558
|
|
|
640
559
|
// Cleanup function
|
|
641
560
|
void cleanupWindowSelector() {
|
|
642
|
-
if (![NSThread isMainThread]) {
|
|
643
|
-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupWindowSelector(); });
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
NSLog(@"🧹 Cleaning up window selector resources");
|
|
648
561
|
g_isWindowSelecting = false;
|
|
649
562
|
|
|
650
563
|
// Stop tracking timer
|
|
@@ -669,25 +582,24 @@ void cleanupWindowSelector() {
|
|
|
669
582
|
|
|
670
583
|
// Clean up delegate
|
|
671
584
|
if (g_delegate) {
|
|
585
|
+
[g_delegate release];
|
|
672
586
|
g_delegate = nil;
|
|
673
587
|
}
|
|
674
588
|
|
|
675
589
|
// Clean up data
|
|
676
590
|
if (g_allWindows) {
|
|
591
|
+
[g_allWindows release];
|
|
677
592
|
g_allWindows = nil;
|
|
678
593
|
}
|
|
679
594
|
|
|
680
595
|
if (g_currentWindowUnderCursor) {
|
|
596
|
+
[g_currentWindowUnderCursor release];
|
|
681
597
|
g_currentWindowUnderCursor = nil;
|
|
682
598
|
}
|
|
683
599
|
}
|
|
684
600
|
|
|
685
601
|
// Recording preview functions
|
|
686
602
|
void cleanupRecordingPreview() {
|
|
687
|
-
if (![NSThread isMainThread]) {
|
|
688
|
-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupRecordingPreview(); });
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
603
|
if (g_recordingPreviewWindow) {
|
|
692
604
|
[g_recordingPreviewWindow close];
|
|
693
605
|
g_recordingPreviewWindow = nil;
|
|
@@ -695,6 +607,7 @@ void cleanupRecordingPreview() {
|
|
|
695
607
|
}
|
|
696
608
|
|
|
697
609
|
if (g_recordingWindowInfo) {
|
|
610
|
+
[g_recordingWindowInfo release];
|
|
698
611
|
g_recordingWindowInfo = nil;
|
|
699
612
|
}
|
|
700
613
|
}
|
|
@@ -707,7 +620,7 @@ bool showRecordingPreview(NSDictionary *windowInfo) {
|
|
|
707
620
|
if (!windowInfo) return false;
|
|
708
621
|
|
|
709
622
|
// Store window info
|
|
710
|
-
g_recordingWindowInfo = windowInfo;
|
|
623
|
+
g_recordingWindowInfo = [windowInfo retain];
|
|
711
624
|
|
|
712
625
|
// Get main screen bounds for full screen overlay
|
|
713
626
|
NSScreen *mainScreen = [NSScreen mainScreen];
|
|
@@ -767,10 +680,6 @@ bool hideRecordingPreview() {
|
|
|
767
680
|
|
|
768
681
|
// Screen selection functions
|
|
769
682
|
void cleanupScreenSelector() {
|
|
770
|
-
if (![NSThread isMainThread]) {
|
|
771
|
-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupScreenSelector(); });
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
683
|
g_isScreenSelecting = false;
|
|
775
684
|
|
|
776
685
|
// Remove key event monitor
|
|
@@ -784,11 +693,13 @@ void cleanupScreenSelector() {
|
|
|
784
693
|
for (NSWindow *overlayWindow in g_screenOverlayWindows) {
|
|
785
694
|
[overlayWindow close];
|
|
786
695
|
}
|
|
696
|
+
[g_screenOverlayWindows release];
|
|
787
697
|
g_screenOverlayWindows = nil;
|
|
788
698
|
}
|
|
789
699
|
|
|
790
700
|
// Clean up screen data
|
|
791
701
|
if (g_allScreens) {
|
|
702
|
+
[g_allScreens release];
|
|
792
703
|
g_allScreens = nil;
|
|
793
704
|
}
|
|
794
705
|
}
|
|
@@ -796,13 +707,7 @@ void cleanupScreenSelector() {
|
|
|
796
707
|
bool startScreenSelection() {
|
|
797
708
|
@try {
|
|
798
709
|
if (g_isScreenSelecting) return false;
|
|
799
|
-
|
|
800
|
-
if (![NSThread isMainThread]) {
|
|
801
|
-
__block BOOL ok = NO;
|
|
802
|
-
dispatch_sync(dispatch_get_main_queue(), ^{ ok = startScreenSelection(); });
|
|
803
|
-
return ok;
|
|
804
|
-
}
|
|
805
|
-
|
|
710
|
+
|
|
806
711
|
// Get all available screens
|
|
807
712
|
NSArray *screens = [NSScreen screens];
|
|
808
713
|
if (!screens || [screens count] == 0) return false;
|
|
@@ -811,7 +716,7 @@ bool startScreenSelection() {
|
|
|
811
716
|
NSMutableArray *screenInfoArray = [[NSMutableArray alloc] init];
|
|
812
717
|
g_screenOverlayWindows = [[NSMutableArray alloc] init];
|
|
813
718
|
|
|
814
|
-
for (
|
|
719
|
+
for (NSInteger i = 0; i < [screens count]; i++) {
|
|
815
720
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
816
721
|
NSRect screenFrame = [screen frame];
|
|
817
722
|
|
|
@@ -934,11 +839,11 @@ bool startScreenSelection() {
|
|
|
934
839
|
[overlayWindow makeKeyAndOrderFront:nil];
|
|
935
840
|
|
|
936
841
|
[g_screenOverlayWindows addObject:overlayWindow];
|
|
937
|
-
screenInfo
|
|
842
|
+
[screenInfo release];
|
|
938
843
|
}
|
|
939
844
|
|
|
940
|
-
g_allScreens = screenInfoArray;
|
|
941
|
-
screenInfoArray
|
|
845
|
+
g_allScreens = [screenInfoArray retain];
|
|
846
|
+
[screenInfoArray release];
|
|
942
847
|
g_isScreenSelecting = true;
|
|
943
848
|
|
|
944
849
|
// Add ESC key event monitor to cancel selection
|
|
@@ -964,12 +869,9 @@ bool startScreenSelection() {
|
|
|
964
869
|
bool stopScreenSelection() {
|
|
965
870
|
@try {
|
|
966
871
|
if (!g_isScreenSelecting) return false;
|
|
967
|
-
|
|
968
|
-
__block BOOL ok = NO;
|
|
969
|
-
dispatch_sync(dispatch_get_main_queue(), ^{ ok = stopScreenSelection(); });
|
|
970
|
-
return ok;
|
|
971
|
-
}
|
|
872
|
+
|
|
972
873
|
cleanupScreenSelector();
|
|
874
|
+
NSLog(@"🖥️ SCREEN SELECTION: Stopped");
|
|
973
875
|
return true;
|
|
974
876
|
|
|
975
877
|
} @catch (NSException *exception) {
|
|
@@ -981,10 +883,11 @@ bool stopScreenSelection() {
|
|
|
981
883
|
NSDictionary* getSelectedScreenInfo() {
|
|
982
884
|
if (!g_selectedScreenInfo) return nil;
|
|
983
885
|
|
|
984
|
-
NSDictionary *result = g_selectedScreenInfo;
|
|
886
|
+
NSDictionary *result = [g_selectedScreenInfo retain];
|
|
887
|
+
[g_selectedScreenInfo release];
|
|
985
888
|
g_selectedScreenInfo = nil;
|
|
986
889
|
|
|
987
|
-
return result;
|
|
890
|
+
return [result autorelease];
|
|
988
891
|
}
|
|
989
892
|
|
|
990
893
|
bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
@@ -999,10 +902,10 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
999
902
|
NSArray *screens = [NSScreen screens];
|
|
1000
903
|
if (!screens || [screens count] == 0) return false;
|
|
1001
904
|
|
|
1002
|
-
|
|
905
|
+
int selectedScreenId = [[screenInfo objectForKey:@"id"] intValue];
|
|
1003
906
|
|
|
1004
907
|
// Create overlay for each screen except the selected one
|
|
1005
|
-
for (
|
|
908
|
+
for (NSInteger i = 0; i < [screens count]; i++) {
|
|
1006
909
|
if (i == selectedScreenId) continue; // Skip selected screen
|
|
1007
910
|
|
|
1008
911
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
@@ -1033,7 +936,7 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
1033
936
|
}
|
|
1034
937
|
}
|
|
1035
938
|
|
|
1036
|
-
NSLog(@"🎬 SCREEN RECORDING PREVIEW: Showing overlay for Screen %
|
|
939
|
+
NSLog(@"🎬 SCREEN RECORDING PREVIEW: Showing overlay for Screen %d", selectedScreenId);
|
|
1037
940
|
|
|
1038
941
|
return true;
|
|
1039
942
|
|
|
@@ -1051,50 +954,14 @@ bool hideScreenRecordingPreview() {
|
|
|
1051
954
|
Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
1052
955
|
Napi::Env env = info.Env();
|
|
1053
956
|
|
|
1054
|
-
// Electron safety check - prevent NSWindow crashes
|
|
1055
|
-
const char* electronVersion = getenv("ELECTRON_VERSION");
|
|
1056
|
-
const char* electronRunAs = getenv("ELECTRON_RUN_AS_NODE");
|
|
1057
|
-
|
|
1058
|
-
NSLog(@"🔍 Debug: electronVersion='%s', electronRunAs='%s'",
|
|
1059
|
-
electronVersion ? electronVersion : "null",
|
|
1060
|
-
electronRunAs ? electronRunAs : "null");
|
|
1061
|
-
|
|
1062
|
-
if (electronVersion || electronRunAs) {
|
|
1063
|
-
NSLog(@"🔍 Detected Electron environment - using safe mode");
|
|
1064
|
-
|
|
1065
|
-
// In Electron, return window list without creating native NSWindow overlays
|
|
1066
|
-
// The Electron app can handle UI selection itself
|
|
1067
|
-
@try {
|
|
1068
|
-
NSArray *windows = getAllSelectableWindows();
|
|
1069
|
-
|
|
1070
|
-
if (!windows || [windows count] == 0) {
|
|
1071
|
-
NSLog(@"❌ No selectable windows found");
|
|
1072
|
-
return Napi::Boolean::New(env, false);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// Store windows for later retrieval via getWindowSelectionStatus
|
|
1076
|
-
g_allWindows = [getAllSelectableWindows() mutableCopy];
|
|
1077
|
-
g_isWindowSelecting = true;
|
|
1078
|
-
|
|
1079
|
-
// Return true to indicate windows are available
|
|
1080
|
-
// Electron app should call getWindowSelectionStatus to get the list
|
|
1081
|
-
NSLog(@"✅ Electron-safe mode: %lu windows available for selection", (unsigned long)[windows count]);
|
|
1082
|
-
return Napi::Boolean::New(env, true);
|
|
1083
|
-
|
|
1084
|
-
} @catch (NSException *exception) {
|
|
1085
|
-
NSLog(@"❌ Exception in Electron-safe window selection: %@", [exception reason]);
|
|
1086
|
-
return Napi::Boolean::New(env, false);
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
957
|
if (g_isWindowSelecting) {
|
|
1091
|
-
|
|
1092
|
-
return
|
|
958
|
+
Napi::TypeError::New(env, "Window selection already in progress").ThrowAsJavaScriptException();
|
|
959
|
+
return env.Null();
|
|
1093
960
|
}
|
|
1094
961
|
|
|
1095
962
|
@try {
|
|
1096
963
|
// Get all windows
|
|
1097
|
-
g_allWindows = [getAllSelectableWindows()
|
|
964
|
+
g_allWindows = [getAllSelectableWindows() retain];
|
|
1098
965
|
|
|
1099
966
|
if (!g_allWindows || [g_allWindows count] == 0) {
|
|
1100
967
|
Napi::Error::New(env, "No selectable windows found").ThrowAsJavaScriptException();
|
|
@@ -1306,6 +1173,7 @@ Napi::Value GetSelectedWindowInfo(const Napi::CallbackInfo& info) {
|
|
|
1306
1173
|
result.Set("screenHeight", Napi::Number::New(env, (int)screenFrame.size.height));
|
|
1307
1174
|
|
|
1308
1175
|
// Clear selected window info after reading
|
|
1176
|
+
[g_selectedWindowInfo release];
|
|
1309
1177
|
g_selectedWindowInfo = nil;
|
|
1310
1178
|
|
|
1311
1179
|
return result;
|
|
@@ -1387,13 +1255,13 @@ Napi::Value ShowRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1387
1255
|
Napi::Env env = info.Env();
|
|
1388
1256
|
|
|
1389
1257
|
if (info.Length() < 1) {
|
|
1390
|
-
|
|
1391
|
-
return
|
|
1258
|
+
Napi::TypeError::New(env, "Window info object required").ThrowAsJavaScriptException();
|
|
1259
|
+
return env.Null();
|
|
1392
1260
|
}
|
|
1393
1261
|
|
|
1394
1262
|
if (!info[0].IsObject()) {
|
|
1395
|
-
|
|
1396
|
-
return
|
|
1263
|
+
Napi::TypeError::New(env, "Window info must be an object").ThrowAsJavaScriptException();
|
|
1264
|
+
return env.Null();
|
|
1397
1265
|
}
|
|
1398
1266
|
|
|
1399
1267
|
@try {
|
|
@@ -1425,7 +1293,7 @@ Napi::Value ShowRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1425
1293
|
}
|
|
1426
1294
|
|
|
1427
1295
|
bool success = showRecordingPreview(windowInfo);
|
|
1428
|
-
windowInfo
|
|
1296
|
+
[windowInfo release];
|
|
1429
1297
|
|
|
1430
1298
|
return Napi::Boolean::New(env, success);
|
|
1431
1299
|
|
|
@@ -1451,57 +1319,11 @@ Napi::Value HideRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1451
1319
|
Napi::Value StartScreenSelection(const Napi::CallbackInfo& info) {
|
|
1452
1320
|
Napi::Env env = info.Env();
|
|
1453
1321
|
|
|
1454
|
-
// Electron safety check - prevent NSWindow crashes
|
|
1455
|
-
const char* electronVersion = getenv("ELECTRON_VERSION");
|
|
1456
|
-
const char* electronRunAs = getenv("ELECTRON_RUN_AS_NODE");
|
|
1457
|
-
|
|
1458
|
-
NSLog(@"🔍 Screen Debug: electronVersion='%s', electronRunAs='%s'",
|
|
1459
|
-
electronVersion ? electronVersion : "null",
|
|
1460
|
-
electronRunAs ? electronRunAs : "null");
|
|
1461
|
-
|
|
1462
|
-
if (electronVersion || electronRunAs) {
|
|
1463
|
-
NSLog(@"🔍 Detected Electron environment - using safe screen selection");
|
|
1464
|
-
|
|
1465
|
-
// In Electron, return screen list without creating native NSWindow overlays
|
|
1466
|
-
@try {
|
|
1467
|
-
NSArray *screens = [NSScreen screens];
|
|
1468
|
-
|
|
1469
|
-
if (!screens || [screens count] == 0) {
|
|
1470
|
-
NSLog(@"❌ No screens available");
|
|
1471
|
-
return Napi::Boolean::New(env, false);
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
// Store screens and select first one automatically for Electron
|
|
1475
|
-
g_allScreens = screens;
|
|
1476
|
-
g_isScreenSelecting = true;
|
|
1477
|
-
|
|
1478
|
-
NSScreen *mainScreen = [screens firstObject];
|
|
1479
|
-
g_selectedScreenInfo = @{
|
|
1480
|
-
@"id": @((int)[screens indexOfObject:mainScreen]),
|
|
1481
|
-
@"width": @((int)mainScreen.frame.size.width),
|
|
1482
|
-
@"height": @((int)mainScreen.frame.size.height),
|
|
1483
|
-
@"x": @((int)mainScreen.frame.origin.x),
|
|
1484
|
-
@"y": @((int)mainScreen.frame.origin.y)
|
|
1485
|
-
};
|
|
1486
|
-
|
|
1487
|
-
// Mark as complete so getSelectedScreenInfo returns the selection
|
|
1488
|
-
g_isScreenSelecting = false;
|
|
1489
|
-
|
|
1490
|
-
NSLog(@"✅ Electron-safe screen selection: %lu screens available", (unsigned long)[screens count]);
|
|
1491
|
-
return Napi::Boolean::New(env, true);
|
|
1492
|
-
|
|
1493
|
-
} @catch (NSException *exception) {
|
|
1494
|
-
NSLog(@"❌ Exception in Electron-safe screen selection: %@", [exception reason]);
|
|
1495
|
-
return Napi::Boolean::New(env, false);
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
1322
|
@try {
|
|
1500
1323
|
bool success = startScreenSelection();
|
|
1501
1324
|
return Napi::Boolean::New(env, success);
|
|
1502
1325
|
|
|
1503
1326
|
} @catch (NSException *exception) {
|
|
1504
|
-
NSLog(@"❌ Screen selection error: %@", exception);
|
|
1505
1327
|
return Napi::Boolean::New(env, false);
|
|
1506
1328
|
}
|
|
1507
1329
|
}
|
|
@@ -1593,7 +1415,7 @@ Napi::Value ShowScreenRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1593
1415
|
}
|
|
1594
1416
|
|
|
1595
1417
|
bool success = showScreenRecordingPreview(screenInfo);
|
|
1596
|
-
screenInfo
|
|
1418
|
+
[screenInfo release];
|
|
1597
1419
|
|
|
1598
1420
|
return Napi::Boolean::New(env, success);
|
|
1599
1421
|
|