1 /*  A program to pick a color by clicking the mouse.
2  *
3  *  RCS:
4  *      $Revision$
5  *      $Date$
6  *
7  *  Description:
8  *
9  *  When this program is run, the mouse pointer is grabbed and changed to
10  *  a cross hair and when the mouse is clicked, the color of the clicked
11  *  pixel is written to stdout in hex prefixed with #
12  *
13  *  This program can be useful when you see a color and want to use the
14  *  color in xterm or your window manager's border but no clue what the
15  *  name of the color is. It's silly to use a image processing software
16  *  to find it out.
17  *
18  * Example:
19  *   cpick
20  *          #ffffff
21  *   xterm -bg `cpick` -fg `cpick` (silly but esoteric!)
22  *
23  *  Development History:
24  *      who                  when               why
25  *      ma_muquit@fccc.edu   march-16-19997     first cut
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <math.h>
35 #include <signal.h>
36 #include <time.h>
37 
38 #include <X11/Xos.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/Xresource.h>
42 #include <X11/Xproto.h>
43 #include <X11/Xatom.h>
44 #include <X11/cursorfont.h>
45 #include <X11/keysym.h>
46 
47 #ifndef True
48 #define True    1
49 #endif
50 
51 #ifndef False
52 #define False   0
53 #endif
54 
55 /* private function prototypes */
56 static Window selectWindow (Display *,int *x,int *y);
57 
58 static Window findSubWindow(Display *display,Window top_winodw,
59     Window window_to_check,int *x,int *y);
60 
61 static int getWindowColor(Display *display,XColor *color);
62 static int MXError(Display *display,XErrorEvent *error);
63 
main(int argc,char ** argv)64 int main(int argc,char **argv)
65 {
66     Display
67         *display;
68 
69     int
70         status;
71 
72     XColor
73         color;
74 
75     Colormap
76         cmap;
77 
78     int
79         i,
80         r,
81         g,
82         b;
83 
84     for (i=1; i < argc; i++)
85     {
86         if (strncmp(argv[i],"-h",2) == 0)
87         {
88             (void) fprintf (stderr,"grabc 1.1 by Muhammad A Muquit\n");
89             exit(1);
90         }
91     }
92     display=XOpenDisplay((char *) NULL);
93     cmap=DefaultColormap(display,DefaultScreen(display));
94 
95     XSetErrorHandler(MXError);
96 
97     if (display == (Display *) NULL)
98     {
99         (void) fprintf (stderr,"Failed to open local DISPLAY!'n");
100         exit(1);
101     }
102 
103     status=getWindowColor(display,&color);
104     if (status == True)
105     {
106         XQueryColor(display,cmap,&color);
107         r=(color.red >> 8);
108         g=(color.green >> 8);
109         b=(color.blue >> 8);
110         (void) fprintf (stdout,"#%02x%02x%02x\n",r,g,b);
111         (void) fflush(stdout);
112         /*
113         ** write the values in decimal on stderr
114         */
115         (void) fprintf(stderr,"%d,%d,%d\n",r,g,b);
116     }
117     else
118     {
119         (void) fprintf (stderr,"Failed to grab color!\n");
120     }
121     return (0);
122 }
123 
124 /*
125 ** function to select a window
126 ** output parameters: x,y (coordinate of the point of click)
127 ** reutrns Window
128 ** exits if mouse can not be grabbed
129 */
selectWindow(Display * display,int * x,int * y)130 static Window selectWindow(Display *display,int *x,int *y)
131 {
132     Cursor
133         target_cursor;
134 
135     static Cursor
136         cross_cursor=(Cursor) NULL;
137     int
138         status;
139 
140     Window
141         target_window,
142         root_window;
143 
144     XEvent
145         event;
146 
147     target_window=(Window) NULL;
148 
149     if (cross_cursor == (Cursor) NULL)
150     {
151         cross_cursor=XCreateFontCursor(display,XC_tcross);
152         if (cross_cursor == (Cursor) NULL)
153         {
154             (void) fprintf (stderr,"Failed to create Cross Cursor!\n");
155             return ((Window) NULL);
156         }
157     }
158     target_cursor=cross_cursor;
159     root_window=XRootWindow(display,XDefaultScreen(display));
160 
161     status=XGrabPointer(display,root_window,False,
162         (unsigned int) ButtonPressMask,GrabModeSync,
163         GrabModeAsync,root_window,target_cursor,CurrentTime);
164 
165     if (status == GrabSuccess)
166     {
167         XAllowEvents(display,SyncPointer,CurrentTime);
168         XWindowEvent(display,root_window,ButtonPressMask,&event);
169 
170         if (event.type == ButtonPress)
171         {
172             target_window=findSubWindow(display,root_window,
173                 event.xbutton.subwindow,
174                 &event.xbutton.x,
175                 &event.xbutton.y );
176 
177             if (target_window == (Window) NULL)
178             {
179                 (void) fprintf (stderr,
180                     "Failed to get target window, getting root window!\n");
181                 target_window=root_window;
182             }
183             XUngrabPointer(display,CurrentTime);
184         }
185     }
186     else
187     {
188         (void) fprintf (stderr,"Failed to grab mouse!\n");
189         exit(1);
190     }
191 
192     /* free things we do not need, always a good practice */
193     XFreeCursor(display,cross_cursor);
194 
195     (*x)=event.xbutton.x;
196     (*y)=event.xbutton.y;
197 
198     return (target_window);
199 }
200 
201 /* find a window */
findSubWindow(Display * display,Window top_window,Window window_to_check,int * x,int * y)202 static Window findSubWindow(Display *display,Window top_window,
203     Window window_to_check,int *x,int *y)
204 {
205     int
206         newx,
207         newy;
208 
209     Window
210         window;
211 
212     if (top_window == (Window) NULL)
213         return ((Window) NULL);
214 
215     if (window_to_check == (Window) NULL)
216         return ((Window) NULL);
217 
218     /* initialize automatics */
219     window=window_to_check;
220 
221     while ((XTranslateCoordinates(display,top_window,window_to_check,
222         *x,*y,&newx,&newy,&window) != 0) &&
223            (window != (Window) NULL))
224     {
225         if (window != (Window) NULL)
226         {
227             top_window=window_to_check;
228             window_to_check=window;
229             (*x)=newx;
230             (*y)=newy;
231         }
232     }
233 
234     if (window == (Window) NULL)
235         window=window_to_check;
236 
237 
238     (*x)=newx;
239     (*y)=newy;
240 
241     return (window);
242 }
243 
244 /*
245  * get the color of the pixel of the point of mouse click
246  * output paramter: XColor *color
247  *
248  * returns True if succeeds
249  *          False if fails
250  */
251 
getWindowColor(Display * display,XColor * color)252 static int getWindowColor(Display *display,XColor *color)
253 {
254     Window
255         root_window,
256         target_window;
257 
258     XImage
259         *ximage;
260 
261     int
262         x,
263         y;
264 
265     Status
266         status;
267 
268     root_window=XRootWindow(display,XDefaultScreen(display));
269     target_window=selectWindow(display,&x,&y);
270 
271     if (target_window == (Window) NULL)
272         return (False);
273 
274     ximage=XGetImage(display,target_window,x,y,1,1,AllPlanes,ZPixmap);
275     if (ximage == (XImage *) NULL)
276         return (False);
277 
278     color->pixel=XGetPixel(ximage,0,0);
279     XDestroyImage(ximage);
280 
281     return (True);
282 }
283 
284 /* forgiving X error handler */
285 
MXError(Display * display,XErrorEvent * error)286 static int MXError (Display *display, XErrorEvent *error)
287 {
288     int
289         xerrcode;
290 
291     xerrcode = error->error_code;
292 
293     if (xerrcode == BadAlloc ||
294         (xerrcode == BadAccess && error->request_code==88))
295     {
296         return (False);
297     }
298     else
299     {
300         switch (error->request_code)
301         {
302             case X_GetGeometry:
303             {
304                 if (error->error_code == BadDrawable)
305                     return (False);
306                 break;
307             }
308 
309             case X_GetWindowAttributes:
310             case X_QueryTree:
311             {
312                 if (error->error_code == BadWindow)
313                     return (False);
314                 break;
315             }
316 
317             case X_QueryColors:
318             {
319                 if (error->error_code == BadValue)
320                     return(False);
321                 break;
322             }
323         }
324     }
325     return (True);
326 }
327 
328