grabc.c (15711B)
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 * xterm -bg `grabc` -fg `grabc` (silly but esoteric!) 20 * 21 * Development History: 22 * who when why 23 * ma_muquit@fccc.edu march-16-1997 first cut 24 * muquit@muquit.com Apr-10-2018 Do not use default colormap, 25 * rather get it from window attributes. 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 <stdarg.h> 35#include <math.h> 36#include <signal.h> 37#include <time.h> 38#include <errno.h> 39 40#include <X11/Xos.h> 41#include <X11/Xlib.h> 42#include <X11/Xutil.h> 43#include <X11/Xresource.h> 44#include <X11/Xproto.h> 45#include <X11/Xatom.h> 46#include <X11/cursorfont.h> 47#include <X11/keysym.h> 48 49#ifndef True 50#define True 1 51#endif 52 53#ifndef False 54#define False 0 55#endif 56 57#define VERSION_S "1.0.2" 58 59static int g_debug = False; 60static int g_print_in_hex = True; 61static int g_print_in_rgb = False; 62static int g_print_all_16_bits = False; 63static Window g_window_id = (Window) NULL; 64static int g_loc_specified = False; 65static int g_x = 1; 66static int g_y = 1; 67static unsigned int g_width = 0; 68static unsigned int g_height = 0; 69static Cursor g_cross_cursor=(Cursor) NULL; 70 71/* private function prototypes */ 72static Window select_window (Display *,int *x,int *y); 73 74static Window findSubWindow(Display *display,Window top_winodw, 75 Window window_to_check,int *x,int *y); 76 77static Window get_window_color(Display *display,XColor *color); 78static int MXError(Display *display,XErrorEvent *error); 79 80static void show_usage(void) 81{ 82 char 83 **p; 84 85 static char *options[]= 86 { 87" -v - show version info", 88" -h - show this usage", 89" -hex - print pixel value as Hex on stdout", 90" -rgb - print pixel value as RGB on stderr", 91" -W - print the Window id at mouse click", 92" -w id - window id in hex, use -l +x+y", 93" -l +x+y - pixel co-ordinate. requires window id", 94" -d - show debug messages", 95" -a - Print all 16 bits RGB components of color", 96" Default is high order 8 bits of components", 97"Example:", 98"* Print pixel color in hex on stdout:", 99" $ grabc", 100"* Show usage:", 101" $ grabc -h", 102"* Print Window Id (Note the upper case W):", 103" $ grabc -W", 104"* Print pixel color of Window iwith id 0x13234 at location 10,20", 105" $ grabc -w 0x13234 -l +10+20", 106(char *) NULL 107 108 }; 109 110 (void) printf("\n"); 111 (void) printf("grabc v%s\n",VERSION_S); 112 (void) printf("A program to identify a pixel color of an X Window\n"); 113 (void) printf("by muquit@muquit.com https://www.muquit.com/\n\n"); 114 (void) printf("Usage: grabc [options]\n"); 115 (void) printf("Where the options are:\n"); 116 for (p=options; *p != NULL; p++) 117 { 118 (void) fprintf(stdout,"%s\n",*p); 119 (void) fflush(stdout); 120 } 121} 122 123 124static void log_debug(const char *fmt,...) 125{ 126 va_list 127 args; 128 if (!g_debug) 129 { 130 return; 131 } 132 va_start(args, fmt); 133 (void) fprintf(stderr,"[Debug]: "); 134 vfprintf(stderr,fmt,args); 135 (void) fprintf(stderr,"\n"); 136 va_end(args); 137} 138 139static Cursor get_cross_cursor(Display *display) 140{ 141 if (g_cross_cursor == (Cursor) NULL) 142 { 143 g_cross_cursor=XCreateFontCursor(display,XC_tcross); 144 if (g_cross_cursor == (Cursor) NULL) 145 { 146 (void) fprintf (stderr,"ERROR: Failed to create Cross Cursor!\n"); 147 exit(1); 148 } 149 } 150 return g_cross_cursor; 151} 152static Window grab_mouse(Display *display,Window root_window) 153{ 154 int 155 status; 156 157 Window 158 subwindow; 159 160 XEvent 161 event; 162 163 Cursor 164 target_cursor; 165 166 167 if (g_window_id != (Window) NULL) 168 { 169 return g_window_id; 170 } 171 172 target_cursor = get_cross_cursor(display); 173 status=XGrabPointer(display,root_window,False, 174 (unsigned int) ButtonPressMask,GrabModeSync, 175 GrabModeAsync,root_window,target_cursor,CurrentTime); 176 if (status == GrabSuccess) 177 { 178 XAllowEvents(display,SyncPointer,CurrentTime); 179 XWindowEvent(display,root_window,ButtonPressMask,&event); 180 subwindow = event.xbutton.subwindow; 181 } 182 else 183 { 184 return root_window; 185 } 186 187 return subwindow; 188} 189static void upgrab_mouse(Display *display) 190{ 191 if (g_window_id != (Window) NULL) 192 { 193 XUngrabPointer(display,CurrentTime); 194 } 195} 196 197 198 199 200 201/* 202** function to select a window 203** output parameters: x,y (coordinate of the point of click) 204** reutrns Window 205** exits if mouse can not be grabbed 206*/ 207static Window select_window(Display *display,int *x,int *y) 208{ 209 Cursor 210 target_cursor; 211 212 int 213 status; 214 215 Window 216 target_window, 217 root_window; 218 219 XEvent 220 event; 221 222 /* 223 ** If window id and location is specified return the window id as 224 ** target window. Also initilaize x, y those specified with -l 225 */ 226 if ((g_window_id != (Window) NULL) && g_loc_specified) 227 { 228 log_debug("Returning passing window: %lx",g_window_id); 229 (*x) = g_x; 230 (*y) = g_y; 231 return g_window_id; 232 } 233 target_window=(Window) NULL; 234 target_cursor = get_cross_cursor(display); 235 root_window=XRootWindow(display,XDefaultScreen(display)); 236// log_debug("Root Window ID: 0x%08lx",root_window); 237 238 status=XGrabPointer(display,root_window,False, 239 (unsigned int) ButtonPressMask,GrabModeSync, 240 GrabModeAsync,root_window,target_cursor,CurrentTime); 241 if (status == GrabSuccess) 242 { 243 XAllowEvents(display,SyncPointer,CurrentTime); 244 XWindowEvent(display,root_window,ButtonPressMask,&event); 245 Window subwindow = event.xbutton.subwindow; 246 247 if (event.type == ButtonPress) 248 { 249 target_window=findSubWindow(display,root_window, 250 subwindow, 251 &event.xbutton.x, 252 &event.xbutton.y ); 253 254 if (target_window == (Window) NULL) 255 { 256 (void) fprintf (stderr, 257 "ERROR: Failed to get target window, getting root window!\n"); 258 target_window=root_window; 259 } 260 if (!g_loc_specified) 261 { 262 XUngrabPointer(display,CurrentTime); 263 } 264 } 265 266 } 267 else 268 { 269 (void) fprintf (stderr,"ERROR: Failed to grab mouse pointer!\n"); 270 exit(1); 271 } 272 273 /* free things we do not need, always a good practice */ 274 (*x)=event.xbutton.x; 275 (*y)=event.xbutton.y; 276 277 return (target_window); 278} 279 280/* find a window */ 281static Window findSubWindow(Display *display,Window top_window, 282 Window window_to_check,int *x,int *y) 283{ 284 int 285 newx, 286 newy; 287 288 Window 289 window; 290 291 if (top_window == (Window) NULL) 292 return ((Window) NULL); 293 294 if (window_to_check == (Window) NULL) 295 return ((Window) NULL); 296 297 /* initialize automatics */ 298 window=window_to_check; 299 300 while ((XTranslateCoordinates(display,top_window,window_to_check, 301 *x,*y,&newx,&newy,&window) != 0) && 302 (window != (Window) NULL)) 303 { 304 if (window != (Window) NULL) 305 { 306 top_window=window_to_check; 307 window_to_check=window; 308 (*x)=newx; 309 (*y)=newy; 310 } 311 } 312 313 if (window == (Window) NULL) 314 window=window_to_check; 315 316 317 (*x)=newx; 318 (*y)=newy; 319 320 return (window); 321} 322 323/* 324 * get the color of the pixel of the point of mouse click 325 * output paramter: XColor *color 326 * 327 * returns 328 * target Window on success 329 * NULL on failure 330 * 331 */ 332 333static Window get_window_color(Display *display,XColor *color) 334{ 335 Window 336 root_window, 337 target_window; 338 339 XImage 340 *ximage; 341 342 int 343 x, 344 y; 345 346 Status 347 status; 348 349 root_window=XRootWindow(display,XDefaultScreen(display)); 350 target_window=select_window(display,&x,&y); 351 352 log_debug(" Root Window Id: 0x%08lx",root_window); 353 log_debug("Target Window Id: 0x%08lx X,Y: +%d+%d",target_window,x,y); 354 355 if (target_window == (Window) NULL) 356 return (Window) NULL; 357 358 ximage=XGetImage(display,target_window,x,y,1,1,AllPlanes,ZPixmap); 359 if (ximage == (XImage *) NULL) 360 { 361 /* Try root window */ 362 log_debug("Could not get XImage from Window: 0x%08lx",target_window); 363 log_debug("Trying to get XImage from root window: 0x%08lx",root_window); 364 ximage=XGetImage(display,root_window,x,y,1,1,AllPlanes,ZPixmap); 365 if (ximage == (XImage *) NULL) 366 { 367 log_debug("Could not get XImage from target or root window"); 368 return (Window) NULL; 369 } 370 else 371 { 372 log_debug("OK successfully got XImage from root window"); 373 target_window = root_window; 374 } 375 376 } 377 378 color->pixel=XGetPixel(ximage,0,0); 379 XDestroyImage(ximage); 380 381 return (target_window); 382} 383 384/* forgiving X error handler */ 385 386static int MXError (Display *display, XErrorEvent *error) 387{ 388 int 389 xerrcode; 390 391 xerrcode = error->error_code; 392 393 if (xerrcode == BadAlloc || 394 (xerrcode == BadAccess && error->request_code==88)) 395 { 396 return (False); 397 } 398 else 399 { 400 switch (error->request_code) 401 { 402 case X_GetGeometry: 403 { 404 if (error->error_code == BadDrawable) 405 return (False); 406 break; 407 } 408 409 case X_GetWindowAttributes: 410 case X_QueryTree: 411 { 412 if (error->error_code == BadWindow) 413 return (False); 414 break; 415 } 416 417 case X_QueryColors: 418 { 419 if (error->error_code == BadValue) 420 return(False); 421 break; 422 } 423 } 424 } 425 return (True); 426} 427 428int main(int argc,char **argv) 429{ 430 Display 431 *display; 432 433 int 434 x, 435 y, 436 status; 437 438 XColor 439 color; 440 441 int 442 rc, 443 i, 444 r, 445 g, 446 b; 447 448 Window 449 window_id, 450 target_window; 451 452 XWindowAttributes 453 window_attributes; 454 455 char 456 *option; 457 458 for (i=1; i < argc; i++) 459 { 460 option = argv[i]; 461 switch(*(option+1)) 462 { 463 case 'a': 464 { 465 g_print_all_16_bits = True; 466 break; 467 } 468 case 'd': 469 { 470 g_debug = True; 471 break; 472 } 473 474 case 'h': 475 { 476 if (strncmp("hex",option+1,3) == 0) 477 { 478 g_print_in_hex = True; 479 } 480 else 481 { 482 show_usage(); 483 return(1); 484 } 485 break; 486 } 487 488 case 'r': 489 { 490 if (strncmp("rgb",option+1,3) == 0) 491 { 492 g_print_in_rgb = True; 493 } 494 break; 495 } 496 497 case 'w': 498 { 499 if (*option == '-') 500 { 501 i++; 502 if (i == argc) 503 { 504 (void) fprintf(stderr,"ERROR: Missing Window id\n"); 505 return(1); 506 } 507 } 508 g_window_id = (Window) strtol(argv[i],NULL, 16); 509 break; 510 } 511 case 'W': 512 { 513 display=XOpenDisplay((char *) NULL); 514 if (display == NULL) 515 { 516 (void) fprintf(stderr,"ERROR: Could not open Display\n"); 517 return(1); 518 } 519 Window window = select_window(display, &x, &y); 520 if (window != (Window) NULL) 521 { 522 log_debug("Window ID: 0x%08lx",window); 523 (void) fprintf(stdout,"0x%lx\n",window); 524 } 525 return(1); 526 break; 527 } 528 529 case 'l': 530 { 531 if (*option == '-') 532 { 533 i++; 534 if (i == argc) 535 { 536 (void) fprintf(stderr,"ERROR: Missing location +x+y\n"); 537 return(1); 538 } 539 } 540 rc = XParseGeometry(argv[i], &g_x,&g_y,&g_width,&g_height); 541 if (rc == 0) 542 { 543 (void) fprintf(stderr,"ERROR: Could not parse location: %s\n",argv[i]); 544 (void) fprintf(stderr,"Example: -l +10+20\n"); 545 return(1); 546 } 547 g_loc_specified = True; 548 549 break; 550 } 551 552 case 'v': 553 { 554 (void) fprintf(stderr,"grabc v%s\n",VERSION_S); 555 return(1); 556 break; 557 } 558 559 default: 560 { 561 break; 562 } 563 } 564 } 565 566 if (g_loc_specified && (g_window_id == (Window) NULL)) 567 { 568 (void) fprintf(stderr,"ERROR: Please specify window id with -w in hex to use this option\n"); 569 (void) fprintf(stderr,"Use -W option to find the Window Id\n"); 570 return(1); 571 } 572 573 display=XOpenDisplay((char *) NULL); 574 XSetErrorHandler(MXError); 575 576 if (display == (Display *) NULL) 577 { 578 (void) fprintf (stderr,"ERROR: Failed to open DISPLAY!\n"); 579 exit(1); 580 } 581 582 target_window = get_window_color(display,&color); 583 if (target_window != (Window) NULL) 584 { 585 status = XGetWindowAttributes(display, target_window, 586 &window_attributes); 587 if (status == False || window_attributes.map_state != IsViewable) 588 { 589 (void) fprintf(stderr,"ERROR: Could not get Window Attributes\n"); 590 return(1); 591 } 592 XQueryColor(display, window_attributes.colormap, &color); 593 if (g_print_all_16_bits) 594 { 595 (void) fprintf(stdout,"#%04x%04x%04x\n", 596 (unsigned int)color.red, 597 (unsigned int) color.green, 598 (unsigned int) color.blue); 599 (void) fflush(stdout); 600 if (g_print_in_rgb) 601 { 602 (void) fprintf(stderr,"%d,%d,%d\n", 603 (unsigned int)color.red, 604 (unsigned int) color.green, 605 (unsigned int) color.blue); 606 } 607 608 } 609 else 610 { 611 r=(color.red >> 8); 612 g=(color.green >> 8); 613 b=(color.blue >> 8); 614 log_debug("Color: #%02x%02x%02x",r,g,b); 615 (void) fprintf (stdout,"#%02x%02x%02x\n",r,g,b); 616 (void) fflush(stdout); 617 /* 618 ** write the values in decimal on stderr 619 */ 620 if (g_print_in_rgb) 621 { 622 (void) fprintf(stderr,"%d,%d,%d\n",r,g,b); 623 } 624 } 625 } 626 else 627 { 628 (void) fprintf (stderr,"ERROR: Failed to grab color!\n"); 629 } 630 return (0); 631}