itermediate working result
[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   if (!disp) {
36     i_push_error(0, "No main display");
37     return NULL;
38   }
39   
40   /* for now, only interested in the first display */
41   rect = CGDisplayBounds(disp);
42   screen_width = rect.size.width;
43   screen_height = rect.size.height;
44
45   /* adjust negative/zero values to window size */
46   if (left < 0)
47     left += screen_width;
48   if (top < 0)
49     top += screen_height;
50   if (right <= 0)
51     right += screen_width;
52   if (bottom <= 0)
53     bottom += screen_height;
54   
55   /* clamp */
56   if (left < 0)
57     left = 0;
58   if (right > screen_width)
59     right = screen_width;
60   if (top < 0)
61     top = 0;
62   if (bottom > screen_height)
63     bottom = screen_height;
64
65   /* validate */
66   if (right <= left || bottom <= top) {
67     i_push_error(0, "image would be empty");
68     return NULL;
69   }
70
71   width = right - left;
72   height = bottom - top;
73
74   /* select a pixel format */
75   pix_attrs[1] = CGDisplayIDToOpenGLDisplayMask(disp);
76   err = CGLChoosePixelFormat(pix_attrs, &pix, &npix);
77   if (err) {
78     i_push_errorf(err, "CGLChoosePixelFormat: %d", (int)err);
79     return NULL;
80   }
81   if (!npix) {
82     i_push_error(0, "No pixel format found - hidden display?");
83     return NULL;
84   }
85
86   /* make ourselves a context */
87   err = CGLCreateContext(pix, NULL, &ctx);
88   CGLDestroyPixelFormat(pix);
89   if (err) {
90     i_push_errorf(err, "CGLCreateContext: %d", (int)err);
91     return NULL;
92   }
93
94   err = CGLSetCurrentContext(ctx);
95   if (err) {
96     i_push_errorf(err, "CGLSetCurrentContext: %d", (int)err);
97     return NULL;
98   }
99
100   err = CGLSetFullScreen(ctx);
101   if (err) {
102     i_push_errorf(err, "CGLSetFullScreen: %d", (int)err);
103     return NULL;
104   }
105
106   /* capture */
107   im = i_img_8_new(width, height, 3);
108   if (im) {
109     size_t line_size = width * 4; 
110     size_t buf_size = line_size * height;
111     unsigned char *buf = malloc(buf_size);
112     i_img_dim y = height - 1;
113     i_color *bufp = (i_color *)buf; /* hackish */
114
115     /* GL has the vertical axis going from bottom to top, so translate it */
116
117     glReadBuffer(GL_FRONT);
118     glReadPixels(left, screen_height - top - height, width, height,
119                  GL_RGBA, GL_UNSIGNED_BYTE, buf);
120
121     /* transfer */
122     while (y >= 0) {
123       i_plin(im, 0, width, y, bufp);
124       bufp += width;
125       --y;
126     }
127     
128     free(buf);
129
130     i_tags_setn(&im->tags, "ss_window_width", width);
131     i_tags_setn(&im->tags, "ss_window_height", height);
132     i_tags_set(&im->tags, "ss_type", "Darwin", 6);
133     i_tags_setn(&im->tags, "ss_left", left);
134     i_tags_setn(&im->tags, "ss_top", top);
135   }
136
137   /* clean up */
138   CGLSetCurrentContext(NULL);
139   CGLDestroyContext(ctx);
140
141   return im;
142 }