node-mac-recorder 2.22.14 → 2.22.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +1 -0
- package/electron-safe-binding.gyp +1 -0
- package/electron-safe-index.js +196 -104
- package/index.js +8 -355
- package/lib/cursorCapture/displayInfo.js +110 -0
- package/lib/cursorCapture/polling.js +404 -0
- package/package.json +1 -1
- package/src/cursor_tracker.mm +42 -109
- package/src/electron_safe/cursor_tracker_electron.mm +34 -0
- package/src/text_input_ax_snapshot.h +3 -0
- package/src/text_input_ax_snapshot.mm +161 -0
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { EventEmitter } = require("events");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const fs = require("fs");
|
|
4
|
+
const cursorCapturePolling = require("./lib/cursorCapture/polling");
|
|
4
5
|
|
|
5
6
|
// Auto-switch to Electron-safe implementation when running under Electron and binary exists
|
|
6
7
|
let USE_ELECTRON_SAFE = false;
|
|
@@ -1340,365 +1341,17 @@ class MacRecorder extends EventEmitter {
|
|
|
1340
1341
|
});
|
|
1341
1342
|
}
|
|
1342
1343
|
|
|
1343
|
-
/**
|
|
1344
|
-
* Event'in kaydedilip kaydedilmeyeceğini belirler
|
|
1345
|
-
*/
|
|
1346
|
-
shouldCaptureEvent(currentData) {
|
|
1347
|
-
if (!this.lastCapturedData) {
|
|
1348
|
-
return true; // İlk event
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
const last = this.lastCapturedData;
|
|
1352
|
-
|
|
1353
|
-
// Event type değişmişse
|
|
1354
|
-
if (currentData.type !== last.type) {
|
|
1355
|
-
return true;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
// Pozisyon değişmişse (minimum 2 pixel tolerans)
|
|
1359
|
-
if (
|
|
1360
|
-
Math.abs(currentData.x - last.x) >= 2 ||
|
|
1361
|
-
Math.abs(currentData.y - last.y) >= 2
|
|
1362
|
-
) {
|
|
1363
|
-
return true;
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
// Cursor type değişmişse
|
|
1367
|
-
if (currentData.cursorType !== last.cursorType) {
|
|
1368
|
-
return true;
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
// Hiçbir değişiklik yoksa kaydetme
|
|
1372
|
-
return false;
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
/**
|
|
1376
|
-
* Unified cursor capture for all recording types - uses video-relative coordinates
|
|
1377
|
-
* @param {string|number} intervalOrFilepath - Cursor data JSON dosya yolu veya interval
|
|
1378
|
-
* @param {Object} options - Cursor capture seçenekleri
|
|
1379
|
-
* @param {boolean} options.videoRelative - Use video-relative coordinates (recommended)
|
|
1380
|
-
* @param {Object} options.displayInfo - Display information for coordinate transformation
|
|
1381
|
-
* @param {string} options.recordingType - Type of recording: 'display', 'window', 'area'
|
|
1382
|
-
* @param {Object} options.captureArea - Capture area for area recording coordinate transformation
|
|
1383
|
-
* @param {number} options.windowId - Window ID for window recording coordinate transformation
|
|
1384
|
-
* @param {number} options.startTimestamp - Pre-defined start timestamp for synchronization (optional)
|
|
1385
|
-
*/
|
|
1386
1344
|
async startCursorCapture(intervalOrFilepath = 100, options = {}) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
filepath = `cursor-data-${Date.now()}.json`;
|
|
1394
|
-
} else if (typeof intervalOrFilepath === "string") {
|
|
1395
|
-
filepath = intervalOrFilepath;
|
|
1396
|
-
} else {
|
|
1397
|
-
throw new Error(
|
|
1398
|
-
"Parameter must be interval (number) or filepath (string)"
|
|
1399
|
-
);
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
if (this.cursorCaptureInterval) {
|
|
1403
|
-
throw new Error("Cursor capture is already running");
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
// SYNC FIX: Use pre-defined timestamp if provided for synchronization
|
|
1407
|
-
const syncStartTime = options.startTimestamp || Date.now();
|
|
1408
|
-
|
|
1409
|
-
// Fetch window bounds for multi-window recording
|
|
1410
|
-
if (options.multiWindowBounds && options.multiWindowBounds.length > 0) {
|
|
1411
|
-
try {
|
|
1412
|
-
const allWindows = await this.getWindows();
|
|
1413
|
-
// Match window IDs and populate bounds
|
|
1414
|
-
for (const windowInfo of options.multiWindowBounds) {
|
|
1415
|
-
const windowData = allWindows.find(w => w.id === windowInfo.windowId);
|
|
1416
|
-
if (windowData) {
|
|
1417
|
-
windowInfo.bounds = {
|
|
1418
|
-
x: windowData.x || 0,
|
|
1419
|
-
y: windowData.y || 0,
|
|
1420
|
-
width: windowData.width || 0,
|
|
1421
|
-
height: windowData.height || 0
|
|
1422
|
-
};
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
} catch (error) {
|
|
1426
|
-
console.warn('Failed to fetch window bounds for multi-window cursor tracking:', error.message);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// Use video-relative coordinate system for all recording types
|
|
1431
|
-
if (options.videoRelative && options.displayInfo) {
|
|
1432
|
-
// Calculate video offset based on recording type
|
|
1433
|
-
let videoOffsetX = 0;
|
|
1434
|
-
let videoOffsetY = 0;
|
|
1435
|
-
let videoWidth = options.displayInfo.width || options.displayInfo.logicalWidth;
|
|
1436
|
-
let videoHeight = options.displayInfo.height || options.displayInfo.logicalHeight;
|
|
1437
|
-
|
|
1438
|
-
if (options.recordingType === 'window' && options.windowId) {
|
|
1439
|
-
// For window recording: offset = window position in display
|
|
1440
|
-
if (options.captureArea) {
|
|
1441
|
-
videoOffsetX = options.captureArea.x;
|
|
1442
|
-
videoOffsetY = options.captureArea.y;
|
|
1443
|
-
videoWidth = options.captureArea.width;
|
|
1444
|
-
videoHeight = options.captureArea.height;
|
|
1445
|
-
}
|
|
1446
|
-
} else if (options.recordingType === 'area' && options.captureArea) {
|
|
1447
|
-
// For area recording: offset = area position in display
|
|
1448
|
-
videoOffsetX = options.captureArea.x;
|
|
1449
|
-
videoOffsetY = options.captureArea.y;
|
|
1450
|
-
videoWidth = options.captureArea.width;
|
|
1451
|
-
videoHeight = options.captureArea.height;
|
|
1452
|
-
}
|
|
1453
|
-
// For display recording: offset remains 0,0
|
|
1454
|
-
|
|
1455
|
-
this.cursorDisplayInfo = {
|
|
1456
|
-
displayId: options.displayInfo.displayId || options.displayInfo.id,
|
|
1457
|
-
displayX: options.displayInfo.x || 0,
|
|
1458
|
-
displayY: options.displayInfo.y || 0,
|
|
1459
|
-
displayWidth: options.displayInfo.width || options.displayInfo.logicalWidth,
|
|
1460
|
-
displayHeight: options.displayInfo.height || options.displayInfo.logicalHeight,
|
|
1461
|
-
videoOffsetX: videoOffsetX,
|
|
1462
|
-
videoOffsetY: videoOffsetY,
|
|
1463
|
-
videoWidth: videoWidth,
|
|
1464
|
-
videoHeight: videoHeight,
|
|
1465
|
-
videoRelative: true,
|
|
1466
|
-
recordingType: options.recordingType || 'display',
|
|
1467
|
-
// Store additional context for debugging
|
|
1468
|
-
captureArea: options.captureArea,
|
|
1469
|
-
windowId: options.windowId,
|
|
1470
|
-
// Multi-window bounds for location detection
|
|
1471
|
-
multiWindowBounds: options.multiWindowBounds || null
|
|
1472
|
-
};
|
|
1473
|
-
} else if (this.recordingDisplayInfo) {
|
|
1474
|
-
// Fallback: Use recording display info if available
|
|
1475
|
-
this.cursorDisplayInfo = {
|
|
1476
|
-
...this.recordingDisplayInfo,
|
|
1477
|
-
displayX: this.recordingDisplayInfo.x || 0,
|
|
1478
|
-
displayY: this.recordingDisplayInfo.y || 0,
|
|
1479
|
-
displayWidth: this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
|
|
1480
|
-
displayHeight: this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
|
|
1481
|
-
videoOffsetX: 0,
|
|
1482
|
-
videoOffsetY: 0,
|
|
1483
|
-
videoWidth: this.recordingDisplayInfo.width || this.recordingDisplayInfo.logicalWidth,
|
|
1484
|
-
videoHeight: this.recordingDisplayInfo.height || this.recordingDisplayInfo.logicalHeight,
|
|
1485
|
-
videoRelative: true,
|
|
1486
|
-
recordingType: options.recordingType || 'display',
|
|
1487
|
-
multiWindowBounds: options.multiWindowBounds || null
|
|
1488
|
-
};
|
|
1489
|
-
} else {
|
|
1490
|
-
// Final fallback: Main display global coordinates
|
|
1491
|
-
try {
|
|
1492
|
-
const displays = await this.getDisplays();
|
|
1493
|
-
const mainDisplay = displays.find((d) => d.isPrimary) || displays[0];
|
|
1494
|
-
if (mainDisplay) {
|
|
1495
|
-
this.cursorDisplayInfo = {
|
|
1496
|
-
displayId: mainDisplay.id,
|
|
1497
|
-
x: mainDisplay.x,
|
|
1498
|
-
y: mainDisplay.y,
|
|
1499
|
-
width: parseInt(mainDisplay.resolution.split("x")[0]),
|
|
1500
|
-
height: parseInt(mainDisplay.resolution.split("x")[1]),
|
|
1501
|
-
multiWindowBounds: options.multiWindowBounds || null
|
|
1502
|
-
};
|
|
1503
|
-
}
|
|
1504
|
-
} catch (error) {
|
|
1505
|
-
console.warn("Main display bilgisi alınamadı:", error.message);
|
|
1506
|
-
this.cursorDisplayInfo = null; // Fallback: global koordinatlar
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
return new Promise((resolve, reject) => {
|
|
1511
|
-
try {
|
|
1512
|
-
// Dosyayı oluştur ve temizle
|
|
1513
|
-
const fs = require("fs");
|
|
1514
|
-
fs.writeFileSync(filepath, "[");
|
|
1515
|
-
|
|
1516
|
-
this.cursorCaptureFile = filepath;
|
|
1517
|
-
// SYNC FIX: Use synchronized start time for accurate timestamp calculation
|
|
1518
|
-
this.cursorCaptureStartTime = syncStartTime;
|
|
1519
|
-
this.cursorCaptureFirstWrite = true;
|
|
1520
|
-
this.lastCapturedData = null;
|
|
1521
|
-
// Store session timestamp for sync metadata
|
|
1522
|
-
this.cursorCaptureSessionTimestamp = this.sessionTimestamp;
|
|
1523
|
-
|
|
1524
|
-
// JavaScript interval ile polling yap (daha sık - mouse event'leri yakalamak için)
|
|
1525
|
-
this.cursorCaptureInterval = setInterval(() => {
|
|
1526
|
-
try {
|
|
1527
|
-
const position = nativeBinding.getCursorPosition();
|
|
1528
|
-
const timestamp = Date.now() - this.cursorCaptureStartTime;
|
|
1529
|
-
|
|
1530
|
-
// Video-relative coordinate transformation for all recording types
|
|
1531
|
-
let x = position.x;
|
|
1532
|
-
let y = position.y;
|
|
1533
|
-
let coordinateSystem = "global";
|
|
1534
|
-
|
|
1535
|
-
// Apply video-relative transformation for all recording types
|
|
1536
|
-
if (this.cursorDisplayInfo && this.cursorDisplayInfo.videoRelative) {
|
|
1537
|
-
// Step 1: Transform global → display-relative coordinates
|
|
1538
|
-
const displayRelativeX = position.x - this.cursorDisplayInfo.displayX;
|
|
1539
|
-
const displayRelativeY = position.y - this.cursorDisplayInfo.displayY;
|
|
1540
|
-
|
|
1541
|
-
// Step 2: Transform display-relative → video-relative coordinates
|
|
1542
|
-
x = displayRelativeX - this.cursorDisplayInfo.videoOffsetX;
|
|
1543
|
-
y = displayRelativeY - this.cursorDisplayInfo.videoOffsetY;
|
|
1544
|
-
coordinateSystem = "video-relative";
|
|
1545
|
-
|
|
1546
|
-
// Bounds check for video area (don't skip, just note if outside)
|
|
1547
|
-
const outsideVideo = x < 0 || y < 0 ||
|
|
1548
|
-
x >= this.cursorDisplayInfo.videoWidth ||
|
|
1549
|
-
y >= this.cursorDisplayInfo.videoHeight;
|
|
1550
|
-
|
|
1551
|
-
// For debugging - add metadata if cursor is outside video area
|
|
1552
|
-
if (outsideVideo) {
|
|
1553
|
-
coordinateSystem = "video-relative-outside";
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
const cursorData = {
|
|
1558
|
-
x: x,
|
|
1559
|
-
y: y,
|
|
1560
|
-
timestamp: timestamp,
|
|
1561
|
-
unixTimeMs: Date.now(),
|
|
1562
|
-
cursorType: position.cursorType,
|
|
1563
|
-
type: position.eventType || "move",
|
|
1564
|
-
coordinateSystem: coordinateSystem,
|
|
1565
|
-
// Video-relative metadata for all recording types
|
|
1566
|
-
recordingType: this.cursorDisplayInfo?.recordingType || "display",
|
|
1567
|
-
videoInfo: this.cursorDisplayInfo ? {
|
|
1568
|
-
width: this.cursorDisplayInfo.videoWidth,
|
|
1569
|
-
height: this.cursorDisplayInfo.videoHeight,
|
|
1570
|
-
offsetX: this.cursorDisplayInfo.videoOffsetX,
|
|
1571
|
-
offsetY: this.cursorDisplayInfo.videoOffsetY
|
|
1572
|
-
} : {},
|
|
1573
|
-
displayInfo: this.cursorDisplayInfo ? {
|
|
1574
|
-
displayId: this.cursorDisplayInfo.displayId,
|
|
1575
|
-
width: this.cursorDisplayInfo.displayWidth,
|
|
1576
|
-
height: this.cursorDisplayInfo.displayHeight
|
|
1577
|
-
} : {}
|
|
1578
|
-
};
|
|
1579
|
-
|
|
1580
|
-
// Multi-window location detection with window-relative coordinates
|
|
1581
|
-
if (this.cursorDisplayInfo?.multiWindowBounds && this.cursorDisplayInfo.multiWindowBounds.length > 0) {
|
|
1582
|
-
const location = { hover: null, click: null };
|
|
1583
|
-
let windowRelativeCoords = null;
|
|
1584
|
-
|
|
1585
|
-
// Detect which window the cursor is over
|
|
1586
|
-
for (const windowInfo of this.cursorDisplayInfo.multiWindowBounds) {
|
|
1587
|
-
if (windowInfo.bounds) {
|
|
1588
|
-
const { x: wx, y: wy, width: ww, height: wh } = windowInfo.bounds;
|
|
1589
|
-
// Check if cursor is inside window bounds (using global coordinates)
|
|
1590
|
-
if (position.x >= wx && position.x <= wx + ww &&
|
|
1591
|
-
position.y >= wy && position.y <= wy + wh) {
|
|
1592
|
-
location.hover = windowInfo.windowId;
|
|
1593
|
-
|
|
1594
|
-
// Calculate window-relative coordinates
|
|
1595
|
-
// These coords are relative to the window's top-left corner (0,0)
|
|
1596
|
-
// This allows the desktop app to position cursor correctly
|
|
1597
|
-
// regardless of where the window is placed on canvas
|
|
1598
|
-
windowRelativeCoords = {
|
|
1599
|
-
windowId: windowInfo.windowId,
|
|
1600
|
-
x: position.x - wx,
|
|
1601
|
-
y: position.y - wy,
|
|
1602
|
-
// Also include window dimensions for reference
|
|
1603
|
-
windowWidth: ww,
|
|
1604
|
-
windowHeight: wh
|
|
1605
|
-
};
|
|
1606
|
-
|
|
1607
|
-
// If this is a click/drag event, mark click location
|
|
1608
|
-
// Native eventType values: 'mousedown', 'mouseup', 'drag', 'rightmousedown', 'rightmouseup', 'rightdrag'
|
|
1609
|
-
const eventType = position.eventType || '';
|
|
1610
|
-
if (eventType === 'mousedown' ||
|
|
1611
|
-
eventType === 'mouseup' ||
|
|
1612
|
-
eventType === 'drag' ||
|
|
1613
|
-
eventType === 'rightmousedown' ||
|
|
1614
|
-
eventType === 'rightmouseup' ||
|
|
1615
|
-
eventType === 'rightdrag') {
|
|
1616
|
-
location.click = windowInfo.windowId;
|
|
1617
|
-
}
|
|
1618
|
-
break; // Found the window, stop searching
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
// Add location info to cursor data
|
|
1624
|
-
cursorData.location = location;
|
|
1625
|
-
|
|
1626
|
-
// Add window-relative coordinates if cursor is over a window
|
|
1627
|
-
if (windowRelativeCoords) {
|
|
1628
|
-
cursorData.windowRelative = windowRelativeCoords;
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
// Add sync metadata to first event only
|
|
1633
|
-
if (this.cursorCaptureFirstWrite && this.cursorCaptureSessionTimestamp) {
|
|
1634
|
-
cursorData._syncMetadata = {
|
|
1635
|
-
videoStartTime: this.cursorCaptureSessionTimestamp,
|
|
1636
|
-
cursorStartTime: this.cursorCaptureStartTime,
|
|
1637
|
-
offsetMs: this.cursorCaptureStartTime - this.cursorCaptureSessionTimestamp
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
// Sadece eventType değiştiğinde veya pozisyon değiştiğinde kaydet
|
|
1642
|
-
if (this.shouldCaptureEvent(cursorData)) {
|
|
1643
|
-
// Dosyaya ekle
|
|
1644
|
-
const jsonString = JSON.stringify(cursorData);
|
|
1645
|
-
|
|
1646
|
-
if (this.cursorCaptureFirstWrite) {
|
|
1647
|
-
fs.appendFileSync(filepath, jsonString);
|
|
1648
|
-
this.cursorCaptureFirstWrite = false;
|
|
1649
|
-
} else {
|
|
1650
|
-
fs.appendFileSync(filepath, "," + jsonString);
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
// Son pozisyonu sakla
|
|
1654
|
-
this.lastCapturedData = { ...cursorData };
|
|
1655
|
-
}
|
|
1656
|
-
} catch (error) {
|
|
1657
|
-
console.error("Cursor capture error:", error);
|
|
1658
|
-
}
|
|
1659
|
-
}, interval); // Configurable FPS
|
|
1660
|
-
|
|
1661
|
-
this.emit("cursorCaptureStarted", filepath);
|
|
1662
|
-
resolve(true);
|
|
1663
|
-
} catch (error) {
|
|
1664
|
-
reject(error);
|
|
1665
|
-
}
|
|
1666
|
-
});
|
|
1345
|
+
return cursorCapturePolling.startCursorCapture(
|
|
1346
|
+
this,
|
|
1347
|
+
nativeBinding,
|
|
1348
|
+
intervalOrFilepath,
|
|
1349
|
+
options,
|
|
1350
|
+
);
|
|
1667
1351
|
}
|
|
1668
1352
|
|
|
1669
|
-
/**
|
|
1670
|
-
* Cursor capture durdurur - dosya yazma işlemini sonlandırır
|
|
1671
|
-
*/
|
|
1672
1353
|
async stopCursorCapture() {
|
|
1673
|
-
return
|
|
1674
|
-
try {
|
|
1675
|
-
if (!this.cursorCaptureInterval) {
|
|
1676
|
-
return resolve(false);
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
// Interval'ı durdur
|
|
1680
|
-
clearInterval(this.cursorCaptureInterval);
|
|
1681
|
-
this.cursorCaptureInterval = null;
|
|
1682
|
-
|
|
1683
|
-
// Dosyayı kapat
|
|
1684
|
-
if (this.cursorCaptureFile) {
|
|
1685
|
-
const fs = require("fs");
|
|
1686
|
-
fs.appendFileSync(this.cursorCaptureFile, "]");
|
|
1687
|
-
this.cursorCaptureFile = null;
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
// Değişkenleri temizle
|
|
1691
|
-
this.lastCapturedData = null;
|
|
1692
|
-
this.cursorCaptureStartTime = null;
|
|
1693
|
-
this.cursorCaptureFirstWrite = true;
|
|
1694
|
-
this.cursorDisplayInfo = null;
|
|
1695
|
-
|
|
1696
|
-
this.emit("cursorCaptureStopped");
|
|
1697
|
-
resolve(true);
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
reject(error);
|
|
1700
|
-
}
|
|
1701
|
-
});
|
|
1354
|
+
return cursorCapturePolling.stopCursorCapture(this);
|
|
1702
1355
|
}
|
|
1703
1356
|
|
|
1704
1357
|
/**
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
async function resolveCursorDisplayInfo(recorder, options) {
|
|
4
|
+
if (options.videoRelative && options.displayInfo) {
|
|
5
|
+
let videoOffsetX = 0;
|
|
6
|
+
let videoOffsetY = 0;
|
|
7
|
+
let videoWidth =
|
|
8
|
+
options.displayInfo.width || options.displayInfo.logicalWidth;
|
|
9
|
+
let videoHeight =
|
|
10
|
+
options.displayInfo.height || options.displayInfo.logicalHeight;
|
|
11
|
+
|
|
12
|
+
if (options.recordingType === "window" && options.windowId) {
|
|
13
|
+
if (options.captureArea) {
|
|
14
|
+
videoOffsetX = options.captureArea.x;
|
|
15
|
+
videoOffsetY = options.captureArea.y;
|
|
16
|
+
videoWidth = options.captureArea.width;
|
|
17
|
+
videoHeight = options.captureArea.height;
|
|
18
|
+
}
|
|
19
|
+
} else if (options.recordingType === "area" && options.captureArea) {
|
|
20
|
+
videoOffsetX = options.captureArea.x;
|
|
21
|
+
videoOffsetY = options.captureArea.y;
|
|
22
|
+
videoWidth = options.captureArea.width;
|
|
23
|
+
videoHeight = options.captureArea.height;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
recorder.cursorDisplayInfo = {
|
|
27
|
+
displayId: options.displayInfo.displayId || options.displayInfo.id,
|
|
28
|
+
displayX: options.displayInfo.x || 0,
|
|
29
|
+
displayY: options.displayInfo.y || 0,
|
|
30
|
+
displayWidth:
|
|
31
|
+
options.displayInfo.width || options.displayInfo.logicalWidth,
|
|
32
|
+
displayHeight:
|
|
33
|
+
options.displayInfo.height || options.displayInfo.logicalHeight,
|
|
34
|
+
videoOffsetX,
|
|
35
|
+
videoOffsetY,
|
|
36
|
+
videoWidth,
|
|
37
|
+
videoHeight,
|
|
38
|
+
videoRelative: true,
|
|
39
|
+
recordingType: options.recordingType || "display",
|
|
40
|
+
captureArea: options.captureArea,
|
|
41
|
+
windowId: options.windowId,
|
|
42
|
+
multiWindowBounds: options.multiWindowBounds || null,
|
|
43
|
+
};
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (recorder.recordingDisplayInfo) {
|
|
48
|
+
recorder.cursorDisplayInfo = {
|
|
49
|
+
...recorder.recordingDisplayInfo,
|
|
50
|
+
displayX: recorder.recordingDisplayInfo.x || 0,
|
|
51
|
+
displayY: recorder.recordingDisplayInfo.y || 0,
|
|
52
|
+
displayWidth:
|
|
53
|
+
recorder.recordingDisplayInfo.width ||
|
|
54
|
+
recorder.recordingDisplayInfo.logicalWidth,
|
|
55
|
+
displayHeight:
|
|
56
|
+
recorder.recordingDisplayInfo.height ||
|
|
57
|
+
recorder.recordingDisplayInfo.logicalHeight,
|
|
58
|
+
videoOffsetX: 0,
|
|
59
|
+
videoOffsetY: 0,
|
|
60
|
+
videoWidth:
|
|
61
|
+
recorder.recordingDisplayInfo.width ||
|
|
62
|
+
recorder.recordingDisplayInfo.logicalWidth,
|
|
63
|
+
videoHeight:
|
|
64
|
+
recorder.recordingDisplayInfo.height ||
|
|
65
|
+
recorder.recordingDisplayInfo.logicalHeight,
|
|
66
|
+
videoRelative: true,
|
|
67
|
+
recordingType: options.recordingType || "display",
|
|
68
|
+
multiWindowBounds: options.multiWindowBounds || null,
|
|
69
|
+
};
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const displays = await recorder.getDisplays();
|
|
75
|
+
const mainDisplay =
|
|
76
|
+
displays.find((d) => d.isPrimary) || displays[0];
|
|
77
|
+
if (mainDisplay) {
|
|
78
|
+
let w = mainDisplay.width;
|
|
79
|
+
let h = mainDisplay.height;
|
|
80
|
+
const res = mainDisplay.resolution;
|
|
81
|
+
if ((w == null || h == null) && res) {
|
|
82
|
+
const parts = String(res).split("x");
|
|
83
|
+
if (w == null) {
|
|
84
|
+
w = parseInt(parts[0], 10);
|
|
85
|
+
}
|
|
86
|
+
if (h == null) {
|
|
87
|
+
h = parseInt(parts[1], 10);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!Number.isFinite(w) || w <= 0) {
|
|
91
|
+
w = 1920;
|
|
92
|
+
}
|
|
93
|
+
if (!Number.isFinite(h) || h <= 0) {
|
|
94
|
+
h = 1080;
|
|
95
|
+
}
|
|
96
|
+
recorder.cursorDisplayInfo = {
|
|
97
|
+
displayId: mainDisplay.id,
|
|
98
|
+
x: mainDisplay.x,
|
|
99
|
+
y: mainDisplay.y,
|
|
100
|
+
width: w,
|
|
101
|
+
height: h,
|
|
102
|
+
multiWindowBounds: options.multiWindowBounds || null,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
recorder.cursorDisplayInfo = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = { resolveCursorDisplayInfo };
|