0.014 release
[imager-screenshot.git] / scx11.c
CommitLineData
b2239557
TC
1#include "imext.h"
2#include <X11/Xlib.h>
87cd516f
TC
3#include <X11/Xutil.h>
4#include "imss.h"
b2239557
TC
5
6static
7int
8my_handler(Display *display, XErrorEvent *error) {
9 char buffer[500];
10
11 XGetErrorText(display, error->error_code, buffer, sizeof(buffer));
12 i_push_error(error->error_code, buffer);
87cd516f
TC
13
14 return 0;
b2239557
TC
15}
16
17i_img *
87cd516f 18imss_x11(unsigned long display_ul, int window_id,
d3f89dd3 19 int left, int top, int right, int bottom, int direct) {
b2239557
TC
20 Display *display = (Display *)display_ul;
21 int own_display = 0; /* non-zero if we connect */
d3f89dd3 22 XImage *image = NULL;
b2239557
TC
23 XWindowAttributes attr;
24 i_img *result;
25 i_color *line, *cp;
26 int x, y;
27 XColor *colors;
28 XErrorHandler old_handler;
87cd516f 29 int width, height;
d3f89dd3
TC
30 int root_id;
31 int screen;
b2239557
TC
32
33 i_clear_error();
34
35 /* we don't want the default noisy error handling */
36 old_handler = XSetErrorHandler(my_handler);
37
38 if (!display) {
39 display = XOpenDisplay(NULL);
40 ++own_display;
41 if (!display) {
b2239557 42 i_push_error(0, "No display supplied and cannot connect");
d3f89dd3 43 goto fail;
b2239557
TC
44 }
45 }
46
d3f89dd3
TC
47 screen = DefaultScreen(display);
48 root_id = RootWindow(display, screen);
b2239557 49 if (!window_id) {
d3f89dd3 50 window_id = root_id;
b2239557
TC
51 }
52
53 if (!XGetWindowAttributes(display, window_id, &attr)) {
b2239557 54 i_push_error(0, "Cannot XGetWindowAttributes");
d3f89dd3 55 goto fail;
b2239557
TC
56 }
57
87cd516f
TC
58 /* adjust negative/zero values to window size */
59 if (left < 0)
60 left += attr.width;
61 if (top < 0)
62 top += attr.height;
63 if (right <= 0)
64 right += attr.width;
65 if (bottom <= 0)
66 bottom += attr.height;
d3f89dd3
TC
67
68 mm_log((3, "window @(%d,%d) %dx%d\n", attr.x, attr.y, attr.width, attr.height));
87cd516f
TC
69
70 /* clamp */
71 if (left < 0)
72 left = 0;
73 if (right > attr.width)
74 right = attr.width;
75 if (top < 0)
76 top = 0;
77 if (bottom > attr.height)
78 bottom = attr.height;
79
80 /* validate */
81 if (right <= left || bottom <= top) {
87cd516f 82 i_push_error(0, "image would be empty");
d3f89dd3 83 goto fail;
87cd516f
TC
84 }
85 width = right - left;
86 height = bottom - top;
d3f89dd3
TC
87 if (direct) {
88 /* try to get the pixels directly, this returns black images in
89 some ill-determined cases, I suspect compositing.
90 */
91 image = XGetImage(display, window_id, left, top, width, height,
92 -1, ZPixmap);
93 }
94 else {
95 int rootx = left, rooty = top;
96 Window child_id; /* ignored */
97
98 if (root_id != window_id) {
99 XWindowAttributes root_attr;
100
101 if (!XTranslateCoordinates(display, window_id, root_id, left, top,
102 &rootx, &rooty, &child_id)) {
103 i_push_error(0, "could not translate co-ordinates");
104 goto fail;
105 }
106
107 if (!XGetWindowAttributes(display, root_id, &root_attr)) {
108 i_push_error(0, "Cannot XGetWindowAttributes for root");
109 goto fail;
110 }
111
112 /* clip the window to the root, in case it's partly off the edge
113 of the root window
114 */
115 if (rootx < 0) {
116 width += rootx;
117 rootx = 0;
118 }
119 if (rootx + width > root_attr.width) {
120 width = root_attr.width - rootx;
121 }
122 if (rooty < 0) {
123 height += rooty;
124 rooty = 0;
125 }
126 if (rooty + height > root_attr.height) {
127 height = root_attr.height - rooty;
128 }
129
130 if (width == 0 || height == 0) {
131 i_push_error(0, "window is completely clipped by the root window");
132 goto fail;
133 }
134 }
135 image = XGetImage(display, root_id, rootx, rooty,
136 width, height, -1, ZPixmap);
137 }
b2239557 138 if (!image) {
b2239557 139 i_push_error(0, "Cannot XGetImage");
d3f89dd3 140 goto fail;
b2239557
TC
141 }
142
87cd516f
TC
143 result = i_img_8_new(width, height, 3);
144 line = mymalloc(sizeof(i_color) * width);
145 colors = mymalloc(sizeof(XColor) * width);
146 for (y = 0; y < height; ++y) {
b2239557
TC
147 cp = line;
148 /* XQueryColors seems to be a round-trip, so do one big request
149 instead of one per pixel */
87cd516f 150 for (x = 0; x < width; ++x) {
b2239557
TC
151 colors[x].pixel = XGetPixel(image, x, y);
152 }
87cd516f
TC
153 XQueryColors(display, attr.colormap, colors, width);
154 for (x = 0; x < width; ++x) {
b2239557
TC
155 cp->rgb.r = colors[x].red >> 8;
156 cp->rgb.g = colors[x].green >> 8;
157 cp->rgb.b = colors[x].blue >> 8;
158 ++cp;
159 }
87cd516f 160 i_plin(result, 0, width, y, line);
b2239557 161 }
bc7a6f7b
TC
162 myfree(line);
163 myfree(colors);
164 XDestroyImage(image);
b2239557
TC
165
166 XSetErrorHandler(old_handler);
167 if (own_display)
168 XCloseDisplay(display);
169
62b84c46
TC
170 i_tags_setn(&result->tags, "ss_window_width", attr.width);
171 i_tags_setn(&result->tags, "ss_window_height", attr.height);
172 i_tags_set(&result->tags, "ss_type", "X11", 3);
173 i_tags_setn(&result->tags, "ss_left", left);
174 i_tags_setn(&result->tags, "ss_top", top);
175
b2239557 176 return result;
d3f89dd3
TC
177
178 fail:
179 if (image)
180 XDestroyImage(image);
181
182 XSetErrorHandler(old_handler);
183 if (own_display)
184 XCloseDisplay(display);
185 return NULL;
b2239557
TC
186}
187
188unsigned long
189imss_x11_open(char const *display_name) {
190 XErrorHandler old_handler;
191 Display *display;
192
193 i_clear_error();
bc7a6f7b 194 old_handler = XSetErrorHandler(my_handler);
b2239557
TC
195 display = XOpenDisplay(display_name);
196 if (!display)
197 i_push_errorf(0, "Cannot connect to X server %s", XDisplayName(display_name));
198
199 XSetErrorHandler(old_handler);
200
201 return (unsigned long)display;
202}
203
204void
205imss_x11_close(unsigned long display) {
206 XCloseDisplay((Display *)display);
207}