简介
几乎所有的Linux发行版都使用
X11
实现剪贴板功能
剪贴板的说法可能是从
Windows
传入的, 在
X11
中剪贴板就叫做
selection
, 并且系统中可以有任意多个
selection
如无特别说明, 下文的剪贴板和selection是相同的意思
复制和粘贴
从用户的直观感受来说, 基本的剪贴板操作有2个: 复制和粘贴
复制, 就是把数据放到剪贴板
粘贴, 就是把数据从剪贴板取出来
但是, 在
X11
中的实现却不是这样
在
X11
中
复制
时, 数据并没有拷贝到剪贴板, 数据是在
粘贴
时, 直接发送给粘贴的窗口
听起来有点抽象, 举个栗子
用户在窗口A选中了一段文字, 点击复制
然后在窗口B点击粘贴
X11剪贴板
X11
的实现流程是这的
-
窗口A所属的程序使用
XSetSelectionOwner()
获取剪贴板的所有权, 此时没有任何数据操作, 仅仅声明现在自己拥有剪贴板 -
窗口A所属的程序等待剪贴板的
粘贴
请求 - 用户在窗口B点击粘贴
-
窗口B所属的程序使用
XConvertSelection()
告诉
XServer
说我要
粘贴
, 请把数据发到我的窗口属性上 -
XServer
向窗口A发送
粘贴
请求, 告诉A把数据设置到窗口B的属性上 -
窗口A使用
XChangeProperty()
把数据设置到窗口B的属性上 -
窗口B收到剪贴板数据设置完成的通知, 使用
XGetWindowProperty()
把数据取出来
以上是大概的流程, 可以看到还是相当麻烦的
这是因为X协议是C/S架构
X客户端只能和XServer通信(可以把窗口A当做一个客户端, B当做另一个客户端)
窗口A要把剪贴板数据发给B只能通过XServer中转
以上流程使用libX11实现的关键代码如下
窗口A
sel = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
// 窗口A拥有剪贴板
XSetSelectionOwner(dpy, sel, A, CurrentTime);
for (;;)
{
// 等待其他X客户端发出获取剪贴板数据的请求
XNextEvent(dpy, &ev);
switch (ev.type)
{
case SelectionRequest:
sev = (XSelectionRequestEvent*)&ev.xselectionrequest;
// 把剪贴板数据设置到想要粘贴的x客户端窗口的属性上
XChangeProperty(dpy, sev->requestor, sev->property, utf8, 8, PropModeReplace,
(unsigned char *)now, strlen(now));
// 发送SelectionNotify事件给请求剪贴板的x客户端
XSendEvent(dpy, sev->requestor, True, NoEventMask, (XEvent *)&ssev);
}
窗口B
sel = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
// 告诉窗口A, 把剪贴板数据转换成指定的格式,
// 并把数据放到窗口B的的属性上
// 此后窗口A会收到SelectionRequest事件
XConvertSelection(dpy, sel, utf8, property, B, CurrentTime);
// 等待窗口A把数据设置完成
XNextEvent(dpy, &ev);
switch (ev.type)
{
// 剪贴板数据已经放到指定的窗口, 可以获取了
case SelectionNotify:
XGetWindowProperty();
}
数据类型
- 剪贴板的数据类型叫做target, 例如UTF8_STRING, TEXT等
- 有一个特殊的剪贴板类型叫做TARGETS, 通过它可以获取剪贴板数据支持转换成的格式
异常情况
理论上来讲
2个X客户端之间传输剪贴板数据, 只能通过XServer实现
XServer不保存剪贴板数据, 只转发selection事件
数据直接由2个X客户端通过设置窗口属性传递(当然这也要通过XServer中转)
那么就会出现一个问题
如果一个x客户端复制了一个内容, 然后这个x客户端关闭了
这时另一个程序想粘贴数据是不可能的
但是实际测试并不是这样
在Ubuntu上测试时发现, 复制内容的那个程序(窗口A)关闭了,
其他程序(窗口B)还是可以粘贴
难道以上的理论错了
当然不是
只是情况更加复杂了
因为又多了一个Clipboard manager
Clipboard manager的大概工作流程如下
- Clipboard manager获取剪贴板的所有权
-
如果其他程序要复制数据, 会获取剪贴板所有权, 这时Clipboard manager失去所有权, 它会这么做
- 获取当前剪贴板的数据
- 自己提供数据
- 再次获取剪贴板所有权
所以当A关闭后, B粘贴的数据实际上是Clipboard manager提供的
参考资料