node-mac-recorder 2.6.7 ā 2.6.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-screen-selection.js +81 -0
- package/package.json +3 -2
- package/quick-screen-test.js +34 -0
- package/src/window_selector.mm +69 -21
- package/test-real-screen-ids.js +32 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
const MacRecorder = require('./index');
|
|
5
|
+
|
|
6
|
+
async function debugScreenSelection() {
|
|
7
|
+
console.log('š Screen Selection Debug Test');
|
|
8
|
+
console.log('================================\n');
|
|
9
|
+
|
|
10
|
+
const selector = new WindowSelector();
|
|
11
|
+
const recorder = new MacRecorder();
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// First, let's see what displays MacRecorder sees
|
|
15
|
+
console.log('šŗ MacRecorder displays:');
|
|
16
|
+
const macDisplays = await recorder.getDisplays();
|
|
17
|
+
macDisplays.forEach((display, i) => {
|
|
18
|
+
console.log(` ${i}: ID=${display.id}, Name="${display.name}", Resolution=${display.resolution}, Primary=${display.isPrimary}`);
|
|
19
|
+
});
|
|
20
|
+
console.log('');
|
|
21
|
+
|
|
22
|
+
// Start screen selection
|
|
23
|
+
console.log('š„ļø Starting screen selection...');
|
|
24
|
+
console.log(' Move mouse to different screens and click "Start Record" on one of them');
|
|
25
|
+
console.log('');
|
|
26
|
+
|
|
27
|
+
const selectedScreen = await selector.selectScreen();
|
|
28
|
+
|
|
29
|
+
console.log('ā
Screen selected!');
|
|
30
|
+
console.log('š Selected screen data:');
|
|
31
|
+
console.log(JSON.stringify(selectedScreen, null, 2));
|
|
32
|
+
|
|
33
|
+
// Check if this ID exists in MacRecorder displays
|
|
34
|
+
const matchingDisplay = macDisplays.find(d => d.id === selectedScreen.id);
|
|
35
|
+
|
|
36
|
+
if (matchingDisplay) {
|
|
37
|
+
console.log('\nā
MATCH FOUND in MacRecorder displays:');
|
|
38
|
+
console.log(` Selected: ${selectedScreen.name} (ID: ${selectedScreen.id})`);
|
|
39
|
+
console.log(` MacRecorder: ${matchingDisplay.name} (ID: ${matchingDisplay.id})`);
|
|
40
|
+
} else {
|
|
41
|
+
console.log('\nā NO MATCH found in MacRecorder displays!');
|
|
42
|
+
console.log(` Selected ID: ${selectedScreen.id}`);
|
|
43
|
+
console.log(` Available IDs: ${macDisplays.map(d => d.id).join(', ')}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('\nš¬ Testing actual recording...');
|
|
47
|
+
console.log(' Setting displayId option and starting short recording');
|
|
48
|
+
|
|
49
|
+
// Set the display ID from screen selection
|
|
50
|
+
recorder.setOptions({
|
|
51
|
+
displayId: selectedScreen.id,
|
|
52
|
+
includeSystemAudio: false,
|
|
53
|
+
includeMicrophone: false
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const testFile = `./test-output/screen-selection-test-${Date.now()}.mov`;
|
|
57
|
+
console.log(` Recording file: ${testFile}`);
|
|
58
|
+
|
|
59
|
+
await recorder.startRecording(testFile);
|
|
60
|
+
console.log(' Recording started...');
|
|
61
|
+
|
|
62
|
+
// Record for 3 seconds
|
|
63
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
64
|
+
|
|
65
|
+
await recorder.stopRecording();
|
|
66
|
+
console.log(' Recording stopped');
|
|
67
|
+
|
|
68
|
+
console.log('\nā
Test completed! Check the recording to see if it captured the correct screen.');
|
|
69
|
+
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('\nā Error during test:', error.message);
|
|
72
|
+
if (error.stack) {
|
|
73
|
+
console.error('Stack:', error.stack);
|
|
74
|
+
}
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (require.main === module) {
|
|
80
|
+
debugScreenSelection();
|
|
81
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-mac-recorder",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.10",
|
|
4
4
|
"description": "Native macOS screen recording package for Node.js applications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"example:window-selector": "node examples/window-selector-example.js"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"node-addon-api": "^7.0.0"
|
|
45
|
+
"node-addon-api": "^7.0.0",
|
|
46
|
+
"node-mac-recorder": "^2.6.7"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"node-gyp": "^10.0.0"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
|
|
5
|
+
console.log('Starting screen selection...');
|
|
6
|
+
console.log('Click "Start Record" on ANY screen then this will show the result:');
|
|
7
|
+
|
|
8
|
+
const selector = new WindowSelector();
|
|
9
|
+
|
|
10
|
+
selector.startScreenSelection().then(() => {
|
|
11
|
+
console.log('Screen selection UI shown');
|
|
12
|
+
|
|
13
|
+
// Check every 500ms for selection
|
|
14
|
+
const checkInterval = setInterval(() => {
|
|
15
|
+
const selected = selector.getSelectedScreen();
|
|
16
|
+
if (selected) {
|
|
17
|
+
console.log('\nš SCREEN SELECTED!');
|
|
18
|
+
console.log('Selected data:', JSON.stringify(selected, null, 2));
|
|
19
|
+
clearInterval(checkInterval);
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
}, 500);
|
|
23
|
+
|
|
24
|
+
// Timeout after 15 seconds
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
console.log('\nā° Timeout - no screen selected');
|
|
27
|
+
clearInterval(checkInterval);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}, 15000);
|
|
30
|
+
|
|
31
|
+
}).catch(error => {
|
|
32
|
+
console.error('Error:', error.message);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
package/src/window_selector.mm
CHANGED
|
@@ -61,32 +61,44 @@ void updateScreenOverlays();
|
|
|
61
61
|
- (instancetype)initWithFrame:(NSRect)frameRect {
|
|
62
62
|
self = [super initWithFrame:frameRect];
|
|
63
63
|
if (self) {
|
|
64
|
+
// Use layer for background instead of custom drawing
|
|
64
65
|
self.wantsLayer = YES;
|
|
65
|
-
self.layer.backgroundColor = [[NSColor clearColor] CGColor];
|
|
66
|
-
// Ensure no borders or decorations
|
|
67
|
-
self.layer.borderWidth = 0.0;
|
|
68
|
-
self.layer.cornerRadius = 0.0;
|
|
69
66
|
self.isActiveWindow = YES; // Default to active for current window under mouse
|
|
67
|
+
|
|
68
|
+
// Set purple background with border using layer
|
|
69
|
+
[self updateAppearance];
|
|
70
|
+
|
|
71
|
+
// Window selector overlay view created
|
|
70
72
|
}
|
|
71
73
|
return self;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
- (void)
|
|
75
|
-
[super drawRect:dirtyRect];
|
|
76
|
-
|
|
77
|
-
if (!self.windowInfo) return;
|
|
78
|
-
|
|
79
|
-
// Background with transparency - same style as screen overlay
|
|
76
|
+
- (void)updateAppearance {
|
|
80
77
|
if (self.isActiveWindow) {
|
|
81
|
-
// Active window: brighter
|
|
82
|
-
[[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4]
|
|
78
|
+
// Active window: brighter background
|
|
79
|
+
self.layer.backgroundColor = [[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4] CGColor];
|
|
80
|
+
// Active window appearance set
|
|
83
81
|
} else {
|
|
84
|
-
// Inactive window: dimmer
|
|
85
|
-
[[NSColor colorWithRed:0.4 green:0.2 blue:0.6 alpha:0.25]
|
|
82
|
+
// Inactive window: dimmer background
|
|
83
|
+
self.layer.backgroundColor = [[NSColor colorWithRed:0.4 green:0.2 blue:0.6 alpha:0.25] CGColor];
|
|
84
|
+
// Inactive window appearance set
|
|
86
85
|
}
|
|
87
|
-
|
|
86
|
+
|
|
87
|
+
// No border to match screen selector
|
|
88
|
+
self.layer.borderColor = [[NSColor clearColor] CGColor];
|
|
89
|
+
self.layer.borderWidth = 0.0;
|
|
90
|
+
self.layer.cornerRadius = 0.0;
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
- (void)setIsActiveWindow:(BOOL)isActiveWindow {
|
|
94
|
+
if (_isActiveWindow != isActiveWindow) {
|
|
95
|
+
_isActiveWindow = isActiveWindow;
|
|
96
|
+
[self updateAppearance];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Layer-based approach, no custom drawing needed
|
|
101
|
+
|
|
90
102
|
@end
|
|
91
103
|
|
|
92
104
|
// Recording preview overlay view - full screen with cutout
|
|
@@ -210,9 +222,9 @@ void updateScreenOverlays();
|
|
|
210
222
|
NSDictionary *screenInfo = [g_allScreens objectAtIndex:screenIndex];
|
|
211
223
|
g_selectedScreenInfo = [screenInfo retain];
|
|
212
224
|
|
|
213
|
-
NSLog(@"š„ļø SCREEN
|
|
225
|
+
NSLog(@"š„ļø SCREEN SELECTED: %@ (ID: %@)",
|
|
214
226
|
[screenInfo objectForKey:@"name"],
|
|
215
|
-
[screenInfo objectForKey:@"
|
|
227
|
+
[screenInfo objectForKey:@"id"]);
|
|
216
228
|
|
|
217
229
|
cleanupScreenSelector();
|
|
218
230
|
}
|
|
@@ -506,7 +518,6 @@ void updateOverlay() {
|
|
|
506
518
|
|
|
507
519
|
// Update overlay view window info
|
|
508
520
|
[(WindowSelectorOverlayView *)g_overlayView setWindowInfo:windowUnderCursor];
|
|
509
|
-
[g_overlayView setNeedsDisplay:YES];
|
|
510
521
|
|
|
511
522
|
// Add/update info label above button
|
|
512
523
|
NSTextField *infoLabel = nil;
|
|
@@ -928,20 +939,57 @@ bool startScreenSelection() {
|
|
|
928
939
|
NSMutableArray *screenInfoArray = [[NSMutableArray alloc] init];
|
|
929
940
|
g_screenOverlayWindows = [[NSMutableArray alloc] init];
|
|
930
941
|
|
|
942
|
+
// Get real display IDs like MacRecorder does
|
|
943
|
+
CGDirectDisplayID activeDisplays[32];
|
|
944
|
+
uint32_t displayCount;
|
|
945
|
+
CGError err = CGGetActiveDisplayList(32, activeDisplays, &displayCount);
|
|
946
|
+
|
|
947
|
+
if (err != kCGErrorSuccess) {
|
|
948
|
+
NSLog(@"ā Failed to get active display list: %d", err);
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
|
|
931
952
|
for (NSInteger i = 0; i < [screens count]; i++) {
|
|
932
953
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
933
954
|
NSRect screenFrame = [screen frame];
|
|
934
955
|
|
|
935
|
-
//
|
|
956
|
+
// Get the real CGDirectDisplayID for this screen by matching frame
|
|
957
|
+
CGDirectDisplayID displayID = 0;
|
|
958
|
+
|
|
959
|
+
// Find matching display by comparing bounds
|
|
960
|
+
for (uint32_t j = 0; j < displayCount; j++) {
|
|
961
|
+
CGDirectDisplayID candidateID = activeDisplays[j];
|
|
962
|
+
CGRect displayBounds = CGDisplayBounds(candidateID);
|
|
963
|
+
|
|
964
|
+
// Compare screen frame with display bounds
|
|
965
|
+
if (fabs(screenFrame.origin.x - displayBounds.origin.x) < 1.0 &&
|
|
966
|
+
fabs(screenFrame.origin.y - displayBounds.origin.y) < 1.0 &&
|
|
967
|
+
fabs(screenFrame.size.width - displayBounds.size.width) < 1.0 &&
|
|
968
|
+
fabs(screenFrame.size.height - displayBounds.size.height) < 1.0) {
|
|
969
|
+
displayID = candidateID;
|
|
970
|
+
// Screen matched to display ID
|
|
971
|
+
break;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Fallback: use array index if no match found
|
|
976
|
+
if (displayID == 0 && i < displayCount) {
|
|
977
|
+
displayID = activeDisplays[i];
|
|
978
|
+
// Used fallback display ID
|
|
979
|
+
} else if (displayID == 0) {
|
|
980
|
+
NSLog(@"ā Screen %ld could not get Display ID", (long)i);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Create screen info dictionary with real display ID
|
|
936
984
|
NSMutableDictionary *screenInfo = [[NSMutableDictionary alloc] init];
|
|
937
|
-
[screenInfo setObject:[NSNumber
|
|
985
|
+
[screenInfo setObject:[NSNumber numberWithUnsignedInt:displayID] forKey:@"id"]; // Real display ID
|
|
938
986
|
[screenInfo setObject:[NSString stringWithFormat:@"Display %ld", (long)(i + 1)] forKey:@"name"];
|
|
939
987
|
[screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.origin.x] forKey:@"x"];
|
|
940
988
|
[screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.origin.y] forKey:@"y"];
|
|
941
989
|
[screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.size.width] forKey:@"width"];
|
|
942
990
|
[screenInfo setObject:[NSNumber numberWithInt:(int)screenFrame.size.height] forKey:@"height"];
|
|
943
991
|
[screenInfo setObject:[NSString stringWithFormat:@"%.0fx%.0f", screenFrame.size.width, screenFrame.size.height] forKey:@"resolution"];
|
|
944
|
-
[screenInfo setObject:[NSNumber numberWithBool:(
|
|
992
|
+
[screenInfo setObject:[NSNumber numberWithBool:(displayID == CGMainDisplayID())] forKey:@"isPrimary"]; // Real primary check
|
|
945
993
|
[screenInfoArray addObject:screenInfo];
|
|
946
994
|
|
|
947
995
|
// Create overlay window for this screen (FULL screen including menu bar)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
|
|
5
|
+
async function testRealScreenIDs() {
|
|
6
|
+
console.log('Testing real screen ID generation...\n');
|
|
7
|
+
|
|
8
|
+
const selector = new WindowSelector();
|
|
9
|
+
|
|
10
|
+
// Start screen selection to generate screen info
|
|
11
|
+
console.log('Starting screen selection (will timeout in 3 seconds)...');
|
|
12
|
+
|
|
13
|
+
const startPromise = selector.startScreenSelection();
|
|
14
|
+
|
|
15
|
+
// Wait a bit for screen info to be generated
|
|
16
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
17
|
+
|
|
18
|
+
// Try to get selected screen info (should be null since nothing selected)
|
|
19
|
+
const selectedInfo = selector.getSelectedScreen();
|
|
20
|
+
console.log('Selected screen info (should be null):', selectedInfo);
|
|
21
|
+
|
|
22
|
+
// Clean up
|
|
23
|
+
try {
|
|
24
|
+
await selector.stopScreenSelection();
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// Ignore
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log('\nTest completed. Check logs above for screen creation details.');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
testRealScreenIDs().catch(console.error);
|