Sign in to follow this  

Programming clipboard for screenshots

This topic is 2942 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites
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))

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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 16384

Display *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.

Share this post


Link to post
Share on other sites
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::string
string 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 default
Atom XA_TARGETS;


//This fetches all the data from a property
Property 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[i]);

//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[i];
}
}

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[i] = prop.data[i];
}
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!

Share this post


Link to post
Share on other sites
Sign in to follow this