如何获取 MAC 上所有显示的屏幕截图?

How can I get screenshot from all displays on MAC?

我尝试将与我的 MAC 连接的所有显示器的屏幕截图获取到一张照片中。我知道,如果每个显示器的屏幕截图都保存到不同的图片中,我该怎么做,但这不是我想要的。我找到函数 CGGetDisplaysWithRect,但我的解决方案不起作用,因为输出图片是空的。我预计,函数 CGDisplayCreateImageForRect (*displays, rect) 会出现问题,因为第一个参数必须是 CGDirectDisplayID 类型,而不是 CGDirectDisplayID*。但是我找不到函数,它可以用一些CGDirectDisplayID对象创建一张图片。
请帮帮我!!!

#include <stdio.h>
#include <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    CGDisplayCount displayCount;
    CGDirectDisplayID displays[32];
    memset(&displays, 0, 32);
    CGImageRef image[32];
    CGRect rect = CGRectNull;

    //grab the active displays
    if (CGGetActiveDisplayList(32, displays, &displayCount) != kCGErrorSuccess)
    {
        printf("Error occured: %s\n", strerror(errno));
    }

    //go through the list
    for (int i = 0; i < displayCount; i++)
    {
        if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
        {
            continue;
        }
        //return the smallest rectangle wich contain the two source rectangles
        rect = CGRectUnion(rect, CGDisplayBounds(displays[i]));
        if (CGRectIsNull(rect))
        {
            printf("Error: %s", strerror(errno));
        }
    }

    CGFloat whitePoint[3];
    CGFloat blackPoint[3];
    CGFloat gamma[3];
    CGFloat matrix[9];

    CGColorSpaceRef colorSpace = CGColorSpaceCreateCalibratedRGB (&whitePoint[3], &blackPoint[3], &gamma[3], &matrix[9] );
    if(colorSpace == NULL)
    {
        printf("Error: %s", strerror(errno));
    }

    //CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);

    //Create bmp context for image
    CGContextRef context = CGBitmapContextCreate(NULL,                      //data
                                          CGRectGetWidth(rect),         //width
                                          CGRectGetHeight(rect),        //height
                                          8,                            //bitPerComponent, for RGB must be 8
                                          0,                            //if data == NULL, it must be 0
                                          colorSpace,                   //colorspace device independent
                                          kCGBitmapByteOrderDefault );  //bitmap info
    if(context == NULL)
    {
        printf("Error: %s", strerror(errno));
    }

    //Create a snapshot image
    for (int i = 0; i < displayCount; i++)
    {
        image[i] = CGBitmapContextCreateImage(context);
        if(image == NULL)
        {
            //printf("Error: %s", strerror(errno));
        }
    }

    //Create destination to image
    CFURLRef url = CFURLCreateWithString ( kCFAllocatorDefault, CFSTR("out.bmp"), NULL);
    if(url == NULL)
    {
        printf("Error: %s", strerror(errno));
    }
    CFErrorRef *error = NULL;
    CFURLRef urlToFile = CFURLCreateFilePathURL ( kCFAllocatorDefault, url, error );
    if(urlToFile == NULL)
    {
        //printf("Error: %s", error);
    }

    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL(urlToFile, kUTTypeBMP, displayCount, NULL);
    if(imageDestination == NULL)
    {
        printf("Error: %s", strerror(errno));
    }

    //CGImageDestinationAddImage(imageDestination, image, NULL);
    CGImageDestinationFinalize(imageDestination);

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    CFRelease(imageDestination);
    return 0;
} 

更新:我尝试了下面告诉我的 smth,但现在我得到错误:

错误:CGBitmapContextCreate:不支持的参数组合:8 个整数bits/component; 24 bits/pixel; 3 分量颜色 space; kCGImageAlphaNone;第3456章bytes/row.

这里有一些代码可以做到这一点。一方面,我还不能在多显示器系统上进行测试,但另一方面,代码是在没有任何关于使用哪个显示器或它的位置的假设的情况下编写的。所以,它应该可以工作。

    CGDirectDisplayID displays[32];
    uint32_t count;
    if (CGGetActiveDisplayList(sizeof(displays)/sizeof(displays[0]), displays, &count) != kCGErrorSuccess)
    {
        NSLog(@"failed to get display list");
        exit(EXIT_FAILURE);
    }

    CGRect rect = CGRectNull;
    CGRect primaryDisplayRect = CGRectZero;
    for (uint32_t i = 0; i < count; i++)
    {
        // if display is secondary mirror of another display, skip it
        if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
            continue;

        CGRect displayRect = CGDisplayBounds(displays[i]);
        if (i == 0)
            primaryDisplayRect = displayRect;
        displayRect.origin.y = CGRectGetMaxY(primaryDisplayRect) - CGRectGetMaxY(displayRect);
        rect = CGRectUnion(rect, displayRect);
    }

    NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
                                                                         pixelsWide:CGRectGetWidth(rect)
                                                                         pixelsHigh:CGRectGetHeight(rect)
                                                                      bitsPerSample:8
                                                                    samplesPerPixel:4
                                                                           hasAlpha:YES
                                                                           isPlanar:NO
                                                                     colorSpaceName:NSCalibratedRGBColorSpace
                                                                       bitmapFormat:0
                                                                        bytesPerRow:0
                                                                       bitsPerPixel:32];
    if (!imageRep)
    {
        NSLog(@"failed to create bitmap image rep");
        exit(EXIT_FAILURE);
    }

    NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
    if (!context)
    {
        NSLog(@"failed to create graphics context");
        exit(EXIT_FAILURE);
    }

    [NSGraphicsContext saveGraphicsState];
    {
        [NSGraphicsContext setCurrentContext:context];
        CGContextRef cgcontext = [context graphicsPort];

        CGContextClearRect(cgcontext, CGRectMake(0, 0, CGRectGetWidth(rect), CGRectGetHeight(rect)));

        for (uint32_t i = 0; i < count; i++)
        {
            // if display is secondary mirror of another display, skip it
            if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
                continue;

            CGRect displayRect = CGDisplayBounds(displays[i]);
            displayRect.origin.y = CGRectGetMaxY(primaryDisplayRect) - CGRectGetMaxY(displayRect);
            CGImageRef image = CGDisplayCreateImage(displays[i]);
            if (!image)
                continue;

            CGRect dest = CGRectMake(displayRect.origin.x - rect.origin.x,
                                     displayRect.origin.y - rect.origin.y,
                                     displayRect.size.width,
                                     displayRect.size.height);
            CGContextDrawImage(cgcontext, dest, image);
            CGImageRelease(image);
        }

        [[NSGraphicsContext currentContext] flushGraphics];
    }
    [NSGraphicsContext restoreGraphicsState];


    NSData* data = [imageRep representationUsingType:NSPNGFileType properties:@{ }];
    [data writeToFile:@"/tmp/screenshot.png" atomically:YES];

主要的可能失败点是为一个大到足以包含所有显示器的矩形分配位图图像上下文。请注意,所有显示器的总矩形可以比任何一个显示器的矩形大得多。例如,如果两个显示器的排列方式使它们在一个角上几乎不接触,则围绕它们的矩形将几乎与 2x2 排列的四个显示器一样大。对于三台显示器,它可以像 9 台显示器一样大,呈 3x3 排列。等等


这是一个不使用 Cocoa 的实现,只使用 Core Graphics:

    CGDirectDisplayID displays[32];
    uint32_t count;
    if (CGGetActiveDisplayList(sizeof(displays)/sizeof(displays[0]), displays, &count) != kCGErrorSuccess)
    {
        NSLog(@"failed to get display list");
        exit(EXIT_FAILURE);
    }

    CGRect rect = CGRectNull;
    for (uint32_t i = 0; i < count; i++)
    {
        // if display is secondary mirror of another display, skip it
        if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
            continue;

        rect = CGRectUnion(rect, CGDisplayBounds(displays[i]));
    }

    CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    if (!colorspace)
    {
        NSLog(@"failed to create colorspace");
        exit(EXIT_FAILURE);
    }

    CGContextRef cgcontext = CGBitmapContextCreate(NULL, CGRectGetWidth(rect), CGRectGetHeight(rect), 8, 0, colorspace, (CGBitmapInfo)kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(colorspace);
    if (!cgcontext)
    {
        NSLog(@"failed to create bitmap context");
        exit(EXIT_FAILURE);
    }

    CGContextClearRect(cgcontext, CGRectMake(0, 0, CGRectGetWidth(rect), CGRectGetHeight(rect)));

    for (uint32_t i = 0; i < count; i++)
    {
        // if display is secondary mirror of another display, skip it
        if (CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
            continue;

        CGRect displayRect = CGDisplayBounds(displays[i]);
        CGImageRef image = CGDisplayCreateImage(displays[i]);
        if (!image)
            continue;

        CGRect dest = CGRectMake(displayRect.origin.x - rect.origin.x,
                                 displayRect.origin.y - rect.origin.y,
                                 displayRect.size.width,
                                 displayRect.size.height);
        CGContextDrawImage(cgcontext, dest, image);
        CGImageRelease(image);
    }

    CGImageRef image = CGBitmapContextCreateImage(cgcontext);
    CGContextRelease(cgcontext);
    if (!image)
    {
        NSLog(@"failed to create image from bitmap context");
        exit(EXIT_FAILURE);
    }

    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/tmp/screenshot.png"), kCFURLPOSIXPathStyle, NO);
    if (!url)
    {
        NSLog(@"failed to create URL");
        exit(EXIT_FAILURE);
    }

    CGImageDestinationRef dest = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
    CFRelease(url);
    if (!dest)
    {
        NSLog(@"failed to create image destination");
        exit(EXIT_FAILURE);
    }

    CGImageDestinationAddImage(dest, image, NULL);
    CGImageRelease(image);
    if (!CGImageDestinationFinalize(dest))
    {
        NSLog(@"failed to finalize image destination");
        exit(EXIT_FAILURE);
    }
    CFRelease(dest);