t* A cli color picker
       
   URI git clone git://git.codevoid.de/xpick.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       txpick.c (3942B)
       ---
            1 /* Copyright (c) 2020 Stefan Hagen <sh+ports[at]codevoid[dot]de>
            2  *
            3  * Permission to use, copy, modify, and distribute this software for any
            4  * purpose with or without fee is hereby granted, provided that the above
            5  * copyright notice and this permission notice appear in all copies.
            6  *
            7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
            9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           10  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           12  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           13  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
           14 
           15 #include <X11/Xlib.h>
           16 #include <X11/Xutil.h>
           17 #include <X11/cursorfont.h>
           18 #include <stdio.h>
           19 #include <string.h>
           20 #include <unistd.h>
           21 #include <stdint.h>
           22 
           23 // original implementation in tmux colour.c
           24 static int rgb_to_x256(uint8_t r, uint8_t g, uint8_t b)
           25 {
           26     // Calculate the nearest 0-based color index at 16 .. 231
           27     #define v2ci(v) (v < 48 ? 0 : v < 115 ? 1 : (v - 35) / 40)
           28     int ir = v2ci(r), ig = v2ci(g), ib = v2ci(b);   // 0..5 each
           29     #define color_index() (36 * ir + 6 * ig + ib)  /* 0..215, lazy evaluation */
           30 
           31     // Calculate the nearest 0-based gray index at 232 .. 255
           32     int average = (r + g + b) / 3;
           33     int gray_index = average > 238 ? 23 : (average - 3) / 10;  // 0..23
           34 
           35     // Calculate the represented colors back from the index
           36     static const int i2cv[6] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
           37     int cr = i2cv[ir], cg = i2cv[ig], cb = i2cv[ib];  // r/g/b, 0..255 each
           38     int gv = 8 + 10 * gray_index;  // same value for r/g/b, 0..255
           39 
           40     // Return the one which is nearer to the original input rgb value
           41     #define dist_square(A,B,C, a,b,c) ((A-a)*(A-a) + (B-b)*(B-b) + (C-c)*(C-c))
           42     int color_err = dist_square(cr, cg, cb, r, g, b);
           43     int gray_err  = dist_square(gv, gv, gv, r, g, b);
           44     return color_err <= gray_err ? 16 + color_index() : 232 + gray_index;
           45 }
           46 
           47 int main(int argc, char *argv[]) {
           48 
           49     if(argc <= 1) {
           50         printf("Usage: xpick [-rhx]\n");
           51         printf("    -r   RGB notation RR/GG/BB\n");
           52         printf("    -h   HEX notation #RRGGBB\n");
           53         printf("    -x   XTerm nearest terminal color\n");
           54         return 2;
           55     }
           56 
           57     Display *dpy = XOpenDisplay(NULL);
           58     Window root = RootWindow(dpy, DefaultScreen(dpy));
           59 
           60     Cursor cursor = XCreateFontCursor(dpy, XC_tcross);
           61     XColor fgc, bgc;
           62     fgc.red = -1; fgc.green = 0; fgc.blue = 0;
           63     bgc.red = -1; bgc.green = -1; bgc.blue = -1;
           64 
           65     XRecolorCursor(dpy, cursor, &fgc, &bgc);
           66     XGrabPointer(dpy, root, 0, ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
           67             None, cursor, CurrentTime);
           68 
           69     XEvent event;
           70     XNextEvent(dpy, &event);
           71     XUngrabPointer(dpy, CurrentTime);
           72     XImage *ximage = XGetImage(dpy, root, event.xbutton.x_root, 
           73             event.xbutton.y_root, 1, 1, -1, ZPixmap);
           74 
           75     unsigned long p = XGetPixel(ximage, 0, 0);
           76     XDestroyImage(ximage);
           77     XColor result; result.pixel = p;
           78     XQueryColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &result);
           79 
           80     int opt;
           81     while((opt = getopt(argc, argv, ":hrx")) != -1){
           82         switch(opt){
           83             case 'h':
           84                 printf("#%02x%02x%02x\n",
           85                         result.red/256, result.green/256, result.blue/256);
           86                 break;
           87             case 'r':
           88                 printf("%03d/%03d/%03d\n",
           89                         result.red/256, result.green/256, result.blue/256);
           90                 break;
           91             case 'x':
           92                 printf("%d\n", rgb_to_x256(result.red/256, result.green/256,
           93                         result.blue/256));
           94                 break;
           95             case '?': //used for some unknown opts
           96                 printf("Unknown option: %c\n", optopt);
           97                 break;
           98         }
           99     }
          100     return 0;
          101 }