node-mac-recorder 2.16.30 โ 2.16.32
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 +1 -3
- package/cursor-debug-test.js +60 -0
- package/cursor-dpr-test.js +73 -0
- package/cursor-dpr-test.json +1 -0
- package/cursor-permission-test.js +46 -0
- package/cursor-scaling-debug.js +53 -0
- package/index.js +32 -4
- package/package.json +1 -1
- package/src/cursor_tracker.mm +137 -8
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
5
|
-
"Bash(grep:*)",
|
|
6
|
-
"Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐งช Testing macOS 14/13 coordinate scaling fix...'');\nconst MacRecorder = require(''./index.js'');\nconst recorder = new MacRecorder();\n\nrecorder.startRecording(''/tmp/coordinate-fix-test.mov'')\n .then(success => {\n console.log(''Start result:'', success ? ''โ
SUCCESS'' : ''โ FAILED'');\n if (success) {\n setTimeout(() => {\n recorder.stopRecording().then(() => {\n console.log(''โ
Recording stopped'');\n const fs = require(''fs'');\n if (fs.existsSync(''/tmp/coordinate-fix-test.mov'')) {\n const size = Math.round(fs.statSync(''/tmp/coordinate-fix-test.mov'').size/1024);\n console.log(''๐น File size:'', size + ''KB'');\n if (size > 100) {\n console.log(''๐ macOS 14/13 coordinate fix test SUCCESS!'');\n } else {\n console.log(''โ ๏ธ File too small, may have issues'');\n }\n } else {\n console.log(''โ No output file created'');\n }\n });\n }, 3000); // 3 seconds recording\n }\n })\n .catch(console.error);\n\")"
|
|
4
|
+
"Bash(node:*)"
|
|
7
5
|
],
|
|
8
6
|
"deny": [],
|
|
9
7
|
"ask": []
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
|
|
3
|
+
console.log('๐ Debugging cursor coordinate scaling issue...\n');
|
|
4
|
+
|
|
5
|
+
const recorder = new MacRecorder();
|
|
6
|
+
|
|
7
|
+
async function testCursorScaling() {
|
|
8
|
+
console.log('Getting display info...');
|
|
9
|
+
const displays = await recorder.getDisplays();
|
|
10
|
+
|
|
11
|
+
displays.forEach((display, index) => {
|
|
12
|
+
console.log(`Display ${index}:`);
|
|
13
|
+
console.log(` Resolution: ${display.resolution}`);
|
|
14
|
+
console.log(` Position: (${display.x}, ${display.y})`);
|
|
15
|
+
console.log(` Primary: ${display.isPrimary}`);
|
|
16
|
+
console.log(` ID: ${display.id}`);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
console.log('\n๐ฏ Please move your mouse to different corners and press Enter...');
|
|
20
|
+
console.log('This will help us understand the coordinate mapping issue.\n');
|
|
21
|
+
|
|
22
|
+
const readline = require('readline');
|
|
23
|
+
const rl = readline.createInterface({
|
|
24
|
+
input: process.stdin,
|
|
25
|
+
output: process.stdout
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
let testCount = 0;
|
|
29
|
+
const testPositions = [
|
|
30
|
+
'Top-Left corner of the display',
|
|
31
|
+
'Top-Right corner of the display',
|
|
32
|
+
'Bottom-Left corner of the display',
|
|
33
|
+
'Bottom-Right corner of the display',
|
|
34
|
+
'Center of the display'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function nextTest() {
|
|
38
|
+
if (testCount >= testPositions.length) {
|
|
39
|
+
console.log('\nโ
Test completed!');
|
|
40
|
+
rl.close();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`\n๐ Test ${testCount + 1}: Move mouse to ${testPositions[testCount]} and press Enter:`);
|
|
46
|
+
rl.question('', () => {
|
|
47
|
+
const position = recorder.getCursorPosition();
|
|
48
|
+
console.log(` Raw cursor position: (${position.rawX || 'N/A'}, ${position.rawY || 'N/A'})`);
|
|
49
|
+
console.log(` Logical cursor position: (${position.x}, ${position.y})`);
|
|
50
|
+
console.log(` Scale factor: ${position.scaleFactor || 'N/A'}x`);
|
|
51
|
+
|
|
52
|
+
testCount++;
|
|
53
|
+
nextTest();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
nextTest();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
testCursorScaling().catch(console.error);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
|
|
3
|
+
console.log('๐งช Testing cursor DPR scaling fixes...\n');
|
|
4
|
+
|
|
5
|
+
const recorder = new MacRecorder();
|
|
6
|
+
|
|
7
|
+
// Test basic cursor position
|
|
8
|
+
console.log('1. Testing basic cursor position:');
|
|
9
|
+
try {
|
|
10
|
+
const position = recorder.getCursorPosition();
|
|
11
|
+
console.log(` Position: (${position.x}, ${position.y})`);
|
|
12
|
+
|
|
13
|
+
// If we have scaling debug info, show it
|
|
14
|
+
if (position.scaleFactor) {
|
|
15
|
+
console.log(` Scale factor: ${position.scaleFactor}x`);
|
|
16
|
+
console.log(` Raw position: (${position.rawX}, ${position.rawY})`);
|
|
17
|
+
console.log(` Logical position: (${position.x}, ${position.y})`);
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(' Error:', error.message);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log('\n2. Testing display information:');
|
|
24
|
+
recorder.getDisplays().then(displays => {
|
|
25
|
+
displays.forEach((display, index) => {
|
|
26
|
+
console.log(` Display ${index}: ${display.resolution} at (${display.x}, ${display.y})`);
|
|
27
|
+
console.log(` Primary: ${display.isPrimary}, ID: ${display.id}`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
console.log('\n3. Testing cursor capture with DPR fix:');
|
|
31
|
+
const outputFile = 'cursor-dpr-test.json';
|
|
32
|
+
|
|
33
|
+
recorder.startCursorCapture(outputFile, 50).then(() => {
|
|
34
|
+
console.log(` โ
Cursor capture started, saving to ${outputFile}`);
|
|
35
|
+
console.log(' Move your mouse around for 5 seconds...');
|
|
36
|
+
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
recorder.stopCursorCapture().then(() => {
|
|
39
|
+
console.log(' โ
Cursor capture stopped');
|
|
40
|
+
|
|
41
|
+
// Read and analyze the captured data
|
|
42
|
+
const fs = require('fs');
|
|
43
|
+
try {
|
|
44
|
+
const data = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
|
|
45
|
+
if (data.length > 0) {
|
|
46
|
+
const first = data[0];
|
|
47
|
+
const last = data[data.length - 1];
|
|
48
|
+
|
|
49
|
+
console.log(` ๐ Captured ${data.length} cursor events`);
|
|
50
|
+
console.log(` ๐ First: (${first.x}, ${first.y}) ${first.coordinateSystem || 'unknown'}`);
|
|
51
|
+
console.log(` ๐ Last: (${last.x}, ${last.y}) ${last.coordinateSystem || 'unknown'}`);
|
|
52
|
+
|
|
53
|
+
// Check coordinate system
|
|
54
|
+
const hasCoordinateSystem = data.some(d => d.coordinateSystem);
|
|
55
|
+
console.log(` ๐ฏ Coordinate system info: ${hasCoordinateSystem ? 'Present' : 'Missing'}`);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(' โ ๏ธ No cursor events captured');
|
|
58
|
+
}
|
|
59
|
+
} catch (readError) {
|
|
60
|
+
console.error(' โ Error reading capture file:', readError.message);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
process.exit(0);
|
|
64
|
+
});
|
|
65
|
+
}, 5000);
|
|
66
|
+
}).catch(error => {
|
|
67
|
+
console.error(' โ Cursor capture failed:', error.message);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
}).catch(error => {
|
|
71
|
+
console.error(' Error getting displays:', error.message);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
|
|
3
|
+
async function testCursorPermissions() {
|
|
4
|
+
console.log('๐ Testing cursor tracking permissions...\n');
|
|
5
|
+
|
|
6
|
+
const recorder = new MacRecorder();
|
|
7
|
+
|
|
8
|
+
// Check permissions first
|
|
9
|
+
console.log('1. Checking permissions:');
|
|
10
|
+
const permissions = await recorder.checkPermissions();
|
|
11
|
+
console.log(' Screen Recording:', permissions.screenRecording ? 'โ
' : 'โ');
|
|
12
|
+
console.log(' Accessibility:', permissions.accessibility ? 'โ
' : 'โ');
|
|
13
|
+
console.log(' Microphone:', permissions.microphone ? 'โ
' : 'โ');
|
|
14
|
+
|
|
15
|
+
if (permissions.error) {
|
|
16
|
+
console.log(' Error:', permissions.error);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log('\n2. Testing direct cursor position (no capture):');
|
|
20
|
+
for (let i = 0; i < 5; i++) {
|
|
21
|
+
try {
|
|
22
|
+
const position = recorder.getCursorPosition();
|
|
23
|
+
console.log(` Position ${i+1}: (${position.x}, ${position.y}) - ${position.cursorType}`);
|
|
24
|
+
|
|
25
|
+
if (position.scaleFactor) {
|
|
26
|
+
console.log(` Scale: ${position.scaleFactor}x, Display: (${position.displayInfo?.displayX}, ${position.displayInfo?.displayY})`);
|
|
27
|
+
console.log(` Logical: ${position.displayInfo?.logicalWidth}x${position.displayInfo?.logicalHeight}`);
|
|
28
|
+
console.log(` Physical: ${position.displayInfo?.physicalWidth}x${position.displayInfo?.physicalHeight}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(` Error getting position ${i+1}:`, error.message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log('\n3. Testing cursor capture status:');
|
|
38
|
+
const status = recorder.getCursorCaptureStatus();
|
|
39
|
+
console.log(' Is Capturing:', status.isCapturing);
|
|
40
|
+
console.log(' Output File:', status.outputFile);
|
|
41
|
+
console.log(' Display Info:', status.displayInfo);
|
|
42
|
+
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
testCursorPermissions().catch(console.error);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const MacRecorder = require('./index.js');
|
|
2
|
+
|
|
3
|
+
async function debugCursorScaling() {
|
|
4
|
+
console.log('๐ Debugging cursor scaling detection...\n');
|
|
5
|
+
|
|
6
|
+
const recorder = new MacRecorder();
|
|
7
|
+
|
|
8
|
+
// Get all displays
|
|
9
|
+
const displays = await recorder.getDisplays();
|
|
10
|
+
console.log('๐บ Available displays:');
|
|
11
|
+
displays.forEach((display, index) => {
|
|
12
|
+
console.log(` Display ${index}: ID=${display.id}, ${display.resolution} at (${display.x}, ${display.y}), Primary: ${display.isPrimary}`);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
console.log('\n๐ฏ Current cursor position analysis:');
|
|
16
|
+
const position = recorder.getCursorPosition();
|
|
17
|
+
|
|
18
|
+
console.log(` Logical position: (${position.x}, ${position.y})`);
|
|
19
|
+
console.log(` Cursor type: ${position.cursorType}`);
|
|
20
|
+
console.log(` Event type: ${position.eventType}`);
|
|
21
|
+
|
|
22
|
+
if (position.scaleFactor) {
|
|
23
|
+
console.log(` Scale factor detected: ${position.scaleFactor}x`);
|
|
24
|
+
if (position.displayInfo) {
|
|
25
|
+
const info = position.displayInfo;
|
|
26
|
+
console.log(` Display bounds: (${info.displayX}, ${info.displayY})`);
|
|
27
|
+
console.log(` Logical size: ${info.logicalWidth}x${info.logicalHeight}`);
|
|
28
|
+
console.log(` Physical size: ${info.physicalWidth}x${info.physicalHeight}`);
|
|
29
|
+
console.log(` Raw position: (${position.rawX}, ${position.rawY})`);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
console.log(` โ No scale factor detected`);
|
|
33
|
+
console.log(` This could mean:`);
|
|
34
|
+
console.log(` - getDisplayScalingInfo() didn't find the correct display`);
|
|
35
|
+
console.log(` - Display detection logic needs debugging`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Find which display the cursor is on
|
|
39
|
+
console.log('\n๐ Manual display detection:');
|
|
40
|
+
displays.forEach((display, index) => {
|
|
41
|
+
const inX = position.x >= display.x && position.x < display.x + parseInt(display.resolution.split('x')[0]);
|
|
42
|
+
const inY = position.y >= display.y && position.y < display.y + parseInt(display.resolution.split('x')[1]);
|
|
43
|
+
const isInside = inX && inY;
|
|
44
|
+
|
|
45
|
+
console.log(` Display ${index} (${display.resolution}): ${isInside ? 'โ
CURSOR IS HERE' : 'โ'}`);
|
|
46
|
+
console.log(` Bounds: X(${display.x} - ${display.x + parseInt(display.resolution.split('x')[0])}), Y(${display.y} - ${display.y + parseInt(display.resolution.split('x')[1])})`);
|
|
47
|
+
console.log(` Cursor: X(${position.x}), Y(${position.y})`);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
debugCursorScaling().catch(console.error);
|
package/index.js
CHANGED
|
@@ -258,6 +258,9 @@ class MacRecorder extends EventEmitter {
|
|
|
258
258
|
y: targetDisplay.y,
|
|
259
259
|
width: parseInt(targetDisplay.resolution.split("x")[0]),
|
|
260
260
|
height: parseInt(targetDisplay.resolution.split("x")[1]),
|
|
261
|
+
// Add scaling information for cursor coordinate transformation
|
|
262
|
+
logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
|
|
263
|
+
logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
|
|
261
264
|
};
|
|
262
265
|
}
|
|
263
266
|
|
|
@@ -292,6 +295,9 @@ class MacRecorder extends EventEmitter {
|
|
|
292
295
|
y: targetDisplay.y,
|
|
293
296
|
width: parseInt(targetDisplay.resolution.split("x")[0]),
|
|
294
297
|
height: parseInt(targetDisplay.resolution.split("x")[1]),
|
|
298
|
+
// Add scaling information for cursor coordinate transformation
|
|
299
|
+
logicalWidth: parseInt(targetDisplay.resolution.split("x")[0]),
|
|
300
|
+
logicalHeight: parseInt(targetDisplay.resolution.split("x")[1]),
|
|
295
301
|
};
|
|
296
302
|
}
|
|
297
303
|
} catch (error) {
|
|
@@ -696,10 +702,32 @@ class MacRecorder extends EventEmitter {
|
|
|
696
702
|
let coordinateSystem = "global";
|
|
697
703
|
|
|
698
704
|
if (this.cursorDisplayInfo) {
|
|
699
|
-
//
|
|
700
|
-
//
|
|
701
|
-
|
|
702
|
-
|
|
705
|
+
// CRITICAL FIX: Handle DPR scaling for cursor coordinates
|
|
706
|
+
// Get scaling information from native cursor position
|
|
707
|
+
const scaleFactor = position.scaleFactor || 1;
|
|
708
|
+
const displayInfo = position.displayInfo;
|
|
709
|
+
|
|
710
|
+
// Convert logical cursor position to physical (if recording uses physical coordinates)
|
|
711
|
+
let physicalX = position.x;
|
|
712
|
+
let physicalY = position.y;
|
|
713
|
+
|
|
714
|
+
if (scaleFactor > 1.1 && displayInfo) {
|
|
715
|
+
// Convert to display-relative logical, then to physical
|
|
716
|
+
const displayRelativeX = position.x - displayInfo.displayX;
|
|
717
|
+
const displayRelativeY = position.y - displayInfo.displayY;
|
|
718
|
+
|
|
719
|
+
// Scale to physical coordinates
|
|
720
|
+
const physicalRelativeX = displayRelativeX * scaleFactor;
|
|
721
|
+
const physicalRelativeY = displayRelativeY * scaleFactor;
|
|
722
|
+
|
|
723
|
+
// Convert back to global physical
|
|
724
|
+
physicalX = displayInfo.displayX + physicalRelativeX;
|
|
725
|
+
physicalY = displayInfo.displayY + physicalRelativeY;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Offset'leri รงฤฑkar (display veya window) - use physical coordinates
|
|
729
|
+
x = physicalX - this.cursorDisplayInfo.x;
|
|
730
|
+
y = physicalY - this.cursorDisplayInfo.y;
|
|
703
731
|
|
|
704
732
|
if (this.cursorDisplayInfo.windowRelative) {
|
|
705
733
|
// Window-relative koordinatlar
|
package/package.json
CHANGED
package/src/cursor_tracker.mm
CHANGED
|
@@ -20,6 +20,7 @@ static bool g_isFirstWrite = true;
|
|
|
20
20
|
// Forward declaration
|
|
21
21
|
void cursorTimerCallback();
|
|
22
22
|
void writeToFile(NSDictionary *cursorData);
|
|
23
|
+
NSDictionary* getDisplayScalingInfo(CGPoint globalPoint);
|
|
23
24
|
|
|
24
25
|
// Timer helper class
|
|
25
26
|
@interface CursorTimerTarget : NSObject
|
|
@@ -198,7 +199,19 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
198
199
|
return event;
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
CGPoint
|
|
202
|
+
CGPoint rawLocation = CGEventGetLocation(event);
|
|
203
|
+
|
|
204
|
+
// Apply DPR scaling correction for Retina displays
|
|
205
|
+
NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
|
|
206
|
+
CGPoint location = rawLocation;
|
|
207
|
+
|
|
208
|
+
if (scalingInfo) {
|
|
209
|
+
CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
|
|
210
|
+
NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
|
|
211
|
+
|
|
212
|
+
// Keep logical coordinates - no scaling needed here
|
|
213
|
+
location = rawLocation;
|
|
214
|
+
}
|
|
202
215
|
NSDate *currentDate = [NSDate date];
|
|
203
216
|
NSTimeInterval timestamp = [currentDate timeIntervalSinceDate:g_trackingStartTime] * 1000; // milliseconds
|
|
204
217
|
NSTimeInterval unixTimeMs = [currentDate timeIntervalSince1970] * 1000; // unix timestamp in milliseconds
|
|
@@ -254,13 +267,25 @@ void cursorTimerCallback() {
|
|
|
254
267
|
return;
|
|
255
268
|
}
|
|
256
269
|
|
|
257
|
-
//
|
|
270
|
+
// Get cursor position with DPR scaling correction
|
|
258
271
|
CGEventRef event = CGEventCreate(NULL);
|
|
259
|
-
CGPoint
|
|
272
|
+
CGPoint rawLocation = CGEventGetLocation(event);
|
|
260
273
|
if (event) {
|
|
261
274
|
CFRelease(event);
|
|
262
275
|
}
|
|
263
276
|
|
|
277
|
+
// Apply DPR scaling correction for Retina displays
|
|
278
|
+
NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
|
|
279
|
+
CGPoint location = rawLocation;
|
|
280
|
+
|
|
281
|
+
if (scalingInfo) {
|
|
282
|
+
CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
|
|
283
|
+
NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
|
|
284
|
+
|
|
285
|
+
// Keep logical coordinates - no scaling needed here
|
|
286
|
+
location = rawLocation;
|
|
287
|
+
}
|
|
288
|
+
|
|
264
289
|
NSDate *currentDate = [NSDate date];
|
|
265
290
|
NSTimeInterval timestamp = [currentDate timeIntervalSinceDate:g_trackingStartTime] * 1000; // milliseconds
|
|
266
291
|
NSTimeInterval unixTimeMs = [currentDate timeIntervalSince1970] * 1000; // unix timestamp in milliseconds
|
|
@@ -435,19 +460,100 @@ Napi::Value StopCursorTracking(const Napi::CallbackInfo& info) {
|
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
462
|
|
|
463
|
+
// Helper function to get display scaling info for cursor coordinates
|
|
464
|
+
NSDictionary* getDisplayScalingInfo(CGPoint globalPoint) {
|
|
465
|
+
@try {
|
|
466
|
+
// Get all displays
|
|
467
|
+
uint32_t displayCount;
|
|
468
|
+
CGDirectDisplayID displayIDs[32];
|
|
469
|
+
CGGetActiveDisplayList(32, displayIDs, &displayCount);
|
|
470
|
+
|
|
471
|
+
// Find which display contains this point
|
|
472
|
+
for (uint32_t i = 0; i < displayCount; i++) {
|
|
473
|
+
CGDirectDisplayID displayID = displayIDs[i];
|
|
474
|
+
CGRect displayBounds = CGDisplayBounds(displayID);
|
|
475
|
+
|
|
476
|
+
NSLog(@"๐ Display %u: bounds(%.0f,%.0f %.0fx%.0f), cursor(%.0f,%.0f)",
|
|
477
|
+
displayID, displayBounds.origin.x, displayBounds.origin.y,
|
|
478
|
+
displayBounds.size.width, displayBounds.size.height,
|
|
479
|
+
globalPoint.x, globalPoint.y);
|
|
480
|
+
|
|
481
|
+
// CRITICAL FIX: Manual bounds check for better coordinate system compatibility
|
|
482
|
+
BOOL isInBounds = (globalPoint.x >= displayBounds.origin.x &&
|
|
483
|
+
globalPoint.x < displayBounds.origin.x + displayBounds.size.width &&
|
|
484
|
+
globalPoint.y >= displayBounds.origin.y &&
|
|
485
|
+
globalPoint.y < displayBounds.origin.y + displayBounds.size.height);
|
|
486
|
+
|
|
487
|
+
NSLog(@"๐ Manual bounds check: %s", isInBounds ? "INSIDE" : "OUTSIDE");
|
|
488
|
+
|
|
489
|
+
// Check if point is within this display
|
|
490
|
+
if (isInBounds) {
|
|
491
|
+
// Get display scaling info
|
|
492
|
+
CGSize logicalSize = displayBounds.size;
|
|
493
|
+
CGSize physicalSize = CGSizeMake(CGDisplayPixelsWide(displayID), CGDisplayPixelsHigh(displayID));
|
|
494
|
+
|
|
495
|
+
NSLog(@"๐ Scaling info: logical(%.0fx%.0f) physical(%.0fx%.0f)",
|
|
496
|
+
logicalSize.width, logicalSize.height, physicalSize.width, physicalSize.height);
|
|
497
|
+
|
|
498
|
+
CGFloat scaleX = physicalSize.width / logicalSize.width;
|
|
499
|
+
CGFloat scaleY = physicalSize.height / logicalSize.height;
|
|
500
|
+
CGFloat scaleFactor = MAX(scaleX, scaleY);
|
|
501
|
+
|
|
502
|
+
NSLog(@"๐ Scale factors: X=%.2f, Y=%.2f, Final=%.2f", scaleX, scaleY, scaleFactor);
|
|
503
|
+
|
|
504
|
+
return @{
|
|
505
|
+
@"displayID": @(displayID),
|
|
506
|
+
@"logicalSize": [NSValue valueWithSize:NSMakeSize(logicalSize.width, logicalSize.height)],
|
|
507
|
+
@"physicalSize": [NSValue valueWithSize:NSMakeSize(physicalSize.width, physicalSize.height)],
|
|
508
|
+
@"scaleFactor": @(scaleFactor),
|
|
509
|
+
@"displayBounds": [NSValue valueWithRect:NSMakeRect(displayBounds.origin.x, displayBounds.origin.y, displayBounds.size.width, displayBounds.size.height)]
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Fallback to main display
|
|
515
|
+
CGDirectDisplayID mainDisplay = CGMainDisplayID();
|
|
516
|
+
CGRect displayBounds = CGDisplayBounds(mainDisplay);
|
|
517
|
+
CGSize logicalSize = displayBounds.size;
|
|
518
|
+
CGSize physicalSize = CGSizeMake(CGDisplayPixelsWide(mainDisplay), CGDisplayPixelsHigh(mainDisplay));
|
|
519
|
+
|
|
520
|
+
return @{
|
|
521
|
+
@"displayID": @(mainDisplay),
|
|
522
|
+
@"logicalSize": [NSValue valueWithSize:NSMakeSize(logicalSize.width, logicalSize.height)],
|
|
523
|
+
@"physicalSize": [NSValue valueWithSize:NSMakeSize(physicalSize.width, physicalSize.height)],
|
|
524
|
+
@"scaleFactor": @(MAX(physicalSize.width / logicalSize.width, physicalSize.height / logicalSize.height)),
|
|
525
|
+
@"displayBounds": [NSValue valueWithRect:NSMakeRect(displayBounds.origin.x, displayBounds.origin.y, displayBounds.size.width, displayBounds.size.height)]
|
|
526
|
+
};
|
|
527
|
+
} @catch (NSException *exception) {
|
|
528
|
+
return nil;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
438
532
|
// NAPI Function: Get Current Cursor Position
|
|
439
533
|
Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
|
|
440
534
|
Napi::Env env = info.Env();
|
|
441
535
|
|
|
442
536
|
@try {
|
|
443
|
-
//
|
|
444
|
-
// CGEventGetLocation kullanarak direkt global koordinat al - daha doฤru
|
|
537
|
+
// Get raw cursor position (may be scaled on Retina displays)
|
|
445
538
|
CGEventRef event = CGEventCreate(NULL);
|
|
446
|
-
CGPoint
|
|
539
|
+
CGPoint rawLocation = CGEventGetLocation(event);
|
|
447
540
|
if (event) {
|
|
448
541
|
CFRelease(event);
|
|
449
542
|
}
|
|
450
543
|
|
|
544
|
+
// Get display scaling information
|
|
545
|
+
NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
|
|
546
|
+
CGPoint logicalLocation = rawLocation;
|
|
547
|
+
|
|
548
|
+
if (scalingInfo) {
|
|
549
|
+
CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
|
|
550
|
+
NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
|
|
551
|
+
|
|
552
|
+
// CGEventGetLocation returns LOGICAL coordinates (correct for JS layer)
|
|
553
|
+
// Keep logical coordinates - transformation happens in JS layer
|
|
554
|
+
logicalLocation = rawLocation;
|
|
555
|
+
}
|
|
556
|
+
|
|
451
557
|
NSString *cursorType = getCursorType();
|
|
452
558
|
|
|
453
559
|
// Mouse button state'ini kontrol et
|
|
@@ -479,11 +585,34 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
|
|
|
479
585
|
g_rightMouseDown = currentRightMouseDown;
|
|
480
586
|
|
|
481
587
|
Napi::Object result = Napi::Object::New(env);
|
|
482
|
-
result.Set("x", Napi::Number::New(env, (int)
|
|
483
|
-
result.Set("y", Napi::Number::New(env, (int)
|
|
588
|
+
result.Set("x", Napi::Number::New(env, (int)logicalLocation.x));
|
|
589
|
+
result.Set("y", Napi::Number::New(env, (int)logicalLocation.y));
|
|
484
590
|
result.Set("cursorType", Napi::String::New(env, [cursorType UTF8String]));
|
|
485
591
|
result.Set("eventType", Napi::String::New(env, [eventType UTF8String]));
|
|
486
592
|
|
|
593
|
+
// Add scaling info for coordinate transformation
|
|
594
|
+
if (scalingInfo) {
|
|
595
|
+
CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
|
|
596
|
+
NSSize logicalSize = [[scalingInfo objectForKey:@"logicalSize"] sizeValue];
|
|
597
|
+
NSSize physicalSize = [[scalingInfo objectForKey:@"physicalSize"] sizeValue];
|
|
598
|
+
NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
|
|
599
|
+
|
|
600
|
+
result.Set("scaleFactor", Napi::Number::New(env, scaleFactor));
|
|
601
|
+
result.Set("rawX", Napi::Number::New(env, (int)rawLocation.x));
|
|
602
|
+
result.Set("rawY", Napi::Number::New(env, (int)rawLocation.y));
|
|
603
|
+
|
|
604
|
+
// Add display dimension info for JS coordinate transformation
|
|
605
|
+
Napi::Object displayInfo = Napi::Object::New(env);
|
|
606
|
+
displayInfo.Set("logicalWidth", Napi::Number::New(env, logicalSize.width));
|
|
607
|
+
displayInfo.Set("logicalHeight", Napi::Number::New(env, logicalSize.height));
|
|
608
|
+
displayInfo.Set("physicalWidth", Napi::Number::New(env, physicalSize.width));
|
|
609
|
+
displayInfo.Set("physicalHeight", Napi::Number::New(env, physicalSize.height));
|
|
610
|
+
displayInfo.Set("displayX", Napi::Number::New(env, displayBounds.origin.x));
|
|
611
|
+
displayInfo.Set("displayY", Napi::Number::New(env, displayBounds.origin.y));
|
|
612
|
+
|
|
613
|
+
result.Set("displayInfo", displayInfo);
|
|
614
|
+
}
|
|
615
|
+
|
|
487
616
|
return result;
|
|
488
617
|
|
|
489
618
|
} @catch (NSException *exception) {
|