- add support for getting a subimage of the window
[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
TC
18imss_x11(unsigned long display_ul, int window_id,
19 int left, int top, int right, int bottom) {
b2239557
TC
20 Display *display = (Display *)display_ul;
21 int own_display = 0; /* non-zero if we connect */
22 GC gc;
23 XImage *image;
24 XWindowAttributes attr;
25 i_img *result;
26 i_color *line, *cp;
27 int x, y;
28 XColor *colors;
29 XErrorHandler old_handler;
87cd516f 30 int width, height;
b2239557
TC
31
32 i_clear_error();
33
34 /* we don't want the default noisy error handling */
35 old_handler = XSetErrorHandler(my_handler);
36
37 if (!display) {
38 display = XOpenDisplay(NULL);
39 ++own_display;
40 if (!display) {
41 XSetErrorHandler(old_handler);
42 i_push_error(0, "No display supplied and cannot connect");
43 return NULL;
44 }
45 }
46
47 if (!window_id) {
48 int screen = DefaultScreen(display);
49 window_id = RootWindow(display, 0);
50 }
51
52 if (!XGetWindowAttributes(display, window_id, &attr)) {
53 XSetErrorHandler(old_handler);
54 if (own_display)
55 XCloseDisplay(display);
56 i_push_error(0, "Cannot XGetWindowAttributes");
57 return NULL;
58 }
59
87cd516f
TC
60 /* adjust negative/zero values to window size */
61 if (left < 0)
62 left += attr.width;
63 if (top < 0)
64 top += attr.height;
65 if (right <= 0)
66 right += attr.width;
67 if (bottom <= 0)
68 bottom += attr.height;
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) {
82 XSetErrorHandler(old_handler);
83 if (own_display)
84 XCloseDisplay(display);
85 i_push_error(0, "image would be empty");
86 return NULL;
87 }
88 width = right - left;
89 height = bottom - top;
90 image = XGetImage(display, window_id, left, top, width, height,
b2239557
TC
91 -1, ZPixmap);
92 if (!image) {
93 XSetErrorHandler(old_handler);
94 if (own_display)
95 XCloseDisplay(display);
96 i_push_error(0, "Cannot XGetImage");
97 return NULL;
98 }
99
87cd516f
TC
100 result = i_img_8_new(width, height, 3);
101 line = mymalloc(sizeof(i_color) * width);
102 colors = mymalloc(sizeof(XColor) * width);
103 for (y = 0; y < height; ++y) {
b2239557
TC
104 cp = line;
105 /* XQueryColors seems to be a round-trip, so do one big request
106 instead of one per pixel */
87cd516f 107 for (x = 0; x < width; ++x) {
b2239557
TC
108 colors[x].pixel = XGetPixel(image, x, y);
109 }
87cd516f
TC
110 XQueryColors(display, attr.colormap, colors, width);
111 for (x = 0; x < width; ++x) {
b2239557
TC
112 cp->rgb.r = colors[x].red >> 8;
113 cp->rgb.g = colors[x].green >> 8;
114 cp->rgb.b = colors[x].blue >> 8;
115 ++cp;
116 }
87cd516f 117 i_plin(result, 0, width, y, line);
b2239557 118 }
bc7a6f7b
TC
119 myfree(line);
120 myfree(colors);
121 XDestroyImage(image);
b2239557
TC
122
123 XSetErrorHandler(old_handler);
124 if (own_display)
125 XCloseDisplay(display);
126
127 return result;
128}
129
130unsigned long
131imss_x11_open(char const *display_name) {
132 XErrorHandler old_handler;
133 Display *display;
134
135 i_clear_error();
bc7a6f7b 136 old_handler = XSetErrorHandler(my_handler);
b2239557
TC
137 display = XOpenDisplay(display_name);
138 if (!display)
139 i_push_errorf(0, "Cannot connect to X server %s", XDisplayName(display_name));
140
141 XSetErrorHandler(old_handler);
142
143 return (unsigned long)display;
144}
145
146void
147imss_x11_close(unsigned long display) {
148 XCloseDisplay((Display *)display);
149}