1/*cc -o /tmp/readfb readfb.c -framework IOKit -framework ApplicationServices -Wall -g -arch i386
2*/
3
4
5#include <IOKit/IOKitLib.h>
6#include <ApplicationServices/ApplicationServices.h>
7#include <IOKit/i2c/IOI2CInterface.h>
8#include <IOKit/graphics/IOFramebufferShared.h>
9#include <IOKit/graphics/IOAccelSurfaceControl.h>
10#include <IOKit/graphics/IOGraphicsLib.h>
11
12
13IOReturn
14IOAccelReadFramebuffer(io_service_t framebuffer, uint32_t width, uint32_t height, size_t rowBytes,
15                        vm_address_t * result, vm_size_t * bytecount)
16{
17    IOReturn     err;
18    io_service_t accelerator;
19    UInt32       framebufferIndex;
20    size_t       size = 0;
21    UInt32       surfaceID = 155;
22    vm_address_t buffer = 0;
23    IOAccelConnect                      connect = MACH_PORT_NULL;
24    IOAccelDeviceRegion *               region = NULL;
25    IOAccelSurfaceInformation           surfaceInfo;
26    IOGraphicsAcceleratorInterface **   interface = 0;
27    IOBlitterPtr                        copyRegionProc;
28    IOBlitCopyRegion                    op;
29    IOBlitSurface                       dest;
30    SInt32                              quality = 0;
31
32    *result    = 0;
33    *bytecount = 0;
34    dest.interfaceRef = NULL;
35
36    do
37    {
38        err = IOAccelFindAccelerator(framebuffer, &accelerator, &framebufferIndex);
39        if (kIOReturnSuccess != err) continue;
40        err = IOAccelCreateSurface(accelerator, surfaceID,
41                                   kIOAccelSurfaceModeWindowedBit | kIOAccelSurfaceModeColorDepth8888,
42                                   &connect);
43        IOObjectRelease(accelerator);
44        if (kIOReturnSuccess != err) continue;
45
46        size = rowBytes * height;
47
48        region = calloc(1, sizeof (IOAccelDeviceRegion) + sizeof(IOAccelBounds));
49        if (!region) continue;
50
51        region->num_rects = 1;
52        region->bounds.x = region->rect[0].x = 0;
53        region->bounds.y = region->rect[0].y = 0;
54        region->bounds.h = region->rect[0].h = height;
55        region->bounds.w = region->rect[0].w = width;
56
57        err = vm_allocate(mach_task_self(), &buffer, size,
58                          VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_COREGRAPHICS_FRAMEBUFFERS));
59        if (kIOReturnSuccess != err) continue;
60
61        err = IOAccelSetSurfaceFramebufferShapeWithBackingAndLength(connect, region,
62                    kIOAccelSurfaceShapeIdentityScaleBit|
63                    kIOAccelSurfaceShapeNonBlockingBit|
64                    //kIOAccelSurfaceShapeStaleBackingBit |
65                    kIOAccelSurfaceShapeNonSimpleBit,
66                    0,
67                    (IOVirtualAddress) buffer,
68                    (UInt32) rowBytes,
69                    (UInt32) size);
70        if (kIOReturnSuccess != err) continue;
71        err = IOCreatePlugInInterfaceForService(framebuffer,
72                            kIOGraphicsAcceleratorTypeID,
73                            kIOGraphicsAcceleratorInterfaceID,
74                            (IOCFPlugInInterface ***)&interface, &quality );
75        if (kIOReturnSuccess != err) continue;
76        err = (*interface)->GetBlitter(interface,
77                                    kIOBlitAllOptions,
78                                    (kIOBlitTypeCopyRegion | kIOBlitTypeOperationType0),
79                                    kIOBlitSourceFramebuffer,
80                                    &copyRegionProc);
81        if (kIOReturnSuccess != err) continue;
82        err = (*interface)->AllocateSurface(interface, kIOBlitHasCGSSurface, &dest, (void *) surfaceID);
83        if (kIOReturnSuccess != err) continue;
84        err = (*interface)->SetDestination(interface, kIOBlitSurfaceDestination, &dest);
85        if (kIOReturnSuccess != err) continue;
86        op.region = region;
87        op.deltaX = 0;
88        op.deltaY = 0;
89        err = (*copyRegionProc)(interface,
90                        kNilOptions,
91                        (kIOBlitTypeCopyRegion | kIOBlitTypeOperationType0),
92                        kIOBlitSourceFramebuffer,
93                        &op.operation,
94                        (void *) 0);
95        if (kIOReturnSuccess != err) continue;
96        (*interface)->Flush(interface, kNilOptions);
97        err = IOAccelWriteLockSurfaceWithOptions(connect,
98                kIOAccelSurfaceLockInBacking, &surfaceInfo, sizeof(surfaceInfo));
99        if (kIOReturnSuccess != err) continue;
100
101        (void ) IOAccelWriteUnlockSurfaceWithOptions(connect, kIOAccelSurfaceLockInBacking);
102    }
103    while (false);
104
105    if (dest.interfaceRef) (*interface)->FreeSurface(interface, kIOBlitHasCGSSurface, &dest);
106
107    // destroy the surface
108    if (connect) (void) IOAccelDestroySurface(connect);
109
110    if (region) free(region);
111
112    if (interface) IODestroyPlugInInterface((IOCFPlugInInterface **)interface);
113
114    if (kIOReturnSuccess == err)
115    {
116        *result    = buffer;
117        *bytecount = size;
118    }
119
120    return (err);
121}
122
123int main(int argc, char * argv[])
124{
125    IOReturn            err;
126    CGDirectDisplayID   dspy = CGMainDisplayID();
127    io_service_t        framebuffer;
128    CGRect              bounds;
129    vm_address_t        buffer;
130    vm_size_t           size, rowBytes;
131
132    framebuffer = CGDisplayIOServicePort(dspy);
133    assert (framebuffer != MACH_PORT_NULL);
134    dspy = CGMainDisplayID();
135    bounds = CGDisplayBounds(dspy);
136    rowBytes = CGDisplayBytesPerRow(dspy);
137
138    err = IOAccelReadFramebuffer(framebuffer, bounds.size.width, bounds.size.height, rowBytes,
139                                 &buffer, &size);
140    if (kIOReturnSuccess == err)
141    {
142        fprintf(stderr, "writing 0x%x bytes from 0x%x\n", size, buffer);
143        write(STDOUT_FILENO, (const void *) buffer, size);
144        vm_deallocate(mach_task_self(), buffer, size);
145    }
146    return (0);
147}
148
149