[Bug 228591] Paste image from clipboard bad interoperability

Aardwolf olifant at gmail.com
Fri Feb 26 09:12:04 GMT 2010


https://bugs.kde.org/show_bug.cgi?id=228591





--- Comment #1 from Aardwolf <olifant gmail com>  2010-02-26 10:12:00 ---
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);
}*/

-- 
Configure bugmail: https://bugs.kde.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.



More information about the Unassigned-bugs mailing list