Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to copy to clipboard with X11?

Using the frameworks on OS X, I can use the following to copy a PNG to the pasteboard (in C — obviously I could use NSPasteboard with Cocoa):

#include <ApplicationServices/ApplicationServices.h>

int copyThatThing(void)
{
    PasteboardRef clipboard;
    if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
        return -1;
    }

    if (PasteboardClear(clipboard) != noErr) {
        CFRelease(clipboard);
        return -1;
    }

    size_t len;
    char *pngbuf = createMyPNGBuffer(&len); /* Defined somewhere else */
    if (pngbuf == NULL) {
        CFRelease(clipboard);
        return -1;
    }

    CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pngbuf, 
                                         len, kCFAllocatorNull);
    if (data == NULL) {
        CFRelease(clipboard);
        free(pngbuf);
        return -1;
    }

    OSStatus err;
    err = PasteboardPutItemFlavor(clipboard, NULL, kUTTypePNG, data, 0);
    CFRelease(clipboard);
    CFRelease(data);
    free(pngbuf);

    return 0;
}

I'm interested in porting this functionality to Linux/*BSD platforms. How can I replicate this using X?

like image 859
Michael Avatar asked Dec 08 '09 17:12

Michael


People also ask

How do you copy and paste on X11?

CMD-C should set the highlighted text/object to the X11 CLIPBOARD selection. CMD-V should paste from the X11 CLIPBOARD buffer -no matter what is in there-- into the active target, like CTL-V or Edit->Paste does by default in almost every X application.

How do I copy and paste in xterm Linux?

In xterm, just drag the mouse across a portion of the text to copy it to the clipboard. Click with the middle mouse button to paste text into an xterm.


2 Answers

Go read X Selections, Cut Buffers, and Kill Rings before anything else. X11 has a rather unique system that nobody else seems to have copied.

One oddity that is different from most other systems: if the program owning the selection (clipboard) goes away, so does the selection. So when your program says "I have a selection (which happens to be an image)" and then exits, nobody will be able to request a copy of that image from you. In order to be useful, the clipboard owner needs to stick around at least until another program takes the selection.

Still here? Here's a short program that does what you want, using PyGTK (because C is a pain).

#!/usr/bin/env python
import gtk
import sys

count = 0
def handle_owner_change(clipboard, event):
    global count
    print 'clipboard.owner-change(%r, %r)' % (clipboard, event)
    count += 1
    if count > 1:
        sys.exit(0)

image = gtk.gdk.pixbuf_new_from_file(sys.argv[1])
clipboard = gtk.clipboard_get()
clipboard.connect('owner-change', handle_owner_change)
clipboard.set_image(image)
clipboard.store()
gtk.main()

What happens under the hood:

  • Gdk loads an image.
  • Gtk claims ownership of the CLIPBOARD selection.
  • Gtk requests that the CLIPBOARD_MANAGER copy and take the selection. (There might not be one running, so this might not happen.)
  • When another program requests data from our selection, Gtk handles the conversion and transfer of data from the image to the target.
  • The first OWNER_CHANGE event corresponds to us taking ownership; wait for the next one corresponding to us losing ownership, and exit.

If a clipboard manager is running, this program may exit immediately. Otherwise, it will wait until "cut/copy" is performed in another program.

like image 171
ephemient Avatar answered Sep 20 '22 01:09

ephemient


The ability to store data on the GTK clipboard after a program terminates is not well supported. GTK.clipboard.store may fail to store larger images (greater than several hundred kB), and advanced desktop features like compiz may conflict with this mechanism. One solution without these drawbacks is to run a simple gtk application in the background. The following Python server application uses the Pyro package to expose the methods of ImageToClipboard:


#! /usr/bin/env python
# gclipboard-imaged.py
import gtk, sys, threading;
import Pyro.core;

class ImageToClipboard(Pyro.core.ObjBase):
   def __init__(self, daemon):
      Pyro.core.ObjBase.__init__(self)
      self.daemon = daemon;
   def _set_image(self, img):
      clp = gtk.clipboard_get();
      clp.set_image(img);
   def set_image_from_filename(self, filename):
      with gtk.gdk.lock:
         img = gtk.gdk.pixbuf_new_from_file(filename);
         self._set_image(img);
   def quit(self):
      with gtk.gdk.lock:
         gtk.main_quit();
      self.daemon.shutdown();

class gtkThread( threading.Thread ):
   def run(self):
      gtk.main();

def main():
   gtk.gdk.threads_init();
   gtkThread().start();
   Pyro.core.initServer();
   daemon = Pyro.core.Daemon();
   uri = daemon.connect(ImageToClipboard(daemon),"imagetoclipboard")
   print "The daemon running on port:",daemon.port
   print "The object's uri is:",uri
   daemon.requestLoop();
   print "Shutting down."
   return 0;

if __name__=="__main__":
   sys.exit( main() )

Start this program as a background process, i.e.

gclipboard-imaged.py &

The following example client application sets the clipboard image using a filename given at the command line:


#! /usr/bin/env python
# gclipboard-setimage.py
import Pyro.core, sys;

serverobj =  Pyro.core.getProxyForURI("PYROLOC://localhost:7766/imagetoclipboard");
filename = sys.argv[1];
serverobj.set_image_from_filename(filename);

To copy an image to the clipboard, run

gclipboard-setimage.py picname.png

like image 32
paulgrif Avatar answered Sep 20 '22 01:09

paulgrif