Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

X11 screenshot of active window fails for GTK windows

Tags:

c++

c

x11

gtk

xlib

this is a sub-project of a much larger research project. I am trying to take screenshots of an active window (browser) every 100ms, which are then to be stored in memory for OpenCV processing. I found from a similar question a solution to taking a screenshot, and I'm currently playing with the code to see if I can use it. The following snippet seems to be working either when taking an entire Desktop screenshot, or a specific Window screenshot, but it doesn't work for GTK windows. I've tried to take a screenshot of Iceweasel & Nautilus on Debian Squeeze, and it simply doesn't work. I am a total noob in X11, and don't know how to check for errors, or if there is something I am missing for GTK, as this seems to work for QT windows.

typedef int (*handler)(Display *, XErrorEvent *);

int handleX11Error(Display *d, XErrorEvent *er)
{
   std::cout << "X11 Error: " << er->error_code << std::endl;
}

int main()
{
    std::cout << "Sleeping 5 seconds" << std::endl;
   // we may need to sleep if we want to focus another window.
   sleep(5); 
   std::cout << "taking screenshot" << std::endl;

   Display *display = XOpenDisplay(NULL);
   //Window root = DefaultRootWindow(display);
   XWindowAttributes gwa;
   int revert = RevertToNone;
   Window active;
   XErrorEvent *error;
   handler myHandler = &handleX11Error;
   XSetErrorHandler(myHandler);

   // X11 - Get Window that has focus
   XGetInputFocus(display,&active,&revert);

   //XGetWindowAttributes(display, root, &gwa);
   if (!XGetWindowAttributes(display, active, &gwa))
     std::cout << "XGetWindowAttributes failed" << std::endl;

   int width = gwa.width;
   int height = gwa.height;

   //XImage *image = XGetImage(display,root, 0,0 , width,height,AllPlanes, ZPixmap);
   XImage *image = XGetImage(display,active, 0,0 , width,height,XAllPlanes(), ZPixmap);

   unsigned char *array = new unsigned char[width * height * 3];
   CImg<unsigned char> pic(array,width,height,1,3);

   for (int x = 0; x < width; x++){
      for (int y = 0; y < height ; y++){
     pic(x,y,0) = (XGetPixel(image,x,y) & image->red_mask ) >> 16;
     pic(x,y,1) = (XGetPixel(image,x,y) & image->green_mask ) >> 8;
     pic(x,y,2) = XGetPixel(image,x,y) & image->blue_mask;
      }
   }
   delete[] array;
   pic.save_png("blah.png");
   std::cout <<  "Finished" << std::endl;
   return 0;
}

The above code works either for full desktop screenshots or QT. I get no error (don't know if I'm handling them correctly). Just an empty picture of a few bytes, which makes me think that one of the X functions fails (XGetInputFocus, XGetWindowAttributes, XGetImage), with my bet on XGetFocus not properly working. What is it that I am missing, or, is there an alternative to this ? Please note that I am running KDE (4.4.5) if it is of any importance.

UPDATE:

I have tried to take a screenshot using Qt4, and while it works fine, it runs in the same problem when trying to get focused windows from X11:

int main(int argc, char **argv)
{
    sleep(5);
    Display *display = XOpenDisplay(NULL);
    int revert = RevertToNone;
    Window active;
    XGetInputFocus(display,&active,&revert);
    QApplication app(argc, argv);    
    QPixmap pixmap = QPixmap::grabWindow(active);
    pixmap.save("test.png","PNG");
    QPushButton quit("Quit");
    quit.resize(75, 30);
    quit.setFont(QFont("Times", 18, QFont::Bold));
    QObject::connect(&quit, SIGNAL(clicked()), &app, SLOT(quit()));
    quit.show();
    return app.exec();
}

I am therefore convinced, that it is XGetInputFocus() that somehow fails.

like image 432
Ælex Avatar asked Nov 30 '12 14:11

Ælex


1 Answers

Since I haven't gotten any answers yet, and sice I've spend the better part of an entire day looking for a solution, I'd thought I'd share how I managed to get this working. The system is Debian Squeeze, running KDE 4.4.5. Apparently KDE and GTK apps don't play nice with eachother. Quoting people from other posts on stackoverflow and the internet in general, a non-kde application may not honor the _NET_WM_STATE, or it could be somethig else, I really don't know. But the fact that the GTK apps I tried did not work with the piece of code that all Qt4 apps worked, hints to an issue related to some form of reporting. Some rare (and a I really mean rare) solutions found on the net point that maybe X11 windows tree could be traversed to find the active window, but that seemed too complicated to me, and I read posts of people not getting succesful results. What I came up with (which is bits and pieces of snippets found online) is the following using xdo (libxdo on Debian):

   Display *display = XOpenDisplay(NULL);
   Window active;
   XWindowAttributes gwa;

   // Use xdo to find the active window - care on the display !
   xdo_t* xdocontext = xdo_new(0);
   xdo_window_get_active(xdocontext, &active);
   if(active){
      XGetWindowAttributes(display, active, &gwa);
      XImage *image = XGetImage(display,active, 0,0 , gwa.width,gwa.height,XAllPlanes(), ZPixmap);
      unsigned char *array = new unsigned char[gwa.width * gwa.height * 3];
      CImg<unsigned char> pic(array,gwa.width,gwa.height,1,3);

      for (int x = 0; x < gwa.width; x++){
         for (int y = 0; y < gwa.height ; y++){
             pic(x,y,0) = (XGetPixel(image,x,y) & image->red_mask ) >> 16;
             pic(x,y,1) = (XGetPixel(image,x,y) & image->green_mask ) >> 8;
             pic(x,y,2) = XGetPixel(image,x,y) & image->blue_mask;
         }
      }
      delete[] array;
      pic.save_png("blah.png");
   } else std::cout << "xdo failed to get active window" << std::endl;

The above works with GTK & KDE apps, I really hope it may help someone stuck at this as there seem to be very few posts on this.

like image 172
Ælex Avatar answered Sep 23 '22 08:09

Ælex