node-mac-recorder 2.22.24 → 2.22.33
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/MultiWindowRecorder.js +1 -0
- package/binding.gyp +0 -1
- package/electron-safe-binding.gyp +0 -1
- package/electron-safe-index.js +104 -196
- package/index.js +369 -9
- package/package.json +4 -2
- package/scripts/cursor-type-live.js +32 -0
- package/scripts/cursor-types-15s-test.js +122 -0
- package/src/avfoundation_recorder.mm +1 -2
- package/src/cursor_tracker.mm +385 -137
- package/src/electron_safe/cursor_tracker_electron.mm +36 -38
- package/src/electron_safe/window_selector_electron.mm +5 -2
- package/src/screen_capture_kit.mm +11 -17
- package/src/window_selector.mm +5 -2
- package/lib/cursorCapture/displayInfo.js +0 -110
- package/lib/cursorCapture/polling.js +0 -585
- package/src/text_input_ax_snapshot.h +0 -3
- package/src/text_input_ax_snapshot.mm +0 -161
package/src/cursor_tracker.mm
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
#import <Accessibility/Accessibility.h>
|
|
8
8
|
#import <dispatch/dispatch.h>
|
|
9
9
|
#import "logging.h"
|
|
10
|
-
#import "text_input_ax_snapshot.h"
|
|
11
10
|
#include <vector>
|
|
11
|
+
#include <cstring>
|
|
12
12
|
#include <math.h>
|
|
13
13
|
|
|
14
14
|
#ifndef kAXHitTestParameterizedAttribute
|
|
@@ -310,6 +310,180 @@ static NSCursor* CursorFromSelector(SEL selector) {
|
|
|
310
310
|
return func([NSCursor class], selector);
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
static BOOL CursorEqualsFactoryNamed(NSCursor *cursor, NSString *factoryMethodName) {
|
|
314
|
+
if (!cursor || !factoryMethodName || [factoryMethodName length] == 0) {
|
|
315
|
+
return NO;
|
|
316
|
+
}
|
|
317
|
+
SEL sel = NSSelectorFromString(factoryMethodName);
|
|
318
|
+
NSCursor *ref = CursorFromSelector(sel);
|
|
319
|
+
return ref != nil && cursor == ref;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
static uint64_t CursorImagePixelHash(NSImage *image) {
|
|
323
|
+
if (!image) {
|
|
324
|
+
return 0;
|
|
325
|
+
}
|
|
326
|
+
NSRect imageRect = NSMakeRect(0, 0, [image size].width, [image size].height);
|
|
327
|
+
CGImageRef cgImage = [image CGImageForProposedRect:&imageRect context:nil hints:nil];
|
|
328
|
+
if (!cgImage) {
|
|
329
|
+
for (NSImageRep *rep in [image representations]) {
|
|
330
|
+
if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
|
|
331
|
+
cgImage = [(NSBitmapImageRep *)rep CGImage];
|
|
332
|
+
if (cgImage) {
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!cgImage) {
|
|
339
|
+
return 0;
|
|
340
|
+
}
|
|
341
|
+
size_t width = CGImageGetWidth(cgImage);
|
|
342
|
+
size_t height = CGImageGetHeight(cgImage);
|
|
343
|
+
if (width == 0 || height == 0) {
|
|
344
|
+
return 0;
|
|
345
|
+
}
|
|
346
|
+
size_t bytesPerPixel = 4;
|
|
347
|
+
size_t bytesPerRow = width * bytesPerPixel;
|
|
348
|
+
size_t bufferSize = bytesPerRow * height;
|
|
349
|
+
if (bufferSize == 0) {
|
|
350
|
+
return 0;
|
|
351
|
+
}
|
|
352
|
+
std::vector<unsigned char> buffer(bufferSize);
|
|
353
|
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
354
|
+
if (!colorSpace) {
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | (CGBitmapInfo)kCGImageAlphaPremultipliedLast;
|
|
358
|
+
CGContextRef context = CGBitmapContextCreate(buffer.data(),
|
|
359
|
+
width,
|
|
360
|
+
height,
|
|
361
|
+
8,
|
|
362
|
+
bytesPerRow,
|
|
363
|
+
colorSpace,
|
|
364
|
+
bitmapInfo);
|
|
365
|
+
CGColorSpaceRelease(colorSpace);
|
|
366
|
+
if (!context) {
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
|
|
370
|
+
CGContextRelease(context);
|
|
371
|
+
return FNV1AHash(buffer.data(), buffer.size());
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static uint64_t CursorImagePixelHashNormalized32(NSImage *image) {
|
|
375
|
+
if (!image) {
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
const int dim = 32;
|
|
379
|
+
size_t bytesPerPixel = 4;
|
|
380
|
+
size_t bytesPerRow = (size_t)dim * bytesPerPixel;
|
|
381
|
+
std::vector<unsigned char> buffer(bytesPerRow * (size_t)dim);
|
|
382
|
+
memset(buffer.data(), 0, buffer.size());
|
|
383
|
+
|
|
384
|
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
385
|
+
if (!colorSpace) {
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
CGContextRef ctx = CGBitmapContextCreate(buffer.data(),
|
|
389
|
+
(size_t)dim,
|
|
390
|
+
(size_t)dim,
|
|
391
|
+
8,
|
|
392
|
+
bytesPerRow,
|
|
393
|
+
colorSpace,
|
|
394
|
+
kCGBitmapByteOrder32Little | (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
|
|
395
|
+
CGColorSpaceRelease(colorSpace);
|
|
396
|
+
if (!ctx) {
|
|
397
|
+
return 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
CGContextClearRect(ctx, CGRectMake(0, 0, dim, dim));
|
|
401
|
+
NSGraphicsContext *nsgc = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
|
|
402
|
+
[NSGraphicsContext saveGraphicsState];
|
|
403
|
+
[NSGraphicsContext setCurrentContext:nsgc];
|
|
404
|
+
|
|
405
|
+
NSSize sz = [image size];
|
|
406
|
+
if (sz.width <= 0 || sz.height <= 0) {
|
|
407
|
+
[NSGraphicsContext restoreGraphicsState];
|
|
408
|
+
CGContextRelease(ctx);
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
CGFloat scale = MIN((CGFloat)dim / sz.width, (CGFloat)dim / sz.height);
|
|
412
|
+
CGFloat rw = sz.width * scale;
|
|
413
|
+
CGFloat rh = sz.height * scale;
|
|
414
|
+
CGFloat ox = ((CGFloat)dim - rw) / 2.0;
|
|
415
|
+
CGFloat oy = ((CGFloat)dim - rh) / 2.0;
|
|
416
|
+
[image drawInRect:NSMakeRect(ox, oy, rw, rh)
|
|
417
|
+
fromRect:NSZeroRect
|
|
418
|
+
operation:NSCompositingOperationSourceOver
|
|
419
|
+
fraction:1.0
|
|
420
|
+
respectFlipped:YES
|
|
421
|
+
hints:nil];
|
|
422
|
+
|
|
423
|
+
[NSGraphicsContext restoreGraphicsState];
|
|
424
|
+
CGContextRelease(ctx);
|
|
425
|
+
return FNV1AHash(buffer.data(), buffer.size());
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
static NSString* DiagonalResizeTypeByVisualMatch(NSCursor *cursor) {
|
|
429
|
+
if (!cursor) {
|
|
430
|
+
return nil;
|
|
431
|
+
}
|
|
432
|
+
if (CursorEqualsFactoryNamed(cursor, @"resizeNorthWestSouthEastCursor")) {
|
|
433
|
+
return @"nwse-resize";
|
|
434
|
+
}
|
|
435
|
+
if (CursorEqualsFactoryNamed(cursor, @"resizeNorthEastSouthWestCursor")) {
|
|
436
|
+
return @"nesw-resize";
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
NSImage *curImg = [cursor image];
|
|
440
|
+
if (!curImg) {
|
|
441
|
+
return nil;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
NSData *curTIFF = [curImg TIFFRepresentation];
|
|
445
|
+
uint64_t curRaw = CursorImagePixelHash(curImg);
|
|
446
|
+
uint64_t curNorm = CursorImagePixelHashNormalized32(curImg);
|
|
447
|
+
|
|
448
|
+
struct DiagonalFactoryEntry {
|
|
449
|
+
const char *selectorName;
|
|
450
|
+
const char *type;
|
|
451
|
+
};
|
|
452
|
+
static const DiagonalFactoryEntry kDiagonalFactories[] = {
|
|
453
|
+
{ "resizeNorthWestSouthEastCursor", "nwse-resize" },
|
|
454
|
+
{ "resizeNorthEastSouthWestCursor", "nesw-resize" },
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
for (size_t i = 0; i < sizeof(kDiagonalFactories) / sizeof(kDiagonalFactories[0]); i++) {
|
|
458
|
+
NSString *selStr = [NSString stringWithUTF8String:kDiagonalFactories[i].selectorName];
|
|
459
|
+
SEL sel = NSSelectorFromString(selStr);
|
|
460
|
+
if (![NSCursor respondsToSelector:sel]) {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
NSCursor *ref = CursorFromSelector(sel);
|
|
464
|
+
if (!ref) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
NSImage *refImg = [ref image];
|
|
468
|
+
if (!refImg) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
NSData *refTIFF = [refImg TIFFRepresentation];
|
|
472
|
+
if (curTIFF && refTIFF && [curTIFF isEqualToData:refTIFF]) {
|
|
473
|
+
return [NSString stringWithUTF8String:kDiagonalFactories[i].type];
|
|
474
|
+
}
|
|
475
|
+
uint64_t refRaw = CursorImagePixelHash(refImg);
|
|
476
|
+
if (curRaw != 0 && curRaw == refRaw) {
|
|
477
|
+
return [NSString stringWithUTF8String:kDiagonalFactories[i].type];
|
|
478
|
+
}
|
|
479
|
+
uint64_t refNorm = CursorImagePixelHashNormalized32(refImg);
|
|
480
|
+
if (curNorm != 0 && curNorm == refNorm) {
|
|
481
|
+
return [NSString stringWithUTF8String:kDiagonalFactories[i].type];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return nil;
|
|
485
|
+
}
|
|
486
|
+
|
|
313
487
|
static void AddStandardCursorFingerprint(NSCursor *cursor, NSString *cursorType) {
|
|
314
488
|
if (!cursor || !cursorType) {
|
|
315
489
|
return;
|
|
@@ -360,7 +534,7 @@ static void InitializeCursorFingerprintMap(void) {
|
|
|
360
534
|
AddCursorIfAvailable(@selector(dragCopyCursor), @"copy");
|
|
361
535
|
AddCursorIfAvailable(@selector(dragLinkCursor), @"alias");
|
|
362
536
|
AddCursorIfAvailable(@selector(resizeLeftRightCursor), @"col-resize");
|
|
363
|
-
AddCursorIfAvailable(@selector(resizeUpDownCursor), @"
|
|
537
|
+
AddCursorIfAvailable(@selector(resizeUpDownCursor), @"ns-resize");
|
|
364
538
|
AddCursorIfAvailableByName(@"resizeLeftCursor", @"col-resize");
|
|
365
539
|
AddCursorIfAvailableByName(@"resizeRightCursor", @"col-resize");
|
|
366
540
|
AddCursorIfAvailableByName(@"resizeUpCursor", @"ns-resize");
|
|
@@ -503,21 +677,26 @@ static BOOL ShouldEmitCursorEvent(CGPoint location, NSString *cursorType, NSStri
|
|
|
503
677
|
BOOL moved = fabs(location.x - g_lastCursorLocation.x) >= movementThreshold ||
|
|
504
678
|
fabs(location.y - g_lastCursorLocation.y) >= movementThreshold;
|
|
505
679
|
BOOL eventChanged = !StringsEqual(eventType, g_lastCursorEventType);
|
|
506
|
-
BOOL
|
|
680
|
+
BOOL isDragEvent = StringsEqual(eventType, @"drag") || StringsEqual(eventType, @"rightdrag");
|
|
681
|
+
BOOL isMoveOnly = StringsEqual(eventType, @"move");
|
|
507
682
|
BOOL isClickEvent = StringsEqual(eventType, @"mousedown") ||
|
|
508
683
|
StringsEqual(eventType, @"mouseup") ||
|
|
509
684
|
StringsEqual(eventType, @"rightmousedown") ||
|
|
510
685
|
StringsEqual(eventType, @"rightmouseup");
|
|
511
686
|
|
|
512
|
-
if (
|
|
513
|
-
return
|
|
687
|
+
if (isDragEvent) {
|
|
688
|
+
return YES;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (isMoveOnly) {
|
|
692
|
+
BOOL cursorChanged = !StringsEqual(cursorType, g_lastCursorType);
|
|
693
|
+
return moved || cursorChanged;
|
|
514
694
|
}
|
|
515
695
|
|
|
516
696
|
if (isClickEvent) {
|
|
517
697
|
return eventChanged || moved;
|
|
518
698
|
}
|
|
519
699
|
|
|
520
|
-
// Fallback: only emit when something actually changed
|
|
521
700
|
BOOL cursorChanged = !StringsEqual(cursorType, g_lastCursorType);
|
|
522
701
|
return moved || cursorChanged || eventChanged;
|
|
523
702
|
}
|
|
@@ -671,7 +850,10 @@ static NSString* CursorTypeForWindowBorder(AXUIElementRef element, CGPoint curso
|
|
|
671
850
|
CFRelease(positionValue);
|
|
672
851
|
CFRelease(sizeValue);
|
|
673
852
|
|
|
674
|
-
|
|
853
|
+
// Köşe: köşe noktasına uzaklık (kutu sınırında col ↔ diagonal sıçramasını azaltır).
|
|
854
|
+
const CGFloat cornerRadius = 9.0;
|
|
855
|
+
const CGFloat edgeInset = 12.0;
|
|
856
|
+
|
|
675
857
|
CGFloat x = cursorPos.x - windowOrigin.x;
|
|
676
858
|
CGFloat y = cursorPos.y - windowOrigin.y;
|
|
677
859
|
CGFloat w = windowSize.width;
|
|
@@ -681,17 +863,44 @@ static NSString* CursorTypeForWindowBorder(AXUIElementRef element, CGPoint curso
|
|
|
681
863
|
return nil;
|
|
682
864
|
}
|
|
683
865
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
866
|
+
if (w < cornerRadius * 2.1 || h < cornerRadius * 2.1) {
|
|
867
|
+
CGFloat edge = edgeInset;
|
|
868
|
+
BOOL nearLeft = (x >= 0 && x <= edge);
|
|
869
|
+
BOOL nearRight = (x >= w - edge && x <= w);
|
|
870
|
+
BOOL nearTop = (y >= 0 && y <= edge);
|
|
871
|
+
BOOL nearBottom = (y >= h - edge && y <= h);
|
|
872
|
+
if ((nearLeft && nearTop) || (nearRight && nearBottom)) {
|
|
873
|
+
return @"nwse-resize";
|
|
874
|
+
}
|
|
875
|
+
if ((nearRight && nearTop) || (nearLeft && nearBottom)) {
|
|
876
|
+
return @"nesw-resize";
|
|
877
|
+
}
|
|
878
|
+
if (nearLeft || nearRight) {
|
|
879
|
+
return @"col-resize";
|
|
880
|
+
}
|
|
881
|
+
if (nearTop || nearBottom) {
|
|
882
|
+
return @"ns-resize";
|
|
883
|
+
}
|
|
884
|
+
return nil;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
CGFloat dBottomLeft = hypot(x, y);
|
|
888
|
+
CGFloat dTopRight = hypot(w - x, h - y);
|
|
889
|
+
CGFloat dBottomRight = hypot(w - x, y);
|
|
890
|
+
CGFloat dTopLeft = hypot(x, h - y);
|
|
688
891
|
|
|
689
|
-
if (
|
|
892
|
+
if (dBottomLeft <= cornerRadius || dTopRight <= cornerRadius) {
|
|
690
893
|
return @"nwse-resize";
|
|
691
894
|
}
|
|
692
|
-
if (
|
|
895
|
+
if (dBottomRight <= cornerRadius || dTopLeft <= cornerRadius) {
|
|
693
896
|
return @"nesw-resize";
|
|
694
897
|
}
|
|
898
|
+
|
|
899
|
+
BOOL nearLeft = (x >= 0 && x <= edgeInset);
|
|
900
|
+
BOOL nearRight = (x >= w - edgeInset && x <= w);
|
|
901
|
+
BOOL nearTop = (y >= 0 && y <= edgeInset);
|
|
902
|
+
BOOL nearBottom = (y >= h - edgeInset && y <= h);
|
|
903
|
+
|
|
695
904
|
if (nearLeft || nearRight) {
|
|
696
905
|
return @"col-resize";
|
|
697
906
|
}
|
|
@@ -702,6 +911,42 @@ static NSString* CursorTypeForWindowBorder(AXUIElementRef element, CGPoint curso
|
|
|
702
911
|
return nil;
|
|
703
912
|
}
|
|
704
913
|
|
|
914
|
+
static NSString* ResizeTypeFromWindowUnderCursor(CGPoint cursorPos) {
|
|
915
|
+
@autoreleasepool {
|
|
916
|
+
AXUIElementRef systemWide = AXUIElementCreateSystemWide();
|
|
917
|
+
if (!systemWide) {
|
|
918
|
+
return nil;
|
|
919
|
+
}
|
|
920
|
+
AXUIElementRef hit = NULL;
|
|
921
|
+
AXError err = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &hit);
|
|
922
|
+
CFRelease(systemWide);
|
|
923
|
+
if (err != kAXErrorSuccess || !hit) {
|
|
924
|
+
if (hit) {
|
|
925
|
+
CFRelease(hit);
|
|
926
|
+
}
|
|
927
|
+
return nil;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
AXUIElementRef node = hit;
|
|
931
|
+
for (int depth = 0; depth < 24 && node; depth++) {
|
|
932
|
+
NSString *role = CopyAttributeString(node, kAXRoleAttribute);
|
|
933
|
+
if ([role isEqualToString:@"AXWindow"]) {
|
|
934
|
+
NSString *borderType = CursorTypeForWindowBorder(node, cursorPos);
|
|
935
|
+
CFRelease(node);
|
|
936
|
+
return borderType;
|
|
937
|
+
}
|
|
938
|
+
AXUIElementRef parent = NULL;
|
|
939
|
+
AXError perr = AXUIElementCopyAttributeValue(node, kAXParentAttribute, (CFTypeRef *)&parent);
|
|
940
|
+
CFRelease(node);
|
|
941
|
+
node = NULL;
|
|
942
|
+
if (perr == kAXErrorSuccess && parent) {
|
|
943
|
+
node = parent;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return nil;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
705
950
|
static NSString* CursorTypeFromAccessibilityElement(AXUIElementRef element, CGPoint cursorPos) {
|
|
706
951
|
if (!element) {
|
|
707
952
|
return nil;
|
|
@@ -815,10 +1060,6 @@ static bool g_leftMouseDown = false;
|
|
|
815
1060
|
static bool g_rightMouseDown = false;
|
|
816
1061
|
static NSString *g_lastEventType = @"move";
|
|
817
1062
|
|
|
818
|
-
// Text input (keyboard) tracking state
|
|
819
|
-
static NSTimeInterval g_lastTextInputEmitTime = 0; // Throttle: son textInput event zamanı
|
|
820
|
-
static const NSTimeInterval TEXT_INPUT_THROTTLE_MS = 50; // Min 50ms aralık (20 FPS)
|
|
821
|
-
|
|
822
1063
|
// Accessibility tabanlı cursor tip tespiti
|
|
823
1064
|
static NSString* detectCursorTypeUsingAccessibility(CGPoint cursorPos) {
|
|
824
1065
|
@autoreleasepool {
|
|
@@ -942,6 +1183,14 @@ static NSString* cursorTypeFromCursorName(NSString *value) {
|
|
|
942
1183
|
return @"zoom-in";
|
|
943
1184
|
}
|
|
944
1185
|
|
|
1186
|
+
// CGS / tema kısa diagonal tokenları (nwse / nesw)
|
|
1187
|
+
if ([normalized containsString:@"nesw"]) {
|
|
1188
|
+
return @"nesw-resize";
|
|
1189
|
+
}
|
|
1190
|
+
if ([normalized containsString:@"nwse"]) {
|
|
1191
|
+
return @"nwse-resize";
|
|
1192
|
+
}
|
|
1193
|
+
|
|
945
1194
|
// All-scroll pattern (move in all directions)
|
|
946
1195
|
if ([normalized containsString:@"all-scroll"] ||
|
|
947
1196
|
[normalized containsString:@"allscroll"] ||
|
|
@@ -1388,6 +1637,13 @@ static NSString* cursorTypeFromImageSignature(NSImage *image, NSPoint hotspot, N
|
|
|
1388
1637
|
CGFloat relativeX = width > 0 ? hotspot.x / width : 0;
|
|
1389
1638
|
CGFloat relativeY = height > 0 ? hotspot.y / height : 0;
|
|
1390
1639
|
|
|
1640
|
+
if (cursor) {
|
|
1641
|
+
NSString *diag = DiagonalResizeTypeByVisualMatch(cursor);
|
|
1642
|
+
if (diag) {
|
|
1643
|
+
return diag;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1391
1647
|
// Tolerance for floating point comparison
|
|
1392
1648
|
CGFloat tolerance = 0.05;
|
|
1393
1649
|
CGFloat tightTolerance = 0.02; // For precise hotspot matching
|
|
@@ -1427,29 +1683,35 @@ static NSString* cursorTypeFromImageSignature(NSImage *image, NSPoint hotspot, N
|
|
|
1427
1683
|
// Distinguished by pointer equality
|
|
1428
1684
|
if (approx(width, 32) && approx(height, 32) && approx(relativeY, 0.531)) {
|
|
1429
1685
|
if (cursor) {
|
|
1430
|
-
if (
|
|
1686
|
+
if ([NSCursor respondsToSelector:@selector(closedHandCursor)] &&
|
|
1687
|
+
cursor == [NSCursor closedHandCursor]) {
|
|
1431
1688
|
return @"grabbing";
|
|
1432
1689
|
}
|
|
1433
|
-
if (
|
|
1690
|
+
if ([NSCursor respondsToSelector:@selector(openHandCursor)] &&
|
|
1691
|
+
cursor == [NSCursor openHandCursor]) {
|
|
1434
1692
|
return @"grab";
|
|
1435
1693
|
}
|
|
1436
1694
|
}
|
|
1437
1695
|
return @"grab"; // Default to grab if can't distinguish
|
|
1438
1696
|
}
|
|
1439
1697
|
|
|
1440
|
-
// 24x24
|
|
1441
|
-
// Distinguished by precise hotspot position
|
|
1698
|
+
// 24x24: köşe diagonal resize ile crosshair/move aynı boyutta — önce diagonal fabrika
|
|
1442
1699
|
if (approx(width, 24) && approx(height, 24)) {
|
|
1700
|
+
if (cursor) {
|
|
1701
|
+
NSString *diag = DiagonalResizeTypeByVisualMatch(cursor);
|
|
1702
|
+
if (diag) {
|
|
1703
|
+
return diag;
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1443
1706
|
// crosshair: hotspot rel=(0.458, 0.458)
|
|
1444
1707
|
if (approxTight(relativeX, 0.458) && approxTight(relativeY, 0.458)) {
|
|
1445
1708
|
return @"crosshair";
|
|
1446
1709
|
}
|
|
1447
1710
|
// move/all-scroll: hotspot rel=(0.5, 0.5)
|
|
1448
1711
|
if (approxTight(relativeX, 0.5) && approxTight(relativeY, 0.5)) {
|
|
1449
|
-
return @"move";
|
|
1712
|
+
return @"move";
|
|
1450
1713
|
}
|
|
1451
|
-
|
|
1452
|
-
return @"crosshair";
|
|
1714
|
+
return nil;
|
|
1453
1715
|
}
|
|
1454
1716
|
|
|
1455
1717
|
// help/cell: 18x18, ratio=1.0, hotspot rel=(0.5, 0.5)
|
|
@@ -1510,7 +1772,29 @@ static NSString* cursorTypeFromImageSignature(NSImage *image, NSPoint hotspot, N
|
|
|
1510
1772
|
|
|
1511
1773
|
// ne-resize/nw-resize/se-resize/sw-resize/nesw-resize/nwse-resize: 22x22, ratio=1.0, hotspot rel=(0.5, 0.5)
|
|
1512
1774
|
if (approx(width, 22) && approx(height, 22)) {
|
|
1513
|
-
|
|
1775
|
+
if (cursor) {
|
|
1776
|
+
NSString *diag = DiagonalResizeTypeByVisualMatch(cursor);
|
|
1777
|
+
if (diag) {
|
|
1778
|
+
return diag;
|
|
1779
|
+
}
|
|
1780
|
+
if ([NSCursor respondsToSelector:@selector(resizeUpCursor)] &&
|
|
1781
|
+
cursor == [NSCursor resizeUpCursor]) {
|
|
1782
|
+
return @"n-resize";
|
|
1783
|
+
}
|
|
1784
|
+
if ([NSCursor respondsToSelector:@selector(resizeDownCursor)] &&
|
|
1785
|
+
cursor == [NSCursor resizeDownCursor]) {
|
|
1786
|
+
return @"s-resize";
|
|
1787
|
+
}
|
|
1788
|
+
if ([NSCursor respondsToSelector:@selector(resizeLeftCursor)] &&
|
|
1789
|
+
cursor == [NSCursor resizeLeftCursor]) {
|
|
1790
|
+
return @"w-resize";
|
|
1791
|
+
}
|
|
1792
|
+
if ([NSCursor respondsToSelector:@selector(resizeRightCursor)] &&
|
|
1793
|
+
cursor == [NSCursor resizeRightCursor]) {
|
|
1794
|
+
return @"e-resize";
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return nil;
|
|
1514
1798
|
}
|
|
1515
1799
|
|
|
1516
1800
|
// zoom-in/zoom-out: 28x26, ratio=1.077, hotspot rel=(0.428, 0.423)
|
|
@@ -1532,10 +1816,12 @@ static NSString* cursorTypeFromImageSignature(NSImage *image, NSPoint hotspot, N
|
|
|
1532
1816
|
if (approxTight(relativeX, 0.161) && approxTight(relativeY, 0.1)) {
|
|
1533
1817
|
return @"default";
|
|
1534
1818
|
}
|
|
1535
|
-
//
|
|
1819
|
+
// Ortak 28x40 ok/beachball hotspot bandı: spinner ile karışmasın diye önce ok
|
|
1536
1820
|
if (approxTight(relativeX, 0.179) && approxTight(relativeY, 0.125)) {
|
|
1537
|
-
// Try pointer equality for standard cursors
|
|
1538
1821
|
if (cursor) {
|
|
1822
|
+
if (cursor == [NSCursor arrowCursor]) {
|
|
1823
|
+
return @"default";
|
|
1824
|
+
}
|
|
1539
1825
|
if (cursor == [NSCursor contextualMenuCursor]) {
|
|
1540
1826
|
return @"context-menu";
|
|
1541
1827
|
}
|
|
@@ -1546,10 +1832,7 @@ static NSString* cursorTypeFromImageSignature(NSImage *image, NSPoint hotspot, N
|
|
|
1546
1832
|
return @"not-allowed";
|
|
1547
1833
|
}
|
|
1548
1834
|
}
|
|
1549
|
-
|
|
1550
|
-
// Return "progress" as default for this hotspot pattern (better than "default")
|
|
1551
|
-
// Let cursor name detection in caller distinguish between progress/wait
|
|
1552
|
-
return @"progress";
|
|
1835
|
+
return @"default";
|
|
1553
1836
|
}
|
|
1554
1837
|
return @"default";
|
|
1555
1838
|
}
|
|
@@ -1576,13 +1859,16 @@ static NSString* cursorTypeFromNSCursor(NSCursor *cursor) {
|
|
|
1576
1859
|
if (cursor == [NSCursor pointingHandCursor]) {
|
|
1577
1860
|
return @"pointer";
|
|
1578
1861
|
}
|
|
1579
|
-
if (
|
|
1862
|
+
if ([NSCursor respondsToSelector:@selector(crosshairCursor)] &&
|
|
1863
|
+
cursor == [NSCursor crosshairCursor]) {
|
|
1580
1864
|
return @"crosshair";
|
|
1581
1865
|
}
|
|
1582
|
-
if (
|
|
1866
|
+
if ([NSCursor respondsToSelector:@selector(openHandCursor)] &&
|
|
1867
|
+
cursor == [NSCursor openHandCursor]) {
|
|
1583
1868
|
return @"grab";
|
|
1584
1869
|
}
|
|
1585
|
-
if (
|
|
1870
|
+
if ([NSCursor respondsToSelector:@selector(closedHandCursor)] &&
|
|
1871
|
+
cursor == [NSCursor closedHandCursor]) {
|
|
1586
1872
|
return @"grabbing";
|
|
1587
1873
|
}
|
|
1588
1874
|
if (cursor == [NSCursor operationNotAllowedCursor]) {
|
|
@@ -1598,14 +1884,42 @@ static NSString* cursorTypeFromNSCursor(NSCursor *cursor) {
|
|
|
1598
1884
|
return @"context-menu";
|
|
1599
1885
|
}
|
|
1600
1886
|
|
|
1601
|
-
|
|
1602
|
-
if (
|
|
1603
|
-
|
|
1887
|
+
NSString *diagonalVisual = DiagonalResizeTypeByVisualMatch(cursor);
|
|
1888
|
+
if (diagonalVisual) {
|
|
1889
|
+
return diagonalVisual;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
if ([NSCursor respondsToSelector:@selector(resizeLeftCursor)] &&
|
|
1893
|
+
cursor == [NSCursor resizeLeftCursor]) {
|
|
1894
|
+
return @"col-resize";
|
|
1895
|
+
}
|
|
1896
|
+
if ([NSCursor respondsToSelector:@selector(resizeRightCursor)] &&
|
|
1897
|
+
cursor == [NSCursor resizeRightCursor]) {
|
|
1898
|
+
return @"col-resize";
|
|
1899
|
+
}
|
|
1900
|
+
if ([NSCursor respondsToSelector:@selector(resizeUpCursor)] &&
|
|
1901
|
+
cursor == [NSCursor resizeUpCursor]) {
|
|
1902
|
+
return @"n-resize";
|
|
1903
|
+
}
|
|
1904
|
+
if ([NSCursor respondsToSelector:@selector(resizeDownCursor)] &&
|
|
1905
|
+
cursor == [NSCursor resizeDownCursor]) {
|
|
1906
|
+
return @"s-resize";
|
|
1907
|
+
}
|
|
1908
|
+
if ([NSCursor respondsToSelector:@selector(resizeLeftRightCursor)] &&
|
|
1909
|
+
cursor == [NSCursor resizeLeftRightCursor]) {
|
|
1910
|
+
return @"col-resize";
|
|
1911
|
+
}
|
|
1912
|
+
if ([NSCursor respondsToSelector:@selector(resizeUpDownCursor)] &&
|
|
1913
|
+
cursor == [NSCursor resizeUpDownCursor]) {
|
|
1914
|
+
return @"ns-resize";
|
|
1915
|
+
}
|
|
1916
|
+
if (@available(macOS 15.0, *)) {
|
|
1917
|
+
if ([NSCursor respondsToSelector:@selector(columnResizeCursor)] &&
|
|
1918
|
+
cursor == [NSCursor columnResizeCursor]) {
|
|
1604
1919
|
return @"col-resize";
|
|
1605
1920
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
if (cursor == [NSCursor resizeUpDownCursor]) {
|
|
1921
|
+
if ([NSCursor respondsToSelector:@selector(rowResizeCursor)] &&
|
|
1922
|
+
cursor == [NSCursor rowResizeCursor]) {
|
|
1609
1923
|
return @"row-resize";
|
|
1610
1924
|
}
|
|
1611
1925
|
}
|
|
@@ -1677,12 +1991,6 @@ static NSString* detectSystemCursorType(void) {
|
|
|
1677
1991
|
}
|
|
1678
1992
|
|
|
1679
1993
|
int cursorSeed = SafeCGSCurrentCursorSeed();
|
|
1680
|
-
if (cursorSeed > 0) {
|
|
1681
|
-
NSString *seedType = cursorTypeFromSeed(cursorSeed);
|
|
1682
|
-
if (seedType) {
|
|
1683
|
-
return seedType;
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
1994
|
|
|
1687
1995
|
void (^fetchCursorBlock)(void) = ^{
|
|
1688
1996
|
NSCursor *currentCursor = nil;
|
|
@@ -1786,14 +2094,23 @@ static NSString* detectSystemCursorType(void) {
|
|
|
1786
2094
|
dispatch_sync(dispatch_get_main_queue(), fetchCursorBlock);
|
|
1787
2095
|
}
|
|
1788
2096
|
|
|
1789
|
-
if (cursorType && ![cursorType isEqualToString:@"default"]
|
|
1790
|
-
|
|
2097
|
+
if (cursorType && ![cursorType isEqualToString:@"default"]) {
|
|
2098
|
+
if (cursorSeed > 0) {
|
|
2099
|
+
addCursorToSeedMap(cursorType, cursorSeed);
|
|
2100
|
+
}
|
|
2101
|
+
return cursorType;
|
|
1791
2102
|
}
|
|
1792
2103
|
|
|
1793
|
-
|
|
2104
|
+
if (cursorSeed > 0) {
|
|
2105
|
+
NSString *seedType = cursorTypeFromSeed(cursorSeed);
|
|
2106
|
+
if (seedType) {
|
|
2107
|
+
return seedType;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
return cursorType ?: @"default";
|
|
1794
2112
|
}
|
|
1795
2113
|
|
|
1796
|
-
// Desktop'ta SVG karşılığı olmayan cursor tiplerini desteklenen tiplere normalize et
|
|
1797
2114
|
static NSString* normalizeCursorTypeForDesktop(NSString *cursorType) {
|
|
1798
2115
|
if (!cursorType || [cursorType length] == 0) {
|
|
1799
2116
|
return @"default";
|
|
@@ -1891,11 +2208,23 @@ NSString* getCursorType() {
|
|
|
1891
2208
|
int currentSeed = SafeCGSCurrentCursorSeed();
|
|
1892
2209
|
g_lastCursorSeed = currentSeed; // Save for getCursorPosition()
|
|
1893
2210
|
|
|
1894
|
-
//
|
|
1895
|
-
// DO NOT use accessibility detection as it's unreliable and causes false positives
|
|
2211
|
+
// Önce NSCursor/CGS; köşe diagonal için sistem imleci güvenilir olmayabiliyor — AX ile pencere çerçevesi düzeltmesi
|
|
1896
2212
|
NSString *systemCursorType = detectSystemCursorType();
|
|
1897
2213
|
NSString *rawType = systemCursorType && [systemCursorType length] > 0 ? systemCursorType : @"default";
|
|
1898
2214
|
|
|
2215
|
+
__block NSString *chromeResize = nil;
|
|
2216
|
+
if ([NSThread isMainThread]) {
|
|
2217
|
+
chromeResize = ResizeTypeFromWindowUnderCursor(cursorPos);
|
|
2218
|
+
} else {
|
|
2219
|
+
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
2220
|
+
chromeResize = ResizeTypeFromWindowUnderCursor(cursorPos);
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
if (chromeResize) {
|
|
2225
|
+
rawType = chromeResize;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
1899
2228
|
// Desktop SVG'lerine uyumlu tipe normalize et
|
|
1900
2229
|
NSString *finalType = normalizeCursorTypeForDesktop(rawType);
|
|
1901
2230
|
|
|
@@ -1947,29 +2276,6 @@ void writeToFile(NSDictionary *cursorData) {
|
|
|
1947
2276
|
}
|
|
1948
2277
|
}
|
|
1949
2278
|
|
|
1950
|
-
// Text input event: Klavye basıldığında focused text field'in caret pozisyonunu yakala
|
|
1951
|
-
static void emitTextInputEvent(NSTimeInterval timestamp, NSTimeInterval unixTimeMs, CGPoint mouseLocation) {
|
|
1952
|
-
if (unixTimeMs - g_lastTextInputEmitTime < TEXT_INPUT_THROTTLE_MS) {
|
|
1953
|
-
return;
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
NSDictionary *snap = MRTextInputSnapshotDictionary();
|
|
1957
|
-
if (!snap) {
|
|
1958
|
-
return;
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
NSMutableDictionary *textInputInfo = [NSMutableDictionary dictionaryWithDictionary:snap];
|
|
1962
|
-
textInputInfo[@"x"] = @((int)mouseLocation.x);
|
|
1963
|
-
textInputInfo[@"y"] = @((int)mouseLocation.y);
|
|
1964
|
-
textInputInfo[@"timestamp"] = @(timestamp);
|
|
1965
|
-
textInputInfo[@"unixTimeMs"] = @(unixTimeMs);
|
|
1966
|
-
textInputInfo[@"cursorType"] = @"text";
|
|
1967
|
-
textInputInfo[@"type"] = @"textInput";
|
|
1968
|
-
|
|
1969
|
-
writeToFile(textInputInfo);
|
|
1970
|
-
g_lastTextInputEmitTime = unixTimeMs;
|
|
1971
|
-
}
|
|
1972
|
-
|
|
1973
2279
|
// Event callback for mouse events
|
|
1974
2280
|
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
|
|
1975
2281
|
@autoreleasepool {
|
|
@@ -1996,23 +2302,6 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
1996
2302
|
// Event tipini belirle
|
|
1997
2303
|
switch (type) {
|
|
1998
2304
|
case kCGEventLeftMouseDown:
|
|
1999
|
-
eventType = @"mousedown";
|
|
2000
|
-
// Odak/caret çoğu uygulamada tıklamadan hemen sonra oluşur; kısa gecikmeyle AX caret yaz.
|
|
2001
|
-
// (Sadece tuşta emit edilince ilk tıkta timeline'da textInput olmuyordu.)
|
|
2002
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.028 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
2003
|
-
if (!g_isCursorTracking || !g_trackingStartTime || !g_fileHandle) return;
|
|
2004
|
-
NSDate *now = [NSDate date];
|
|
2005
|
-
NSTimeInterval ts = [now timeIntervalSinceDate:g_trackingStartTime] * 1000.0;
|
|
2006
|
-
NSTimeInterval unixMs = [now timeIntervalSince1970] * 1000.0;
|
|
2007
|
-
CGPoint loc = CGPointZero;
|
|
2008
|
-
CGEventRef posEv = CGEventCreate(NULL);
|
|
2009
|
-
if (posEv) {
|
|
2010
|
-
loc = CGEventGetLocation(posEv);
|
|
2011
|
-
CFRelease(posEv);
|
|
2012
|
-
}
|
|
2013
|
-
emitTextInputEvent(ts, unixMs, loc);
|
|
2014
|
-
});
|
|
2015
|
-
break;
|
|
2016
2305
|
case kCGEventRightMouseDown:
|
|
2017
2306
|
case kCGEventOtherMouseDown:
|
|
2018
2307
|
eventType = @"mousedown";
|
|
@@ -2027,10 +2316,6 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
2027
2316
|
case kCGEventOtherMouseDragged:
|
|
2028
2317
|
eventType = @"drag";
|
|
2029
2318
|
break;
|
|
2030
|
-
case kCGEventKeyDown:
|
|
2031
|
-
// Klavye event'i — text caret tracking için
|
|
2032
|
-
emitTextInputEvent(timestamp, unixTimeMs, location);
|
|
2033
|
-
return event; // Mouse event olarak işleme, ayrı handle edildi
|
|
2034
2319
|
case kCGEventMouseMoved:
|
|
2035
2320
|
default:
|
|
2036
2321
|
eventType = @"move";
|
|
@@ -2040,7 +2325,7 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
2040
2325
|
if (!ShouldEmitCursorEvent(location, cursorType, eventType)) {
|
|
2041
2326
|
return event;
|
|
2042
2327
|
}
|
|
2043
|
-
|
|
2328
|
+
|
|
2044
2329
|
// Cursor data oluştur
|
|
2045
2330
|
NSDictionary *cursorInfo = @{
|
|
2046
2331
|
@"x": @((int)location.x),
|
|
@@ -2050,7 +2335,7 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
|
|
|
2050
2335
|
@"cursorType": cursorType,
|
|
2051
2336
|
@"type": eventType
|
|
2052
2337
|
};
|
|
2053
|
-
|
|
2338
|
+
|
|
2054
2339
|
// Direkt dosyaya yaz
|
|
2055
2340
|
writeToFile(cursorInfo);
|
|
2056
2341
|
RememberCursorEvent(location, cursorType, eventType);
|
|
@@ -2086,13 +2371,6 @@ void cursorTimerCallback() {
|
|
|
2086
2371
|
cursorType = @"default";
|
|
2087
2372
|
}
|
|
2088
2373
|
|
|
2089
|
-
// Timer-only mod: CGEventTap yokken kCGEventKeyDown gelmez; caret satırları hiç yazılmazdı.
|
|
2090
|
-
// I-beam görünürken AX ile periyodik textInput üret (emitTextInputEvent içinde throttle var).
|
|
2091
|
-
if ([cursorType isEqualToString:@"text"] ||
|
|
2092
|
-
[cursorType isEqualToString:@"vertical-text"]) {
|
|
2093
|
-
emitTextInputEvent(timestamp, unixTimeMs, location);
|
|
2094
|
-
}
|
|
2095
|
-
|
|
2096
2374
|
// Mouse button state polling — event tap olmadığında click/drag tespiti
|
|
2097
2375
|
bool currentLeftMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonLeft);
|
|
2098
2376
|
bool currentRightMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight);
|
|
@@ -2196,7 +2474,6 @@ void cleanupCursorTracking() {
|
|
|
2196
2474
|
g_lastDetectedCursorType = nil;
|
|
2197
2475
|
g_cursorTypeCounter = 0;
|
|
2198
2476
|
g_isFirstWrite = true;
|
|
2199
|
-
g_lastTextInputEmitTime = 0;
|
|
2200
2477
|
ResetCursorEventHistory();
|
|
2201
2478
|
}
|
|
2202
2479
|
|
|
@@ -2237,7 +2514,7 @@ Napi::Value StartCursorTracking(const Napi::CallbackInfo& info) {
|
|
|
2237
2514
|
g_trackingStartTime = [NSDate date];
|
|
2238
2515
|
ResetCursorEventHistory();
|
|
2239
2516
|
|
|
2240
|
-
// Create event tap for mouse
|
|
2517
|
+
// Create event tap for mouse events
|
|
2241
2518
|
CGEventMask eventMask = (CGEventMaskBit(kCGEventLeftMouseDown) |
|
|
2242
2519
|
CGEventMaskBit(kCGEventLeftMouseUp) |
|
|
2243
2520
|
CGEventMaskBit(kCGEventRightMouseDown) |
|
|
@@ -2247,8 +2524,7 @@ Napi::Value StartCursorTracking(const Napi::CallbackInfo& info) {
|
|
|
2247
2524
|
CGEventMaskBit(kCGEventMouseMoved) |
|
|
2248
2525
|
CGEventMaskBit(kCGEventLeftMouseDragged) |
|
|
2249
2526
|
CGEventMaskBit(kCGEventRightMouseDragged) |
|
|
2250
|
-
CGEventMaskBit(kCGEventOtherMouseDragged)
|
|
2251
|
-
CGEventMaskBit(kCGEventKeyDown));
|
|
2527
|
+
CGEventMaskBit(kCGEventOtherMouseDragged));
|
|
2252
2528
|
|
|
2253
2529
|
bool eventTapActive = false;
|
|
2254
2530
|
g_eventTap = CGEventTapCreate(kCGSessionEventTap,
|
|
@@ -2578,33 +2854,6 @@ Napi::Value GetCursorDebugInfo(const Napi::CallbackInfo& info) {
|
|
|
2578
2854
|
}
|
|
2579
2855
|
}
|
|
2580
2856
|
|
|
2581
|
-
static Napi::Value DictToNapiTextInputSnapshot(Napi::Env env, NSDictionary *snap) {
|
|
2582
|
-
Napi::Object o = Napi::Object::New(env);
|
|
2583
|
-
NSNumber *cx = snap[@"caretX"];
|
|
2584
|
-
NSNumber *cy = snap[@"caretY"];
|
|
2585
|
-
o.Set("caretX", Napi::Number::New(env, cx ? [cx doubleValue] : 0));
|
|
2586
|
-
o.Set("caretY", Napi::Number::New(env, cy ? [cy doubleValue] : 0));
|
|
2587
|
-
NSDictionary *frame = snap[@"inputFrame"];
|
|
2588
|
-
Napi::Object fo = Napi::Object::New(env);
|
|
2589
|
-
if ([frame isKindOfClass:[NSDictionary class]]) {
|
|
2590
|
-
fo.Set("x", Napi::Number::New(env, [frame[@"x"] doubleValue]));
|
|
2591
|
-
fo.Set("y", Napi::Number::New(env, [frame[@"y"] doubleValue]));
|
|
2592
|
-
fo.Set("width", Napi::Number::New(env, [frame[@"width"] doubleValue]));
|
|
2593
|
-
fo.Set("height", Napi::Number::New(env, [frame[@"height"] doubleValue]));
|
|
2594
|
-
}
|
|
2595
|
-
o.Set("inputFrame", fo);
|
|
2596
|
-
return o;
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
static Napi::Value GetTextInputSnapshot(const Napi::CallbackInfo& info) {
|
|
2600
|
-
Napi::Env env = info.Env();
|
|
2601
|
-
NSDictionary *snap = MRTextInputSnapshotDictionary();
|
|
2602
|
-
if (!snap) {
|
|
2603
|
-
return env.Null();
|
|
2604
|
-
}
|
|
2605
|
-
return DictToNapiTextInputSnapshot(env, snap);
|
|
2606
|
-
}
|
|
2607
|
-
|
|
2608
2857
|
// Export functions
|
|
2609
2858
|
Napi::Object InitCursorTracker(Napi::Env env, Napi::Object exports) {
|
|
2610
2859
|
exports.Set("startCursorTracking", Napi::Function::New(env, StartCursorTracking));
|
|
@@ -2612,7 +2861,6 @@ Napi::Object InitCursorTracker(Napi::Env env, Napi::Object exports) {
|
|
|
2612
2861
|
exports.Set("getCursorPosition", Napi::Function::New(env, GetCursorPosition));
|
|
2613
2862
|
exports.Set("getCursorTrackingStatus", Napi::Function::New(env, GetCursorTrackingStatus));
|
|
2614
2863
|
exports.Set("getCursorDebugInfo", Napi::Function::New(env, GetCursorDebugInfo));
|
|
2615
|
-
exports.Set("getTextInputSnapshot", Napi::Function::New(env, GetTextInputSnapshot));
|
|
2616
2864
|
|
|
2617
2865
|
return exports;
|
|
2618
2866
|
}
|