8

I want to get the RGB value of the top/left pixel (0;0) of the whole x11 display.

what I've got so far:

XColor c;
Display *d = XOpenDisplay((char *) NULL);

XImage *image;
image = XGetImage (d, RootWindow (d, DefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
c->pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (d, DefaultColormap(d, DefaultScreen (d)), c);
cout << c.red << " " << c.green << " " << c.blue << "\n";

but I need those values to be 0..255 or (0.00)..(1.00), while they look like 0..57825, which is no format I recognize.

also, copying the whole screen just to get one pixel is very slow. as this will be used in a speed-critical environment, I'd appreciate if someone knows a more performant way to do this. Maybe using XGetSubImage of a 1x1 size, but I'm very bad at x11 development and don't know how to implement that.

what shall I do?

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
nonchip
  • 1,084
  • 1
  • 18
  • 36
  • sure, that's what I'm doing right now, but it's giving me creeps, because a) I don't know why it works, b) I don't know how reliable it is and c) It's still slow (time says "`cpu 0,054 total`" for a single pixel!). – nonchip Jul 08 '13 at 03:03
  • Actually, according to [this](http://http://tronche.com/gui/x/xlib/color/structures.html), it should just be uninitialized junk values. Use some basic bit wise operators on the long returned by XGetPixel and you should be set. – Richard J. Ross III Jul 08 '13 at 03:04
  • Screen caps are inherently slow. If there is any other way to get what you want, use that instead of a screen capture. – Richard J. Ross III Jul 08 '13 at 03:04
  • the only other way to get what I want would require getting a randomized runtime memory offset and messing around in foreign processes running through wine. so, basically, I think using that one pixel is still faster. `XGetSubImage` would be faster (effectively screencapping only the pixel I need), but dunno how. – nonchip Jul 08 '13 at 03:07
  • Use a toolkit like Qt or Gtk... It is much simpler.... – Basile Starynkevitch Jul 08 '13 at 04:57
  • simpler and slower. by the way, the only way to do this in QT is doing exactly what I did above, but using QImage. dunno how it's done in gtk, never wrote anything with gtk, the whole gobject thing is strange ;-) – nonchip Jul 08 '13 at 10:29

2 Answers2

13

I took your code and got it to compile. The values printed (scaled to 0-255) give me the same values as I set to the desktop background image.

#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

using namespace std;

int main(int, char**)
{
    XColor c;
    Display *d = XOpenDisplay((char *) NULL);

    int x=0;  // Pixel x 
    int y=0;  // Pixel y

    XImage *image;
    image = XGetImage (d, XRootWindow (d, XDefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
    c.pixel = XGetPixel (image, 0, 0);
    XFree (image);
    XQueryColor (d, XDefaultColormap(d, XDefaultScreen (d)), &c);
    cout << c.red/256 << " " << c.green/256 << " " << c.blue/256 << "\n";

    return 0;
}
parkydr
  • 7,596
  • 3
  • 32
  • 42
  • Your code doesn't compile for me. I needed to add an `X` before some functions: `XRootWindow`, `XDefaultScreen` and `XDefaultColormap`. – Rakete1111 May 07 '17 at 07:59
2

From the XColor(3) man page:

The red, green, and blue values are always in the range 0 to 65535 inclusive, independent of the number of bits actually used in the display hardware. The server scales these values down to the range used by the hardware. Black is represented by (0,0,0), and white is represented by (65535,65535,65535). In some functions, the flags member controls which of the red, green, and blue members is used and can be the inclusive OR of zero or more of DoRed, DoGreen, and DoBlue.

So you must scale these values to whatever range you want.

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • actually I tried that (because it's the nearest value making some sense), but it's more inaccurate compared to 57825 :-( – nonchip Jul 08 '13 at 03:10