- add support for getting a subimage of the window
[imager-screenshot.git] / scx11.c
1 #include "imext.h"
2 #include <X11/Xlib.h>
3 #include <X11/Xutil.h>
4 #include "imss.h"
5
6 static
7 int
8 my_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);
13
14   return 0;
15 }
16
17 i_img *
18 imss_x11(unsigned long display_ul, int window_id,
19          int left, int top, int right, int bottom) {
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;
30   int width, height;
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
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,
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
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) {
104     cp = line;
105     /* XQueryColors seems to be a round-trip, so do one big request
106        instead of one per pixel */
107     for (x = 0; x < width; ++x) {
108       colors[x].pixel = XGetPixel(image, x, y);
109     }
110     XQueryColors(display, attr.colormap, colors, width);
111     for (x = 0; x < width; ++x) {
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     }
117     i_plin(result, 0, width, y, line);
118   }
119   myfree(line);
120   myfree(colors);
121   XDestroyImage(image);
122
123   XSetErrorHandler(old_handler);
124   if (own_display)
125     XCloseDisplay(display);
126
127   return result;
128 }
129
130 unsigned long
131 imss_x11_open(char const *display_name) {
132   XErrorHandler old_handler;
133   Display *display;
134
135   i_clear_error();
136   old_handler = XSetErrorHandler(my_handler);
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
146 void
147 imss_x11_close(unsigned long display) {
148   XCloseDisplay((Display *)display);
149 }