initial OS X screenshot support
[imager-screenshot.git] / scdarwin.c
1 /* Darwin support via OpenGL */
2 #include "imext.h"
3 #include "imss.h"
4 #include <ApplicationServices/ApplicationServices.h>
5 #include "OpenGL/OpenGL.h"
6 #include "OpenGL/gl.h"
7 #include "OpenGL/glu.h"
8 #include "OpenGL/glext.h"
9
10 i_img *
11 imss_darwin(i_img_dim left, i_img_dim top, i_img_dim right, i_img_dim bottom) {
12   CGDisplayCount count;
13   CGDisplayErr err;
14   CGRect rect;
15   CGLPixelFormatObj pix;
16   GLint npix;
17   CGLContextObj ctx;
18   i_img *im;
19   CGDirectDisplayID disp;
20   i_img_dim screen_width, screen_height;
21   i_img_dim width, height;
22
23   CGLPixelFormatAttribute pix_attrs[] =
24     {
25       kCGLPFADisplayMask, 0, /* filled in later */
26       kCGLPFAColorSize, 24,
27       kCGLPFAAlphaSize, 0,
28       kCGLPFAFullScreen,
29       0
30     };
31
32   i_clear_error();
33
34   disp = CGMainDisplayID();
35   
36   /* for now, only interested in the first display */
37   rect = CGDisplayBounds(disp);
38   screen_width = rect.size.width;
39   screen_height = rect.size.height;
40
41   /* adjust negative/zero values to window size */
42   if (left < 0)
43     left += screen_width;
44   if (top < 0)
45     top += screen_height;
46   if (right <= 0)
47     right += screen_width;
48   if (bottom <= 0)
49     bottom += screen_height;
50   
51   /* clamp */
52   if (left < 0)
53     left = 0;
54   if (right > screen_width)
55     right = screen_width;
56   if (top < 0)
57     top = 0;
58   if (bottom > screen_height)
59     bottom = screen_height;
60
61   /* validate */
62   if (right <= left || bottom <= top) {
63     i_push_error(0, "image would be empty");
64     return NULL;
65   }
66
67   width = right - left;
68   height = bottom - top;
69
70   /* select a pixel format */
71   pix_attrs[1] = CGDisplayIDToOpenGLDisplayMask(disp);
72   err = CGLChoosePixelFormat(pix_attrs, &pix, &npix);
73   if (err) {
74     i_push_errorf(err, "CGLChoosePixelFormat: %d", (int)err);
75     return NULL;
76   }
77   if (!npix) {
78     i_push_error(0, "No pixel format found");
79     return NULL;
80   }
81
82   /* make ourselves a context */
83   err = CGLCreateContext(pix, NULL, &ctx);
84   CGLDestroyPixelFormat(pix);
85   if (err) {
86     i_push_errorf(err, "CGLCreateContext: %d", (int)err);
87     return NULL;
88   }
89
90   err = CGLSetCurrentContext(ctx);
91   if (err) {
92     i_push_errorf(err, "CGLSetCurrentContext: %d", (int)err);
93     return NULL;
94   }
95
96   err = CGLSetFullScreen(ctx);
97   if (err) {
98     i_push_errorf(err, "CGLSetFullScreen: %d", (int)err);
99     return NULL;
100   }
101
102   /* capture */
103   im = i_img_8_new(width, height, 3);
104   if (im) {
105     size_t line_size = width * 4; 
106     size_t buf_size = line_size * height;
107     unsigned char *buf = malloc(buf_size);
108     i_img_dim y = height - 1;
109     i_color *bufp = (i_color *)buf; /* hackish */
110
111     /* GL has the vertical axis going from bottom to top, so translate it */
112
113     glReadBuffer(GL_FRONT);
114     glReadPixels(left, screen_height - top - height, width, height,
115                  GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buf);
116
117     /* transfer */
118     while (y >= 0) {
119       i_plin(im, 0, width, y, bufp);
120       bufp += width;
121       --y;
122     }
123     
124     free(buf);
125   }
126
127   /* clean up */
128   CGLSetCurrentContext(NULL);
129   CGLDestroyContext(ctx);
130
131   return im;
132 }