2009-12-08 18 views
8

Utilizzando i quadri su OS X, posso utilizzare il seguente per copiare un PNG al tavolo di montaggio (in C - ovviamente ho potuto utilizzare NSPasteboard con cacao):Come copiare negli appunti 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; 
} 

Mi interessa porting questa funzionalità per Piattaforme Linux/* BSD. Come posso replicarlo usando X?

risposta

10

Vai a leggere X Selections, Cut Buffers, and Kill Rings prima di ogni altra cosa. X11 ha un sistema piuttosto unico che nessun altro sembra aver copiato.

Una stranezza che è diversa dalla maggior parte degli altri sistemi: se il programma proprietario della selezione (Appunti) scompare, lo stesso vale per la selezione. Quindi, quando il tuo programma dice "Ho una selezione (che sembra essere un'immagine)" e poi esce, nessuno sarà in grado di richiedere una copia di quell'immagine da te. Per essere utile, il proprietario degli appunti deve rimanere in attesa almeno fino a quando un altro programma accetta la selezione.

Ancora qui? Ecco un breve programma che fa quello che vuoi, usando PyGTK (perché C è un dolore).

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

Cosa accade sotto il cofano:

  • Gdk carica un'immagine.
  • Gtk rivendica la proprietà della selezione CLIPBOARD.
  • Gtk requests che CLIPBOARD_MANAGER copia e accetta la selezione. (Potrebbe non essercene uno in esecuzione, quindi potrebbe non succedere.)
  • Quando un altro programma richiede dati dalla nostra selezione, Gtk gestisce la conversione e il trasferimento dei dati dall'immagine alla destinazione.
  • Il primo evento OWNER_CHANGE corrisponde a noi che assumiamo la proprietà; aspetta il prossimo che ci corrisponde perdendo la proprietà ed esci.

Se un gestore di appunti è in esecuzione, questo programma potrebbe uscire immediatamente. Altrimenti, aspetterà che "taglia/copia" venga eseguito in un altro programma.

+0

Grazie mille. Molto utile, con lo strumento screenshot di Compiz! – Drasill

+0

Script pulito! La seguente voce su superuser contiene anche uno script python simile, ma funziona solo con gnome: http://superuser.com/questions/301851/how-to-copy-a-picture-to-clipboard-from-command-line -in-linux – qed

+0

Ci sono modi in cui questo può essere migliorato però. Ad esempio, possiamo farlo uccidere automaticamente gtk.main una volta che il contenuto negli appunti è stato incollato? – qed

3

La possibilità di memorizzare i dati negli Appunti GTK dopo la chiusura di un programma non è ben supportata. GTK.clipboard.store potrebbe non riuscire a memorizzare immagini più grandi (superiori a diverse centinaia di kB) e le funzionalità avanzate del desktop come compiz potrebbero entrare in conflitto con questo meccanismo. Una soluzione senza questi svantaggi consiste nell'eseguire una semplice applicazione gtk in background. La seguente applicazione server Python usa il pacchetto Piro per esporre i metodi di 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()) 

Avviare questo programma come un processo in background, cioè

gclipboard-imaged.py &

Il seguente esempio cliente l'applicazione imposta l'immagine degli appunti utilizzando un nome di file indicato nella riga di 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); 

Per copiare un mago negli appunti, eseguire

picname.png gclipboard-setimage.py

+0

Questo è un po 'complicato, ma sicuramente non merita un voto negativo. Grazie per aver postato la tua soluzione! – qed

Problemi correlati