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.
@@ -2,7 +2,22 @@
2
2
  #import <CoreGraphics/CoreGraphics.h>
3
3
  #import <AppKit/AppKit.h>
4
4
  #import "../logging.h"
5
- #import "../text_input_ax_snapshot.h"
5
+
6
+ static NSCursor *CursorFactoryNamed(NSString *name) {
7
+ if (!name || [name length] == 0) return nil;
8
+ SEL sel = NSSelectorFromString(name);
9
+ if (!sel || ![NSCursor respondsToSelector:sel]) return nil;
10
+ IMP imp = [NSCursor methodForSelector:sel];
11
+ if (!imp) return nil;
12
+ typedef NSCursor *(*CursorFactoryFunc)(id, SEL);
13
+ CursorFactoryFunc fn = (CursorFactoryFunc)imp;
14
+ return fn([NSCursor class], sel);
15
+ }
16
+
17
+ static BOOL CursorEqualsFactoryNamed(NSCursor *cursor, NSString *factoryName) {
18
+ NSCursor *ref = CursorFactoryNamed(factoryName);
19
+ return ref != nil && cursor == ref;
20
+ }
6
21
 
7
22
  // Thread-safe cursor tracking for Electron
8
23
  static dispatch_queue_t g_cursorQueue = nil;
@@ -24,18 +39,34 @@ static NSString* MapCursorToType(NSCursor *cursor) {
24
39
  if (cursor == [NSCursor pointingHandCursor]) return @"pointer";
25
40
  if ([NSCursor respondsToSelector:@selector(resizeLeftRightCursor)]) {
26
41
  if (cursor == [NSCursor resizeLeftRightCursor] ||
27
- [NSCursor instancesRespondToSelector:@selector(resizeLeftCursor)] && (cursor == [NSCursor resizeLeftCursor]) ||
28
- [NSCursor instancesRespondToSelector:@selector(resizeRightCursor)] && (cursor == [NSCursor resizeRightCursor])) {
42
+ ([NSCursor respondsToSelector:@selector(resizeLeftCursor)] && cursor == [NSCursor resizeLeftCursor]) ||
43
+ ([NSCursor respondsToSelector:@selector(resizeRightCursor)] && cursor == [NSCursor resizeRightCursor])) {
29
44
  return @"col-resize";
30
45
  }
31
46
  }
32
47
  if ([NSCursor respondsToSelector:@selector(resizeUpDownCursor)]) {
33
48
  if (cursor == [NSCursor resizeUpDownCursor] ||
34
- [NSCursor instancesRespondToSelector:@selector(resizeUpCursor)] && (cursor == [NSCursor resizeUpCursor]) ||
35
- [NSCursor instancesRespondToSelector:@selector(resizeDownCursor)] && (cursor == [NSCursor resizeDownCursor])) {
49
+ ([NSCursor respondsToSelector:@selector(resizeUpCursor)] && cursor == [NSCursor resizeUpCursor]) ||
50
+ ([NSCursor respondsToSelector:@selector(resizeDownCursor)] && cursor == [NSCursor resizeDownCursor])) {
36
51
  return @"ns-resize";
37
52
  }
38
53
  }
54
+ if (CursorEqualsFactoryNamed(cursor, @"resizeNorthWestSouthEastCursor")) {
55
+ return @"nwse-resize";
56
+ }
57
+ if (CursorEqualsFactoryNamed(cursor, @"resizeNorthEastSouthWestCursor")) {
58
+ return @"nesw-resize";
59
+ }
60
+ if (@available(macOS 15.0, *)) {
61
+ if ([NSCursor respondsToSelector:@selector(columnResizeCursor)] &&
62
+ cursor == [NSCursor columnResizeCursor]) {
63
+ return @"col-resize";
64
+ }
65
+ if ([NSCursor respondsToSelector:@selector(rowResizeCursor)] &&
66
+ cursor == [NSCursor rowResizeCursor]) {
67
+ return @"row-resize";
68
+ }
69
+ }
39
70
  if ([NSCursor respondsToSelector:@selector(openHandCursor)] && cursor == [NSCursor openHandCursor]) return @"grab";
40
71
  if ([NSCursor respondsToSelector:@selector(closedHandCursor)] && cursor == [NSCursor closedHandCursor]) return @"grabbing";
41
72
  if ([NSCursor respondsToSelector:@selector(crosshairCursor)] && cursor == [NSCursor crosshairCursor]) return @"crosshair";
@@ -93,45 +124,12 @@ Napi::Value GetCursorPositionElectronSafe(const Napi::CallbackInfo& info) {
93
124
  }
94
125
  }
95
126
 
96
- static Napi::Value DictToNapiTextInputSnapshotElectron(Napi::Env env, NSDictionary *snap) {
97
- Napi::Object o = Napi::Object::New(env);
98
- NSNumber *cx = snap[@"caretX"];
99
- NSNumber *cy = snap[@"caretY"];
100
- o.Set("caretX", Napi::Number::New(env, cx ? [cx doubleValue] : 0));
101
- o.Set("caretY", Napi::Number::New(env, cy ? [cy doubleValue] : 0));
102
- NSDictionary *frame = snap[@"inputFrame"];
103
- Napi::Object fo = Napi::Object::New(env);
104
- if ([frame isKindOfClass:[NSDictionary class]]) {
105
- fo.Set("x", Napi::Number::New(env, [frame[@"x"] doubleValue]));
106
- fo.Set("y", Napi::Number::New(env, [frame[@"y"] doubleValue]));
107
- fo.Set("width", Napi::Number::New(env, [frame[@"width"] doubleValue]));
108
- fo.Set("height", Napi::Number::New(env, [frame[@"height"] doubleValue]));
109
- }
110
- o.Set("inputFrame", fo);
111
- return o;
112
- }
113
-
114
- Napi::Value GetTextInputSnapshotElectronSafe(const Napi::CallbackInfo& info) {
115
- Napi::Env env = info.Env();
116
- @try {
117
- NSDictionary *snap = MRTextInputSnapshotDictionary();
118
- if (!snap) {
119
- return env.Null();
120
- }
121
- return DictToNapiTextInputSnapshotElectron(env, snap);
122
- } @catch (NSException *e) {
123
- NSLog(@"❌ getTextInputSnapshot: %@", e.reason);
124
- return env.Null();
125
- }
126
- }
127
-
128
127
  // Initialize cursor tracker module
129
128
  Napi::Object InitCursorTrackerElectron(Napi::Env env, Napi::Object exports) {
130
129
  @try {
131
130
  initializeCursorQueue();
132
131
 
133
132
  exports.Set("getCursorPosition", Napi::Function::New(env, GetCursorPositionElectronSafe));
134
- exports.Set("getTextInputSnapshot", Napi::Function::New(env, GetTextInputSnapshotElectronSafe));
135
133
 
136
134
  MRLog(@"✅ Electron-safe cursor tracker initialized");
137
135
  return exports;
@@ -13,9 +13,12 @@ static void initializeWindowQueue() {
13
13
  }
14
14
 
15
15
  static BOOL ShouldAllowElectronWindows(void) {
16
- NSString *flag = [[[NSProcessInfo processInfo] environment] objectForKey:@"CREAVIT_ALLOW_ELECTRON_WINDOWS"];
17
- if (!flag) return NO;
16
+ // NSProcessInfo.environment snapshot'lanır ve runtime'da process.env değişikliklerini yansıtmaz.
17
+ // getenv() libc üzerinden her seferinde güncel değeri okur — menüden toggle'lanabilsin diye.
18
+ const char *raw = getenv("CREAVIT_ALLOW_ELECTRON_WINDOWS");
19
+ if (!raw) return NO;
18
20
 
21
+ NSString *flag = [NSString stringWithUTF8String:raw];
19
22
  NSString *normalized = [[flag lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
20
23
  return [normalized isEqualToString:@"1"] ||
21
24
  [normalized isEqualToString:@"true"] ||
@@ -342,23 +342,17 @@ static void FinishWriter(AVAssetWriter *writer, AVAssetWriterInput *input) {
342
342
  if (!writer) {
343
343
  return;
344
344
  }
345
-
346
- // markAsFinished sadece AVAssetWriterStatusWriting (1) durumunda çağrılabilir
347
- // Status 0 (Unknown) veya diğer durumlarda crash eder
348
- if (input && writer.status == AVAssetWriterStatusWriting) {
345
+
346
+ if (input) {
349
347
  [input markAsFinished];
350
348
  }
351
-
352
- if (writer.status == AVAssetWriterStatusWriting) {
353
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
354
- [writer finishWritingWithCompletionHandler:^{
355
- dispatch_semaphore_signal(semaphore);
356
- }];
357
- dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
358
- dispatch_semaphore_wait(semaphore, timeout);
359
- } else if (writer.status == AVAssetWriterStatusFailed) {
360
- NSLog(@"⚠️ Writer already failed: %@", writer.error);
361
- }
349
+
350
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
351
+ [writer finishWritingWithCompletionHandler:^{
352
+ dispatch_semaphore_signal(semaphore);
353
+ }];
354
+ dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
355
+ dispatch_semaphore_wait(semaphore, timeout);
362
356
  }
363
357
 
364
358
  static void CleanupWriters(void) {
@@ -379,10 +373,10 @@ static void CleanupWriters(void) {
379
373
  }
380
374
 
381
375
  if (g_audioWriter) {
382
- if (g_systemAudioInput && g_audioWriter.status == AVAssetWriterStatusWriting) {
376
+ if (g_systemAudioInput) {
383
377
  [g_systemAudioInput markAsFinished];
384
378
  }
385
- if (g_microphoneAudioInput && g_audioWriter.status == AVAssetWriterStatusWriting) {
379
+ if (g_microphoneAudioInput) {
386
380
  [g_microphoneAudioInput markAsFinished];
387
381
  }
388
382
  FinishWriter(g_audioWriter, nil);
@@ -57,9 +57,12 @@ static NSTimer *g_screenTrackingTimer = nil;
57
57
  static NSInteger g_currentActiveScreenIndex = -1;
58
58
 
59
59
  static bool shouldAllowElectronWindows() {
60
- NSString *flag = [[[NSProcessInfo processInfo] environment] objectForKey:@"CREAVIT_ALLOW_ELECTRON_WINDOWS"];
61
- if (!flag) return false;
60
+ // NSProcessInfo.environment snapshot'lanır ve runtime değişikliklerini yansıtmaz.
61
+ // getenv() ile process.env güncellemelerini her seferinde okuyoruz.
62
+ const char *raw = getenv("CREAVIT_ALLOW_ELECTRON_WINDOWS");
63
+ if (!raw) return false;
62
64
 
65
+ NSString *flag = [NSString stringWithUTF8String:raw];
63
66
  NSString *normalized = [[flag lowercaseString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
64
67
  return [normalized isEqualToString:@"1"] ||
65
68
  [normalized isEqualToString:@"true"] ||
@@ -1,110 +0,0 @@
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 };