2009-12-08 16 views
8

Utilizando los marcos en OS X, puedo usar lo siguiente para copiar un archivo PNG a la mesa de trabajo (en C - obviamente que podría utilizar NSPasteboard con cacao):¿Cómo copiar al portapapeles con X11?

#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; 
} 

Estoy interesado en portar a esta funcionalidad Plataformas Linux/* BSD. ¿Cómo puedo replicar esto usando X?

Respuesta

10

Lea X Selections, Cut Buffers, and Kill Rings antes que cualquier otra cosa. X11 tiene un sistema bastante único que nadie más parece haber copiado.

Una rareza que es diferente de la mayoría de los demás sistemas: si el programa que posee la selección (portapapeles) desaparece, también lo hace la selección. Entonces, cuando su programa dice "Tengo una selección (que pasa a ser una imagen)" y luego sale, nadie podrá solicitarle una copia de esa imagen. Para ser útil, el propietario del portapapeles debe permanecer al menos hasta que otro programa tome la selección.

¿Seguir? Aquí hay un programa corto que hace lo que quiere, usando PyGTK (porque C es un dolor).

#!/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() 

¿Qué ocurre bajo el capó:

  • Gdk carga una imagen.
  • Gtk reclama la propiedad de la selección CLIPBOARD.
  • Gtk requests que el CLIPBOARD_MANAGER copia y toma la selección. (Es posible que no haya uno en ejecución, por lo que esto podría no suceder.)
  • Cuando otro programa solicita datos de nuestra selección, Gtk maneja la conversión y transferencia de datos de la imagen al destino.
  • El primer evento OWNER_CHANGE corresponde a nosotros tomando posesión; espere a que el siguiente nos corresponda perdiendo la propiedad y salga.

Si se está ejecutando un gestor de portapapeles, este programa puede cerrarse inmediatamente. De lo contrario, esperará hasta que se realice "cortar/copiar" en otro programa.

+0

muchas gracias. ¡Muy útil, con la herramienta de captura de pantalla compiz! – Drasill

+0

¡Buen script! La siguiente entrada en superusuario también contiene un script python similar, pero solo funciona bajo gnome: http://superuser.com/questions/301851/how-to-copy-a-picture-to-clipboard-from-command-line -in-linux – qed

+0

Sin embargo, hay formas de mejorar esto. Por ejemplo, ¿podemos hacer que mate automáticamente a gtk.main una vez que se pega el contenido del portapapeles? – qed

3

La capacidad de almacenar datos en el portapapeles GTK después de que un programa finaliza no es bien soportada. Es posible que GTK.clipboard.store no almacene imágenes más grandes (más de varios cientos de kB), y las funciones de escritorio avanzadas como compiz pueden entrar en conflicto con este mecanismo. Una solución sin estos inconvenientes es ejecutar una aplicación gtk simple en segundo plano. La siguiente aplicación de servidor Python usa el paquete Pyro para exponer los métodos de 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()) 

inicio de este programa como un proceso en segundo plano, es decir

gclipboard-imaged.py &

el siguiente ejemplo de cliente aplicación establece la imagen del portapapeles usando un nombre de archivo dado en la línea de comando:


#! /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); 

Para copiar un i mago en el portapapeles, ejecute

picname.png gclipboard-setimage.py

+0

Esto es un poco complicado, pero definitivamente no merece un voto negativo. ¡Gracias por publicar tu solución! – qed

Cuestiones relacionadas