原理介绍

在 X11 中,剪贴板被称为selection,使用的是X11中的ICCCM协议,窗口间通信协议。

X11 剪贴板是 X Window 系统中的一种机制,用于在应用程序之间复制和粘贴文本、图像等数据。X11 提供了多种选择(Selection)用于处理剪贴板操作,主要包括 PRIMARYSECONDARYCLIPBOARD 三种。

selection的协议内容见:

https://tronche.com/gui/x/icccm/sec-2.html#s-2
,当你把这些通信都调用一遍,你应该就能理解X11剪贴板是如何工作的。

或者参考:

https://www.uninformativ.de/blog/postings/2017-04-02/0/POSTING-en.html

参考这些内容实现了一个剪贴板,并按需要做了小量的改动,记录步骤和说明如下文。

首先要了解了X11中属性 Property这个概念,窗口管理ICCCM和EWMH协议的实现都是通过Property里的数据来实现的。在我的另一篇文档中有说明属性是如何使用的。剪贴板操作也是通过传递Property来实现数据在不同的窗口间复制粘贴。

具体步骤

当复制发生时,需要先设置本身为剪贴板的owner,通过XSetSelectionOwner函数来修改,成为owner后就可以收到SelectionClear/SelectionRequest事件,SelectionClear是另一个窗口获取剪贴板owner,SelectionRequest是另一个窗口来获取剪贴板内容,如果是获取"TARGETS ",则是想获取剪贴板里面的数据有哪些Property,如果是获取其他指定Property,则是真正的获取Property对应的数据了。

1//复制文件后 2:~$ xclip -o -target TARGETS -selection clipboard 3peony-qt/is-cut 4text/uri-list 5text/x-moz-url 6text/plain 7peony-qt/encoded-uris 8UTF8_STRING 9STRING 10TEXT 11TARGETS 12MULTIPLE 13TIMESTAMP 14SAVE_TARGETS 1516//然后获取指定Property 17~$ xclip -o -target text/uri-list -selection clipboard 18file:///home/warlice/i3.log 19~$ xclip -o -target peony-qt/is-cut -selection clipboard 20false 2122//复制文本后 23~$ xclip -o -target TARGETS -selection clipboard 24text/plain 25UTF8_STRING 26STRING 27TEXT 28TARGETS 29MULTIPLE 30TIMESTAMP 31SAVE_TARGETS 3233//然后获取指定Property 34~$ xclip -o -target text/plain -selection clipboard 35arlice 36

简单描述一般窗口发生复制粘贴的通信步骤如下:

  1. A window copy(control + c) :

    XSetSelectionOwner

    XChangeProperty TARGET

    XChangeProperty anytype

  2. B window paste(control + v) :

  3. A window

    while OnSelectionRequest target

  4. A window XSendEvent target

  5. B window SelectionRequest property in target

  6. A window

    while OnSelectionRequest property in target

  7. A window XSendEvent anyproperty

    最终B window XGetWindowProperty from A window

从上面的步骤可以看出,实际在处理SelectionRequest事件返回的XSendEvent时,把Property提供给请求者就完成了粘贴操作。 但是有一个问题,如果做为owner的窗口关闭了,就相当于剪贴板没有复制过了,所以X11上有一些框架就自带剪贴板窗口,即使提供数据的窗口程序退出了,数据也还保存在剪贴板窗口上。

需要一个 clipboard manager进程作为 clipboard 的 owner。 当其它进程进行复制操作成为 owner 时,clipboard manager应当进行以下操作:

  1. 向新的 owner 询问所有数据,并将这些数据保存起来;

  2. 重新将自己声明为 owner。

简单描述自定义实现的剪贴板管理器的步骤如下:

  1. XSetSelectionOwner

    till OnSelectionClear

  2. XConvertSelection

    while SelectionNotify

    XChangeProperty target/anyproperty

  3. XSetSelectionOwner

    while OnSelectionRequest target

  4. XSendEvent target

    while OnSelectionRequest anyproperty

  5. XSendEvent anyproperty

    最终window XGetWindowProperty from ClipBoardManager

在收到SelectionClear事件时,将所以复制的内容都XConvertSelection到自己这个窗口上,包括TARGETS 和所有类型的内容,并且可以取出来想到的类型数据保存,再通过XSetSelectionOwner将当前窗口重新设置为owner。在收到SelectionRequest事件时,按需要返回TARGET类型和对应类型的内容。

即可以实现,实时获取剪贴板数据和更新自定义数据到剪贴板,具体在我们的实现中就是将复制的内容传给Android,并且在Android复制后,将内容写进UTF8_STRING属性,清除其他属性,实现Android和Linux的剪贴板同步。

现在还只实现了在Xserver内的所有类型复制,Xserver和Android的剪贴板只实现了文本同步,实际上文件复制同步只是对不同类型的复制内容的处理实现罢了。