如何使用X11复制到剪贴板?

11

在OS X上使用框架,我可以使用以下代码将PNG复制到剪贴板(用C语言编写 - 当然我也可以使用Cocoa的NSPasteboard):

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

我想要将这个功能移植到Linux/*BSD平台。我该如何使用X来复制它?

2个回答

10

在开始之前,请先阅读X Selections,Cut Buffers和Kill Rings。 X11有一个相当独特的系统,似乎没有其他人复制过。

与大多数其他系统不同的奇怪之处在于:如果拥有选择(剪贴板)的程序消失了,那么选择也会消失。 因此,当您的程序说“我有一个选择(恰好是图像)”然后退出时,没有人能够从您那里请求该图像的副本。为了有用,剪贴板所有者至少需要保留到另一个程序获取选择为止。

还在这里吗?以下是一个简短的程序,使用PyGTK执行您想要的操作(因为C很麻烦)。

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

底层发生了什么:

  • Gdk 加载一张图片。
  • Gtk 声明拥有 CLIPBOARD 选择。
  • Gtk 请求 复制并接管 CLIPBOARD_MANAGER 选择(可能没有运行),
  • 当另一个程序从我们的选择中请求数据时,Gtk 处理数据从图像到目标的转换和传输。
  • 第一个 OWNER_CHANGE 事件对应于我们获取所有权;等待下一个对应于我们失去所有权的事件,并退出。

如果有剪贴板管理器正在运行,则此程序可能立即退出。否则,它将等待直到在另一个程序中执行“剪切/复制”操作。


非常感谢。使用compiz截屏工具非常有用! - Drasill
整洁的脚本!以下这篇在superuser上的文章也包含了一个类似的Python脚本,但它只能在gnome下工作:http://superuser.com/questions/301851/how-to-copy-a-picture-to-clipboard-from-command-line-in-linux - qed
有一些方法可以改进这个问题。例如,我们能否使它在剪贴板中的内容被粘贴后自动终止 gtk.main? - qed

3

程序终止后在GTK剪贴板上存储数据的能力得不到很好的支持。GTK.clipboard.store可能无法存储大型图像(大于几百kB),而高级桌面功能如Compiz可能会与此机制冲突。一种没有这些缺点的解决方案是在后台运行一个简单的gtk应用程序。下面的Python服务器应用程序使用Pyro包来公开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() )

将此程序作为后台进程启动,即

gclipboard-imaged.py &

以下示例客户端应用程序使用命令行中给定的文件名设置剪贴板图像:


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

要将图片复制到剪贴板,请执行以下操作:

运行gclipboard-setimage.py picname.png


这有点复杂,但绝对不应该被否决。感谢您发布您的解决方案! - qed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接