node-mac-recorder 2.4.11 → 2.4.13

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.
@@ -3,7 +3,6 @@
3
3
 
4
4
  #import <Foundation/Foundation.h>
5
5
  #import <CoreGraphics/CoreGraphics.h>
6
- #import <napi.h>
7
6
 
8
7
  @interface ScreenCapture : NSObject
9
8
 
@@ -17,8 +16,4 @@
17
16
 
18
17
  @end
19
18
 
20
- // NAPI function declarations for legacy fallback
21
- Napi::Value GetAvailableDisplays(const Napi::CallbackInfo& info);
22
- Napi::Value GetWindowList(const Napi::CallbackInfo& info);
23
-
24
19
  #endif // SCREEN_CAPTURE_H
@@ -1,9 +1,19 @@
1
- #import "screen_capture.h"
2
- #import <ScreenCaptureKit/ScreenCaptureKit.h>
3
1
  #import <AVFoundation/AVFoundation.h>
4
2
  #import <CoreGraphics/CoreGraphics.h>
5
3
  #import <AppKit/AppKit.h>
6
4
 
5
+ @interface ScreenCapture : NSObject
6
+
7
+ + (NSArray *)getAvailableDisplays;
8
+ + (BOOL)captureDisplay:(CGDirectDisplayID)displayID
9
+ toFile:(NSString *)filePath
10
+ rect:(CGRect)rect
11
+ includeCursor:(BOOL)includeCursor;
12
+ + (CGImageRef)createScreenshotFromDisplay:(CGDirectDisplayID)displayID
13
+ rect:(CGRect)rect;
14
+
15
+ @end
16
+
7
17
  @implementation ScreenCapture
8
18
 
9
19
  + (NSArray *)getAvailableDisplays {
@@ -74,7 +84,7 @@
74
84
  NSURL *fileURL = [NSURL fileURLWithPath:filePath];
75
85
  CGImageDestinationRef destination = CGImageDestinationCreateWithURL(
76
86
  (__bridge CFURLRef)fileURL,
77
- (__bridge CFStringRef)@"public.png",
87
+ kUTTypePNG,
78
88
  1,
79
89
  NULL
80
90
  );
@@ -86,15 +96,51 @@
86
96
 
87
97
  // Add cursor if requested
88
98
  if (includeCursor) {
89
- // For simplicity, we'll just save the image without cursor compositing
90
- // Cursor compositing would require more complex image manipulation
99
+ // Get cursor position
100
+ CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
101
+
102
+ // Create mutable image context
103
+ size_t width = CGImageGetWidth(screenshot);
104
+ size_t height = CGImageGetHeight(screenshot);
105
+
106
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
107
+ CGContextRef context = CGBitmapContextCreate(
108
+ NULL, width, height, 8, width * 4,
109
+ colorSpace, kCGImageAlphaPremultipliedFirst
110
+ );
111
+
112
+ if (context) {
113
+ // Draw original screenshot
114
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), screenshot);
115
+
116
+ // Draw cursor (simplified - just a small circle)
117
+ CGRect displayBounds = CGDisplayBounds(displayID);
118
+ CGFloat relativeX = cursorPos.x - displayBounds.origin.x;
119
+ CGFloat relativeY = height - (cursorPos.y - displayBounds.origin.y);
120
+
121
+ if (!CGRectIsNull(rect)) {
122
+ relativeX -= rect.origin.x;
123
+ relativeY -= rect.origin.y;
124
+ }
125
+
126
+ if (relativeX >= 0 && relativeX < width && relativeY >= 0 && relativeY < height) {
127
+ CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 0.8); // Red cursor
128
+ CGContextFillEllipseInRect(context, CGRectMake(relativeX - 5, relativeY - 5, 10, 10));
129
+ }
130
+
131
+ CGImageRef finalImage = CGBitmapContextCreateImage(context);
132
+ CGContextRelease(context);
133
+ CGImageRelease(screenshot);
134
+ screenshot = finalImage;
135
+ }
136
+
137
+ CGColorSpaceRelease(colorSpace);
91
138
  }
92
139
 
93
- // Write the image
140
+ // Save image
94
141
  CGImageDestinationAddImage(destination, screenshot, NULL);
95
142
  BOOL success = CGImageDestinationFinalize(destination);
96
143
 
97
- // Cleanup
98
144
  CFRelease(destination);
99
145
  CGImageRelease(screenshot);
100
146
 
@@ -103,139 +149,14 @@
103
149
 
104
150
  + (CGImageRef)createScreenshotFromDisplay:(CGDirectDisplayID)displayID
105
151
  rect:(CGRect)rect {
106
- if (CGRectIsNull(rect) || CGRectIsEmpty(rect)) {
107
- rect = CGDisplayBounds(displayID);
108
- }
109
-
110
- return CGDisplayCreateImageForRect(displayID, rect);
111
- }
112
-
113
- @end
114
-
115
- // NAPI Functions for Legacy Fallback
116
-
117
- // NAPI Function: Get Available Displays (Legacy)
118
- Napi::Value GetAvailableDisplays(const Napi::CallbackInfo& info) {
119
- Napi::Env env = info.Env();
120
-
121
- NSArray *displays = [ScreenCapture getAvailableDisplays];
122
- Napi::Array displaysArray = Napi::Array::New(env);
123
152
 
124
- for (NSUInteger i = 0; i < [displays count]; i++) {
125
- NSDictionary *displayInfo = displays[i];
126
-
127
- Napi::Object displayObj = Napi::Object::New(env);
128
- displayObj.Set("id", Napi::Number::New(env, [[displayInfo objectForKey:@"id"] unsignedIntValue]));
129
- displayObj.Set("name", Napi::String::New(env, [[displayInfo objectForKey:@"name"] UTF8String]));
130
- displayObj.Set("width", Napi::Number::New(env, [[displayInfo objectForKey:@"width"] doubleValue]));
131
- displayObj.Set("height", Napi::Number::New(env, [[displayInfo objectForKey:@"height"] doubleValue]));
132
-
133
- // Create frame object
134
- Napi::Object frameObj = Napi::Object::New(env);
135
- frameObj.Set("x", Napi::Number::New(env, [[displayInfo objectForKey:@"x"] doubleValue]));
136
- frameObj.Set("y", Napi::Number::New(env, [[displayInfo objectForKey:@"y"] doubleValue]));
137
- frameObj.Set("width", Napi::Number::New(env, [[displayInfo objectForKey:@"width"] doubleValue]));
138
- frameObj.Set("height", Napi::Number::New(env, [[displayInfo objectForKey:@"height"] doubleValue]));
139
-
140
- displayObj.Set("frame", frameObj);
141
- displayObj.Set("isPrimary", Napi::Boolean::New(env, [[displayInfo objectForKey:@"isPrimary"] boolValue]));
142
-
143
- displaysArray.Set(static_cast<uint32_t>(i), displayObj);
153
+ if (CGRectIsNull(rect)) {
154
+ // Capture entire display
155
+ return CGDisplayCreateImage(displayID);
156
+ } else {
157
+ // Capture specific rect
158
+ return CGDisplayCreateImageForRect(displayID, rect);
144
159
  }
145
-
146
- return displaysArray;
147
160
  }
148
161
 
149
- // NAPI Function: Get Window List (Legacy)
150
- Napi::Value GetWindowList(const Napi::CallbackInfo& info) {
151
- Napi::Env env = info.Env();
152
-
153
- // Get window list using CGWindowList
154
- CFArrayRef windowList = CGWindowListCopyWindowInfo(
155
- kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
156
- kCGNullWindowID
157
- );
158
-
159
- if (!windowList) {
160
- return Napi::Array::New(env);
161
- }
162
-
163
- Napi::Array windowsArray = Napi::Array::New(env);
164
- CFIndex count = CFArrayGetCount(windowList);
165
- uint32_t index = 0;
166
-
167
- for (CFIndex i = 0; i < count; i++) {
168
- CFDictionaryRef windowInfo = (CFDictionaryRef)CFArrayGetValueAtIndex(windowList, i);
169
-
170
- // Get window ID
171
- CFNumberRef windowIDRef = (CFNumberRef)CFDictionaryGetValue(windowInfo, kCGWindowNumber);
172
- uint32_t windowID;
173
- CFNumberGetValue(windowIDRef, kCFNumberSInt32Type, &windowID);
174
-
175
- // Get window title
176
- CFStringRef windowTitleRef = (CFStringRef)CFDictionaryGetValue(windowInfo, kCGWindowName);
177
- std::string windowTitle = "";
178
- if (windowTitleRef) {
179
- const char *titleCStr = CFStringGetCStringPtr(windowTitleRef, kCFStringEncodingUTF8);
180
- if (titleCStr) {
181
- windowTitle = std::string(titleCStr);
182
- } else {
183
- // Fallback for when CFStringGetCStringPtr returns NULL
184
- CFIndex length = CFStringGetLength(windowTitleRef);
185
- CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
186
- char *buffer = (char *)malloc(maxSize);
187
- if (CFStringGetCString(windowTitleRef, buffer, maxSize, kCFStringEncodingUTF8)) {
188
- windowTitle = std::string(buffer);
189
- }
190
- free(buffer);
191
- }
192
- }
193
-
194
- // Get owner name
195
- CFStringRef ownerNameRef = (CFStringRef)CFDictionaryGetValue(windowInfo, kCGWindowOwnerName);
196
- std::string ownerName = "";
197
- if (ownerNameRef) {
198
- const char *ownerCStr = CFStringGetCStringPtr(ownerNameRef, kCFStringEncodingUTF8);
199
- if (ownerCStr) {
200
- ownerName = std::string(ownerCStr);
201
- } else {
202
- CFIndex length = CFStringGetLength(ownerNameRef);
203
- CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
204
- char *buffer = (char *)malloc(maxSize);
205
- if (CFStringGetCString(ownerNameRef, buffer, maxSize, kCFStringEncodingUTF8)) {
206
- ownerName = std::string(buffer);
207
- }
208
- free(buffer);
209
- }
210
- }
211
-
212
- // Get window bounds
213
- CFDictionaryRef boundsRef = (CFDictionaryRef)CFDictionaryGetValue(windowInfo, kCGWindowBounds);
214
- CGRect bounds = CGRectNull;
215
- if (boundsRef) {
216
- CGRectMakeWithDictionaryRepresentation(boundsRef, &bounds);
217
- }
218
-
219
- // Filter out small/invalid windows
220
- if (bounds.size.width > 50 && bounds.size.height > 50 && !windowTitle.empty()) {
221
- Napi::Object windowObj = Napi::Object::New(env);
222
- windowObj.Set("id", Napi::Number::New(env, windowID));
223
- windowObj.Set("title", Napi::String::New(env, windowTitle));
224
- windowObj.Set("ownerName", Napi::String::New(env, ownerName));
225
-
226
- // Create bounds object
227
- Napi::Object boundsObj = Napi::Object::New(env);
228
- boundsObj.Set("x", Napi::Number::New(env, bounds.origin.x));
229
- boundsObj.Set("y", Napi::Number::New(env, bounds.origin.y));
230
- boundsObj.Set("width", Napi::Number::New(env, bounds.size.width));
231
- boundsObj.Set("height", Napi::Number::New(env, bounds.size.height));
232
-
233
- windowObj.Set("bounds", boundsObj);
234
-
235
- windowsArray.Set(index++, windowObj);
236
- }
237
- }
238
-
239
- CFRelease(windowList);
240
- return windowsArray;
241
- }
162
+ @end