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