Summary: | Paste image from clipboard bad interoperability | ||
---|---|---|---|
Product: | [I don't know] kde | Reporter: | Aardwolf <olifant> |
Component: | clipboard | Assignee: | Lubos Lunak <l.lunak> |
Status: | RESOLVED UPSTREAM | ||
Severity: | normal | CC: | kdelibs-bugs, mss |
Priority: | NOR | ||
Version: | unspecified | ||
Target Milestone: | --- | ||
Platform: | Ubuntu | ||
OS: | Linux | ||
Latest Commit: | Version Fixed In: | ||
Sentry Crash Report: |
Description
Aardwolf
2010-02-26 09:59:39 UTC
I can reproduce this problem in KDE 3.5 and in KDE 4.X. Also, below is C++ code that I use to get an image from the clipboard. The reason that I believe that the bug is in KDE or Qt and not in the code below, is the fact that first of all the code below works correct in Gnome, and second, the fact that Gimp shows the same problem as my code below (while written completely independent). The code below can be useful to try to reproduce the problem... ------------------------ #include <X11/Xlib.h> #include <X11/Xatom.h> #include <cstdio> #include <climits> #include <cstring> #include <iostream> /* This code is created thanks to the "ad-hoc" tutorials from http://mi.eng.cam.ac.uk/~er258/code/x11.html, but written from scratch. Also used for documentation: http://tronche.com/gui/x/icccm/sec-2.html xsel.c (search for a recent enough version of it that has INCR related code in it) */ //the return values of XGetWindowProperty struct WindowProperty { unsigned char* prop; int actual_format; int nitems; Atom actual_type; WindowProperty() : prop(0) { } ~WindowProperty() { if(prop) XFree(prop); } }; std::string GetAtomName(Display* display, Atom atom) { if(atom == None) return "None"; char* name = XGetAtomName(display, atom); std::string result(name); XFree(name); return result; } void getWindowProperty(WindowProperty& property, Display* display, const Window& window, Atom atom, Bool del) { Atom actual_type; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char* prop = 0; int length = 1024; //read all bytes do { if(prop) XFree(prop); XGetWindowProperty(display, window, atom, del, length, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop); length *= 2; } while(bytes_after != 0); property.prop = prop; property.actual_format = actual_format; property.nitems = nitems; property.actual_type = actual_type; } Atom findAtomOfType(Display* display, const WindowProperty& property, const std::string& datatype) { Atom* atoms = (Atom*)property.prop; for(int i = 0; i < property.nitems; i++) std::cout << "available target type: " << GetAtomName(display, atoms[i]) << std::endl; for(int i = 0; i < property.nitems; i++) { if(GetAtomName(display, atoms[i]) == datatype) return atoms[i]; } return None; } bool getNextEvent(Display *display, XEvent *event_return) { #if 1 //This one doesn't always get the event double t = getSeconds(); while(!XPending(display)) { if(getSeconds() - t > 5.0) { std::cout << "Error: The XNextEvent never came... :(" << std::endl; return false; //he's probably never going to send us an event :( } } XNextEvent(display, event_return); return true; #else //This one blocks forever when seeing complex visuals fullscreen (so > 300K data), pressing ctrl + printscreen, and then pasting it in the program, causing INCR transfer //Gimp also hangs in this scenario. And not in Gnome. So KDE is the bug! XNextEvent(display, event_return); return true; #endif } void getIncrData(std::vector<unsigned char>& data,const XSelectionEvent& selevent) { std::cout << "Incremental transfer starting due to INCR property" << std::endl; XEvent event; XSelectInput(selevent.display, selevent.requestor, PropertyChangeMask); XDeleteProperty(selevent.display, selevent.requestor, selevent.property); //this SHOULD start the INCR mechanism (but in KDE 3.5 I don't get any events after this???) for(;;) { if(!getNextEvent(selevent.display, &event)) break; if(event.type == PropertyNotify) { if (event.xproperty.state != PropertyNewValue) continue; WindowProperty property; getWindowProperty(property, selevent.display, selevent.requestor, selevent.property, False); size_t num_bytes = property.nitems * property.actual_format / 8; std::cout<<"INCR data size: " << num_bytes << std::endl; for(size_t i = 0; i < num_bytes; i++) data.push_back(property.prop[i]); XDeleteProperty(selevent.display, selevent.requestor, selevent.property); if(num_bytes == 0) break; } else break; } } //stores the image as RGBA in image, and its width and height in w and h. So image.size() is w * h * 4. Returns true if there was an image on the clipboard, false if not (in that case the output parameters should not be used) bool getClipboardImage(std::vector<unsigned char>& image, int& w, int& h) { Display* display = XOpenDisplay(NULL); int screen = DefaultScreen(display); Window root = RootWindow(display, screen); std::string datatype = "image/png"; //datatype can be something like STRING, image/png, ... We use "image/png" here, and that is used by lots of linux programs luckily so it works almost always for images. //dummy window Window window = XCreateSimpleWindow(display, root, 0, 0, 100, 100, 0, BlackPixel(display, screen), BlackPixel(display, screen)); Atom ATOM_TARGETS = XInternAtom(display, "TARGETS", False); //possible formats in which source program can output the data Atom ATOM_CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0); XConvertSelection(display, ATOM_CLIPBOARD, ATOM_TARGETS, ATOM_CLIPBOARD, window, CurrentTime); XFlush(display); std::vector<unsigned char> data; XEvent event; bool sent_request = false; Atom image_png_atom = None; for(;;) { if(!getNextEvent(display, &event)) break; if(event.type == SelectionNotify) { Atom target = event.xselection.target; std::cout << "target atom name: " << GetAtomName(display, target) << std::endl; if(event.xselection.property != None) { WindowProperty property; getWindowProperty(property, display, window, ATOM_CLIPBOARD, False); std::cout << "property atom name: " << GetAtomName(display, property.actual_type) << std::endl; if(target == ATOM_TARGETS && !sent_request) { //property.prop now contains a list of Atoms, and each atom has a datatype associated with it sent_request = true; image_png_atom = findAtomOfType(display, property, "image/png"); if(image_png_atom != None) XConvertSelection(display, ATOM_CLIPBOARD, image_png_atom, ATOM_CLIPBOARD, window, CurrentTime); else break; } else if(target == image_png_atom) { if(GetAtomName(display, property.actual_type) == "image/png") { //property.prop now contains actual data bytes, the image size_t num_bytes = property.nitems * property.actual_format / 8; std::cout<<"data size: " << num_bytes << std::endl; data.resize(num_bytes); for(size_t i = 0; i < data.size(); i++) data[i] = property.prop[i]; break; } else if(GetAtomName(display, property.actual_type) == "INCR") { //XConvertSelection(display, ATOM_CLIPBOARD, ATOM_TARGETS, ATOM_CLIPBOARD, window, CurrentTime); //XFlush(display); getIncrData(data, event.xselection); break; } } else break; } else break; } } if(!data.empty()) { //Decode the PNG data here, store the image pixels as RGBA in the output parameters image, w and h std::cout << successfully got full PNG image << std::endl; return true; } std::cout<<"no image on clipboard (or error)" << std::endl; return false; } /*int main() { std::vector<unsigned char> image; int w, h; getClipboardImage(image, w, h); }*/ KDE just uses QClipboard from Qt, so I suggest creating a Qt-only testcase and reporting the problem at http://bugreports.qt.nokia.com . *** Bug 245514 has been marked as a duplicate of this bug. *** |