node-mac-recorder 2.6.0 → 2.6.2
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/index.js +52 -5
- package/package.json +1 -1
- package/src/window_selector.mm +135 -4
- package/test-audio-controls.js +104 -0
- package/test-multi-display-overlay.js +185 -0
- package/test-multi-screen-selection.js +171 -0
package/index.js
CHANGED
|
@@ -37,7 +37,7 @@ class MacRecorder extends EventEmitter {
|
|
|
37
37
|
|
|
38
38
|
this.options = {
|
|
39
39
|
includeMicrophone: false, // Default olarak mikrofon kapalı
|
|
40
|
-
includeSystemAudio:
|
|
40
|
+
includeSystemAudio: false, // Default olarak sistem sesi kapalı - kullanıcı explicit olarak açmalı
|
|
41
41
|
quality: "medium",
|
|
42
42
|
frameRate: 30,
|
|
43
43
|
captureArea: null, // { x, y, width, height }
|
|
@@ -110,8 +110,8 @@ class MacRecorder extends EventEmitter {
|
|
|
110
110
|
*/
|
|
111
111
|
setOptions(options = {}) {
|
|
112
112
|
this.options = {
|
|
113
|
-
includeMicrophone: options.includeMicrophone
|
|
114
|
-
includeSystemAudio: options.includeSystemAudio
|
|
113
|
+
includeMicrophone: options.includeMicrophone === true, // Explicit true required, default false
|
|
114
|
+
includeSystemAudio: options.includeSystemAudio === true, // Explicit true required, default false
|
|
115
115
|
captureCursor: options.captureCursor || false,
|
|
116
116
|
displayId: options.displayId || null, // null = ana ekran
|
|
117
117
|
windowId: options.windowId || null, // null = tam ekran
|
|
@@ -121,6 +121,53 @@ class MacRecorder extends EventEmitter {
|
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Mikrofon kaydını açar/kapatır
|
|
126
|
+
*/
|
|
127
|
+
setMicrophoneEnabled(enabled) {
|
|
128
|
+
this.options.includeMicrophone = enabled === true;
|
|
129
|
+
return this.options.includeMicrophone;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sistem sesi kaydını açar/kapatır
|
|
134
|
+
*/
|
|
135
|
+
setSystemAudioEnabled(enabled) {
|
|
136
|
+
this.options.includeSystemAudio = enabled === true;
|
|
137
|
+
return this.options.includeSystemAudio;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Mikrofon durumunu döndürür
|
|
142
|
+
*/
|
|
143
|
+
isMicrophoneEnabled() {
|
|
144
|
+
return this.options.includeMicrophone === true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Sistem sesi durumunu döndürür
|
|
149
|
+
*/
|
|
150
|
+
isSystemAudioEnabled() {
|
|
151
|
+
return this.options.includeSystemAudio === true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Audio ayarlarını toplu olarak değiştirir
|
|
156
|
+
*/
|
|
157
|
+
setAudioSettings(settings = {}) {
|
|
158
|
+
if (typeof settings.microphone === 'boolean') {
|
|
159
|
+
this.setMicrophoneEnabled(settings.microphone);
|
|
160
|
+
}
|
|
161
|
+
if (typeof settings.systemAudio === 'boolean') {
|
|
162
|
+
this.setSystemAudioEnabled(settings.systemAudio);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
microphone: this.isMicrophoneEnabled(),
|
|
167
|
+
systemAudio: this.isSystemAudioEnabled()
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
124
171
|
/**
|
|
125
172
|
* Ekran kaydını başlatır (macOS native AVFoundation kullanarak)
|
|
126
173
|
*/
|
|
@@ -266,8 +313,8 @@ class MacRecorder extends EventEmitter {
|
|
|
266
313
|
try {
|
|
267
314
|
// Native kayıt başlat
|
|
268
315
|
const recordingOptions = {
|
|
269
|
-
includeMicrophone: this.options.includeMicrophone
|
|
270
|
-
includeSystemAudio: this.options.includeSystemAudio
|
|
316
|
+
includeMicrophone: this.options.includeMicrophone === true, // Only if explicitly enabled
|
|
317
|
+
includeSystemAudio: this.options.includeSystemAudio === true, // Only if explicitly enabled
|
|
271
318
|
captureCursor: this.options.captureCursor || false,
|
|
272
319
|
displayId: this.options.displayId || null, // null = ana ekran
|
|
273
320
|
windowId: this.options.windowId || null, // null = tam ekran
|
package/package.json
CHANGED
package/src/window_selector.mm
CHANGED
|
@@ -30,6 +30,8 @@ static NSMutableArray *g_screenOverlayWindows = nil;
|
|
|
30
30
|
static NSDictionary *g_selectedScreenInfo = nil;
|
|
31
31
|
static NSArray *g_allScreens = nil;
|
|
32
32
|
static id g_screenKeyEventMonitor = nil;
|
|
33
|
+
static NSTimer *g_screenTrackingTimer = nil;
|
|
34
|
+
static NSInteger g_currentActiveScreenIndex = -1;
|
|
33
35
|
|
|
34
36
|
// Forward declarations
|
|
35
37
|
void cleanupWindowSelector();
|
|
@@ -46,6 +48,7 @@ bool stopScreenSelection();
|
|
|
46
48
|
NSDictionary* getSelectedScreenInfo();
|
|
47
49
|
bool showScreenRecordingPreview(NSDictionary *screenInfo);
|
|
48
50
|
bool hideScreenRecordingPreview();
|
|
51
|
+
void updateScreenOverlays();
|
|
49
52
|
|
|
50
53
|
// Custom overlay view class
|
|
51
54
|
@interface WindowSelectorOverlayView : NSView
|
|
@@ -139,6 +142,7 @@ bool hideScreenRecordingPreview();
|
|
|
139
142
|
// Screen selection overlay view
|
|
140
143
|
@interface ScreenSelectorOverlayView : NSView
|
|
141
144
|
@property (nonatomic, strong) NSDictionary *screenInfo;
|
|
145
|
+
@property (nonatomic) BOOL isActiveScreen;
|
|
142
146
|
@end
|
|
143
147
|
|
|
144
148
|
@implementation ScreenSelectorOverlayView
|
|
@@ -151,6 +155,7 @@ bool hideScreenRecordingPreview();
|
|
|
151
155
|
// Ensure no borders or decorations
|
|
152
156
|
self.layer.borderWidth = 0.0;
|
|
153
157
|
self.layer.cornerRadius = 0.0;
|
|
158
|
+
self.isActiveScreen = NO;
|
|
154
159
|
}
|
|
155
160
|
return self;
|
|
156
161
|
}
|
|
@@ -160,12 +165,23 @@ bool hideScreenRecordingPreview();
|
|
|
160
165
|
|
|
161
166
|
if (!self.screenInfo) return;
|
|
162
167
|
|
|
163
|
-
// Background with transparency - purple tone
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
// Background with transparency - purple tone varies by active state
|
|
169
|
+
if (self.isActiveScreen) {
|
|
170
|
+
// Active screen: brighter, more opaque
|
|
171
|
+
[[NSColor colorWithRed:0.6 green:0.4 blue:0.9 alpha:0.4] setFill];
|
|
172
|
+
} else {
|
|
173
|
+
// Inactive screen: dimmer, less opaque
|
|
174
|
+
[[NSColor colorWithRed:0.4 green:0.2 blue:0.6 alpha:0.25] setFill];
|
|
175
|
+
}
|
|
166
176
|
NSRectFill(self.bounds);
|
|
167
177
|
|
|
168
|
-
//
|
|
178
|
+
// Add subtle border for active screen
|
|
179
|
+
if (self.isActiveScreen) {
|
|
180
|
+
[[NSColor colorWithRed:0.7 green:0.5 blue:1.0 alpha:0.6] setStroke];
|
|
181
|
+
NSBezierPath *borderPath = [NSBezierPath bezierPathWithRect:NSInsetRect(self.bounds, 2, 2)];
|
|
182
|
+
[borderPath setLineWidth:4.0];
|
|
183
|
+
[borderPath stroke];
|
|
184
|
+
}
|
|
169
185
|
}
|
|
170
186
|
|
|
171
187
|
@end
|
|
@@ -216,6 +232,10 @@ bool hideScreenRecordingPreview();
|
|
|
216
232
|
- (void)timerUpdate:(NSTimer *)timer {
|
|
217
233
|
updateOverlay();
|
|
218
234
|
}
|
|
235
|
+
|
|
236
|
+
- (void)screenTimerUpdate:(NSTimer *)timer {
|
|
237
|
+
updateScreenOverlays();
|
|
238
|
+
}
|
|
219
239
|
@end
|
|
220
240
|
|
|
221
241
|
static WindowSelectorDelegate *g_delegate = nil;
|
|
@@ -766,10 +786,106 @@ bool hideRecordingPreview() {
|
|
|
766
786
|
}
|
|
767
787
|
}
|
|
768
788
|
|
|
789
|
+
// Update screen overlays based on mouse position
|
|
790
|
+
void updateScreenOverlays() {
|
|
791
|
+
@autoreleasepool {
|
|
792
|
+
if (!g_isScreenSelecting || !g_screenOverlayWindows || !g_allScreens) return;
|
|
793
|
+
|
|
794
|
+
// Get current mouse position
|
|
795
|
+
NSPoint mouseLocation = [NSEvent mouseLocation];
|
|
796
|
+
// Convert from NSEvent coordinates (bottom-left) to screen coordinates
|
|
797
|
+
NSArray *screens = [NSScreen screens];
|
|
798
|
+
NSScreen *mouseScreen = nil;
|
|
799
|
+
NSInteger mouseScreenIndex = -1;
|
|
800
|
+
|
|
801
|
+
// Find which screen contains the mouse
|
|
802
|
+
for (NSInteger i = 0; i < [screens count]; i++) {
|
|
803
|
+
NSScreen *screen = [screens objectAtIndex:i];
|
|
804
|
+
NSRect screenFrame = [screen frame];
|
|
805
|
+
|
|
806
|
+
if (NSPointInRect(mouseLocation, screenFrame)) {
|
|
807
|
+
mouseScreen = screen;
|
|
808
|
+
mouseScreenIndex = i;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// If mouse screen changed, update overlays
|
|
814
|
+
if (mouseScreenIndex != g_currentActiveScreenIndex) {
|
|
815
|
+
g_currentActiveScreenIndex = mouseScreenIndex;
|
|
816
|
+
|
|
817
|
+
// Update all screen overlays
|
|
818
|
+
for (NSInteger i = 0; i < [g_screenOverlayWindows count] && i < [screens count]; i++) {
|
|
819
|
+
NSWindow *overlayWindow = [g_screenOverlayWindows objectAtIndex:i];
|
|
820
|
+
ScreenSelectorOverlayView *overlayView = (ScreenSelectorOverlayView *)[overlayWindow contentView];
|
|
821
|
+
|
|
822
|
+
// Update overlay appearance based on whether it's the active screen
|
|
823
|
+
bool isActiveScreen = (i == mouseScreenIndex);
|
|
824
|
+
|
|
825
|
+
// Update overlay state and appearance
|
|
826
|
+
[overlayView setIsActiveScreen:isActiveScreen];
|
|
827
|
+
[overlayView setNeedsDisplay:YES];
|
|
828
|
+
|
|
829
|
+
// Update UI elements based on active state
|
|
830
|
+
for (NSView *subview in [overlayView subviews]) {
|
|
831
|
+
if ([subview isKindOfClass:[NSButton class]]) {
|
|
832
|
+
NSButton *button = (NSButton *)subview;
|
|
833
|
+
if ([button.title isEqualToString:@"Start Record"]) {
|
|
834
|
+
if (isActiveScreen) {
|
|
835
|
+
// Active screen: bright, prominent button
|
|
836
|
+
[button.layer setBackgroundColor:[[NSColor colorWithRed:0.7 green:0.45 blue:0.9 alpha:1.0] CGColor]];
|
|
837
|
+
[button setAlphaValue:1.0];
|
|
838
|
+
} else {
|
|
839
|
+
// Inactive screen: dimmer button
|
|
840
|
+
[button.layer setBackgroundColor:[[NSColor colorWithRed:0.4 green:0.25 blue:0.55 alpha:0.8] CGColor]];
|
|
841
|
+
[button setAlphaValue:0.7];
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if ([subview isKindOfClass:[NSTextField class]]) {
|
|
846
|
+
NSTextField *label = (NSTextField *)subview;
|
|
847
|
+
if (isActiveScreen) {
|
|
848
|
+
[label setTextColor:[NSColor whiteColor]];
|
|
849
|
+
[label setAlphaValue:1.0];
|
|
850
|
+
} else {
|
|
851
|
+
[label setTextColor:[NSColor colorWithWhite:0.8 alpha:0.8]];
|
|
852
|
+
[label setAlphaValue:0.7];
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if ([subview isKindOfClass:[NSImageView class]]) {
|
|
856
|
+
NSImageView *imageView = (NSImageView *)subview;
|
|
857
|
+
if (isActiveScreen) {
|
|
858
|
+
[imageView setAlphaValue:1.0];
|
|
859
|
+
} else {
|
|
860
|
+
[imageView setAlphaValue:0.6];
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (isActiveScreen) {
|
|
866
|
+
NSLog(@"🖥️ ACTIVE SCREEN: Display %ld", (long)(i + 1));
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Bring active screen overlay to front
|
|
870
|
+
if (isActiveScreen) {
|
|
871
|
+
[overlayWindow orderFront:nil];
|
|
872
|
+
[overlayWindow makeKeyAndOrderFront:nil];
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
769
879
|
// Screen selection functions
|
|
770
880
|
void cleanupScreenSelector() {
|
|
771
881
|
g_isScreenSelecting = false;
|
|
772
882
|
|
|
883
|
+
// Stop screen tracking timer
|
|
884
|
+
if (g_screenTrackingTimer) {
|
|
885
|
+
[g_screenTrackingTimer invalidate];
|
|
886
|
+
g_screenTrackingTimer = nil;
|
|
887
|
+
}
|
|
888
|
+
|
|
773
889
|
// Remove key event monitor
|
|
774
890
|
if (g_screenKeyEventMonitor) {
|
|
775
891
|
[NSEvent removeMonitor:g_screenKeyEventMonitor];
|
|
@@ -790,6 +906,9 @@ void cleanupScreenSelector() {
|
|
|
790
906
|
[g_allScreens release];
|
|
791
907
|
g_allScreens = nil;
|
|
792
908
|
}
|
|
909
|
+
|
|
910
|
+
// Reset active screen tracking
|
|
911
|
+
g_currentActiveScreenIndex = -1;
|
|
793
912
|
}
|
|
794
913
|
|
|
795
914
|
bool startScreenSelection() {
|
|
@@ -1003,6 +1122,7 @@ bool startScreenSelection() {
|
|
|
1003
1122
|
g_allScreens = [screenInfoArray retain];
|
|
1004
1123
|
[screenInfoArray release];
|
|
1005
1124
|
g_isScreenSelecting = true;
|
|
1125
|
+
g_currentActiveScreenIndex = -1;
|
|
1006
1126
|
|
|
1007
1127
|
// Add ESC key event monitor to cancel selection
|
|
1008
1128
|
g_screenKeyEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown
|
|
@@ -1013,7 +1133,18 @@ bool startScreenSelection() {
|
|
|
1013
1133
|
}
|
|
1014
1134
|
}];
|
|
1015
1135
|
|
|
1136
|
+
// Start screen tracking timer to update overlays based on mouse position
|
|
1137
|
+
if (!g_delegate) {
|
|
1138
|
+
g_delegate = [[WindowSelectorDelegate alloc] init];
|
|
1139
|
+
}
|
|
1140
|
+
g_screenTrackingTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 // 20 FPS
|
|
1141
|
+
target:g_delegate
|
|
1142
|
+
selector:@selector(screenTimerUpdate:)
|
|
1143
|
+
userInfo:nil
|
|
1144
|
+
repeats:YES];
|
|
1145
|
+
|
|
1016
1146
|
NSLog(@"🖥️ SCREEN SELECTION: Started with %lu screens (ESC to cancel)", (unsigned long)[screens count]);
|
|
1147
|
+
NSLog(@"🖥️ SCREEN TRACKING: Timer started for overlay updates");
|
|
1017
1148
|
|
|
1018
1149
|
return true;
|
|
1019
1150
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const MacRecorder = require('./index');
|
|
4
|
+
|
|
5
|
+
async function testAudioControls() {
|
|
6
|
+
console.log('🎵 Audio Control Test');
|
|
7
|
+
console.log('====================\n');
|
|
8
|
+
|
|
9
|
+
const recorder = new MacRecorder();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Test initial state
|
|
13
|
+
console.log('📋 Initial Audio State:');
|
|
14
|
+
console.log(` Microphone: ${recorder.isMicrophoneEnabled() ? '🎤 ON' : '🔇 OFF'}`);
|
|
15
|
+
console.log(` System Audio: ${recorder.isSystemAudioEnabled() ? '🔊 ON' : '🔇 OFF'}\n`);
|
|
16
|
+
|
|
17
|
+
// Test individual controls
|
|
18
|
+
console.log('🔧 Testing Individual Controls:\n');
|
|
19
|
+
|
|
20
|
+
console.log(' Enabling microphone...');
|
|
21
|
+
recorder.setMicrophoneEnabled(true);
|
|
22
|
+
console.log(` Microphone: ${recorder.isMicrophoneEnabled() ? '🎤 ON' : '🔇 OFF'}`);
|
|
23
|
+
|
|
24
|
+
console.log(' Enabling system audio...');
|
|
25
|
+
recorder.setSystemAudioEnabled(true);
|
|
26
|
+
console.log(` System Audio: ${recorder.isSystemAudioEnabled() ? '🔊 ON' : '🔇 OFF'}\n`);
|
|
27
|
+
|
|
28
|
+
console.log(' Disabling microphone...');
|
|
29
|
+
recorder.setMicrophoneEnabled(false);
|
|
30
|
+
console.log(` Microphone: ${recorder.isMicrophoneEnabled() ? '🎤 ON' : '🔇 OFF'}`);
|
|
31
|
+
|
|
32
|
+
console.log(' Disabling system audio...');
|
|
33
|
+
recorder.setSystemAudioEnabled(false);
|
|
34
|
+
console.log(` System Audio: ${recorder.isSystemAudioEnabled() ? '🔊 ON' : '🔇 OFF'}\n`);
|
|
35
|
+
|
|
36
|
+
// Test bulk settings
|
|
37
|
+
console.log('🔧 Testing Bulk Audio Settings:\n');
|
|
38
|
+
|
|
39
|
+
console.log(' Setting both to ON...');
|
|
40
|
+
let settings = recorder.setAudioSettings({
|
|
41
|
+
microphone: true,
|
|
42
|
+
systemAudio: true
|
|
43
|
+
});
|
|
44
|
+
console.log(` Result: Microphone=${settings.microphone ? 'ON' : 'OFF'}, System Audio=${settings.systemAudio ? 'ON' : 'OFF'}\n`);
|
|
45
|
+
|
|
46
|
+
console.log(' Setting microphone OFF, system audio ON...');
|
|
47
|
+
settings = recorder.setAudioSettings({
|
|
48
|
+
microphone: false,
|
|
49
|
+
systemAudio: true
|
|
50
|
+
});
|
|
51
|
+
console.log(` Result: Microphone=${settings.microphone ? 'ON' : 'OFF'}, System Audio=${settings.systemAudio ? 'ON' : 'OFF'}\n`);
|
|
52
|
+
|
|
53
|
+
// Test recording with different audio settings
|
|
54
|
+
const testOutput = `./test-output/audio-test-${Date.now()}.mov`;
|
|
55
|
+
|
|
56
|
+
console.log('🎬 Testing Recording with Audio Settings:\n');
|
|
57
|
+
|
|
58
|
+
// Test 1: No audio
|
|
59
|
+
console.log(' Test 1: No Audio Recording');
|
|
60
|
+
recorder.setAudioSettings({ microphone: false, systemAudio: false });
|
|
61
|
+
console.log(` Starting 3-second recording: ${testOutput}`);
|
|
62
|
+
console.log(` Audio settings: Mic=${recorder.isMicrophoneEnabled() ? 'ON' : 'OFF'}, System=${recorder.isSystemAudioEnabled() ? 'ON' : 'OFF'}`);
|
|
63
|
+
|
|
64
|
+
await recorder.startRecording(testOutput);
|
|
65
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
66
|
+
await recorder.stopRecording();
|
|
67
|
+
console.log(' ✅ Recording completed (no audio)\n');
|
|
68
|
+
|
|
69
|
+
// Test 2: Microphone only
|
|
70
|
+
console.log(' Test 2: Microphone Only Recording');
|
|
71
|
+
recorder.setAudioSettings({ microphone: true, systemAudio: false });
|
|
72
|
+
const testOutput2 = `./test-output/audio-test-mic-${Date.now()}.mov`;
|
|
73
|
+
console.log(` Starting 3-second recording: ${testOutput2}`);
|
|
74
|
+
console.log(` Audio settings: Mic=${recorder.isMicrophoneEnabled() ? 'ON' : 'OFF'}, System=${recorder.isSystemAudioEnabled() ? 'ON' : 'OFF'}`);
|
|
75
|
+
|
|
76
|
+
await recorder.startRecording(testOutput2);
|
|
77
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
78
|
+
await recorder.stopRecording();
|
|
79
|
+
console.log(' ✅ Recording completed (microphone only)\n');
|
|
80
|
+
|
|
81
|
+
// Test 3: System audio only
|
|
82
|
+
console.log(' Test 3: System Audio Only Recording');
|
|
83
|
+
recorder.setAudioSettings({ microphone: false, systemAudio: true });
|
|
84
|
+
const testOutput3 = `./test-output/audio-test-system-${Date.now()}.mov`;
|
|
85
|
+
console.log(` Starting 3-second recording: ${testOutput3}`);
|
|
86
|
+
console.log(` Audio settings: Mic=${recorder.isMicrophoneEnabled() ? 'ON' : 'OFF'}, System=${recorder.isSystemAudioEnabled() ? 'ON' : 'OFF'}`);
|
|
87
|
+
|
|
88
|
+
await recorder.startRecording(testOutput3);
|
|
89
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
90
|
+
await recorder.stopRecording();
|
|
91
|
+
console.log(' ✅ Recording completed (system audio only)\n');
|
|
92
|
+
|
|
93
|
+
console.log('✅ All audio control tests completed successfully!');
|
|
94
|
+
console.log('\n📁 Check test-output/ directory for video files.');
|
|
95
|
+
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('\n❌ Test failed:', error.message);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (require.main === module) {
|
|
103
|
+
testAudioControls();
|
|
104
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
|
|
5
|
+
async function testMultiDisplayOverlay() {
|
|
6
|
+
console.log('🖥️ Multi-Display Overlay Test');
|
|
7
|
+
console.log('=============================\n');
|
|
8
|
+
|
|
9
|
+
const selector = new WindowSelector();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Check permissions first
|
|
13
|
+
const permissions = await selector.checkPermissions();
|
|
14
|
+
console.log('🔐 Permissions:');
|
|
15
|
+
console.log(` Screen Recording: ${permissions.screenRecording ? '✅' : '❌'}`);
|
|
16
|
+
console.log(` Accessibility: ${permissions.accessibility ? '✅' : '❌'}\n`);
|
|
17
|
+
|
|
18
|
+
// Get display information
|
|
19
|
+
const MacRecorder = require('./index');
|
|
20
|
+
const recorder = new MacRecorder();
|
|
21
|
+
const displays = await recorder.getDisplays();
|
|
22
|
+
|
|
23
|
+
console.log(`🖥️ Found ${displays.length} display(s):`);
|
|
24
|
+
displays.forEach((display, index) => {
|
|
25
|
+
console.log(` Display ${index + 1}: ${display.name} (${display.resolution}) at (${display.x}, ${display.y}) ${display.isPrimary ? '🌟 PRIMARY' : ''}`);
|
|
26
|
+
});
|
|
27
|
+
console.log('');
|
|
28
|
+
|
|
29
|
+
if (displays.length < 2) {
|
|
30
|
+
console.log('⚠️ Only one display detected. Connect a second display to fully test multi-display overlay functionality.');
|
|
31
|
+
console.log('Continuing with single display test...\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let windowDetectionCount = 0;
|
|
35
|
+
let displaySwitchCount = 0;
|
|
36
|
+
let lastDisplayId = null;
|
|
37
|
+
|
|
38
|
+
// Event listeners for detailed tracking
|
|
39
|
+
selector.on('windowEntered', (window) => {
|
|
40
|
+
windowDetectionCount++;
|
|
41
|
+
|
|
42
|
+
// Try to determine which display this window is on
|
|
43
|
+
let windowDisplayId = null;
|
|
44
|
+
const windowCenterX = window.x + window.width / 2;
|
|
45
|
+
const windowCenterY = window.y + window.height / 2;
|
|
46
|
+
|
|
47
|
+
for (const display of displays) {
|
|
48
|
+
if (windowCenterX >= display.x &&
|
|
49
|
+
windowCenterX < display.x + display.width &&
|
|
50
|
+
windowCenterY >= display.y &&
|
|
51
|
+
windowCenterY < display.y + display.height) {
|
|
52
|
+
windowDisplayId = display.id;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (windowDisplayId !== lastDisplayId) {
|
|
58
|
+
displaySwitchCount++;
|
|
59
|
+
lastDisplayId = windowDisplayId;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const displayName = displays.find(d => d.id === windowDisplayId)?.name || 'Unknown';
|
|
63
|
+
|
|
64
|
+
console.log(`\n🎯 WINDOW DETECTED #${windowDetectionCount}:`);
|
|
65
|
+
console.log(` App: ${window.appName}`);
|
|
66
|
+
console.log(` Title: "${window.title}"`);
|
|
67
|
+
console.log(` Position: (${window.x}, ${window.y})`);
|
|
68
|
+
console.log(` Size: ${window.width} × ${window.height}`);
|
|
69
|
+
console.log(` 🖥️ Estimated Display: ${displayName} (ID: ${windowDisplayId})`);
|
|
70
|
+
console.log(` Display switches so far: ${displaySwitchCount}`);
|
|
71
|
+
|
|
72
|
+
if (displays.length > 1) {
|
|
73
|
+
console.log(`\n💡 Multi-Display Test Instructions:`);
|
|
74
|
+
console.log(` • Move cursor to windows on different displays`);
|
|
75
|
+
console.log(` • Overlay should follow cursor across displays`);
|
|
76
|
+
console.log(` • Window detection should work on all displays`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
selector.on('windowLeft', (window) => {
|
|
81
|
+
console.log(`\n🚪 LEFT WINDOW: ${window.appName} - "${window.title}"`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
selector.on('windowSelected', (selectedWindow) => {
|
|
85
|
+
console.log('\n' + '🎉'.repeat(20));
|
|
86
|
+
console.log('🎯 WINDOW SELECTED!');
|
|
87
|
+
console.log('🎉'.repeat(20));
|
|
88
|
+
|
|
89
|
+
const selectedDisplayName = displays.find(d =>
|
|
90
|
+
selectedWindow.x >= d.x &&
|
|
91
|
+
selectedWindow.x < d.x + d.width &&
|
|
92
|
+
selectedWindow.y >= d.y &&
|
|
93
|
+
selectedWindow.y < d.y + d.height
|
|
94
|
+
)?.name || 'Unknown';
|
|
95
|
+
|
|
96
|
+
console.log(`\n📊 Selection Results:`);
|
|
97
|
+
console.log(` Selected: ${selectedWindow.appName} - "${selectedWindow.title}"`);
|
|
98
|
+
console.log(` Position: (${selectedWindow.x}, ${selectedWindow.y})`);
|
|
99
|
+
console.log(` Size: ${selectedWindow.width} × ${selectedWindow.height}`);
|
|
100
|
+
console.log(` Display: ${selectedDisplayName}`);
|
|
101
|
+
console.log(`\n📈 Test Statistics:`);
|
|
102
|
+
console.log(` Total window detections: ${windowDetectionCount}`);
|
|
103
|
+
console.log(` Display switches: ${displaySwitchCount}`);
|
|
104
|
+
console.log(` Multi-display support: ${displaySwitchCount > 0 || displays.length === 1 ? '✅' : '❌'}`);
|
|
105
|
+
|
|
106
|
+
process.exit(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Interactive selection with instructions
|
|
110
|
+
console.log('🚀 Starting multi-display overlay test...\n');
|
|
111
|
+
console.log('📋 Test Instructions:');
|
|
112
|
+
console.log(' 1. Move your cursor to windows on different displays');
|
|
113
|
+
console.log(' 2. Watch for overlay following cursor across displays');
|
|
114
|
+
console.log(' 3. Verify window detection works on all displays');
|
|
115
|
+
console.log(' 4. Press ENTER when you want to select a window');
|
|
116
|
+
console.log(' 5. Press ESC or Ctrl+C to cancel\n');
|
|
117
|
+
|
|
118
|
+
if (displays.length > 1) {
|
|
119
|
+
console.log('🖥️ Multi-Display Tips:');
|
|
120
|
+
console.log(' • Try windows on both primary and secondary displays');
|
|
121
|
+
console.log(' • Move cursor quickly between displays to test overlay tracking');
|
|
122
|
+
console.log(' • Overlay should appear on the display where the cursor is\n');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Setup manual selection with ENTER key
|
|
126
|
+
const readline = require('readline');
|
|
127
|
+
const rl = readline.createInterface({
|
|
128
|
+
input: process.stdin,
|
|
129
|
+
output: process.stdout
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
rl.on('line', () => {
|
|
133
|
+
// Get current status to see if there's a window under cursor
|
|
134
|
+
const status = selector.getStatus();
|
|
135
|
+
if (status.nativeStatus && status.nativeStatus.currentWindow) {
|
|
136
|
+
selector.emit('windowSelected', status.nativeStatus.currentWindow);
|
|
137
|
+
} else {
|
|
138
|
+
console.log('\n⚠️ No window under cursor. Move cursor over a window first.');
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await selector.startSelection();
|
|
143
|
+
|
|
144
|
+
// Status monitoring
|
|
145
|
+
let statusCount = 0;
|
|
146
|
+
const statusInterval = setInterval(() => {
|
|
147
|
+
statusCount++;
|
|
148
|
+
|
|
149
|
+
if (statusCount % 40 === 0) { // Every 20 seconds
|
|
150
|
+
console.log(`\n⏱️ Status Update (${statusCount/2}s):`);
|
|
151
|
+
console.log(` Window detections: ${windowDetectionCount}`);
|
|
152
|
+
console.log(` Display switches: ${displaySwitchCount}`);
|
|
153
|
+
console.log(` Current displays: ${displays.length}`);
|
|
154
|
+
if (displays.length > 1) {
|
|
155
|
+
console.log(` Multi-display overlay: ${displaySwitchCount > 0 ? '✅ Working' : '⏳ Waiting for cross-display movement'}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}, 500);
|
|
159
|
+
|
|
160
|
+
// Graceful shutdown
|
|
161
|
+
process.on('SIGINT', async () => {
|
|
162
|
+
clearInterval(statusInterval);
|
|
163
|
+
rl.close();
|
|
164
|
+
|
|
165
|
+
console.log('\n\n🛑 Test stopped by user');
|
|
166
|
+
console.log('\n📊 Final Test Results:');
|
|
167
|
+
console.log(` Total window detections: ${windowDetectionCount}`);
|
|
168
|
+
console.log(` Display switches observed: ${displaySwitchCount}`);
|
|
169
|
+
console.log(` Displays available: ${displays.length}`);
|
|
170
|
+
console.log(` Multi-display support: ${displaySwitchCount > 0 || displays.length === 1 ? '✅ WORKING' : '❌ NEEDS INVESTIGATION'}`);
|
|
171
|
+
|
|
172
|
+
await selector.cleanup();
|
|
173
|
+
console.log('✅ Cleanup completed');
|
|
174
|
+
process.exit(0);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('\n❌ Test failed:', error.message);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (require.main === module) {
|
|
184
|
+
testMultiDisplayOverlay();
|
|
185
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const WindowSelector = require('./window-selector');
|
|
4
|
+
|
|
5
|
+
async function testMultiScreenSelection() {
|
|
6
|
+
console.log('🖥️ Multi-Screen Selection Test');
|
|
7
|
+
console.log('==============================\n');
|
|
8
|
+
|
|
9
|
+
const selector = new WindowSelector();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
// Check permissions first
|
|
13
|
+
const permissions = await selector.checkPermissions();
|
|
14
|
+
console.log('🔐 Permissions:');
|
|
15
|
+
console.log(` Screen Recording: ${permissions.screenRecording ? '✅' : '❌'}`);
|
|
16
|
+
console.log(` Accessibility: ${permissions.accessibility ? '✅' : '❌'}\n`);
|
|
17
|
+
|
|
18
|
+
if (!permissions.screenRecording || !permissions.accessibility) {
|
|
19
|
+
console.log('⚠️ Missing required permissions. Please grant permissions in System Preferences.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Get display information
|
|
24
|
+
const MacRecorder = require('./index');
|
|
25
|
+
const recorder = new MacRecorder();
|
|
26
|
+
const displays = await recorder.getDisplays();
|
|
27
|
+
|
|
28
|
+
console.log(`🖥️ Found ${displays.length} display(s):`);
|
|
29
|
+
displays.forEach((display, index) => {
|
|
30
|
+
console.log(` Display ${index + 1}: ${display.name} (${display.resolution}) at (${display.x}, ${display.y}) ${display.isPrimary ? '🌟 PRIMARY' : ''}`);
|
|
31
|
+
});
|
|
32
|
+
console.log('');
|
|
33
|
+
|
|
34
|
+
if (displays.length < 2) {
|
|
35
|
+
console.log('⚠️ Only one display detected. Connect a second display to fully test multi-screen selection functionality.');
|
|
36
|
+
console.log('Continuing with single display test...\n');
|
|
37
|
+
} else {
|
|
38
|
+
console.log('✅ Multiple displays detected. Perfect for multi-screen testing!\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('🚀 Starting multi-screen selection test...\n');
|
|
42
|
+
console.log('📋 What to expect:');
|
|
43
|
+
console.log(' 1. Overlay will appear on ALL screens simultaneously');
|
|
44
|
+
console.log(' 2. The screen where your mouse is located will be HIGHLIGHTED (brighter)');
|
|
45
|
+
console.log(' 3. Other screens will be dimmer');
|
|
46
|
+
console.log(' 4. Move mouse between screens to see the highlighting change');
|
|
47
|
+
console.log(' 5. Click "Start Record" on the screen you want to record');
|
|
48
|
+
console.log(' 6. Press ESC to cancel\n');
|
|
49
|
+
|
|
50
|
+
if (displays.length > 1) {
|
|
51
|
+
console.log('🖱️ Multi-Screen Instructions:');
|
|
52
|
+
console.log(' • Move your mouse from one screen to another');
|
|
53
|
+
console.log(' • Watch the overlay highlighting follow your mouse');
|
|
54
|
+
console.log(' • Notice how the active screen becomes brighter');
|
|
55
|
+
console.log(' • All screens should show the overlay simultaneously\n');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log('⏱️ Starting screen selection in 3 seconds...');
|
|
59
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
60
|
+
|
|
61
|
+
// Start screen selection
|
|
62
|
+
const success = await selector.startScreenSelection();
|
|
63
|
+
|
|
64
|
+
if (!success) {
|
|
65
|
+
console.error('❌ Failed to start screen selection');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log('✅ Screen selection started successfully!');
|
|
70
|
+
console.log('🖱️ Move your mouse between screens to test highlighting...\n');
|
|
71
|
+
|
|
72
|
+
// Monitor for selection completion
|
|
73
|
+
let checkCount = 0;
|
|
74
|
+
const selectionChecker = setInterval(() => {
|
|
75
|
+
checkCount++;
|
|
76
|
+
const selectedScreen = selector.getSelectedScreen();
|
|
77
|
+
|
|
78
|
+
if (selectedScreen) {
|
|
79
|
+
clearInterval(selectionChecker);
|
|
80
|
+
|
|
81
|
+
console.log('\n' + '🎉'.repeat(25));
|
|
82
|
+
console.log('🎯 SCREEN SELECTED!');
|
|
83
|
+
console.log('🎉'.repeat(25));
|
|
84
|
+
|
|
85
|
+
console.log(`\n📊 Selected Screen Details:`);
|
|
86
|
+
console.log(` Name: ${selectedScreen.name}`);
|
|
87
|
+
console.log(` Resolution: ${selectedScreen.resolution}`);
|
|
88
|
+
console.log(` Position: (${selectedScreen.x}, ${selectedScreen.y})`);
|
|
89
|
+
console.log(` Size: ${selectedScreen.width} × ${selectedScreen.height}`);
|
|
90
|
+
console.log(` Primary: ${selectedScreen.isPrimary ? 'Yes' : 'No'}`);
|
|
91
|
+
|
|
92
|
+
console.log(`\n✅ Multi-screen selection test completed successfully!`);
|
|
93
|
+
console.log(`📈 Test ran for ${(checkCount * 100) / 1000} seconds`);
|
|
94
|
+
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Show progress every 5 seconds
|
|
99
|
+
if (checkCount % 50 === 0) {
|
|
100
|
+
const elapsed = (checkCount * 100) / 1000;
|
|
101
|
+
console.log(`⏱️ Test running for ${elapsed}s - Move mouse between screens to test highlighting`);
|
|
102
|
+
|
|
103
|
+
if (displays.length > 1) {
|
|
104
|
+
console.log('💡 Remember: The overlay should appear on ALL screens, but only the one with your mouse should be bright');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, 100); // Check every 100ms
|
|
108
|
+
|
|
109
|
+
// Timeout after 60 seconds
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
clearInterval(selectionChecker);
|
|
112
|
+
console.log('\n⏰ Test timeout reached (60 seconds)');
|
|
113
|
+
console.log('🛑 Stopping screen selection...');
|
|
114
|
+
|
|
115
|
+
selector.stopScreenSelection().then(() => {
|
|
116
|
+
console.log('✅ Screen selection stopped');
|
|
117
|
+
|
|
118
|
+
if (displays.length > 1) {
|
|
119
|
+
console.log('\n📊 Multi-Screen Test Summary:');
|
|
120
|
+
console.log(' Expected behavior:');
|
|
121
|
+
console.log(' ✓ Overlays should have appeared on all screens');
|
|
122
|
+
console.log(' ✓ Mouse screen should have been highlighted brighter');
|
|
123
|
+
console.log(' ✓ Non-mouse screens should have been dimmer');
|
|
124
|
+
console.log(' ✓ Highlighting should have changed as you moved mouse');
|
|
125
|
+
} else {
|
|
126
|
+
console.log('\n📊 Single-Screen Test Summary:');
|
|
127
|
+
console.log(' ✓ Overlay should have appeared on your screen');
|
|
128
|
+
console.log(' ✓ Screen should have been highlighted');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}).catch((error) => {
|
|
133
|
+
console.error('❌ Error stopping screen selection:', error.message);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
});
|
|
136
|
+
}, 60000);
|
|
137
|
+
|
|
138
|
+
// Handle Ctrl+C gracefully
|
|
139
|
+
process.on('SIGINT', async () => {
|
|
140
|
+
clearInterval(selectionChecker);
|
|
141
|
+
console.log('\n\n🛑 Test interrupted by user');
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
await selector.stopScreenSelection();
|
|
145
|
+
console.log('✅ Screen selection stopped');
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.log('⚠️ Error during cleanup:', error.message);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log('\n📊 Test Results Summary:');
|
|
151
|
+
console.log(` Displays available: ${displays.length}`);
|
|
152
|
+
console.log(` Test duration: ${(checkCount * 100) / 1000} seconds`);
|
|
153
|
+
if (displays.length > 1) {
|
|
154
|
+
console.log(' Multi-screen support: Test interrupted before completion');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
process.exit(0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('\n❌ Test failed:', error.message);
|
|
162
|
+
if (error.stack) {
|
|
163
|
+
console.error('Stack trace:', error.stack);
|
|
164
|
+
}
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (require.main === module) {
|
|
170
|
+
testMultiScreenSelection();
|
|
171
|
+
}
|