node-mac-recorder 2.4.11 → 2.4.13
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 +113 -399
- package/window-selector.js +34 -112
- package/ELECTRON-INTEGRATION.md +0 -710
- 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-overlay-fix.js +0 -72
- 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
|
|
|
@@ -497,56 +419,22 @@ NSArray* getAllSelectableWindowsLegacy() {
|
|
|
497
419
|
}
|
|
498
420
|
}
|
|
499
421
|
|
|
500
|
-
// Get window under cursor point
|
|
422
|
+
// Get window under cursor point
|
|
501
423
|
NSDictionary* getWindowUnderCursor(CGPoint point) {
|
|
502
424
|
@autoreleasepool {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
425
|
+
if (!g_allWindows) return nil;
|
|
426
|
+
|
|
427
|
+
// Find window that contains the cursor point
|
|
428
|
+
for (NSDictionary *window in g_allWindows) {
|
|
429
|
+
int x = [[window objectForKey:@"x"] intValue];
|
|
430
|
+
int y = [[window objectForKey:@"y"] intValue];
|
|
431
|
+
int width = [[window objectForKey:@"width"] intValue];
|
|
432
|
+
int height = [[window objectForKey:@"height"] intValue];
|
|
508
433
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
NSNumber *layer = [windowInfo objectForKey:(NSString *)kCGWindowLayer];
|
|
513
|
-
NSString *owner = [windowInfo objectForKey:(NSString *)kCGWindowOwnerName];
|
|
514
|
-
NSNumber *winID = [windowInfo objectForKey:(NSString *)kCGWindowNumber];
|
|
515
|
-
|
|
516
|
-
if (!bounds || !winID || !owner) continue;
|
|
517
|
-
if ([layer intValue] != 0) continue; // Only normal windows
|
|
518
|
-
if ([owner isEqualToString:@"WindowServer"] || [owner isEqualToString:@"Dock"]) continue;
|
|
519
|
-
|
|
520
|
-
int x = [[bounds objectForKey:@"X"] intValue];
|
|
521
|
-
int y = [[bounds objectForKey:@"Y"] intValue];
|
|
522
|
-
int width = [[bounds objectForKey:@"Width"] intValue];
|
|
523
|
-
int height = [[bounds objectForKey:@"Height"] intValue];
|
|
524
|
-
|
|
525
|
-
// Skip too small windows
|
|
526
|
-
if (width < 50 || height < 50) continue;
|
|
527
|
-
|
|
528
|
-
// Check if cursor is within window bounds
|
|
529
|
-
if (point.x >= x && point.x <= x + width &&
|
|
530
|
-
point.y >= y && point.y <= y + height) {
|
|
531
|
-
|
|
532
|
-
NSString *windowName = [windowInfo objectForKey:(NSString *)kCGWindowName];
|
|
533
|
-
|
|
534
|
-
// Create window info in our format
|
|
535
|
-
NSDictionary *window = @{
|
|
536
|
-
@"id": winID,
|
|
537
|
-
@"title": windowName ?: @"Untitled",
|
|
538
|
-
@"appName": owner,
|
|
539
|
-
@"x": @(x),
|
|
540
|
-
@"y": @(y),
|
|
541
|
-
@"width": @(width),
|
|
542
|
-
@"height": @(height)
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
CFRelease(windowList);
|
|
546
|
-
return window;
|
|
547
|
-
}
|
|
434
|
+
if (point.x >= x && point.x <= x + width &&
|
|
435
|
+
point.y >= y && point.y <= y + height) {
|
|
436
|
+
return window;
|
|
548
437
|
}
|
|
549
|
-
CFRelease(windowList);
|
|
550
438
|
}
|
|
551
439
|
|
|
552
440
|
return nil;
|
|
@@ -555,11 +443,6 @@ NSDictionary* getWindowUnderCursor(CGPoint point) {
|
|
|
555
443
|
|
|
556
444
|
// Update overlay to highlight window under cursor
|
|
557
445
|
void updateOverlay() {
|
|
558
|
-
// Ensure AppKit usage on main thread
|
|
559
|
-
if (![NSThread isMainThread]) {
|
|
560
|
-
dispatch_async(dispatch_get_main_queue(), ^{ updateOverlay(); });
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
446
|
@autoreleasepool {
|
|
564
447
|
if (!g_isWindowSelecting || !g_overlayWindow) return;
|
|
565
448
|
|
|
@@ -575,7 +458,8 @@ void updateOverlay() {
|
|
|
575
458
|
|
|
576
459
|
if (windowUnderCursor && ![windowUnderCursor isEqualToDictionary:g_currentWindowUnderCursor]) {
|
|
577
460
|
// Update current window
|
|
578
|
-
g_currentWindowUnderCursor
|
|
461
|
+
[g_currentWindowUnderCursor release];
|
|
462
|
+
g_currentWindowUnderCursor = [windowUnderCursor retain];
|
|
579
463
|
|
|
580
464
|
// Update overlay position and size
|
|
581
465
|
int x = [[windowUnderCursor objectForKey:@"x"] intValue];
|
|
@@ -583,19 +467,36 @@ void updateOverlay() {
|
|
|
583
467
|
int width = [[windowUnderCursor objectForKey:@"width"] intValue];
|
|
584
468
|
int height = [[windowUnderCursor objectForKey:@"height"] intValue];
|
|
585
469
|
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
CGFloat
|
|
470
|
+
// Find which screen contains the window center
|
|
471
|
+
NSArray *screens = [NSScreen screens];
|
|
472
|
+
NSScreen *windowScreen = nil;
|
|
473
|
+
CGFloat windowCenterX = x + width / 2;
|
|
474
|
+
CGFloat windowCenterY = y + height / 2;
|
|
590
475
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
476
|
+
for (NSScreen *screen in screens) {
|
|
477
|
+
NSRect screenFrame = [screen frame];
|
|
478
|
+
// Convert screen frame to CGWindow coordinates
|
|
479
|
+
CGFloat screenTop = screenFrame.origin.y + screenFrame.size.height;
|
|
480
|
+
CGFloat screenBottom = screenFrame.origin.y;
|
|
481
|
+
CGFloat screenLeft = screenFrame.origin.x;
|
|
482
|
+
CGFloat screenRight = screenFrame.origin.x + screenFrame.size.width;
|
|
483
|
+
|
|
484
|
+
if (windowCenterX >= screenLeft && windowCenterX <= screenRight &&
|
|
485
|
+
windowCenterY >= screenBottom && windowCenterY <= screenTop) {
|
|
486
|
+
windowScreen = screen;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
597
490
|
|
|
598
|
-
|
|
491
|
+
// Use main screen if no specific screen found
|
|
492
|
+
if (!windowScreen) windowScreen = [NSScreen mainScreen];
|
|
493
|
+
|
|
494
|
+
// Convert coordinates from CGWindow (top-left) to NSWindow (bottom-left) for the specific screen
|
|
495
|
+
CGFloat screenHeight = [windowScreen frame].size.height;
|
|
496
|
+
CGFloat adjustedY = screenHeight - y - height;
|
|
497
|
+
|
|
498
|
+
// Use actual window coordinates without clamping to preserve overlay accuracy
|
|
499
|
+
NSRect overlayFrame = NSMakeRect(x, adjustedY, width, height);
|
|
599
500
|
|
|
600
501
|
NSString *windowTitle = [windowUnderCursor objectForKey:@"title"] ?: @"Untitled";
|
|
601
502
|
NSString *appName = [windowUnderCursor objectForKey:@"appName"] ?: @"Unknown";
|
|
@@ -617,6 +518,8 @@ void updateOverlay() {
|
|
|
617
518
|
}
|
|
618
519
|
}
|
|
619
520
|
}
|
|
521
|
+
|
|
522
|
+
// Ensure overlay is on the correct screen
|
|
620
523
|
[g_overlayWindow setFrame:overlayFrame display:YES];
|
|
621
524
|
|
|
622
525
|
// Update overlay view window info
|
|
@@ -666,6 +569,7 @@ void updateOverlay() {
|
|
|
666
569
|
NSLog(@"🚪 WINDOW LEFT: %@ - \"%@\"", leftAppName, leftWindowTitle);
|
|
667
570
|
|
|
668
571
|
[g_overlayWindow orderOut:nil];
|
|
572
|
+
[g_currentWindowUnderCursor release];
|
|
669
573
|
g_currentWindowUnderCursor = nil;
|
|
670
574
|
}
|
|
671
575
|
}
|
|
@@ -673,12 +577,6 @@ void updateOverlay() {
|
|
|
673
577
|
|
|
674
578
|
// Cleanup function
|
|
675
579
|
void cleanupWindowSelector() {
|
|
676
|
-
if (![NSThread isMainThread]) {
|
|
677
|
-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupWindowSelector(); });
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
NSLog(@"🧹 Cleaning up window selector resources");
|
|
682
580
|
g_isWindowSelecting = false;
|
|
683
581
|
|
|
684
582
|
// Stop tracking timer
|
|
@@ -703,25 +601,24 @@ void cleanupWindowSelector() {
|
|
|
703
601
|
|
|
704
602
|
// Clean up delegate
|
|
705
603
|
if (g_delegate) {
|
|
604
|
+
[g_delegate release];
|
|
706
605
|
g_delegate = nil;
|
|
707
606
|
}
|
|
708
607
|
|
|
709
608
|
// Clean up data
|
|
710
609
|
if (g_allWindows) {
|
|
610
|
+
[g_allWindows release];
|
|
711
611
|
g_allWindows = nil;
|
|
712
612
|
}
|
|
713
613
|
|
|
714
614
|
if (g_currentWindowUnderCursor) {
|
|
615
|
+
[g_currentWindowUnderCursor release];
|
|
715
616
|
g_currentWindowUnderCursor = nil;
|
|
716
617
|
}
|
|
717
618
|
}
|
|
718
619
|
|
|
719
620
|
// Recording preview functions
|
|
720
621
|
void cleanupRecordingPreview() {
|
|
721
|
-
if (![NSThread isMainThread]) {
|
|
722
|
-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupRecordingPreview(); });
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
622
|
if (g_recordingPreviewWindow) {
|
|
726
623
|
[g_recordingPreviewWindow close];
|
|
727
624
|
g_recordingPreviewWindow = nil;
|
|
@@ -729,6 +626,7 @@ void cleanupRecordingPreview() {
|
|
|
729
626
|
}
|
|
730
627
|
|
|
731
628
|
if (g_recordingWindowInfo) {
|
|
629
|
+
[g_recordingWindowInfo release];
|
|
732
630
|
g_recordingWindowInfo = nil;
|
|
733
631
|
}
|
|
734
632
|
}
|
|
@@ -741,7 +639,7 @@ bool showRecordingPreview(NSDictionary *windowInfo) {
|
|
|
741
639
|
if (!windowInfo) return false;
|
|
742
640
|
|
|
743
641
|
// Store window info
|
|
744
|
-
g_recordingWindowInfo = windowInfo;
|
|
642
|
+
g_recordingWindowInfo = [windowInfo retain];
|
|
745
643
|
|
|
746
644
|
// Get main screen bounds for full screen overlay
|
|
747
645
|
NSScreen *mainScreen = [NSScreen mainScreen];
|
|
@@ -801,10 +699,6 @@ bool hideRecordingPreview() {
|
|
|
801
699
|
|
|
802
700
|
// Screen selection functions
|
|
803
701
|
void cleanupScreenSelector() {
|
|
804
|
-
if (![NSThread isMainThread]) {
|
|
805
|
-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupScreenSelector(); });
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
702
|
g_isScreenSelecting = false;
|
|
809
703
|
|
|
810
704
|
// Remove key event monitor
|
|
@@ -818,11 +712,13 @@ void cleanupScreenSelector() {
|
|
|
818
712
|
for (NSWindow *overlayWindow in g_screenOverlayWindows) {
|
|
819
713
|
[overlayWindow close];
|
|
820
714
|
}
|
|
715
|
+
[g_screenOverlayWindows release];
|
|
821
716
|
g_screenOverlayWindows = nil;
|
|
822
717
|
}
|
|
823
718
|
|
|
824
719
|
// Clean up screen data
|
|
825
720
|
if (g_allScreens) {
|
|
721
|
+
[g_allScreens release];
|
|
826
722
|
g_allScreens = nil;
|
|
827
723
|
}
|
|
828
724
|
}
|
|
@@ -830,13 +726,7 @@ void cleanupScreenSelector() {
|
|
|
830
726
|
bool startScreenSelection() {
|
|
831
727
|
@try {
|
|
832
728
|
if (g_isScreenSelecting) return false;
|
|
833
|
-
|
|
834
|
-
if (![NSThread isMainThread]) {
|
|
835
|
-
__block BOOL ok = NO;
|
|
836
|
-
dispatch_sync(dispatch_get_main_queue(), ^{ ok = startScreenSelection(); });
|
|
837
|
-
return ok;
|
|
838
|
-
}
|
|
839
|
-
|
|
729
|
+
|
|
840
730
|
// Get all available screens
|
|
841
731
|
NSArray *screens = [NSScreen screens];
|
|
842
732
|
if (!screens || [screens count] == 0) return false;
|
|
@@ -845,7 +735,7 @@ bool startScreenSelection() {
|
|
|
845
735
|
NSMutableArray *screenInfoArray = [[NSMutableArray alloc] init];
|
|
846
736
|
g_screenOverlayWindows = [[NSMutableArray alloc] init];
|
|
847
737
|
|
|
848
|
-
for (
|
|
738
|
+
for (NSInteger i = 0; i < [screens count]; i++) {
|
|
849
739
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
850
740
|
NSRect screenFrame = [screen frame];
|
|
851
741
|
|
|
@@ -886,16 +776,15 @@ bool startScreenSelection() {
|
|
|
886
776
|
NSButton *selectButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 180, 60)];
|
|
887
777
|
[selectButton setTitle:@"Start Record"];
|
|
888
778
|
[selectButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
889
|
-
[selectButton setBezelStyle:
|
|
779
|
+
[selectButton setBezelStyle:NSBezelStyleRegularSquare];
|
|
890
780
|
[selectButton setFont:[NSFont systemFontOfSize:16 weight:NSFontWeightSemibold]];
|
|
891
781
|
[selectButton setTag:i]; // Set screen index as tag
|
|
892
782
|
|
|
893
783
|
// Blue background with white text
|
|
894
784
|
[selectButton setWantsLayer:YES];
|
|
895
|
-
[selectButton.layer setBackgroundColor:[[NSColor colorWithRed:0.
|
|
896
|
-
[selectButton.layer setCornerRadius:
|
|
897
|
-
[selectButton.layer
|
|
898
|
-
[selectButton.layer setBorderWidth:2.0];
|
|
785
|
+
[selectButton.layer setBackgroundColor:[[NSColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:0.95] CGColor]];
|
|
786
|
+
[selectButton.layer setCornerRadius:12.0];
|
|
787
|
+
[selectButton.layer setBorderWidth:0.0];
|
|
899
788
|
|
|
900
789
|
// White text color
|
|
901
790
|
NSMutableAttributedString *titleString = [[NSMutableAttributedString alloc]
|
|
@@ -922,15 +811,14 @@ bool startScreenSelection() {
|
|
|
922
811
|
NSButton *screenCancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
|
|
923
812
|
[screenCancelButton setTitle:@"Cancel"];
|
|
924
813
|
[screenCancelButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
925
|
-
[screenCancelButton setBezelStyle:
|
|
814
|
+
[screenCancelButton setBezelStyle:NSBezelStyleRegularSquare];
|
|
926
815
|
[screenCancelButton setFont:[NSFont systemFontOfSize:14 weight:NSFontWeightMedium]];
|
|
927
816
|
|
|
928
817
|
// Gray cancel button styling
|
|
929
818
|
[screenCancelButton setWantsLayer:YES];
|
|
930
|
-
[screenCancelButton.layer setBackgroundColor:[[NSColor colorWithRed:0.
|
|
931
|
-
[screenCancelButton.layer setCornerRadius:
|
|
932
|
-
[screenCancelButton.layer
|
|
933
|
-
[screenCancelButton.layer setBorderWidth:1.0];
|
|
819
|
+
[screenCancelButton.layer setBackgroundColor:[[NSColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:0.9] CGColor]];
|
|
820
|
+
[screenCancelButton.layer setCornerRadius:10.0];
|
|
821
|
+
[screenCancelButton.layer setBorderWidth:0.0];
|
|
934
822
|
|
|
935
823
|
// White text for cancel button
|
|
936
824
|
NSMutableAttributedString *screenCancelTitleString = [[NSMutableAttributedString alloc]
|
|
@@ -968,11 +856,11 @@ bool startScreenSelection() {
|
|
|
968
856
|
[overlayWindow makeKeyAndOrderFront:nil];
|
|
969
857
|
|
|
970
858
|
[g_screenOverlayWindows addObject:overlayWindow];
|
|
971
|
-
screenInfo
|
|
859
|
+
[screenInfo release];
|
|
972
860
|
}
|
|
973
861
|
|
|
974
|
-
g_allScreens = screenInfoArray;
|
|
975
|
-
screenInfoArray
|
|
862
|
+
g_allScreens = [screenInfoArray retain];
|
|
863
|
+
[screenInfoArray release];
|
|
976
864
|
g_isScreenSelecting = true;
|
|
977
865
|
|
|
978
866
|
// Add ESC key event monitor to cancel selection
|
|
@@ -998,12 +886,9 @@ bool startScreenSelection() {
|
|
|
998
886
|
bool stopScreenSelection() {
|
|
999
887
|
@try {
|
|
1000
888
|
if (!g_isScreenSelecting) return false;
|
|
1001
|
-
|
|
1002
|
-
__block BOOL ok = NO;
|
|
1003
|
-
dispatch_sync(dispatch_get_main_queue(), ^{ ok = stopScreenSelection(); });
|
|
1004
|
-
return ok;
|
|
1005
|
-
}
|
|
889
|
+
|
|
1006
890
|
cleanupScreenSelector();
|
|
891
|
+
NSLog(@"🖥️ SCREEN SELECTION: Stopped");
|
|
1007
892
|
return true;
|
|
1008
893
|
|
|
1009
894
|
} @catch (NSException *exception) {
|
|
@@ -1015,10 +900,11 @@ bool stopScreenSelection() {
|
|
|
1015
900
|
NSDictionary* getSelectedScreenInfo() {
|
|
1016
901
|
if (!g_selectedScreenInfo) return nil;
|
|
1017
902
|
|
|
1018
|
-
NSDictionary *result = g_selectedScreenInfo;
|
|
903
|
+
NSDictionary *result = [g_selectedScreenInfo retain];
|
|
904
|
+
[g_selectedScreenInfo release];
|
|
1019
905
|
g_selectedScreenInfo = nil;
|
|
1020
906
|
|
|
1021
|
-
return result;
|
|
907
|
+
return [result autorelease];
|
|
1022
908
|
}
|
|
1023
909
|
|
|
1024
910
|
bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
@@ -1033,10 +919,10 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
1033
919
|
NSArray *screens = [NSScreen screens];
|
|
1034
920
|
if (!screens || [screens count] == 0) return false;
|
|
1035
921
|
|
|
1036
|
-
|
|
922
|
+
int selectedScreenId = [[screenInfo objectForKey:@"id"] intValue];
|
|
1037
923
|
|
|
1038
924
|
// Create overlay for each screen except the selected one
|
|
1039
|
-
for (
|
|
925
|
+
for (NSInteger i = 0; i < [screens count]; i++) {
|
|
1040
926
|
if (i == selectedScreenId) continue; // Skip selected screen
|
|
1041
927
|
|
|
1042
928
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
@@ -1067,7 +953,7 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
1067
953
|
}
|
|
1068
954
|
}
|
|
1069
955
|
|
|
1070
|
-
NSLog(@"🎬 SCREEN RECORDING PREVIEW: Showing overlay for Screen %
|
|
956
|
+
NSLog(@"🎬 SCREEN RECORDING PREVIEW: Showing overlay for Screen %d", selectedScreenId);
|
|
1071
957
|
|
|
1072
958
|
return true;
|
|
1073
959
|
|
|
@@ -1085,50 +971,14 @@ bool hideScreenRecordingPreview() {
|
|
|
1085
971
|
Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
1086
972
|
Napi::Env env = info.Env();
|
|
1087
973
|
|
|
1088
|
-
// Electron safety check - prevent NSWindow crashes
|
|
1089
|
-
const char* electronVersion = getenv("ELECTRON_VERSION");
|
|
1090
|
-
const char* electronRunAs = getenv("ELECTRON_RUN_AS_NODE");
|
|
1091
|
-
|
|
1092
|
-
NSLog(@"🔍 Debug: electronVersion='%s', electronRunAs='%s'",
|
|
1093
|
-
electronVersion ? electronVersion : "null",
|
|
1094
|
-
electronRunAs ? electronRunAs : "null");
|
|
1095
|
-
|
|
1096
|
-
if (electronVersion || electronRunAs) {
|
|
1097
|
-
NSLog(@"🔍 Detected Electron environment - using safe mode");
|
|
1098
|
-
|
|
1099
|
-
// In Electron, return window list without creating native NSWindow overlays
|
|
1100
|
-
// The Electron app can handle UI selection itself
|
|
1101
|
-
@try {
|
|
1102
|
-
NSArray *windows = getAllSelectableWindows();
|
|
1103
|
-
|
|
1104
|
-
if (!windows || [windows count] == 0) {
|
|
1105
|
-
NSLog(@"❌ No selectable windows found");
|
|
1106
|
-
return Napi::Boolean::New(env, false);
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
// Store windows for later retrieval via getWindowSelectionStatus
|
|
1110
|
-
g_allWindows = [getAllSelectableWindows() mutableCopy];
|
|
1111
|
-
g_isWindowSelecting = true;
|
|
1112
|
-
|
|
1113
|
-
// Return true to indicate windows are available
|
|
1114
|
-
// Electron app should call getWindowSelectionStatus to get the list
|
|
1115
|
-
NSLog(@"✅ Electron-safe mode: %lu windows available for selection", (unsigned long)[windows count]);
|
|
1116
|
-
return Napi::Boolean::New(env, true);
|
|
1117
|
-
|
|
1118
|
-
} @catch (NSException *exception) {
|
|
1119
|
-
NSLog(@"❌ Exception in Electron-safe window selection: %@", [exception reason]);
|
|
1120
|
-
return Napi::Boolean::New(env, false);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
974
|
if (g_isWindowSelecting) {
|
|
1125
|
-
|
|
1126
|
-
return
|
|
975
|
+
Napi::TypeError::New(env, "Window selection already in progress").ThrowAsJavaScriptException();
|
|
976
|
+
return env.Null();
|
|
1127
977
|
}
|
|
1128
978
|
|
|
1129
979
|
@try {
|
|
1130
980
|
// Get all windows
|
|
1131
|
-
g_allWindows = [getAllSelectableWindows()
|
|
981
|
+
g_allWindows = [getAllSelectableWindows() retain];
|
|
1132
982
|
|
|
1133
983
|
if (!g_allWindows || [g_allWindows count] == 0) {
|
|
1134
984
|
Napi::Error::New(env, "No selectable windows found").ThrowAsJavaScriptException();
|
|
@@ -1159,15 +1009,14 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1159
1009
|
g_selectButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 160, 60)];
|
|
1160
1010
|
[g_selectButton setTitle:@"Start Record"];
|
|
1161
1011
|
[g_selectButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1162
|
-
[g_selectButton setBezelStyle:
|
|
1012
|
+
[g_selectButton setBezelStyle:NSBezelStyleRegularSquare];
|
|
1163
1013
|
[g_selectButton setFont:[NSFont systemFontOfSize:16 weight:NSFontWeightSemibold]];
|
|
1164
1014
|
|
|
1165
1015
|
// Blue background with white text
|
|
1166
1016
|
[g_selectButton setWantsLayer:YES];
|
|
1167
|
-
[g_selectButton.layer setBackgroundColor:[[NSColor colorWithRed:0.
|
|
1168
|
-
[g_selectButton.layer setCornerRadius:
|
|
1169
|
-
[g_selectButton.layer
|
|
1170
|
-
[g_selectButton.layer setBorderWidth:2.0];
|
|
1017
|
+
[g_selectButton.layer setBackgroundColor:[[NSColor colorWithRed:0.2 green:0.6 blue:1.0 alpha:0.95] CGColor]];
|
|
1018
|
+
[g_selectButton.layer setCornerRadius:12.0];
|
|
1019
|
+
[g_selectButton.layer setBorderWidth:0.0];
|
|
1171
1020
|
|
|
1172
1021
|
// White text color
|
|
1173
1022
|
NSMutableAttributedString *titleString = [[NSMutableAttributedString alloc]
|
|
@@ -1195,15 +1044,14 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1195
1044
|
NSButton *cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 120, 40)];
|
|
1196
1045
|
[cancelButton setTitle:@"Cancel"];
|
|
1197
1046
|
[cancelButton setButtonType:NSButtonTypeMomentaryPushIn];
|
|
1198
|
-
[cancelButton setBezelStyle:
|
|
1047
|
+
[cancelButton setBezelStyle:NSBezelStyleRegularSquare];
|
|
1199
1048
|
[cancelButton setFont:[NSFont systemFontOfSize:14 weight:NSFontWeightMedium]];
|
|
1200
1049
|
|
|
1201
1050
|
// Gray cancel button styling
|
|
1202
1051
|
[cancelButton setWantsLayer:YES];
|
|
1203
|
-
[cancelButton.layer setBackgroundColor:[[NSColor colorWithRed:0.
|
|
1204
|
-
[cancelButton.layer setCornerRadius:
|
|
1205
|
-
[cancelButton.layer
|
|
1206
|
-
[cancelButton.layer setBorderWidth:1.0];
|
|
1052
|
+
[cancelButton.layer setBackgroundColor:[[NSColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:0.9] CGColor]];
|
|
1053
|
+
[cancelButton.layer setCornerRadius:10.0];
|
|
1054
|
+
[cancelButton.layer setBorderWidth:0.0];
|
|
1207
1055
|
|
|
1208
1056
|
// White text for cancel button
|
|
1209
1057
|
NSMutableAttributedString *cancelTitleString = [[NSMutableAttributedString alloc]
|
|
@@ -1308,54 +1156,26 @@ Napi::Value GetSelectedWindowInfo(const Napi::CallbackInfo& info) {
|
|
|
1308
1156
|
NSLog(@" 📊 Details: ID=%@, Pos=(%d,%d), Size=%dx%d",
|
|
1309
1157
|
[g_selectedWindowInfo objectForKey:@"id"], x, y, width, height);
|
|
1310
1158
|
|
|
1311
|
-
// Get all screens
|
|
1159
|
+
// Get all screens
|
|
1312
1160
|
NSArray *screens = [NSScreen screens];
|
|
1313
1161
|
NSScreen *windowScreen = nil;
|
|
1314
1162
|
NSScreen *mainScreen = [NSScreen mainScreen];
|
|
1315
1163
|
|
|
1316
|
-
// Calculate window center point for better screen detection
|
|
1317
|
-
CGPoint windowCenter = CGPointMake(x + width/2, y + height/2);
|
|
1318
|
-
|
|
1319
1164
|
for (NSScreen *screen in screens) {
|
|
1320
1165
|
NSRect screenFrame = [screen frame];
|
|
1321
1166
|
|
|
1322
|
-
//
|
|
1323
|
-
if (
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1167
|
+
// Convert window coordinates to screen-relative
|
|
1168
|
+
if (x >= screenFrame.origin.x &&
|
|
1169
|
+
x < screenFrame.origin.x + screenFrame.size.width &&
|
|
1170
|
+
y >= screenFrame.origin.y &&
|
|
1171
|
+
y < screenFrame.origin.y + screenFrame.size.height) {
|
|
1327
1172
|
windowScreen = screen;
|
|
1328
|
-
NSLog(@" 🖥️ Window found on screen: (%.0f,%.0f) %.0fx%.0f",
|
|
1329
|
-
screenFrame.origin.x, screenFrame.origin.y,
|
|
1330
|
-
screenFrame.size.width, screenFrame.size.height);
|
|
1331
1173
|
break;
|
|
1332
1174
|
}
|
|
1333
1175
|
}
|
|
1334
1176
|
|
|
1335
|
-
// If no exact match, find screen with maximum overlap
|
|
1336
|
-
if (!windowScreen) {
|
|
1337
|
-
CGFloat maxOverlapArea = 0;
|
|
1338
|
-
NSRect windowRect = NSMakeRect(x, y, width, height);
|
|
1339
|
-
|
|
1340
|
-
for (NSScreen *screen in screens) {
|
|
1341
|
-
NSRect screenFrame = [screen frame];
|
|
1342
|
-
NSRect intersection = NSIntersectionRect(windowRect, screenFrame);
|
|
1343
|
-
CGFloat overlapArea = intersection.size.width * intersection.size.height;
|
|
1344
|
-
|
|
1345
|
-
if (overlapArea > maxOverlapArea) {
|
|
1346
|
-
maxOverlapArea = overlapArea;
|
|
1347
|
-
windowScreen = screen;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
if (windowScreen) {
|
|
1352
|
-
NSLog(@" 🖥️ Window assigned to screen with max overlap: %.0f pixels²", maxOverlapArea);
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
1177
|
if (!windowScreen) {
|
|
1357
1178
|
windowScreen = mainScreen;
|
|
1358
|
-
NSLog(@" 🖥️ Window defaulted to main screen");
|
|
1359
1179
|
}
|
|
1360
1180
|
|
|
1361
1181
|
// Add screen information
|
|
@@ -1368,6 +1188,7 @@ Napi::Value GetSelectedWindowInfo(const Napi::CallbackInfo& info) {
|
|
|
1368
1188
|
result.Set("screenHeight", Napi::Number::New(env, (int)screenFrame.size.height));
|
|
1369
1189
|
|
|
1370
1190
|
// Clear selected window info after reading
|
|
1191
|
+
[g_selectedWindowInfo release];
|
|
1371
1192
|
g_selectedWindowInfo = nil;
|
|
1372
1193
|
|
|
1373
1194
|
return result;
|
|
@@ -1423,74 +1244,13 @@ Napi::Value GetWindowSelectionStatus(const Napi::CallbackInfo& info) {
|
|
|
1423
1244
|
updateOverlay();
|
|
1424
1245
|
}
|
|
1425
1246
|
|
|
1426
|
-
// For Electron mode, also get real-time window under cursor
|
|
1427
|
-
const char* electronVersion = getenv("ELECTRON_VERSION");
|
|
1428
|
-
const char* electronRunAs = getenv("ELECTRON_RUN_AS_NODE");
|
|
1429
|
-
|
|
1430
1247
|
Napi::Object result = Napi::Object::New(env);
|
|
1431
1248
|
result.Set("isSelecting", Napi::Boolean::New(env, g_isWindowSelecting));
|
|
1432
1249
|
result.Set("hasSelectedWindow", Napi::Boolean::New(env, g_selectedWindowInfo != nil));
|
|
1433
1250
|
result.Set("windowCount", Napi::Number::New(env, g_allWindows ? [g_allWindows count] : 0));
|
|
1434
1251
|
result.Set("hasOverlay", Napi::Boolean::New(env, g_overlayWindow != nil));
|
|
1435
1252
|
|
|
1436
|
-
if (
|
|
1437
|
-
// In Electron mode, get real-time window under cursor
|
|
1438
|
-
@try {
|
|
1439
|
-
NSPoint mouseLocation = [NSEvent mouseLocation];
|
|
1440
|
-
NSScreen *mainScreen = [NSScreen mainScreen];
|
|
1441
|
-
CGFloat screenHeight = [mainScreen frame].size.height;
|
|
1442
|
-
CGPoint globalPoint = CGPointMake(mouseLocation.x, screenHeight - mouseLocation.y);
|
|
1443
|
-
|
|
1444
|
-
NSDictionary *windowUnderCursor = getWindowUnderCursor(globalPoint);
|
|
1445
|
-
|
|
1446
|
-
if (windowUnderCursor) {
|
|
1447
|
-
Napi::Object currentWindow = Napi::Object::New(env);
|
|
1448
|
-
currentWindow.Set("id", Napi::Number::New(env, [[windowUnderCursor objectForKey:@"id"] intValue]));
|
|
1449
|
-
currentWindow.Set("title", Napi::String::New(env, [[windowUnderCursor objectForKey:@"title"] UTF8String]));
|
|
1450
|
-
currentWindow.Set("appName", Napi::String::New(env, [[windowUnderCursor objectForKey:@"appName"] UTF8String]));
|
|
1451
|
-
currentWindow.Set("x", Napi::Number::New(env, [[windowUnderCursor objectForKey:@"x"] intValue]));
|
|
1452
|
-
currentWindow.Set("y", Napi::Number::New(env, [[windowUnderCursor objectForKey:@"y"] intValue]));
|
|
1453
|
-
currentWindow.Set("width", Napi::Number::New(env, [[windowUnderCursor objectForKey:@"width"] intValue]));
|
|
1454
|
-
currentWindow.Set("height", Napi::Number::New(env, [[windowUnderCursor objectForKey:@"height"] intValue]));
|
|
1455
|
-
|
|
1456
|
-
// Add screen detection for Electron
|
|
1457
|
-
int x = [[windowUnderCursor objectForKey:@"x"] intValue];
|
|
1458
|
-
int y = [[windowUnderCursor objectForKey:@"y"] intValue];
|
|
1459
|
-
int width = [[windowUnderCursor objectForKey:@"width"] intValue];
|
|
1460
|
-
int height = [[windowUnderCursor objectForKey:@"height"] intValue];
|
|
1461
|
-
|
|
1462
|
-
NSArray *screens = [NSScreen screens];
|
|
1463
|
-
NSScreen *windowScreen = nil;
|
|
1464
|
-
CGPoint windowCenter = CGPointMake(x + width/2, y + height/2);
|
|
1465
|
-
|
|
1466
|
-
for (NSScreen *screen in screens) {
|
|
1467
|
-
NSRect screenFrame = [screen frame];
|
|
1468
|
-
if (windowCenter.x >= screenFrame.origin.x &&
|
|
1469
|
-
windowCenter.x < screenFrame.origin.x + screenFrame.size.width &&
|
|
1470
|
-
windowCenter.y >= screenFrame.origin.y &&
|
|
1471
|
-
windowCenter.y < screenFrame.origin.y + screenFrame.size.height) {
|
|
1472
|
-
windowScreen = screen;
|
|
1473
|
-
break;
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
if (windowScreen) {
|
|
1478
|
-
NSRect screenFrame = [windowScreen frame];
|
|
1479
|
-
currentWindow.Set("screenId", Napi::Number::New(env, [[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] ?
|
|
1480
|
-
[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] intValue] : 0));
|
|
1481
|
-
currentWindow.Set("screenX", Napi::Number::New(env, (int)screenFrame.origin.x));
|
|
1482
|
-
currentWindow.Set("screenY", Napi::Number::New(env, (int)screenFrame.origin.y));
|
|
1483
|
-
currentWindow.Set("screenWidth", Napi::Number::New(env, (int)screenFrame.size.width));
|
|
1484
|
-
currentWindow.Set("screenHeight", Napi::Number::New(env, (int)screenFrame.size.height));
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
result.Set("currentWindow", currentWindow);
|
|
1488
|
-
}
|
|
1489
|
-
} @catch (NSException *exception) {
|
|
1490
|
-
// Ignore mouse tracking errors in Electron mode
|
|
1491
|
-
}
|
|
1492
|
-
} else if (g_currentWindowUnderCursor) {
|
|
1493
|
-
// Native mode
|
|
1253
|
+
if (g_currentWindowUnderCursor) {
|
|
1494
1254
|
Napi::Object currentWindow = Napi::Object::New(env);
|
|
1495
1255
|
currentWindow.Set("id", Napi::Number::New(env, [[g_currentWindowUnderCursor objectForKey:@"id"] intValue]));
|
|
1496
1256
|
currentWindow.Set("title", Napi::String::New(env, [[g_currentWindowUnderCursor objectForKey:@"title"] UTF8String]));
|
|
@@ -1510,13 +1270,13 @@ Napi::Value ShowRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1510
1270
|
Napi::Env env = info.Env();
|
|
1511
1271
|
|
|
1512
1272
|
if (info.Length() < 1) {
|
|
1513
|
-
|
|
1514
|
-
return
|
|
1273
|
+
Napi::TypeError::New(env, "Window info object required").ThrowAsJavaScriptException();
|
|
1274
|
+
return env.Null();
|
|
1515
1275
|
}
|
|
1516
1276
|
|
|
1517
1277
|
if (!info[0].IsObject()) {
|
|
1518
|
-
|
|
1519
|
-
return
|
|
1278
|
+
Napi::TypeError::New(env, "Window info must be an object").ThrowAsJavaScriptException();
|
|
1279
|
+
return env.Null();
|
|
1520
1280
|
}
|
|
1521
1281
|
|
|
1522
1282
|
@try {
|
|
@@ -1548,7 +1308,7 @@ Napi::Value ShowRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1548
1308
|
}
|
|
1549
1309
|
|
|
1550
1310
|
bool success = showRecordingPreview(windowInfo);
|
|
1551
|
-
windowInfo
|
|
1311
|
+
[windowInfo release];
|
|
1552
1312
|
|
|
1553
1313
|
return Napi::Boolean::New(env, success);
|
|
1554
1314
|
|
|
@@ -1574,57 +1334,11 @@ Napi::Value HideRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1574
1334
|
Napi::Value StartScreenSelection(const Napi::CallbackInfo& info) {
|
|
1575
1335
|
Napi::Env env = info.Env();
|
|
1576
1336
|
|
|
1577
|
-
// Electron safety check - prevent NSWindow crashes
|
|
1578
|
-
const char* electronVersion = getenv("ELECTRON_VERSION");
|
|
1579
|
-
const char* electronRunAs = getenv("ELECTRON_RUN_AS_NODE");
|
|
1580
|
-
|
|
1581
|
-
NSLog(@"🔍 Screen Debug: electronVersion='%s', electronRunAs='%s'",
|
|
1582
|
-
electronVersion ? electronVersion : "null",
|
|
1583
|
-
electronRunAs ? electronRunAs : "null");
|
|
1584
|
-
|
|
1585
|
-
if (electronVersion || electronRunAs) {
|
|
1586
|
-
NSLog(@"🔍 Detected Electron environment - using safe screen selection");
|
|
1587
|
-
|
|
1588
|
-
// In Electron, return screen list without creating native NSWindow overlays
|
|
1589
|
-
@try {
|
|
1590
|
-
NSArray *screens = [NSScreen screens];
|
|
1591
|
-
|
|
1592
|
-
if (!screens || [screens count] == 0) {
|
|
1593
|
-
NSLog(@"❌ No screens available");
|
|
1594
|
-
return Napi::Boolean::New(env, false);
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
// Store screens and select first one automatically for Electron
|
|
1598
|
-
g_allScreens = screens;
|
|
1599
|
-
g_isScreenSelecting = true;
|
|
1600
|
-
|
|
1601
|
-
NSScreen *mainScreen = [screens firstObject];
|
|
1602
|
-
g_selectedScreenInfo = @{
|
|
1603
|
-
@"id": @((int)[screens indexOfObject:mainScreen]),
|
|
1604
|
-
@"width": @((int)mainScreen.frame.size.width),
|
|
1605
|
-
@"height": @((int)mainScreen.frame.size.height),
|
|
1606
|
-
@"x": @((int)mainScreen.frame.origin.x),
|
|
1607
|
-
@"y": @((int)mainScreen.frame.origin.y)
|
|
1608
|
-
};
|
|
1609
|
-
|
|
1610
|
-
// Mark as complete so getSelectedScreenInfo returns the selection
|
|
1611
|
-
g_isScreenSelecting = false;
|
|
1612
|
-
|
|
1613
|
-
NSLog(@"✅ Electron-safe screen selection: %lu screens available", (unsigned long)[screens count]);
|
|
1614
|
-
return Napi::Boolean::New(env, true);
|
|
1615
|
-
|
|
1616
|
-
} @catch (NSException *exception) {
|
|
1617
|
-
NSLog(@"❌ Exception in Electron-safe screen selection: %@", [exception reason]);
|
|
1618
|
-
return Napi::Boolean::New(env, false);
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
1337
|
@try {
|
|
1623
1338
|
bool success = startScreenSelection();
|
|
1624
1339
|
return Napi::Boolean::New(env, success);
|
|
1625
1340
|
|
|
1626
1341
|
} @catch (NSException *exception) {
|
|
1627
|
-
NSLog(@"❌ Screen selection error: %@", exception);
|
|
1628
1342
|
return Napi::Boolean::New(env, false);
|
|
1629
1343
|
}
|
|
1630
1344
|
}
|
|
@@ -1716,7 +1430,7 @@ Napi::Value ShowScreenRecordingPreview(const Napi::CallbackInfo& info) {
|
|
|
1716
1430
|
}
|
|
1717
1431
|
|
|
1718
1432
|
bool success = showScreenRecordingPreview(screenInfo);
|
|
1719
|
-
screenInfo
|
|
1433
|
+
[screenInfo release];
|
|
1720
1434
|
|
|
1721
1435
|
return Napi::Boolean::New(env, success);
|
|
1722
1436
|
|