node-mac-recorder 1.2.3 → 1.2.5

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/README.md CHANGED
@@ -92,6 +92,7 @@ await recorder.startRecording("./recording.mov", {
92
92
  // Audio Controls
93
93
  includeMicrophone: false, // Enable microphone (default: false)
94
94
  includeSystemAudio: true, // Enable system audio (default: true)
95
+ audioDeviceId: "device-id", // Specific audio input device (default: system default)
95
96
 
96
97
  // Display & Window Selection
97
98
  displayId: 0, // Display index (null = main display)
@@ -159,14 +160,14 @@ console.log(displays);
159
160
 
160
161
  #### `getAudioDevices()`
161
162
 
162
- Lists all available audio input devices.
163
+ Returns a list of available audio input devices.
163
164
 
164
165
  ```javascript
165
166
  const devices = await recorder.getAudioDevices();
166
167
  console.log(devices);
167
168
  // [
168
169
  // {
169
- // id: "device-id-123",
170
+ // id: "device-id",
170
171
  // name: "Built-in Microphone",
171
172
  // manufacturer: "Apple Inc.",
172
173
  // isDefault: true
@@ -0,0 +1,87 @@
1
+ const MacRecorder = require("./");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ async function saveBase64Image(base64String, filePath) {
6
+ // Remove the data:image/png;base64, prefix if it exists
7
+ const base64Data = base64String.replace(/^data:image\/png;base64,/, "");
8
+
9
+ // Create directory if it doesn't exist
10
+ const dir = path.dirname(filePath);
11
+ if (!fs.existsSync(dir)) {
12
+ fs.mkdirSync(dir, { recursive: true });
13
+ }
14
+
15
+ // Write the file
16
+ fs.writeFileSync(filePath, base64Data, "base64");
17
+ console.log(`✅ Saved image to: ${filePath}`);
18
+ }
19
+
20
+ async function captureTest() {
21
+ const recorder = new MacRecorder();
22
+
23
+ // Create output directory
24
+ const outputDir = path.join(__dirname, "thumbnails");
25
+ if (!fs.existsSync(outputDir)) {
26
+ fs.mkdirSync(outputDir, { recursive: true });
27
+ }
28
+
29
+ console.log("📸 Testing Display Capture");
30
+
31
+ // Get displays
32
+ const displays = await recorder.getDisplays();
33
+ console.log(`Found ${displays.length} displays`);
34
+
35
+ // Capture each display
36
+ for (const display of displays) {
37
+ console.log(
38
+ `\nCapturing display ${display.id} (${display.width}x${display.height})`
39
+ );
40
+ try {
41
+ const thumbnail = await recorder.getDisplayThumbnail(display.id, {
42
+ maxWidth: 800,
43
+ maxHeight: 600,
44
+ });
45
+
46
+ const fileName = `display_${display.id}.png`;
47
+ const filePath = path.join(outputDir, fileName);
48
+ await saveBase64Image(thumbnail, filePath);
49
+ } catch (error) {
50
+ console.error(`Failed to capture display ${display.id}:`, error);
51
+ }
52
+ }
53
+
54
+ console.log("\n📸 Testing Window Capture");
55
+
56
+ // Get windows
57
+ const windows = await recorder.getWindows();
58
+ console.log(`Found ${windows.length} windows`);
59
+
60
+ // Capture each window
61
+ for (const window of windows) {
62
+ console.log(
63
+ `\nCapturing window "${window.appName}" (${window.width}x${window.height})`
64
+ );
65
+ try {
66
+ const thumbnail = await recorder.getWindowThumbnail(window.id, {
67
+ maxWidth: 800,
68
+ maxHeight: 600,
69
+ });
70
+
71
+ const fileName = `window_${window.id}_${window.appName.replace(
72
+ /[^a-z0-9]/gi,
73
+ "_"
74
+ )}.png`;
75
+ const filePath = path.join(outputDir, fileName);
76
+ await saveBase64Image(thumbnail, filePath);
77
+ } catch (error) {
78
+ console.error(`Failed to capture window "${window.appName}":`, error);
79
+ }
80
+ }
81
+
82
+ console.log(
83
+ "\n✅ Test completed. Check the thumbnails directory for results."
84
+ );
85
+ }
86
+
87
+ captureTest().catch(console.error);
package/index.js CHANGED
@@ -69,31 +69,17 @@ class MacRecorder extends EventEmitter {
69
69
  * macOS ekranlarını listeler
70
70
  */
71
71
  async getDisplays() {
72
- return new Promise((resolve, reject) => {
73
- try {
74
- const displays = nativeBinding.getDisplays();
75
- const formattedDisplays = displays.map((display, index) => ({
76
- id: index,
77
- name:
78
- typeof display === "string"
79
- ? display
80
- : display.name || `Display ${index + 1}`,
81
- resolution:
82
- typeof display === "object"
83
- ? `${display.width}x${display.height}`
84
- : "Unknown",
85
- width: typeof display === "object" ? display.width : null,
86
- height: typeof display === "object" ? display.height : null,
87
- x: typeof display === "object" ? display.x : 0,
88
- y: typeof display === "object" ? display.y : 0,
89
- isPrimary:
90
- typeof display === "object" ? display.isPrimary : index === 0,
91
- }));
92
- resolve(formattedDisplays);
93
- } catch (error) {
94
- reject(error);
95
- }
96
- });
72
+ const displays = nativeBinding.getDisplays();
73
+ return displays.map((display, index) => ({
74
+ id: display.id, // Use the actual display ID from native code
75
+ name: display.name,
76
+ width: display.width,
77
+ height: display.height,
78
+ x: display.x,
79
+ y: display.y,
80
+ isPrimary: display.isPrimary,
81
+ resolution: `${display.width}x${display.height}`,
82
+ }));
97
83
  }
98
84
 
99
85
  /**
@@ -113,8 +99,16 @@ class MacRecorder extends EventEmitter {
113
99
  /**
114
100
  * Kayıt seçeneklerini ayarlar
115
101
  */
116
- setOptions(options) {
117
- this.options = { ...this.options, ...options };
102
+ setOptions(options = {}) {
103
+ this.options = {
104
+ includeMicrophone: options.includeMicrophone || false,
105
+ includeSystemAudio: options.includeSystemAudio !== false, // Default true
106
+ captureCursor: options.captureCursor || false,
107
+ displayId: options.displayId || null, // null = ana ekran
108
+ windowId: options.windowId || null, // null = tam ekran
109
+ audioDeviceId: options.audioDeviceId || null, // null = default device
110
+ captureArea: options.captureArea || null,
111
+ };
118
112
  }
119
113
 
120
114
  /**
@@ -233,6 +227,7 @@ class MacRecorder extends EventEmitter {
233
227
  captureCursor: this.options.captureCursor || false,
234
228
  displayId: this.options.displayId || null, // null = ana ekran
235
229
  windowId: this.options.windowId || null, // null = tam ekran
230
+ audioDeviceId: this.options.audioDeviceId || null, // null = default device
236
231
  };
237
232
 
238
233
  // Manuel captureArea varsa onu kullan
@@ -404,8 +399,16 @@ class MacRecorder extends EventEmitter {
404
399
 
405
400
  return new Promise((resolve, reject) => {
406
401
  try {
402
+ // Get all displays first to validate the ID
403
+ const displays = nativeBinding.getDisplays();
404
+ const display = displays.find((d) => d.id === displayId);
405
+
406
+ if (!display) {
407
+ throw new Error(`Display with ID ${displayId} not found`);
408
+ }
409
+
407
410
  const base64Image = nativeBinding.getDisplayThumbnail(
408
- displayId,
411
+ display.id, // Use the actual CGDirectDisplayID
409
412
  maxWidth,
410
413
  maxHeight
411
414
  );
@@ -586,6 +589,52 @@ class MacRecorder extends EventEmitter {
586
589
  nativeModule: "mac_recorder.node",
587
590
  };
588
591
  }
592
+
593
+ async getDisplaysWithThumbnails(options = {}) {
594
+ const displays = await this.getDisplays();
595
+
596
+ // Get thumbnails for each display
597
+ const displayPromises = displays.map(async (display) => {
598
+ try {
599
+ const thumbnail = await this.getDisplayThumbnail(display.id, options);
600
+ return {
601
+ ...display,
602
+ thumbnail,
603
+ };
604
+ } catch (error) {
605
+ return {
606
+ ...display,
607
+ thumbnail: null,
608
+ thumbnailError: error.message,
609
+ };
610
+ }
611
+ });
612
+
613
+ return Promise.all(displayPromises);
614
+ }
615
+
616
+ async getWindowsWithThumbnails(options = {}) {
617
+ const windows = await this.getWindows();
618
+
619
+ // Get thumbnails for each window
620
+ const windowPromises = windows.map(async (window) => {
621
+ try {
622
+ const thumbnail = await this.getWindowThumbnail(window.id, options);
623
+ return {
624
+ ...window,
625
+ thumbnail,
626
+ };
627
+ } catch (error) {
628
+ return {
629
+ ...window,
630
+ thumbnail: null,
631
+ thumbnailError: error.message,
632
+ };
633
+ }
634
+ });
635
+
636
+ return Promise.all(windowPromises);
637
+ }
589
638
  }
590
639
 
591
640
  module.exports = MacRecorder;
package/list-test.js ADDED
@@ -0,0 +1,46 @@
1
+ const MacRecorder = require("./");
2
+
3
+ async function testListing() {
4
+ const recorder = new MacRecorder();
5
+
6
+ console.log("📺 Testing Displays with Thumbnails");
7
+ const displays = await recorder.getDisplaysWithThumbnails({
8
+ maxWidth: 200,
9
+ maxHeight: 150,
10
+ });
11
+ console.log(`Found ${displays.length} displays:`);
12
+ for (const display of displays) {
13
+ console.log(`\nDisplay ID: ${display.id}`);
14
+ console.log(`Resolution: ${display.width}x${display.height}`);
15
+ console.log(`Position: (${display.x}, ${display.y})`);
16
+ console.log(`Primary: ${display.isPrimary}`);
17
+ console.log(
18
+ `Thumbnail included: ${display.thumbnail.substring(0, 50)}... (${
19
+ display.thumbnail.length
20
+ } chars)`
21
+ );
22
+ }
23
+
24
+ console.log("\n🪟 Testing Windows with Thumbnails");
25
+ const windows = await recorder.getWindowsWithThumbnails({
26
+ maxWidth: 200,
27
+ maxHeight: 150,
28
+ });
29
+ console.log(`Found ${windows.length} windows:`);
30
+ for (const window of windows.slice(0, 3)) {
31
+ // Just show first 3 for brevity
32
+ console.log(`\nWindow: ${window.appName}`);
33
+ console.log(`ID: ${window.id}`);
34
+ console.log(`Size: ${window.width}x${window.height}`);
35
+ console.log(
36
+ `Thumbnail included: ${window.thumbnail.substring(0, 50)}... (${
37
+ window.thumbnail.length
38
+ } chars)`
39
+ );
40
+ }
41
+ if (windows.length > 3) {
42
+ console.log(`\n... and ${windows.length - 3} more windows`);
43
+ }
44
+ }
45
+
46
+ testListing().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -7,6 +7,9 @@
7
7
  #import <ImageIO/ImageIO.h>
8
8
  #import <CoreAudio/CoreAudio.h>
9
9
 
10
+ // Import screen capture
11
+ #import "screen_capture.h"
12
+
10
13
  // Cursor tracker function declarations
11
14
  Napi::Object InitCursorTracker(Napi::Env env, Napi::Object exports);
12
15
 
@@ -67,6 +70,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
67
70
  bool includeMicrophone = false; // Default olarak mikrofon kapalı
68
71
  bool includeSystemAudio = true; // Default olarak sistem sesi açık
69
72
  CGDirectDisplayID displayID = CGMainDisplayID(); // Default ana ekran
73
+ NSString *audioDeviceId = nil; // Default audio device ID
70
74
 
71
75
  if (info.Length() > 1 && info[1].IsObject()) {
72
76
  Napi::Object options = info[1].As<Napi::Object>();
@@ -94,6 +98,12 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
94
98
  includeMicrophone = options.Get("includeMicrophone").As<Napi::Boolean>();
95
99
  }
96
100
 
101
+ // Audio device ID
102
+ if (options.Has("audioDeviceId") && !options.Get("audioDeviceId").IsNull()) {
103
+ std::string deviceId = options.Get("audioDeviceId").As<Napi::String>().Utf8Value();
104
+ audioDeviceId = [NSString stringWithUTF8String:deviceId.c_str()];
105
+ }
106
+
97
107
  // System audio
98
108
  if (options.Has("includeSystemAudio")) {
99
109
  includeSystemAudio = options.Get("includeSystemAudio").As<Napi::Boolean>();
@@ -151,12 +161,41 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
151
161
 
152
162
  // Add microphone input if requested
153
163
  if (includeMicrophone) {
154
- AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
164
+ AVCaptureDevice *audioDevice = nil;
165
+
166
+ if (audioDeviceId) {
167
+ // Try to find the specified device
168
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
169
+ NSLog(@"[DEBUG] Looking for audio device with ID: %@", audioDeviceId);
170
+ NSLog(@"[DEBUG] Available audio devices:");
171
+ for (AVCaptureDevice *device in devices) {
172
+ NSLog(@"[DEBUG] - Device: %@ (ID: %@)", device.localizedName, device.uniqueID);
173
+ if ([device.uniqueID isEqualToString:audioDeviceId]) {
174
+ NSLog(@"[DEBUG] Found matching device: %@", device.localizedName);
175
+ audioDevice = device;
176
+ break;
177
+ }
178
+ }
179
+
180
+ if (!audioDevice) {
181
+ NSLog(@"[DEBUG] Specified audio device not found, falling back to default");
182
+ }
183
+ }
184
+
185
+ // Fallback to default device if specified device not found
186
+ if (!audioDevice) {
187
+ audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
188
+ NSLog(@"[DEBUG] Using default audio device: %@ (ID: %@)", audioDevice.localizedName, audioDevice.uniqueID);
189
+ }
190
+
155
191
  if (audioDevice) {
156
192
  NSError *error;
157
193
  g_audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:&error];
158
194
  if (g_audioInput && [g_captureSession canAddInput:g_audioInput]) {
159
195
  [g_captureSession addInput:g_audioInput];
196
+ NSLog(@"[DEBUG] Successfully added audio input device");
197
+ } else {
198
+ NSLog(@"[DEBUG] Failed to add audio input device: %@", error);
160
199
  }
161
200
  }
162
201
  }
@@ -363,35 +402,20 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
363
402
  Napi::Env env = info.Env();
364
403
 
365
404
  @try {
366
- NSMutableArray *displays = [NSMutableArray array];
367
-
368
- uint32_t displayCount;
369
- CGGetActiveDisplayList(0, NULL, &displayCount);
370
-
371
- CGDirectDisplayID *displayList = (CGDirectDisplayID *)malloc(displayCount * sizeof(CGDirectDisplayID));
372
- CGGetActiveDisplayList(displayCount, displayList, &displayCount);
373
-
374
- for (uint32_t i = 0; i < displayCount; i++) {
375
- CGDirectDisplayID displayID = displayList[i];
376
- CGRect bounds = CGDisplayBounds(displayID);
377
-
378
- [displays addObject:@{
379
- @"id": @(displayID),
380
- @"name": [NSString stringWithFormat:@"Display %d", i + 1],
381
- @"width": @(bounds.size.width),
382
- @"height": @(bounds.size.height),
383
- @"x": @(bounds.origin.x),
384
- @"y": @(bounds.origin.y),
385
- @"isPrimary": @(CGDisplayIsMain(displayID))
386
- }];
387
- }
405
+ NSArray *displays = [ScreenCapture getAvailableDisplays];
406
+ Napi::Array result = Napi::Array::New(env, displays.count);
388
407
 
389
- free(displayList);
408
+ NSLog(@"Found %lu displays", (unsigned long)displays.count);
390
409
 
391
- // Convert to NAPI array
392
- Napi::Array result = Napi::Array::New(env, displays.count);
393
410
  for (NSUInteger i = 0; i < displays.count; i++) {
394
411
  NSDictionary *display = displays[i];
412
+ NSLog(@"Display %lu: ID=%u, Name=%@, Size=%@x%@",
413
+ (unsigned long)i,
414
+ [display[@"id"] unsignedIntValue],
415
+ display[@"name"],
416
+ display[@"width"],
417
+ display[@"height"]);
418
+
395
419
  Napi::Object displayObj = Napi::Object::New(env);
396
420
  displayObj.Set("id", Napi::Number::New(env, [display[@"id"] unsignedIntValue]));
397
421
  displayObj.Set("name", Napi::String::New(env, [display[@"name"] UTF8String]));
@@ -406,6 +430,7 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
406
430
  return result;
407
431
 
408
432
  } @catch (NSException *exception) {
433
+ NSLog(@"Exception in GetDisplays: %@", exception);
409
434
  return Napi::Array::New(env, 0);
410
435
  }
411
436
  }
@@ -536,10 +561,34 @@ Napi::Value GetDisplayThumbnail(const Napi::CallbackInfo& info) {
536
561
  }
537
562
 
538
563
  @try {
564
+ // Verify display exists
565
+ CGDirectDisplayID activeDisplays[32];
566
+ uint32_t displayCount;
567
+ CGError err = CGGetActiveDisplayList(32, activeDisplays, &displayCount);
568
+
569
+ if (err != kCGErrorSuccess) {
570
+ NSLog(@"Failed to get active display list: %d", err);
571
+ return env.Null();
572
+ }
573
+
574
+ bool displayFound = false;
575
+ for (uint32_t i = 0; i < displayCount; i++) {
576
+ if (activeDisplays[i] == displayID) {
577
+ displayFound = true;
578
+ break;
579
+ }
580
+ }
581
+
582
+ if (!displayFound) {
583
+ NSLog(@"Display ID %u not found in active displays", displayID);
584
+ return env.Null();
585
+ }
586
+
539
587
  // Create display image
540
588
  CGImageRef displayImage = CGDisplayCreateImage(displayID);
541
589
 
542
590
  if (!displayImage) {
591
+ NSLog(@"CGDisplayCreateImage failed for display ID: %u", displayID);
543
592
  return env.Null();
544
593
  }
545
594
 
@@ -547,6 +596,8 @@ Napi::Value GetDisplayThumbnail(const Napi::CallbackInfo& info) {
547
596
  size_t originalWidth = CGImageGetWidth(displayImage);
548
597
  size_t originalHeight = CGImageGetHeight(displayImage);
549
598
 
599
+ NSLog(@"Original dimensions: %zux%zu", originalWidth, originalHeight);
600
+
550
601
  // Calculate scaled dimensions maintaining aspect ratio
551
602
  double scaleX = (double)maxWidth / originalWidth;
552
603
  double scaleY = (double)maxHeight / originalHeight;
@@ -555,6 +606,8 @@ Napi::Value GetDisplayThumbnail(const Napi::CallbackInfo& info) {
555
606
  size_t thumbnailWidth = (size_t)(originalWidth * scale);
556
607
  size_t thumbnailHeight = (size_t)(originalHeight * scale);
557
608
 
609
+ NSLog(@"Thumbnail dimensions: %zux%zu (scale: %f)", thumbnailWidth, thumbnailHeight, scale);
610
+
558
611
  // Create scaled image
559
612
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
560
613
  CGContextRef context = CGBitmapContextCreate(
@@ -564,43 +617,61 @@ Napi::Value GetDisplayThumbnail(const Napi::CallbackInfo& info) {
564
617
  8,
565
618
  thumbnailWidth * 4,
566
619
  colorSpace,
567
- kCGImageAlphaPremultipliedLast
620
+ kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big
568
621
  );
569
622
 
570
- if (context) {
571
- CGContextDrawImage(context, CGRectMake(0, 0, thumbnailWidth, thumbnailHeight), displayImage);
572
- CGImageRef thumbnailImage = CGBitmapContextCreateImage(context);
573
-
574
- if (thumbnailImage) {
575
- // Convert to PNG data
576
- NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:thumbnailImage];
577
- NSData *pngData = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
578
-
579
- if (pngData) {
580
- // Convert to Base64
581
- NSString *base64String = [pngData base64EncodedStringWithOptions:0];
582
- std::string base64Std = [base64String UTF8String];
583
-
584
- CGImageRelease(thumbnailImage);
585
- CGContextRelease(context);
586
- CGColorSpaceRelease(colorSpace);
587
- CGImageRelease(displayImage);
588
-
589
- return Napi::String::New(env, base64Std);
590
- }
591
-
592
- CGImageRelease(thumbnailImage);
593
- }
594
-
623
+ if (!context) {
624
+ NSLog(@"Failed to create bitmap context");
625
+ CGImageRelease(displayImage);
626
+ CGColorSpaceRelease(colorSpace);
627
+ return env.Null();
628
+ }
629
+
630
+ // Set interpolation quality for better scaling
631
+ CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
632
+
633
+ // Draw the image
634
+ CGContextDrawImage(context, CGRectMake(0, 0, thumbnailWidth, thumbnailHeight), displayImage);
635
+ CGImageRef thumbnailImage = CGBitmapContextCreateImage(context);
636
+
637
+ if (!thumbnailImage) {
638
+ NSLog(@"Failed to create thumbnail image");
595
639
  CGContextRelease(context);
640
+ CGImageRelease(displayImage);
641
+ CGColorSpaceRelease(colorSpace);
642
+ return env.Null();
596
643
  }
597
644
 
645
+ // Convert to PNG data
646
+ NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:thumbnailImage];
647
+ NSDictionary *properties = @{NSImageCompressionFactor: @0.8};
648
+ NSData *pngData = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:properties];
649
+
650
+ if (!pngData) {
651
+ NSLog(@"Failed to convert image to PNG data");
652
+ CGImageRelease(thumbnailImage);
653
+ CGContextRelease(context);
654
+ CGImageRelease(displayImage);
655
+ CGColorSpaceRelease(colorSpace);
656
+ return env.Null();
657
+ }
658
+
659
+ // Convert to Base64
660
+ NSString *base64String = [pngData base64EncodedStringWithOptions:0];
661
+ std::string base64Std = [base64String UTF8String];
662
+
663
+ NSLog(@"Successfully created thumbnail with base64 length: %lu", (unsigned long)base64Std.length());
664
+
665
+ // Cleanup
666
+ CGImageRelease(thumbnailImage);
667
+ CGContextRelease(context);
598
668
  CGColorSpaceRelease(colorSpace);
599
669
  CGImageRelease(displayImage);
600
670
 
601
- return env.Null();
671
+ return Napi::String::New(env, base64Std);
602
672
 
603
673
  } @catch (NSException *exception) {
674
+ NSLog(@"Exception in GetDisplayThumbnail: %@", exception);
604
675
  return env.Null();
605
676
  }
606
677
  }
@@ -0,0 +1,19 @@
1
+ #ifndef SCREEN_CAPTURE_H
2
+ #define SCREEN_CAPTURE_H
3
+
4
+ #import <Foundation/Foundation.h>
5
+ #import <CoreGraphics/CoreGraphics.h>
6
+
7
+ @interface ScreenCapture : NSObject
8
+
9
+ + (NSArray *)getAvailableDisplays;
10
+ + (BOOL)captureDisplay:(CGDirectDisplayID)displayID
11
+ toFile:(NSString *)filePath
12
+ rect:(CGRect)rect
13
+ includeCursor:(BOOL)includeCursor;
14
+ + (CGImageRef)createScreenshotFromDisplay:(CGDirectDisplayID)displayID
15
+ rect:(CGRect)rect;
16
+
17
+ @end
18
+
19
+ #endif // SCREEN_CAPTURE_H