initial OS X screenshot support
[imager-screenshot.git] / scdarwin.c
CommitLineData
bc99c241
TC
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
10i_img *
11imss_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}