Programming clipboard for screenshots

Started by
3 comments, last by Lode 14 years, 5 months ago
Hi, Using C++, is there a way to access the clipboard in linux for images (not text)? For example, if I have KolourPaint and GIMP open, select something in KolourPaint, press CTRL-C in KolourPaint, then press CTRL+V in GIMP, then it WORKS (pasting the data from KolourPaint into GIMP), no matter if I use Gnome or KDE! Same for pressing "ctrl or alt+print screen" in KDE and then pressing CTRL+V in any painting program. How do KolourPaint and GIMP do that? What standard way to communicate such a screenshot through the clipboard is used in Linux and how can I access it from C++? Without using libraries like Qt (my app is SDL+OpenGL, no GTK or Qt or similar). Using X11 headers is fine for me though. Thanks!
Advertisement
The internet is really not that loud on that topic, so my idea would be to look at the sourcecode of QClipboard; after some browsing I find in src/gui/kernel the files qclipboard.cpp and qclipboard_x11.cpp.

The relevant function seems to be QClipboard::image() in qclipboard.cpp, which itself calls mimeData(), as declared in the x11 unit.

Sorry for not deriving further, I lack the time. This is something I could need myself, so I'll watch your thread (and later maybe look at the x11 documentation itself (x.org; best of domains ever))
On top of that, here's what I found:

http://michael.toren.net/mirrors/doc/X-copy+paste.txt
http://tronche.com/gui/x/xlib/events/client-communication/selection.html

It looks like you have to send a SelectionNotify event to the X server to get the job done. I hope these links are helpful. I myself have never attempted much beyond setting up a window in X11.

EDIT:
On top of that, I found a Wikipedia entry

[Edited by - aryx on November 11, 2009 9:26:35 PM]
It's a lot to learn at once, so I'm starting now with a simple program I found on the internet that should output the X11 clipboard to console. Here's the code of it:

#include <stdio.h>#include <stdlib.h>#include <X11/Xlib.h>#include <X11/Xatom.h>#define MAX_CLIP_SIZE        16384Display *disp;Window w;Atom x11_clipboard;char nothing;char *clip_get(void) { unsigned char *xdata; Atom prop, type; unsigned long len, dummy; int fmt; if(!(x11_clipboard = XInternAtom(disp, "CLIPBOARD", True)))   return &nothing;  w = XGetSelectionOwner(disp, x11_clipboard); if(w==0) return &nothing; prop = 1; XChangeProperty(disp, w, prop, XA_STRING,                 8, PropModeReplace, (unsigned char *)"", 0); XConvertSelection(disp,x11_clipboard, XA_STRING, prop, w, CurrentTime); XFlush(disp); XGetWindowProperty(disp, w, prop, 0, MAX_CLIP_SIZE,0,                    AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); if(len == 0) return &nothing; if(len>MAX_CLIP_SIZE) return &nothing; return (char*)xdata;}int main(void) { if(!(disp = XOpenDisplay(NULL))) {  fprintf(stderr,"cannot open display\n");  exit(-1);  } nothing = 0; printf(clip_get());}


When I run this program (after compiling it with "g++ main.cpp -lX11"), its output is sometimes the text of the clipboard, something nothing.

So, I copy some text, and then run the program 20 times in a row. About half of those 20 times, it will show the copied text in the console, the other half it shows nothing. There is no regularity in it, it's quite random.

This can't be useful, because... If a user presses "ctrl+v" in my program, he should not "randomly" sometimes have something pasted and sometimes not... Any ideas where this randomness could come from and how I can make it more reliable?

I'm just experimenting so I'll first try to get this to work properly, then move up to images :)

The Qt code looks interesting, too bad I haven't had the time to study it in detail so far.
I got it working!! Thanks to the links above I could understand the terminology better and better and find more things with google, until I found this site:

http://mi.eng.cam.ac.uk/~er258/code/x11.html

Where I downloaded "x_clipboard.tar.gz". Then I had to study the code quite a lot before I could make any sense out of the program, and then I modified it to interpret clipboard type "image/png", decode it, and put it all into a vector. The result is the following, which outputs "1280 1024" to me if I hit "ctrl + print screen" to take a screenshot first, and then run the program.

#include <X11/Xlib.h>#include <X11/Xatom.h>#include <map>#include <cstdio>#include <iostream>#include <climits>#include <cstring>#include "lodepng.h"using namespace std;//This code comes from http://mi.eng.cam.ac.uk/~er258/code/x11.html and was then modified to fullfil my needs//Convert an atom name in to a std::stringstring GetAtomName(Display* disp, Atom a){  if(a == None)    return "None";  else    return XGetAtomName(disp, a);}struct Property{  unsigned char *data;  int format, nitems;  Atom type;};//This atom isn't provided by defaultAtom XA_TARGETS;//This fetches all the data from a propertyProperty read_property(Display* disp, Window w, Atom property){  Atom actual_type;  int actual_format;  unsigned long nitems;  unsigned long bytes_after;  unsigned char *ret=0;    int read_bytes = 1024;    //Keep trying to read the property until there are no  //bytes unread.  do  {    if(ret != 0)      XFree(ret);    XGetWindowProperty(disp, w, property, 0, read_bytes, False, AnyPropertyType,              &actual_type, &actual_format, &nitems, &bytes_after,               &ret);    read_bytes *= 2;  }while(bytes_after != 0);    Property p = {ret, actual_format, nitems, actual_type};  return p;}// This function takes a list of targets which can be converted to (atom_list, nitems)// and a list of acceptable targets with prioritees (datatypes). It returns the highest// entry in datatypes which is also in atom_list: ie it finds the best match.Atom pick_target_from_list(Display* disp, Atom* atom_list, int nitems, std::map<string, int>& datatypes){  Atom to_be_requested = None;  //This is higger than the maximum priority.  int priority = INT_MAX;  for(int i = 0; i < nitems; i++)  {    string atom_name = GetAtomName(disp, atom_list);      //See if this data type is allowed and of higher priority (closer to zero)    //than the present one.    if(datatypes.find(atom_name)!= datatypes.end())      if(priority > datatypes[atom_name])      {          priority = datatypes[atom_name];        to_be_requested = atom_list;      }  }  return to_be_requested;}// Finds the best target given a local copy of a property.Atom pick_target_from_targets(Display* disp, Property p, std::map<string, int>& datatypes){  //The list of targets is a list of atoms, so it should have type XA_ATOM  //but it may have the type TARGETS instead.  if((p.type != XA_ATOM && p.type != XA_TARGETS) || p.format != 32)  {    //This would be really broken. Targets have to be an atom list    //and applications should support this. Nevertheless, some    //seem broken (MATLAB 7, for instance), so ask for STRING    //next instead as the lowest common denominator    if(datatypes.count("STRING"))      return XA_STRING;    else      return None;  }  else  {    Atom *atom_list = (Atom*)p.data;        return pick_target_from_list(disp, atom_list, p.nitems, datatypes);  }}//stores the image as RGBA in image, and its width and height in w and h. So image.size() is w * h * 4.void getClipboardImage(std::vector<unsigned char>& image, int& w, int& h){  w = h = 0; //indication that there is no image.    Display* disp;  Window root, window;  int screen;  XEvent e;    //The usual Xinit stuff...  disp = XOpenDisplay(NULL);  screen = DefaultScreen(disp);  root = RootWindow(disp, screen);  //Process commandline args  //This is the kind of data we're prepared to select  //Each argument corresponds to a type, in order of preference  //The key is the type the data is the priority (bigger int is lower)  map<string, int> datatypes;  datatypes["image/png"] = 1;  //The first command line argument selects the buffer.  //by default we use CLIPBOARD, the only other option  //which is normally sensible is PRIMARY  Atom sel = XInternAtom(disp, "CLIPBOARD", 0);      //We need a target window for the pasted data to be sent to.  //However, this does not need to be mapped.  window = XCreateSimpleWindow(disp, root, 0, 0, 100, 100, 0, BlackPixel(disp, screen), BlackPixel(disp, screen));  //This is a meta-format for data to be "pasted" in to.  //Requesting this format acquires a list of possible  //formats from the application which copied the data.  XA_TARGETS = XInternAtom(disp, "TARGETS", False);  //Request a list of possible conversions, if we're pasting.  XConvertSelection(disp, sel, XA_TARGETS, sel, window, CurrentTime);  XFlush(disp);  Atom to_be_requested = None;  bool sent_request = 0;    std::vector<unsigned char> data;  for(;;)  {    XNextEvent(disp, &e);        if(e.type == SelectionNotify)    {      Atom target = e.xselection.target;      if(e.xselection.property == None)      {        //If the selection can not be converted, quit with error 2.        //If TARGETS can not be converted (nothing owns the selection)        //then quit with code 3.        return;      }      else       {        Property prop = read_property(disp, window, sel);        //If we're being given a list of targets (possible conversions)        if(target == XA_TARGETS && !sent_request)        {          sent_request = 1;          to_be_requested = pick_target_from_targets(disp, prop, datatypes);          if(to_be_requested == None)          {            return;          }          else //Request the data type we are able to select          {            XConvertSelection(disp, sel, to_be_requested, sel, window, CurrentTime);          }        }        else if(target == to_be_requested)        {          //Dump the binary data          size_t size = prop.nitems * prop.format/8;          data.resize(size);          for(size_t i = 0; i < size; i++)          {            data = prop.data;          }          break;        }        else return;        XFree(prop.data);      }    }  }    LodePNG::Decoder decoder;  decoder.decode(image, data);  if(!decoder.hasError())  {    w = decoder.getWidth();    h = decoder.getHeight();  }}int main(){  std::vector<unsigned char> image;  int w, h;  getClipboardImage(image, w, h);  std::cout<<w<<" "<<h<<std::endl;}


NOTE: the above code is under GPL as is mentioned somewhere on the website of the link above!

This topic is closed to new replies.

Advertisement