node-mac-recorder 2.4.8 → 2.4.9
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/.claude/settings.local.json +2 -1
- package/electron-window-selector.js +671 -0
- package/index.js +57 -0
- package/package.json +1 -1
- package/src/mac_recorder.mm +27 -2
- package/src/window_selector.mm +85 -8
- package/test-sck-availability.js +26 -0
- package/test-screencapture-overlay.js +54 -0
- package/test-simple-windows.js +29 -0
- package/test-window-details.js +34 -0
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"Bash(git checkout:*)",
|
|
26
26
|
"Bash(ELECTRON_VERSION=25.0.0 node -e \"\nconsole.log(''ELECTRON_VERSION env:'', process.env.ELECTRON_VERSION);\nconsole.log(''getenv result would be:'', process.env.ELECTRON_VERSION || ''null'');\n\")",
|
|
27
27
|
"Bash(ELECTRON_VERSION=25.0.0 node test-env-detection.js)",
|
|
28
|
-
"Bash(ELECTRON_VERSION=25.0.0 node test-native-call.js)"
|
|
28
|
+
"Bash(ELECTRON_VERSION=25.0.0 node test-native-call.js)",
|
|
29
|
+
"Bash(chmod:*)"
|
|
29
30
|
],
|
|
30
31
|
"deny": []
|
|
31
32
|
}
|
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
const { EventEmitter } = require("events");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
// Native modülü yükle
|
|
5
|
+
let nativeBinding;
|
|
6
|
+
try {
|
|
7
|
+
if (process.platform === "darwin" && process.arch === "arm64") {
|
|
8
|
+
nativeBinding = require("./prebuilds/darwin-arm64/node.napi.node");
|
|
9
|
+
} else {
|
|
10
|
+
nativeBinding = require("./build/Release/mac_recorder.node");
|
|
11
|
+
}
|
|
12
|
+
} catch (error) {
|
|
13
|
+
try {
|
|
14
|
+
nativeBinding = require("./build/Release/mac_recorder.node");
|
|
15
|
+
} catch (_) {
|
|
16
|
+
try {
|
|
17
|
+
nativeBinding = require("./build/Debug/mac_recorder.node");
|
|
18
|
+
} catch (debugError) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'Native module not found. Please run "npm run build" to compile the native module.\n' +
|
|
21
|
+
"Original error: " +
|
|
22
|
+
error.message
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class ElectronWindowSelector extends EventEmitter {
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
this.isSelecting = false;
|
|
32
|
+
this.isScreenSelecting = false;
|
|
33
|
+
this.selectedWindow = null;
|
|
34
|
+
this.selectedScreen = null;
|
|
35
|
+
this.selectionTimer = null;
|
|
36
|
+
this.lastStatus = null;
|
|
37
|
+
|
|
38
|
+
// Electron detection
|
|
39
|
+
this.isElectron = !!(process.versions && process.versions.electron) ||
|
|
40
|
+
!!(process.env.ELECTRON_VERSION) ||
|
|
41
|
+
!!(process.env.ELECTRON_RUN_AS_NODE);
|
|
42
|
+
|
|
43
|
+
console.log(`🔍 ElectronWindowSelector: ${this.isElectron ? 'Electron' : 'Node.js'} environment detected`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Pencere seçim modunu başlatır - Electron'da overlay ile
|
|
48
|
+
* @returns {Promise<Object>} Seçilen pencere bilgisi
|
|
49
|
+
*/
|
|
50
|
+
async selectWindow() {
|
|
51
|
+
if (this.isSelecting) {
|
|
52
|
+
throw new Error("Window selection is already in progress");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
try {
|
|
57
|
+
// Event listener'ları ayarla
|
|
58
|
+
const onWindowSelected = (windowInfo) => {
|
|
59
|
+
this.removeAllListeners("windowSelected");
|
|
60
|
+
this.removeAllListeners("error");
|
|
61
|
+
this.removeAllListeners("selectionCancelled");
|
|
62
|
+
resolve(windowInfo);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const onError = (error) => {
|
|
66
|
+
this.removeAllListeners("windowSelected");
|
|
67
|
+
this.removeAllListeners("error");
|
|
68
|
+
this.removeAllListeners("selectionCancelled");
|
|
69
|
+
reject(error);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const onCancelled = () => {
|
|
73
|
+
this.removeAllListeners("windowSelected");
|
|
74
|
+
this.removeAllListeners("error");
|
|
75
|
+
this.removeAllListeners("selectionCancelled");
|
|
76
|
+
reject(new Error("Window selection was cancelled"));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
this.once("windowSelected", onWindowSelected);
|
|
80
|
+
this.once("error", onError);
|
|
81
|
+
this.once("selectionCancelled", onCancelled);
|
|
82
|
+
|
|
83
|
+
// Native window selection başlat (overlay ile)
|
|
84
|
+
this.startWindowSelectionWithOverlay();
|
|
85
|
+
|
|
86
|
+
} catch (error) {
|
|
87
|
+
this.removeAllListeners("windowSelected");
|
|
88
|
+
this.removeAllListeners("error");
|
|
89
|
+
this.removeAllListeners("selectionCancelled");
|
|
90
|
+
reject(error);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Ekran seçim modunu başlatır - Electron'da overlay ile
|
|
97
|
+
* @returns {Promise<Object>} Seçilen ekran bilgisi
|
|
98
|
+
*/
|
|
99
|
+
async selectScreen() {
|
|
100
|
+
if (this.isScreenSelecting) {
|
|
101
|
+
throw new Error("Screen selection is already in progress");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
try {
|
|
106
|
+
// Event listener'ları ayarla
|
|
107
|
+
const onScreenSelected = (screenInfo) => {
|
|
108
|
+
this.removeAllListeners("screenSelected");
|
|
109
|
+
this.removeAllListeners("error");
|
|
110
|
+
this.removeAllListeners("selectionCancelled");
|
|
111
|
+
resolve(screenInfo);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const onError = (error) => {
|
|
115
|
+
this.removeAllListeners("screenSelected");
|
|
116
|
+
this.removeAllListeners("error");
|
|
117
|
+
this.removeAllListeners("selectionCancelled");
|
|
118
|
+
reject(error);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const onCancelled = () => {
|
|
122
|
+
this.removeAllListeners("screenSelected");
|
|
123
|
+
this.removeAllListeners("error");
|
|
124
|
+
this.removeAllListeners("selectionCancelled");
|
|
125
|
+
reject(new Error("Screen selection was cancelled"));
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
this.once("screenSelected", onScreenSelected);
|
|
129
|
+
this.once("error", onError);
|
|
130
|
+
this.once("selectionCancelled", onCancelled);
|
|
131
|
+
|
|
132
|
+
// Native screen selection başlat (overlay ile)
|
|
133
|
+
this.startScreenSelectionWithOverlay();
|
|
134
|
+
|
|
135
|
+
} catch (error) {
|
|
136
|
+
this.removeAllListeners("screenSelected");
|
|
137
|
+
this.removeAllListeners("error");
|
|
138
|
+
this.removeAllListeners("selectionCancelled");
|
|
139
|
+
reject(error);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Native window selection'ı overlay ile başlatır
|
|
146
|
+
*/
|
|
147
|
+
async startWindowSelectionWithOverlay() {
|
|
148
|
+
try {
|
|
149
|
+
// Native binding'den window selection başlat
|
|
150
|
+
const success = await this.callNativeWindowSelection();
|
|
151
|
+
|
|
152
|
+
if (success) {
|
|
153
|
+
this.isSelecting = true;
|
|
154
|
+
this.selectedWindow = null;
|
|
155
|
+
|
|
156
|
+
// Polling timer başlat (overlay update'leri için)
|
|
157
|
+
this.selectionTimer = setInterval(() => {
|
|
158
|
+
this.checkWindowSelectionStatus();
|
|
159
|
+
}, 50); // 20 FPS
|
|
160
|
+
|
|
161
|
+
this.emit("selectionStarted");
|
|
162
|
+
console.log("✅ Window selection overlay started");
|
|
163
|
+
} else {
|
|
164
|
+
throw new Error("Failed to start window selection overlay");
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error("❌ Window selection failed:", error.message);
|
|
168
|
+
this.emit("error", error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Native screen selection'ı overlay ile başlatır
|
|
174
|
+
*/
|
|
175
|
+
async startScreenSelectionWithOverlay() {
|
|
176
|
+
try {
|
|
177
|
+
// Native binding'den screen selection başlat
|
|
178
|
+
const success = await this.callNativeScreenSelection();
|
|
179
|
+
|
|
180
|
+
if (success) {
|
|
181
|
+
this.isScreenSelecting = true;
|
|
182
|
+
this.selectedScreen = null;
|
|
183
|
+
|
|
184
|
+
// Polling timer başlat (overlay update'leri için)
|
|
185
|
+
this.selectionTimer = setInterval(() => {
|
|
186
|
+
this.checkScreenSelectionStatus();
|
|
187
|
+
}, 50); // 20 FPS
|
|
188
|
+
|
|
189
|
+
this.emit("screenSelectionStarted");
|
|
190
|
+
console.log("✅ Screen selection overlay started");
|
|
191
|
+
} else {
|
|
192
|
+
throw new Error("Failed to start screen selection overlay");
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error("❌ Screen selection failed:", error.message);
|
|
196
|
+
this.emit("error", error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Native window selection API'yi çağırır - Electron-compatible
|
|
202
|
+
*/
|
|
203
|
+
async callNativeWindowSelection() {
|
|
204
|
+
try {
|
|
205
|
+
// Electron'da bile overlay'lerin çalışması için environment variable'ı geçici olarak kaldır
|
|
206
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
207
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
208
|
+
|
|
209
|
+
// Geçici olarak Electron environment variable'larını kaldır
|
|
210
|
+
delete process.env.ELECTRON_VERSION;
|
|
211
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
// Native function çağır
|
|
215
|
+
const result = nativeBinding.startWindowSelection();
|
|
216
|
+
return result;
|
|
217
|
+
} finally {
|
|
218
|
+
// Environment variable'ları geri yükle
|
|
219
|
+
if (originalElectronVersion) {
|
|
220
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
221
|
+
}
|
|
222
|
+
if (originalElectronRunAs) {
|
|
223
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error("Native window selection failed:", error.message);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Native screen selection API'yi çağırır - Electron-compatible
|
|
234
|
+
*/
|
|
235
|
+
async callNativeScreenSelection() {
|
|
236
|
+
try {
|
|
237
|
+
// Electron'da bile overlay'lerin çalışması için environment variable'ı geçici olarak kaldır
|
|
238
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
239
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
240
|
+
|
|
241
|
+
// Geçici olarak Electron environment variable'larını kaldır
|
|
242
|
+
delete process.env.ELECTRON_VERSION;
|
|
243
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
// Native function çağır
|
|
247
|
+
const result = nativeBinding.startScreenSelection();
|
|
248
|
+
return result;
|
|
249
|
+
} finally {
|
|
250
|
+
// Environment variable'ları geri yükle
|
|
251
|
+
if (originalElectronVersion) {
|
|
252
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
253
|
+
}
|
|
254
|
+
if (originalElectronRunAs) {
|
|
255
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error("Native screen selection failed:", error.message);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Window selection durumunu kontrol eder
|
|
266
|
+
*/
|
|
267
|
+
checkWindowSelectionStatus() {
|
|
268
|
+
if (!this.isSelecting) return;
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const status = nativeBinding.getWindowSelectionStatus();
|
|
272
|
+
|
|
273
|
+
// Seçim tamamlandı mı kontrol et
|
|
274
|
+
if (status.hasSelectedWindow && !this.selectedWindow) {
|
|
275
|
+
const windowInfo = nativeBinding.getSelectedWindowInfo();
|
|
276
|
+
if (windowInfo) {
|
|
277
|
+
this.selectedWindow = windowInfo;
|
|
278
|
+
this.isSelecting = false;
|
|
279
|
+
|
|
280
|
+
// Timer'ı durdur
|
|
281
|
+
if (this.selectionTimer) {
|
|
282
|
+
clearInterval(this.selectionTimer);
|
|
283
|
+
this.selectionTimer = null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log("🎯 Window selected:", windowInfo.title || windowInfo.appName);
|
|
287
|
+
this.emit("windowSelected", windowInfo);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Mouse movement events için mevcut pencere değişikliklerini kontrol et
|
|
293
|
+
if (this.lastStatus) {
|
|
294
|
+
const lastWindow = this.lastStatus.currentWindow;
|
|
295
|
+
const currentWindow = status.currentWindow;
|
|
296
|
+
|
|
297
|
+
if (!lastWindow && currentWindow) {
|
|
298
|
+
// Yeni pencere üstüne gelindi
|
|
299
|
+
this.emit("windowEntered", currentWindow);
|
|
300
|
+
} else if (lastWindow && !currentWindow) {
|
|
301
|
+
// Pencere üstünden ayrıldı
|
|
302
|
+
this.emit("windowLeft", lastWindow);
|
|
303
|
+
} else if (
|
|
304
|
+
lastWindow &&
|
|
305
|
+
currentWindow &&
|
|
306
|
+
(lastWindow.id !== currentWindow.id ||
|
|
307
|
+
lastWindow.title !== currentWindow.title ||
|
|
308
|
+
lastWindow.appName !== currentWindow.appName)
|
|
309
|
+
) {
|
|
310
|
+
// Farklı bir pencereye geçildi
|
|
311
|
+
this.emit("windowLeft", lastWindow);
|
|
312
|
+
this.emit("windowEntered", currentWindow);
|
|
313
|
+
}
|
|
314
|
+
} else if (!this.lastStatus && status.currentWindow) {
|
|
315
|
+
// İlk pencere detection
|
|
316
|
+
this.emit("windowEntered", status.currentWindow);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
this.lastStatus = status;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error("Window selection status check error:", error.message);
|
|
322
|
+
this.emit("error", error);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Screen selection durumunu kontrol eder
|
|
328
|
+
*/
|
|
329
|
+
checkScreenSelectionStatus() {
|
|
330
|
+
if (!this.isScreenSelecting) return;
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
const selectedScreen = nativeBinding.getSelectedScreenInfo();
|
|
334
|
+
|
|
335
|
+
if (selectedScreen && !this.selectedScreen) {
|
|
336
|
+
this.selectedScreen = selectedScreen;
|
|
337
|
+
this.isScreenSelecting = false;
|
|
338
|
+
|
|
339
|
+
// Timer'ı durdur
|
|
340
|
+
if (this.selectionTimer) {
|
|
341
|
+
clearInterval(this.selectionTimer);
|
|
342
|
+
this.selectionTimer = null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
console.log("🖥️ Screen selected:", selectedScreen);
|
|
346
|
+
this.emit("screenSelected", selectedScreen);
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
console.error("Screen selection status check error:", error.message);
|
|
350
|
+
this.emit("error", error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Window selection'ı durdurur
|
|
356
|
+
*/
|
|
357
|
+
async stopWindowSelection() {
|
|
358
|
+
if (!this.isSelecting) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
// Environment variable'ları geçici kaldır
|
|
364
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
365
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
366
|
+
|
|
367
|
+
delete process.env.ELECTRON_VERSION;
|
|
368
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const success = nativeBinding.stopWindowSelection();
|
|
372
|
+
|
|
373
|
+
// Timer'ı durdur
|
|
374
|
+
if (this.selectionTimer) {
|
|
375
|
+
clearInterval(this.selectionTimer);
|
|
376
|
+
this.selectionTimer = null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.isSelecting = false;
|
|
380
|
+
this.lastStatus = null;
|
|
381
|
+
|
|
382
|
+
this.emit("selectionStopped");
|
|
383
|
+
console.log("🛑 Window selection stopped");
|
|
384
|
+
return success;
|
|
385
|
+
} finally {
|
|
386
|
+
// Environment variable'ları geri yükle
|
|
387
|
+
if (originalElectronVersion) {
|
|
388
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
389
|
+
}
|
|
390
|
+
if (originalElectronRunAs) {
|
|
391
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.error("Stop window selection error:", error.message);
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Screen selection'ı durdurur
|
|
402
|
+
*/
|
|
403
|
+
async stopScreenSelection() {
|
|
404
|
+
if (!this.isScreenSelecting) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
// Environment variable'ları geçici kaldır
|
|
410
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
411
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
412
|
+
|
|
413
|
+
delete process.env.ELECTRON_VERSION;
|
|
414
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const success = nativeBinding.stopScreenSelection();
|
|
418
|
+
|
|
419
|
+
// Timer'ı durdur
|
|
420
|
+
if (this.selectionTimer) {
|
|
421
|
+
clearInterval(this.selectionTimer);
|
|
422
|
+
this.selectionTimer = null;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
this.isScreenSelecting = false;
|
|
426
|
+
|
|
427
|
+
this.emit("screenSelectionStopped");
|
|
428
|
+
console.log("🛑 Screen selection stopped");
|
|
429
|
+
return success;
|
|
430
|
+
} finally {
|
|
431
|
+
// Environment variable'ları geri yükle
|
|
432
|
+
if (originalElectronVersion) {
|
|
433
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
434
|
+
}
|
|
435
|
+
if (originalElectronRunAs) {
|
|
436
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} catch (error) {
|
|
440
|
+
console.error("Stop screen selection error:", error.message);
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Recording preview gösterir
|
|
447
|
+
* @param {Object} windowInfo - Pencere bilgisi
|
|
448
|
+
*/
|
|
449
|
+
async showRecordingPreview(windowInfo) {
|
|
450
|
+
if (!windowInfo) {
|
|
451
|
+
throw new Error("Window info is required");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
// Environment variable'ları geçici kaldır
|
|
456
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
457
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
458
|
+
|
|
459
|
+
delete process.env.ELECTRON_VERSION;
|
|
460
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
461
|
+
|
|
462
|
+
try {
|
|
463
|
+
const success = nativeBinding.showRecordingPreview(windowInfo);
|
|
464
|
+
console.log("🎬 Recording preview shown");
|
|
465
|
+
return success;
|
|
466
|
+
} finally {
|
|
467
|
+
// Environment variable'ları geri yükle
|
|
468
|
+
if (originalElectronVersion) {
|
|
469
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
470
|
+
}
|
|
471
|
+
if (originalElectronRunAs) {
|
|
472
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.error("Show recording preview error:", error.message);
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Recording preview'ı gizler
|
|
483
|
+
*/
|
|
484
|
+
async hideRecordingPreview() {
|
|
485
|
+
try {
|
|
486
|
+
// Environment variable'ları geçici kaldır
|
|
487
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
488
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
489
|
+
|
|
490
|
+
delete process.env.ELECTRON_VERSION;
|
|
491
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const success = nativeBinding.hideRecordingPreview();
|
|
495
|
+
console.log("🎬 Recording preview hidden");
|
|
496
|
+
return success;
|
|
497
|
+
} finally {
|
|
498
|
+
// Environment variable'ları geri yükle
|
|
499
|
+
if (originalElectronVersion) {
|
|
500
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
501
|
+
}
|
|
502
|
+
if (originalElectronRunAs) {
|
|
503
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.error("Hide recording preview error:", error.message);
|
|
508
|
+
throw error;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Screen recording preview gösterir
|
|
514
|
+
* @param {Object} screenInfo - Ekran bilgisi
|
|
515
|
+
*/
|
|
516
|
+
async showScreenRecordingPreview(screenInfo) {
|
|
517
|
+
if (!screenInfo) {
|
|
518
|
+
throw new Error("Screen info is required");
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
// Environment variable'ları geçici kaldır
|
|
523
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
524
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
525
|
+
|
|
526
|
+
delete process.env.ELECTRON_VERSION;
|
|
527
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const success = nativeBinding.showScreenRecordingPreview(screenInfo);
|
|
531
|
+
console.log("🖥️ Screen recording preview shown");
|
|
532
|
+
return success;
|
|
533
|
+
} finally {
|
|
534
|
+
// Environment variable'ları geri yükle
|
|
535
|
+
if (originalElectronVersion) {
|
|
536
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
537
|
+
}
|
|
538
|
+
if (originalElectronRunAs) {
|
|
539
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.error("Show screen recording preview error:", error.message);
|
|
544
|
+
throw error;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Screen recording preview'ı gizler
|
|
550
|
+
*/
|
|
551
|
+
async hideScreenRecordingPreview() {
|
|
552
|
+
try {
|
|
553
|
+
// Environment variable'ları geçici kaldır
|
|
554
|
+
const originalElectronVersion = process.env.ELECTRON_VERSION;
|
|
555
|
+
const originalElectronRunAs = process.env.ELECTRON_RUN_AS_NODE;
|
|
556
|
+
|
|
557
|
+
delete process.env.ELECTRON_VERSION;
|
|
558
|
+
delete process.env.ELECTRON_RUN_AS_NODE;
|
|
559
|
+
|
|
560
|
+
try {
|
|
561
|
+
const success = nativeBinding.hideScreenRecordingPreview();
|
|
562
|
+
console.log("🖥️ Screen recording preview hidden");
|
|
563
|
+
return success;
|
|
564
|
+
} finally {
|
|
565
|
+
// Environment variable'ları geri yükle
|
|
566
|
+
if (originalElectronVersion) {
|
|
567
|
+
process.env.ELECTRON_VERSION = originalElectronVersion;
|
|
568
|
+
}
|
|
569
|
+
if (originalElectronRunAs) {
|
|
570
|
+
process.env.ELECTRON_RUN_AS_NODE = originalElectronRunAs;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
} catch (error) {
|
|
574
|
+
console.error("Hide screen recording preview error:", error.message);
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Mevcut durumu döndürür
|
|
581
|
+
*/
|
|
582
|
+
getStatus() {
|
|
583
|
+
return {
|
|
584
|
+
isSelecting: this.isSelecting,
|
|
585
|
+
isScreenSelecting: this.isScreenSelecting,
|
|
586
|
+
hasSelectedWindow: !!this.selectedWindow,
|
|
587
|
+
hasSelectedScreen: !!this.selectedScreen,
|
|
588
|
+
selectedWindow: this.selectedWindow,
|
|
589
|
+
selectedScreen: this.selectedScreen,
|
|
590
|
+
isElectron: this.isElectron
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Seçilen pencere bilgisini döndürür
|
|
596
|
+
*/
|
|
597
|
+
getSelectedWindow() {
|
|
598
|
+
return this.selectedWindow;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Seçilen ekran bilgisini döndürür
|
|
603
|
+
*/
|
|
604
|
+
getSelectedScreen() {
|
|
605
|
+
return this.selectedScreen;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Tüm kaynakları temizler
|
|
610
|
+
*/
|
|
611
|
+
async cleanup() {
|
|
612
|
+
try {
|
|
613
|
+
// Selection'ları durdur
|
|
614
|
+
if (this.isSelecting) {
|
|
615
|
+
await this.stopWindowSelection();
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (this.isScreenSelecting) {
|
|
619
|
+
await this.stopScreenSelection();
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Timer'ları temizle
|
|
623
|
+
if (this.selectionTimer) {
|
|
624
|
+
clearInterval(this.selectionTimer);
|
|
625
|
+
this.selectionTimer = null;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Preview'ları gizle
|
|
629
|
+
try {
|
|
630
|
+
await this.hideRecordingPreview();
|
|
631
|
+
await this.hideScreenRecordingPreview();
|
|
632
|
+
} catch (error) {
|
|
633
|
+
// Preview errors are not critical
|
|
634
|
+
console.warn("Preview cleanup warning:", error.message);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Event listener'ları temizle
|
|
638
|
+
this.removeAllListeners();
|
|
639
|
+
|
|
640
|
+
// State'i sıfırla
|
|
641
|
+
this.selectedWindow = null;
|
|
642
|
+
this.selectedScreen = null;
|
|
643
|
+
this.lastStatus = null;
|
|
644
|
+
this.isSelecting = false;
|
|
645
|
+
this.isScreenSelecting = false;
|
|
646
|
+
|
|
647
|
+
console.log("🧹 ElectronWindowSelector cleaned up");
|
|
648
|
+
} catch (error) {
|
|
649
|
+
console.error("Cleanup error:", error.message);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* İzinleri kontrol eder
|
|
655
|
+
*/
|
|
656
|
+
async checkPermissions() {
|
|
657
|
+
try {
|
|
658
|
+
const MacRecorder = require("./index.js");
|
|
659
|
+
const recorder = new MacRecorder();
|
|
660
|
+
return await recorder.checkPermissions();
|
|
661
|
+
} catch (error) {
|
|
662
|
+
return {
|
|
663
|
+
screenRecording: false,
|
|
664
|
+
accessibility: false,
|
|
665
|
+
error: error.message,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
module.exports = ElectronWindowSelector;
|
package/index.js
CHANGED
|
@@ -950,6 +950,63 @@ class MacRecorder extends EventEmitter {
|
|
|
950
950
|
|
|
951
951
|
return Promise.all(windowPromises);
|
|
952
952
|
}
|
|
953
|
+
|
|
954
|
+
// Window Selection Methods (ScreenCaptureKit compatible)
|
|
955
|
+
startWindowSelection() {
|
|
956
|
+
if (!nativeBinding.startWindowSelection) {
|
|
957
|
+
throw new Error('Window selection is not available in this build');
|
|
958
|
+
}
|
|
959
|
+
return nativeBinding.startWindowSelection();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
stopWindowSelection() {
|
|
963
|
+
if (!nativeBinding.stopWindowSelection) {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
return nativeBinding.stopWindowSelection();
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
getSelectedWindowInfo() {
|
|
970
|
+
if (!nativeBinding.getSelectedWindowInfo) {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
return nativeBinding.getSelectedWindowInfo();
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
getWindowSelectionStatus() {
|
|
977
|
+
if (!nativeBinding.getWindowSelectionStatus) {
|
|
978
|
+
return { isSelecting: false, hasSelectedWindow: false };
|
|
979
|
+
}
|
|
980
|
+
return nativeBinding.getWindowSelectionStatus();
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
bringWindowToFront(windowId) {
|
|
984
|
+
if (!nativeBinding.bringWindowToFront) {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
return nativeBinding.bringWindowToFront(windowId);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
setBringToFrontEnabled(enabled) {
|
|
991
|
+
if (!nativeBinding.setBringToFrontEnabled) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
return nativeBinding.setBringToFrontEnabled(enabled);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
showRecordingPreview(windowInfo) {
|
|
998
|
+
if (!nativeBinding.showRecordingPreview) {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
return nativeBinding.showRecordingPreview(windowInfo);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
hideRecordingPreview() {
|
|
1005
|
+
if (!nativeBinding.hideRecordingPreview) {
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
return nativeBinding.hideRecordingPreview();
|
|
1009
|
+
}
|
|
953
1010
|
}
|
|
954
1011
|
|
|
955
1012
|
// WindowSelector modülünü de export edelim
|
package/package.json
CHANGED
package/src/mac_recorder.mm
CHANGED
|
@@ -770,8 +770,33 @@ Napi::Value GetWindows(const Napi::CallbackInfo& info) {
|
|
|
770
770
|
Napi::Object windowObj = Napi::Object::New(env);
|
|
771
771
|
windowObj.Set("id", Napi::Number::New(env, window.windowID));
|
|
772
772
|
windowObj.Set("title", Napi::String::New(env, window.title ? [window.title UTF8String] : ""));
|
|
773
|
-
|
|
774
|
-
|
|
773
|
+
|
|
774
|
+
// Safely get application information (can be nil for some windows)
|
|
775
|
+
NSString *appName = @"";
|
|
776
|
+
NSString *bundleId = @"";
|
|
777
|
+
if (window.owningApplication) {
|
|
778
|
+
appName = window.owningApplication.applicationName ?: @"";
|
|
779
|
+
bundleId = window.owningApplication.bundleIdentifier ?: @"";
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
windowObj.Set("appName", Napi::String::New(env, [appName UTF8String]));
|
|
783
|
+
windowObj.Set("ownerName", Napi::String::New(env, [appName UTF8String]));
|
|
784
|
+
windowObj.Set("bundleId", Napi::String::New(env, [bundleId UTF8String]));
|
|
785
|
+
|
|
786
|
+
// Add frame details
|
|
787
|
+
CGRect frame = window.frame;
|
|
788
|
+
windowObj.Set("x", Napi::Number::New(env, (int)frame.origin.x));
|
|
789
|
+
windowObj.Set("y", Napi::Number::New(env, (int)frame.origin.y));
|
|
790
|
+
windowObj.Set("width", Napi::Number::New(env, (int)frame.size.width));
|
|
791
|
+
windowObj.Set("height", Napi::Number::New(env, (int)frame.size.height));
|
|
792
|
+
|
|
793
|
+
// Legacy bounds object for compatibility
|
|
794
|
+
Napi::Object boundsObj = Napi::Object::New(env);
|
|
795
|
+
boundsObj.Set("x", Napi::Number::New(env, (int)frame.origin.x));
|
|
796
|
+
boundsObj.Set("y", Napi::Number::New(env, (int)frame.origin.y));
|
|
797
|
+
boundsObj.Set("width", Napi::Number::New(env, (int)frame.size.width));
|
|
798
|
+
boundsObj.Set("height", Napi::Number::New(env, (int)frame.size.height));
|
|
799
|
+
windowObj.Set("bounds", boundsObj);
|
|
775
800
|
|
|
776
801
|
windowsArray.Set(index++, windowObj);
|
|
777
802
|
}
|
package/src/window_selector.mm
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#import <ApplicationServices/ApplicationServices.h>
|
|
6
6
|
#import <Carbon/Carbon.h>
|
|
7
7
|
#import <Accessibility/Accessibility.h>
|
|
8
|
+
#import <ScreenCaptureKit/ScreenCaptureKit.h>
|
|
8
9
|
|
|
9
10
|
// Global state for window selection
|
|
10
11
|
static bool g_isWindowSelecting = false;
|
|
@@ -35,6 +36,7 @@ void cleanupWindowSelector();
|
|
|
35
36
|
void updateOverlay();
|
|
36
37
|
NSDictionary* getWindowUnderCursor(CGPoint point);
|
|
37
38
|
NSArray* getAllSelectableWindows();
|
|
39
|
+
NSArray* getAllSelectableWindowsLegacy();
|
|
38
40
|
bool bringWindowToFront(int windowId);
|
|
39
41
|
void cleanupRecordingPreview();
|
|
40
42
|
bool showRecordingPreview(NSDictionary *windowInfo);
|
|
@@ -233,7 +235,7 @@ bool hideScreenRecordingPreview();
|
|
|
233
235
|
NSInteger screenIndex = [button tag];
|
|
234
236
|
|
|
235
237
|
// Get screen info from global array using button tag
|
|
236
|
-
if (g_allScreens && screenIndex >= 0 && screenIndex < [g_allScreens count]) {
|
|
238
|
+
if (g_allScreens && screenIndex >= 0 && screenIndex < (NSInteger)[g_allScreens count]) {
|
|
237
239
|
NSDictionary *screenInfo = [g_allScreens objectAtIndex:screenIndex];
|
|
238
240
|
g_selectedScreenInfo = [screenInfo copy];
|
|
239
241
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
@@ -367,8 +369,83 @@ bool bringWindowToFront(int windowId) {
|
|
|
367
369
|
}
|
|
368
370
|
}
|
|
369
371
|
|
|
370
|
-
// Get all selectable windows
|
|
372
|
+
// Get all selectable windows using ScreenCaptureKit
|
|
371
373
|
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() {
|
|
372
449
|
@autoreleasepool {
|
|
373
450
|
NSMutableArray *windows = [NSMutableArray array];
|
|
374
451
|
|
|
@@ -734,7 +811,7 @@ bool startScreenSelection() {
|
|
|
734
811
|
NSMutableArray *screenInfoArray = [[NSMutableArray alloc] init];
|
|
735
812
|
g_screenOverlayWindows = [[NSMutableArray alloc] init];
|
|
736
813
|
|
|
737
|
-
for (
|
|
814
|
+
for (NSUInteger i = 0; i < [screens count]; i++) {
|
|
738
815
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
739
816
|
NSRect screenFrame = [screen frame];
|
|
740
817
|
|
|
@@ -922,10 +999,10 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
922
999
|
NSArray *screens = [NSScreen screens];
|
|
923
1000
|
if (!screens || [screens count] == 0) return false;
|
|
924
1001
|
|
|
925
|
-
|
|
1002
|
+
NSUInteger selectedScreenId = (NSUInteger)[[screenInfo objectForKey:@"id"] intValue];
|
|
926
1003
|
|
|
927
1004
|
// Create overlay for each screen except the selected one
|
|
928
|
-
for (
|
|
1005
|
+
for (NSUInteger i = 0; i < [screens count]; i++) {
|
|
929
1006
|
if (i == selectedScreenId) continue; // Skip selected screen
|
|
930
1007
|
|
|
931
1008
|
NSScreen *screen = [screens objectAtIndex:i];
|
|
@@ -956,7 +1033,7 @@ bool showScreenRecordingPreview(NSDictionary *screenInfo) {
|
|
|
956
1033
|
}
|
|
957
1034
|
}
|
|
958
1035
|
|
|
959
|
-
NSLog(@"🎬 SCREEN RECORDING PREVIEW: Showing overlay for Screen %
|
|
1036
|
+
NSLog(@"🎬 SCREEN RECORDING PREVIEW: Showing overlay for Screen %lu", (unsigned long)selectedScreenId);
|
|
960
1037
|
|
|
961
1038
|
return true;
|
|
962
1039
|
|
|
@@ -996,7 +1073,7 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
996
1073
|
}
|
|
997
1074
|
|
|
998
1075
|
// Store windows for later retrieval via getWindowSelectionStatus
|
|
999
|
-
g_allWindows = [
|
|
1076
|
+
g_allWindows = [getAllSelectableWindows() mutableCopy];
|
|
1000
1077
|
g_isWindowSelecting = true;
|
|
1001
1078
|
|
|
1002
1079
|
// Return true to indicate windows are available
|
|
@@ -1017,7 +1094,7 @@ Napi::Value StartWindowSelection(const Napi::CallbackInfo& info) {
|
|
|
1017
1094
|
|
|
1018
1095
|
@try {
|
|
1019
1096
|
// Get all windows
|
|
1020
|
-
g_allWindows = getAllSelectableWindows();
|
|
1097
|
+
g_allWindows = [getAllSelectableWindows() mutableCopy];
|
|
1021
1098
|
|
|
1022
1099
|
if (!g_allWindows || [g_allWindows count] == 0) {
|
|
1023
1100
|
Napi::Error::New(env, "No selectable windows found").ThrowAsJavaScriptException();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const nativeBinding = require('./build/Release/mac_recorder.node');
|
|
4
|
+
|
|
5
|
+
console.log('Testing ScreenCaptureKit availability...');
|
|
6
|
+
|
|
7
|
+
// Test the native functions directly
|
|
8
|
+
try {
|
|
9
|
+
console.log('Native binding methods:', Object.keys(nativeBinding));
|
|
10
|
+
|
|
11
|
+
// Check if we have the getWindows method
|
|
12
|
+
if (nativeBinding.getWindows) {
|
|
13
|
+
console.log('✅ getWindows method available');
|
|
14
|
+
const windows = nativeBinding.getWindows();
|
|
15
|
+
console.log(`Found ${windows.length} windows`);
|
|
16
|
+
|
|
17
|
+
if (windows.length > 0) {
|
|
18
|
+
console.log('First window:', windows[0]);
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
console.log('❌ getWindows method not available');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Error:', error.message);
|
|
26
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const MacRecorder = require('./index.js');
|
|
4
|
+
|
|
5
|
+
async function testWindowSelector() {
|
|
6
|
+
try {
|
|
7
|
+
console.log('🧪 Testing ScreenCaptureKit-compatible window selector...');
|
|
8
|
+
|
|
9
|
+
// Create recorder instance
|
|
10
|
+
const recorder = new MacRecorder();
|
|
11
|
+
|
|
12
|
+
// Test if we can get windows
|
|
13
|
+
const windows = await recorder.getWindows();
|
|
14
|
+
console.log(`✅ Found ${windows.length} windows`);
|
|
15
|
+
|
|
16
|
+
if (windows.length > 0) {
|
|
17
|
+
console.log('🔍 Sample windows:');
|
|
18
|
+
windows.slice(0, 3).forEach((win, i) => {
|
|
19
|
+
console.log(` ${i+1}. ${win.appName || 'Unknown'} - "${win.title || 'Untitled'}" [${win.width || 0}x${win.height || 0}]`);
|
|
20
|
+
console.log(` ID: ${win.id}, Bundle: ${win.bundleId || 'N/A'}`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log('🪟 Testing window selection overlay...');
|
|
25
|
+
|
|
26
|
+
// Test window selection (will use ScreenCaptureKit if available)
|
|
27
|
+
const started = recorder.startWindowSelection();
|
|
28
|
+
console.log('Window selection started:', started);
|
|
29
|
+
|
|
30
|
+
if (started) {
|
|
31
|
+
console.log('✅ Window selector started successfully with ScreenCaptureKit integration');
|
|
32
|
+
console.log('📝 Press ESC to cancel or interact with the overlay');
|
|
33
|
+
|
|
34
|
+
// Wait a bit then cleanup
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
recorder.stopWindowSelection();
|
|
37
|
+
console.log('🧹 Window selector stopped');
|
|
38
|
+
|
|
39
|
+
console.log('✅ Test completed successfully!');
|
|
40
|
+
console.log('🎉 ScreenCaptureKit integration is working properly');
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}, 10000);
|
|
43
|
+
} else {
|
|
44
|
+
console.log('❌ Failed to start window selector');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('❌ Test failed:', error.message);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
testWindowSelector();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const MacRecorder = require('./index.js');
|
|
4
|
+
|
|
5
|
+
async function test() {
|
|
6
|
+
try {
|
|
7
|
+
const recorder = new MacRecorder();
|
|
8
|
+
console.log('Getting windows directly from native binding...');
|
|
9
|
+
|
|
10
|
+
const nativeBinding = require('./build/Release/mac_recorder.node');
|
|
11
|
+
const nativeWindows = nativeBinding.getWindows();
|
|
12
|
+
console.log('Native windows:', nativeWindows.length);
|
|
13
|
+
if (nativeWindows.length > 0) {
|
|
14
|
+
console.log('First native window:', nativeWindows[0]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
console.log('\nGetting windows through MacRecorder class...');
|
|
18
|
+
const windows = await recorder.getWindows();
|
|
19
|
+
console.log('MacRecorder windows:', windows.length);
|
|
20
|
+
if (windows.length > 0) {
|
|
21
|
+
console.log('First MacRecorder window:', windows[0]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Error:', error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const MacRecorder = require('./index.js');
|
|
4
|
+
|
|
5
|
+
async function testWindowDetails() {
|
|
6
|
+
try {
|
|
7
|
+
const recorder = new MacRecorder();
|
|
8
|
+
const windows = await recorder.getWindows();
|
|
9
|
+
|
|
10
|
+
console.log(`Found ${windows.length} windows:`);
|
|
11
|
+
|
|
12
|
+
windows.forEach((win, i) => {
|
|
13
|
+
console.log(`\n${i+1}. Window ${win.id}:`);
|
|
14
|
+
console.log(` Title: "${win.title}"`);
|
|
15
|
+
console.log(` App: ${win.appName}`);
|
|
16
|
+
console.log(` Bundle: ${win.bundleId}`);
|
|
17
|
+
console.log(` Position: (${win.x}, ${win.y})`);
|
|
18
|
+
console.log(` Size: ${win.width} x ${win.height}`);
|
|
19
|
+
if (win.bounds) {
|
|
20
|
+
console.log(` Bounds: (${win.bounds.x}, ${win.bounds.y}) ${win.bounds.width}x${win.bounds.height}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (i >= 10) { // Limit output
|
|
24
|
+
console.log(`\n... and ${windows.length - 11} more windows`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Error:', error.message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
testWindowDetails();
|