grabc

Tool to identify pixel colors on X
git clone https://git.sinitax.com/muquit/grabc
Log | Files | Refs | README | Upstream | sfeed.txt

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}