node-mac-recorder 2.15.8 ā 2.15.10
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/debug-coordinates.js +69 -0
- package/package.json +1 -1
- package/src/mac_recorder.mm +37 -3
- package/src/window_selector.mm +64 -34
- package/test-multidisplay-fix.js +120 -0
- package/test-window-selector-fix.js +88 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
|
|
3
|
+
async function debugCoordinates() {
|
|
4
|
+
const recorder = new MacRecorder();
|
|
5
|
+
const displays = await recorder.getDisplays();
|
|
6
|
+
|
|
7
|
+
console.log('š„ļø Display coordinates analysis:');
|
|
8
|
+
displays.forEach((display, index) => {
|
|
9
|
+
console.log(`Display ${index}: ${display.name}`);
|
|
10
|
+
console.log(` ID: ${display.id}`);
|
|
11
|
+
console.log(` Position: (${display.x}, ${display.y})`);
|
|
12
|
+
console.log(` Size: ${display.width}x${display.height}`);
|
|
13
|
+
console.log(` isPrimary: ${display.isPrimary}`);
|
|
14
|
+
console.log('');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Calculate combined frame like we do in native code
|
|
18
|
+
let combinedFrame = { x: 0, y: 0, width: 0, height: 0 };
|
|
19
|
+
let first = true;
|
|
20
|
+
|
|
21
|
+
for (const display of displays) {
|
|
22
|
+
if (first) {
|
|
23
|
+
combinedFrame = {
|
|
24
|
+
x: display.x,
|
|
25
|
+
y: display.y,
|
|
26
|
+
width: display.width,
|
|
27
|
+
height: display.height
|
|
28
|
+
};
|
|
29
|
+
first = false;
|
|
30
|
+
} else {
|
|
31
|
+
const minX = Math.min(combinedFrame.x, display.x);
|
|
32
|
+
const minY = Math.min(combinedFrame.y, display.y);
|
|
33
|
+
const maxX = Math.max(combinedFrame.x + combinedFrame.width, display.x + display.width);
|
|
34
|
+
const maxY = Math.max(combinedFrame.y + combinedFrame.height, display.y + display.height);
|
|
35
|
+
|
|
36
|
+
combinedFrame = {
|
|
37
|
+
x: minX,
|
|
38
|
+
y: minY,
|
|
39
|
+
width: maxX - minX,
|
|
40
|
+
height: maxY - minY
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log('š Combined frame calculation:');
|
|
46
|
+
console.log(` Origin: (${combinedFrame.x}, ${combinedFrame.y})`);
|
|
47
|
+
console.log(` Size: ${combinedFrame.width}x${combinedFrame.height}`);
|
|
48
|
+
console.log('');
|
|
49
|
+
|
|
50
|
+
console.log('š Coordinate conversion examples:');
|
|
51
|
+
displays.forEach((display, index) => {
|
|
52
|
+
console.log(`Display ${index} (${display.name}):`);
|
|
53
|
+
const localOriginX = display.x - combinedFrame.x;
|
|
54
|
+
const localOriginY = display.y - combinedFrame.y;
|
|
55
|
+
console.log(` Global offset from combined: (${localOriginX}, ${localOriginY})`);
|
|
56
|
+
|
|
57
|
+
// Test window at display origin
|
|
58
|
+
const testWindowGlobalX = display.x + 100;
|
|
59
|
+
const testWindowGlobalY = display.y + 100;
|
|
60
|
+
const localWindowX = testWindowGlobalX - combinedFrame.x;
|
|
61
|
+
const localWindowY = testWindowGlobalY - combinedFrame.y;
|
|
62
|
+
|
|
63
|
+
console.log(` Test window at (${testWindowGlobalX}, ${testWindowGlobalY}) global`);
|
|
64
|
+
console.log(` -> Local coordinates: (${localWindowX}, ${localWindowY})`);
|
|
65
|
+
console.log('');
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
debugCoordinates();
|
package/package.json
CHANGED
package/src/mac_recorder.mm
CHANGED
|
@@ -451,13 +451,47 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
|
|
|
451
451
|
NSArray *screens = [NSScreen screens];
|
|
452
452
|
NSMutableArray *displays = [NSMutableArray array];
|
|
453
453
|
|
|
454
|
+
// Get real CGDirectDisplayIDs first
|
|
455
|
+
CGDirectDisplayID activeDisplays[32];
|
|
456
|
+
uint32_t displayCount;
|
|
457
|
+
CGGetActiveDisplayList(32, activeDisplays, &displayCount);
|
|
458
|
+
|
|
454
459
|
for (NSUInteger i = 0; i < [screens count]; i++) {
|
|
455
460
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
461
|
+
NSRect frame = [screen frame];
|
|
462
|
+
|
|
463
|
+
// Find matching CGDirectDisplayID
|
|
464
|
+
CGDirectDisplayID displayID = 0;
|
|
465
|
+
bool isPrimary = false;
|
|
466
|
+
|
|
467
|
+
for (uint32_t j = 0; j < displayCount; j++) {
|
|
468
|
+
CGDirectDisplayID candidateID = activeDisplays[j];
|
|
469
|
+
CGRect displayBounds = CGDisplayBounds(candidateID);
|
|
470
|
+
|
|
471
|
+
if (fabs(frame.origin.x - displayBounds.origin.x) < 1.0 &&
|
|
472
|
+
fabs(frame.origin.y - displayBounds.origin.y) < 1.0 &&
|
|
473
|
+
fabs(frame.size.width - displayBounds.size.width) < 1.0 &&
|
|
474
|
+
fabs(frame.size.height - displayBounds.size.height) < 1.0) {
|
|
475
|
+
displayID = candidateID;
|
|
476
|
+
isPrimary = (candidateID == CGMainDisplayID());
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Fallback if no match found
|
|
482
|
+
if (displayID == 0 && i < displayCount) {
|
|
483
|
+
displayID = activeDisplays[i];
|
|
484
|
+
isPrimary = (displayID == CGMainDisplayID());
|
|
485
|
+
}
|
|
486
|
+
|
|
456
487
|
NSDictionary *displayInfo = @{
|
|
457
|
-
@"id": @(
|
|
488
|
+
@"id": @(displayID), // Use real display ID
|
|
458
489
|
@"name": [NSString stringWithFormat:@"Display %lu", (unsigned long)(i + 1)],
|
|
459
|
-
@"width": @((int)
|
|
460
|
-
@"height": @((int)
|
|
490
|
+
@"width": @((int)frame.size.width),
|
|
491
|
+
@"height": @((int)frame.size.height),
|
|
492
|
+
@"x": @((int)frame.origin.x),
|
|
493
|
+
@"y": @((int)frame.origin.y),
|
|
494
|
+
@"isPrimary": @(isPrimary)
|
|
461
495
|
};
|
|
462
496
|
[displays addObject:displayInfo];
|
|
463
497
|
}
|
package/src/window_selector.mm
CHANGED
|
@@ -74,6 +74,7 @@ void updateScreenOverlays();
|
|
|
74
74
|
@property (nonatomic) BOOL isActiveWindow;
|
|
75
75
|
@property (nonatomic) BOOL isToggled;
|
|
76
76
|
@property (nonatomic) NSRect highlightFrame;
|
|
77
|
+
@property (nonatomic, strong) NSValue *globalOriginOffset; // Store global coordinate offset
|
|
77
78
|
- (void)setHighlightFrame:(NSRect)frame;
|
|
78
79
|
@end
|
|
79
80
|
|
|
@@ -863,10 +864,21 @@ void updateOverlay() {
|
|
|
863
864
|
}
|
|
864
865
|
}
|
|
865
866
|
|
|
866
|
-
//
|
|
867
|
+
// Convert global window coordinates to local view coordinates
|
|
868
|
+
// Account for multi-display offset from global origin
|
|
869
|
+
WindowSelectorOverlayView *overlayView = (WindowSelectorOverlayView *)g_overlayView;
|
|
870
|
+
NSPoint globalOffset = NSZeroPoint;
|
|
871
|
+
if (overlayView.globalOriginOffset) {
|
|
872
|
+
globalOffset = [overlayView.globalOriginOffset pointValue];
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Convert global window position to local view position
|
|
876
|
+
CGFloat localX = x - globalOffset.x;
|
|
877
|
+
CGFloat localY = ([g_overlayView frame].size.height - (y - globalOffset.y)) - height;
|
|
878
|
+
|
|
867
879
|
// Update overlay view window info for highlighting
|
|
868
|
-
[
|
|
869
|
-
[
|
|
880
|
+
[overlayView setWindowInfo:targetWindow];
|
|
881
|
+
[overlayView setHighlightFrame:NSMakeRect(localX, localY, width, height)];
|
|
870
882
|
|
|
871
883
|
// Only reset toggle state when switching to different window (not for position updates)
|
|
872
884
|
if (!g_hasToggledWindow) {
|
|
@@ -967,19 +979,19 @@ void updateOverlay() {
|
|
|
967
979
|
// Position buttons - Start Record in center of selected window
|
|
968
980
|
if (g_selectButton) {
|
|
969
981
|
NSSize buttonSize = [g_selectButton frame].size;
|
|
970
|
-
// Use window center for positioning
|
|
971
|
-
CGFloat
|
|
972
|
-
CGFloat
|
|
982
|
+
// Use local window center for positioning
|
|
983
|
+
CGFloat localWindowCenterX = localX + (width / 2);
|
|
984
|
+
CGFloat localWindowCenterY = localY + (height / 2);
|
|
973
985
|
NSPoint buttonCenter = NSMakePoint(
|
|
974
|
-
|
|
975
|
-
|
|
986
|
+
localWindowCenterX - (buttonSize.width / 2),
|
|
987
|
+
localWindowCenterY - (buttonSize.height / 2) // Perfect center of window
|
|
976
988
|
);
|
|
977
989
|
[g_selectButton setFrameOrigin:buttonCenter];
|
|
978
990
|
|
|
979
991
|
// Position app icon above window center
|
|
980
992
|
NSPoint iconCenter = NSMakePoint(
|
|
981
|
-
|
|
982
|
-
|
|
993
|
+
localWindowCenterX - (96 / 2), // Center horizontally on window
|
|
994
|
+
localWindowCenterY + 120 // 120px above window center
|
|
983
995
|
);
|
|
984
996
|
[appIconView setFrameOrigin:iconCenter];
|
|
985
997
|
NSLog(@"šÆ Positioning app icon at: (%.0f, %.0f) for window size: (%.0f, %.0f)",
|
|
@@ -1001,8 +1013,8 @@ void updateOverlay() {
|
|
|
1001
1013
|
|
|
1002
1014
|
// Position info label between icon and button relative to window center
|
|
1003
1015
|
NSPoint labelCenter = NSMakePoint(
|
|
1004
|
-
|
|
1005
|
-
|
|
1016
|
+
localWindowCenterX - ([infoLabel frame].size.width / 2), // Center horizontally on window
|
|
1017
|
+
localWindowCenterY + 50 // 50px above window center, below icon
|
|
1006
1018
|
);
|
|
1007
1019
|
[infoLabel setFrameOrigin:labelCenter];
|
|
1008
1020
|
|
|
@@ -1019,8 +1031,8 @@ void updateOverlay() {
|
|
|
1019
1031
|
if (cancelButton) {
|
|
1020
1032
|
NSSize cancelButtonSize = [cancelButton frame].size;
|
|
1021
1033
|
NSPoint cancelButtonCenter = NSMakePoint(
|
|
1022
|
-
|
|
1023
|
-
|
|
1034
|
+
localWindowCenterX - (cancelButtonSize.width / 2), // Center horizontally on window
|
|
1035
|
+
localWindowCenterY - 80 // 80px below window center
|
|
1024
1036
|
);
|
|
1025
1037
|
[cancelButton setFrameOrigin:cancelButtonCenter];
|
|
1026
1038
|
}
|
|
@@ -1436,8 +1448,9 @@ bool startScreenSelection() {
|
|
|
1436
1448
|
[overlayWindow setOpaque:NO];
|
|
1437
1449
|
[overlayWindow setBackgroundColor:[NSColor clearColor]];
|
|
1438
1450
|
|
|
1439
|
-
// Create overlay view
|
|
1440
|
-
|
|
1451
|
+
// Create overlay view - use local frame (0,0) not screen frame
|
|
1452
|
+
NSRect localFrame = NSMakeRect(0, 0, screenFrame.size.width, screenFrame.size.height);
|
|
1453
|
+
ScreenSelectorOverlayView *overlayView = [[ScreenSelectorOverlayView alloc] initWithFrame:localFrame];
|
|
1441
1454
|
[overlayView setScreenInfo:screenInfo];
|
|
1442
1455
|
[overlayWindow setContentView:overlayView];
|
|
1443
1456
|
|
|
@@ -1521,8 +1534,8 @@ bool startScreenSelection() {
|
|
|
1521
1534
|
[screenCancelButton setFocusRingType:NSFocusRingTypeNone];
|
|
1522
1535
|
[screenCancelButton setShowsBorderOnlyWhileMouseInside:NO];
|
|
1523
1536
|
|
|
1524
|
-
// Create info label for screen
|
|
1525
|
-
NSTextField *screenInfoLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0,
|
|
1537
|
+
// Create info label for screen - use local coordinates
|
|
1538
|
+
NSTextField *screenInfoLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, localFrame.size.width - 40, 60)];
|
|
1526
1539
|
[screenInfoLabel setEditable:NO];
|
|
1527
1540
|
[screenInfoLabel setSelectable:NO];
|
|
1528
1541
|
[screenInfoLabel setBezeled:NO];
|
|
@@ -1548,17 +1561,17 @@ bool startScreenSelection() {
|
|
|
1548
1561
|
NSString *resolution = [screenInfo objectForKey:@"resolution"] ?: @"Unknown Resolution";
|
|
1549
1562
|
[screenInfoLabel setStringValue:[NSString stringWithFormat:@"%@\n%@", screenName, resolution]];
|
|
1550
1563
|
|
|
1551
|
-
// Position buttons - Start Record in perfect center
|
|
1564
|
+
// Position buttons - Start Record in perfect center using local coordinates
|
|
1552
1565
|
NSPoint buttonCenter = NSMakePoint(
|
|
1553
|
-
(
|
|
1554
|
-
(
|
|
1566
|
+
(localFrame.size.width - [selectButton frame].size.width) / 2,
|
|
1567
|
+
(localFrame.size.height - [selectButton frame].size.height) / 2 // Perfect center
|
|
1555
1568
|
);
|
|
1556
1569
|
[selectButton setFrameOrigin:buttonCenter];
|
|
1557
1570
|
|
|
1558
|
-
// Position screen icon above center
|
|
1571
|
+
// Position screen icon above center using local coordinates
|
|
1559
1572
|
NSPoint iconCenter = NSMakePoint(
|
|
1560
|
-
(
|
|
1561
|
-
(
|
|
1573
|
+
(localFrame.size.width - 96) / 2, // Center horizontally (icon is 96px wide)
|
|
1574
|
+
(localFrame.size.height / 2) + 120 // 120px above center
|
|
1562
1575
|
);
|
|
1563
1576
|
[screenIconView setFrameOrigin:iconCenter];
|
|
1564
1577
|
|
|
@@ -1572,16 +1585,16 @@ bool startScreenSelection() {
|
|
|
1572
1585
|
screenFloatAnimationX.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
|
1573
1586
|
[screenIconView.layer addAnimation:screenFloatAnimationX forKey:@"floatAnimationX"];
|
|
1574
1587
|
|
|
1575
|
-
// Position info label between icon and button
|
|
1588
|
+
// Position info label between icon and button using local coordinates
|
|
1576
1589
|
NSPoint labelCenter = NSMakePoint(
|
|
1577
|
-
(
|
|
1578
|
-
(
|
|
1590
|
+
(localFrame.size.width - [screenInfoLabel frame].size.width) / 2, // Center horizontally
|
|
1591
|
+
(localFrame.size.height / 2) + 50 // 50px above center, below icon
|
|
1579
1592
|
);
|
|
1580
1593
|
[screenInfoLabel setFrameOrigin:labelCenter];
|
|
1581
1594
|
|
|
1582
1595
|
NSPoint cancelButtonCenter = NSMakePoint(
|
|
1583
|
-
(
|
|
1584
|
-
(
|
|
1596
|
+
(localFrame.size.width - [screenCancelButton frame].size.width) / 2,
|
|
1597
|
+
(localFrame.size.height / 2) - 80 // 80px below center
|
|
1585
1598
|
);
|
|
1586
1599
|
[screenCancelButton setFrameOrigin:cancelButtonCenter];
|
|
1587
1600
|
|
|
@@ -1801,9 +1814,21 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1801
1814
|
return env.Null();
|
|
1802
1815
|
}
|
|
1803
1816
|
|
|
1804
|
-
// Create
|
|
1805
|
-
|
|
1806
|
-
NSRect
|
|
1817
|
+
// Create multi-display overlay window to prevent window dragging
|
|
1818
|
+
NSArray *allScreens = [NSScreen screens];
|
|
1819
|
+
NSRect combinedFrame = NSZeroRect;
|
|
1820
|
+
|
|
1821
|
+
// Calculate combined frame that covers all displays
|
|
1822
|
+
for (NSScreen *screen in allScreens) {
|
|
1823
|
+
NSRect screenFrame = [screen frame];
|
|
1824
|
+
if (NSEqualRects(combinedFrame, NSZeroRect)) {
|
|
1825
|
+
combinedFrame = screenFrame;
|
|
1826
|
+
} else {
|
|
1827
|
+
combinedFrame = NSUnionRect(combinedFrame, screenFrame);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
NSRect fullScreenFrame = combinedFrame;
|
|
1807
1832
|
g_overlayWindow = [[NoFocusWindow alloc] initWithContentRect:fullScreenFrame
|
|
1808
1833
|
styleMask:NSWindowStyleMaskBorderless
|
|
1809
1834
|
backing:NSBackingStoreBuffered
|
|
@@ -1832,8 +1857,13 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1832
1857
|
[g_overlayWindow setOpaque:NO];
|
|
1833
1858
|
[g_overlayWindow setBackgroundColor:[NSColor clearColor]];
|
|
1834
1859
|
|
|
1835
|
-
// Create overlay view covering
|
|
1836
|
-
|
|
1860
|
+
// Create overlay view covering all displays
|
|
1861
|
+
// Convert global coordinates to local view coordinates (offset by origin)
|
|
1862
|
+
NSRect localViewFrame = NSMakeRect(0, 0, fullScreenFrame.size.width, fullScreenFrame.size.height);
|
|
1863
|
+
g_overlayView = [[WindowSelectorOverlayView alloc] initWithFrame:localViewFrame];
|
|
1864
|
+
|
|
1865
|
+
// Store the global origin offset for coordinate conversion
|
|
1866
|
+
[(WindowSelectorOverlayView *)g_overlayView setValue:[NSValue valueWithPoint:fullScreenFrame.origin] forKey:@"globalOriginOffset"];
|
|
1837
1867
|
[g_overlayWindow setContentView:g_overlayView];
|
|
1838
1868
|
|
|
1839
1869
|
// Note: NSWindow doesn't have setWantsLayer method, only NSView does
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
const WindowSelector = MacRecorder.WindowSelector;
|
|
3
|
+
|
|
4
|
+
console.log('š§Ŗ Testing multi-display fixes...');
|
|
5
|
+
|
|
6
|
+
async function testDisplaySelection() {
|
|
7
|
+
console.log('\nš„ļø Testing display selection:');
|
|
8
|
+
|
|
9
|
+
const recorder = new MacRecorder();
|
|
10
|
+
const displays = await recorder.getDisplays();
|
|
11
|
+
|
|
12
|
+
console.log(`Found ${displays.length} displays:`);
|
|
13
|
+
displays.forEach((display, index) => {
|
|
14
|
+
console.log(` ${index + 1}. ${display.name} (ID: ${display.id}) - ${display.resolution} at (${display.x}, ${display.y}) ${display.isPrimary ? '[PRIMARY]' : ''}`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Test recording on each display
|
|
18
|
+
for (const display of displays) {
|
|
19
|
+
console.log(`\nš¹ Testing recording on ${display.name}:`);
|
|
20
|
+
try {
|
|
21
|
+
const outputPath = `./test-output/display-${display.id}-test.mov`;
|
|
22
|
+
|
|
23
|
+
await recorder.startRecording(outputPath, {
|
|
24
|
+
displayId: display.id,
|
|
25
|
+
captureCursor: false,
|
|
26
|
+
includeMicrophone: false,
|
|
27
|
+
includeSystemAudio: false
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
console.log(`ā
Recording started on ${display.name}`);
|
|
31
|
+
|
|
32
|
+
// Record for 2 seconds
|
|
33
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
34
|
+
|
|
35
|
+
await recorder.stopRecording();
|
|
36
|
+
console.log(`ā
Recording stopped on ${display.name}`);
|
|
37
|
+
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(`ā Recording failed on ${display.name}: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Small delay between tests
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function testScreenSelector() {
|
|
48
|
+
console.log('\nšÆ Testing screen selector multi-display...');
|
|
49
|
+
|
|
50
|
+
const selector = new WindowSelector();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
console.log('š Starting screen selection... (This should show overlays on ALL displays)');
|
|
54
|
+
await selector.startScreenSelection();
|
|
55
|
+
|
|
56
|
+
console.log('ā±ļø Screen selection UI is running...');
|
|
57
|
+
console.log(' - Check if overlays appear on ALL displays');
|
|
58
|
+
console.log(' - Check if buttons and text are positioned correctly');
|
|
59
|
+
console.log(' - Check if app icons are visible and animated');
|
|
60
|
+
|
|
61
|
+
// Let it run for 10 seconds for manual inspection
|
|
62
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
63
|
+
|
|
64
|
+
console.log('š Stopping screen selection...');
|
|
65
|
+
await selector.stopScreenSelection();
|
|
66
|
+
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log(`ā Screen selector test failed: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function testWindowSelector() {
|
|
73
|
+
console.log('\nšŖ Testing window selector multi-display...');
|
|
74
|
+
|
|
75
|
+
const selector = new WindowSelector();
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
console.log('š Starting window selection... (This should work across ALL displays)');
|
|
79
|
+
await selector.startSelection();
|
|
80
|
+
|
|
81
|
+
console.log('ā±ļø Window selection UI is running...');
|
|
82
|
+
console.log(' - Move cursor to windows on different displays');
|
|
83
|
+
console.log(' - Check if highlighting works properly');
|
|
84
|
+
console.log(' - Check if buttons appear correctly over highlighted windows');
|
|
85
|
+
|
|
86
|
+
// Let it run for 15 seconds for manual inspection
|
|
87
|
+
await new Promise(resolve => setTimeout(resolve, 15000));
|
|
88
|
+
|
|
89
|
+
console.log('š Stopping window selection...');
|
|
90
|
+
await selector.stopSelection();
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.log(`ā Window selector test failed: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function runTests() {
|
|
98
|
+
try {
|
|
99
|
+
// Check permissions first
|
|
100
|
+
const recorder = new MacRecorder();
|
|
101
|
+
const permissions = await recorder.checkPermissions();
|
|
102
|
+
|
|
103
|
+
if (!permissions.screenRecording) {
|
|
104
|
+
console.log('ā Screen recording permission required. Enable in System Preferences > Security & Privacy > Privacy > Screen Recording');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await testDisplaySelection();
|
|
109
|
+
await testScreenSelector();
|
|
110
|
+
await testWindowSelector();
|
|
111
|
+
|
|
112
|
+
console.log('\nš Multi-display tests completed!');
|
|
113
|
+
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.log(`ā Test suite failed: ${error.message}`);
|
|
116
|
+
console.log(error.stack);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
runTests();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
const WindowSelector = MacRecorder.WindowSelector;
|
|
3
|
+
|
|
4
|
+
async function testWindowSelector() {
|
|
5
|
+
console.log('šŖ Testing FIXED window selector multi-display...');
|
|
6
|
+
|
|
7
|
+
const recorder = new MacRecorder();
|
|
8
|
+
const displays = await recorder.getDisplays();
|
|
9
|
+
|
|
10
|
+
console.log('š Display setup:');
|
|
11
|
+
displays.forEach((display, index) => {
|
|
12
|
+
console.log(` ${display.name}: ${display.resolution} at (${display.x}, ${display.y}) ${display.isPrimary ? '[PRIMARY]' : ''}`);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const selector = new WindowSelector();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
console.log('\nš Starting window selection...');
|
|
19
|
+
console.log(' - Test windows on BOTH displays');
|
|
20
|
+
console.log(' - Check if buttons appear correctly positioned');
|
|
21
|
+
console.log(' - Primary display should now work correctly');
|
|
22
|
+
|
|
23
|
+
await selector.startSelection();
|
|
24
|
+
|
|
25
|
+
// Let it run for 20 seconds for thorough testing
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, 20000));
|
|
27
|
+
|
|
28
|
+
console.log('š Stopping window selection...');
|
|
29
|
+
await selector.stopSelection();
|
|
30
|
+
|
|
31
|
+
console.log('ā
Window selector test completed!');
|
|
32
|
+
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.log(`ā Window selector test failed: ${error.message}`);
|
|
35
|
+
console.log(error.stack);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function testSecondaryDisplayRecording() {
|
|
40
|
+
console.log('\nš„ Testing secondary display recording...');
|
|
41
|
+
|
|
42
|
+
const recorder = new MacRecorder();
|
|
43
|
+
const displays = await recorder.getDisplays();
|
|
44
|
+
|
|
45
|
+
const secondaryDisplay = displays.find(d => !d.isPrimary);
|
|
46
|
+
if (!secondaryDisplay) {
|
|
47
|
+
console.log('ā ļø No secondary display found');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(`š„ļø Testing recording on ${secondaryDisplay.name} (ID: ${secondaryDisplay.id})`);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const outputPath = `./test-output/secondary-display-fix-test.mov`;
|
|
55
|
+
|
|
56
|
+
await recorder.startRecording(outputPath, {
|
|
57
|
+
displayId: secondaryDisplay.id,
|
|
58
|
+
captureCursor: true,
|
|
59
|
+
includeMicrophone: false,
|
|
60
|
+
includeSystemAudio: false
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
console.log('ā
Recording started on secondary display');
|
|
64
|
+
|
|
65
|
+
// Record for 3 seconds
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
67
|
+
|
|
68
|
+
await recorder.stopRecording();
|
|
69
|
+
console.log('ā
Recording completed on secondary display');
|
|
70
|
+
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.log(`ā Secondary display recording failed: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function runTests() {
|
|
77
|
+
try {
|
|
78
|
+
await testWindowSelector();
|
|
79
|
+
await testSecondaryDisplayRecording();
|
|
80
|
+
|
|
81
|
+
console.log('\nš All tests completed!');
|
|
82
|
+
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.log(`ā Test suite failed: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
runTests();
|