1/*
2cc -g -o /tmp/fbshared fbshared.c -framework ApplicationServices -framework IOKit -Wall -arch i386
3*/
4#define IOCONNECT_MAPMEMORY_10_6    1
5#define IOFB_ARBITRARY_SIZE_CURSOR
6#define IOFB_ARBITRARY_FRAMES_CURSOR    1
7#include <CoreFoundation/CoreFoundation.h>
8#include <ApplicationServices/ApplicationServices.h>
9#include <mach/mach_time.h>
10#include <IOKit/graphics/IOGraphicsLib.h>
11#include <IOKit/graphics/IOFramebufferShared.h>
12#include <IOKit/graphics/IOGraphicsTypesPrivate.h>
13#include <stdlib.h>
14#include <stdio.h>
15
16
17int main(int argc, char * argv[])
18{
19    kern_return_t       kr;
20    io_iterator_t       iter;
21    io_service_t        framebuffer;
22    io_string_t         path;
23    uint32_t            index, maxIndex;
24    io_connect_t        connect;
25    mach_timebase_info_data_t timebase;
26    StdFBShmem_t *      shmem[16];
27    vm_size_t           shmemSize;
28    CFNumberRef         clk, count;
29    vm_address_t        mapAddr;
30
31    kr = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(
32                IOFRAMEBUFFER_CONFORMSTO), &iter);
33    assert( KERN_SUCCESS == kr );
34
35    for ( index = 0;
36            index++, (framebuffer = IOIteratorNext(iter));
37            IOObjectRelease(framebuffer))
38    {
39        kr = IORegistryEntryGetPath(framebuffer, kIOServicePlane, path);
40        assert( KERN_SUCCESS == kr );
41        printf("\n/* [%d] Using device: %s */\n", index, path);
42
43	clk = IORegistryEntryCreateCFProperty(framebuffer, CFSTR(kIOFBCurrentPixelClockKey),
44								kCFAllocatorDefault, kNilOptions);
45	count = IORegistryEntryCreateCFProperty(framebuffer, CFSTR(kIOFBCurrentPixelCountKey),
46								kCFAllocatorDefault, kNilOptions);
47	if (clk && count)
48	{
49	    float num, div;
50	    CFNumberGetValue(clk, kCFNumberFloatType, &num);
51	    CFNumberGetValue(count, kCFNumberFloatType, &div);
52	    printf("clock %.0f, count %.0f, rate %f Hz, period %f us\n",
53		    num, div, num / div, div * 1000 / num);
54	}
55	if (clk)
56	    CFRelease(clk);
57	if (count)
58	    CFRelease(count);
59
60	kr = mach_timebase_info(&timebase);
61	assert(kIOReturnSuccess == kr);
62
63	kr = IOServiceOpen(framebuffer, mach_task_self(), kIOFBSharedConnectType, &connect);
64	if (kIOReturnSuccess != kr)
65	{
66    	    printf("IOServiceOpen(%x)\n", kr);
67    	    continue;
68    	}
69	kr = IOConnectMapMemory(connect, kIOFBCursorMemory, mach_task_self(),
70			&mapAddr, &shmemSize,
71			kIOMapAnywhere);
72	if (kIOReturnSuccess != kr)
73	{
74    	    printf("IOConnectMapMemory(%x)\n", kr);
75    	    continue;
76    	}
77	shmem[index] = (StdFBShmem_t *) mapAddr;
78
79 //     bzero( shmem, shmemSize); // make sure its read only!
80
81	printf("screenBounds (%d, %d), (%d, %d)\n",
82		shmem[index]->screenBounds.minx, shmem[index]->screenBounds.miny,
83		shmem[index]->screenBounds.maxx, shmem[index]->screenBounds.maxy);
84
85	printf("size[0] (%d, %d)\n",
86		shmem[index]->cursorSize[0].width, shmem[index]->cursorSize[0].height);
87
88        if (shmem[index]->hardwareCursorActive && !shmem[index]->cursorShow)
89        do
90        {
91            struct {
92               uint8_t  identLength;
93               uint8_t  colorMapType;
94               uint8_t  dataType;
95               uint8_t  colorMap[5];
96               uint16_t origin[2];
97               uint16_t width;
98               uint16_t height;
99               uint8_t  bitsPerPixel;
100               uint8_t  imageDesc;
101            } hdr;
102            FILE *    f;
103            char      path[256];
104            uint8_t * bits;
105            uint32_t  w, h, y;
106
107            w = shmem[index]->cursorSize[0].width;
108            h = shmem[index]->cursorSize[0].height;
109
110            bzero(&hdr, sizeof(hdr));
111            hdr.dataType     = 2;
112            hdr.width        = OSSwapHostToLittleInt16(w);
113            hdr.height       = OSSwapHostToLittleInt16(h);
114            hdr.bitsPerPixel = 32;
115            hdr.imageDesc    = (1<<5) | 8;
116
117            snprintf(path, sizeof(path), "/tmp/curs%d.tga", index);
118            f = fopen(path, "w" /*"r+"*/);
119            if (!f) continue;
120            fwrite(&hdr, sizeof(hdr), 1, f);
121            bits = (uint8_t *)(uintptr_t) &shmem[index]->cursor[0];
122            for (y = 0; y < h; y++)
123            {
124                fwrite(bits, sizeof(uint32_t), w, f);
125                bits += w * sizeof(uint32_t);
126            }
127            fclose(f);
128        }
129        while (false);
130    }
131
132    maxIndex = index;
133    while (true)
134    {
135	printf("\n");
136	for (index = 0; index < maxIndex; index++)
137	{
138	    if (!shmem[index])
139		continue;
140
141	    uint64_t time  = (((uint64_t) shmem[index]->vblTime.hi) << 32 | shmem[index]->vblTime.lo);
142	    uint64_t delta = (((uint64_t) shmem[index]->vblDelta.hi) << 32 | shmem[index]->vblDelta.lo);
143	    double usecs = delta * timebase.numer / timebase.denom / 1e6;
144
145		if (!delta) continue;
146
147	    printf("[%d] time of last VBL 0x%qx, delta %qd (%f us), count %qd, measured delta %qd(%f%%), drift %qd(%qd%%)\n",
148		    index, time, delta, usecs, shmem[index]->vblCount,
149		    shmem[index]->vblDeltaMeasured, ((shmem[index]->vblDeltaMeasured * 100.0) / delta),
150		    shmem[index]->vblDrift, ((shmem[index]->vblDrift * 100) / delta));
151	}
152	for (index = 0; index < maxIndex; index++)
153	{
154	    if (!shmem[index]) continue;
155		if ((shmem[index]->screenBounds.maxx - shmem[index]->screenBounds.minx) < 128) continue;
156
157	    printf("[%d] cursorShow %d, hw %d, frame %d, loc (%d, %d), hs (%d, %d), cursorRect (%d, %d), (%d, %d), saveRect (%d, %d), (%d, %d)\n",
158		    index,
159		    shmem[index]->cursorShow, shmem[index]->hardwareCursorActive,
160		    shmem[index]->frame, shmem[index]->cursorLoc.x, shmem[index]->cursorLoc.y,
161			shmem[index]->hotSpot[0].x, shmem[index]->hotSpot[0].y,
162		    shmem[index]->cursorRect.minx, shmem[index]->cursorRect.miny,
163		    shmem[index]->cursorRect.maxx, shmem[index]->cursorRect.maxy,
164		    shmem[index]->saveRect.minx, shmem[index]->saveRect.miny,
165		    shmem[index]->saveRect.maxx, shmem[index]->saveRect.maxy);
166	}
167	sleep(1);
168    }
169
170    exit(0);
171    return(0);
172}
173
174