X11: How does “the” clipboard work?
If you have used another operating system before you switched to something that runs X11, you will have noticed that there is more than one clipboard:
如果你在切换到运行X11的操作系统之前使用过其他操作系统,你会注意到这里有不止一个剪贴板:
- Sometimes, you can use the mouse to select some text, switch to another window, and then hit the middle mouse button to paste text.
有时,你可以用鼠标选择一些文本,切换到另一个窗口,然后点击鼠标中键粘贴文本。 - Sometimes, you can select text, then hit some hotkey, e.g. Ctrl+C, switch to another window, hit another hotkey, e.g. Ctrl+V, and paste said text.
有时,你可以选择文本,然后按一些热键,如Ctrl+C,切换到另一个窗口,按另一个热键,如Ctrl+V,并粘贴说的文本。 - Sometimes, you can do both.
有时,你可以两者兼顾。
Those two clipboards usually don’t interfere. You can keep the content of the “Ctrl+C clipboard” while using the “middle mouse clipboard” to copy and paste something else.
您可以保留“Ctrl+C剪贴板”的内容,同时使用“鼠标中间剪贴板”复制和粘贴其他东西。
How does that work? Is there more than one clipboard? How many are there? Do all X11 clients support all forms of clipboards?
是不是不止一个剪贴板?有多少个?所有的X11客户端都支持所有形式的剪贴板吗?
Here’s the approriate section of ICCCM on this topic.
以下是ICCCM关于这个主题的适当部分。
Selections as a form of IPC IPC的一种形式
First things first, in X11 land, “clipboards” are called “selections”.
首先,在X11中,“剪贴板”被称为“选择”。
Yes, there is more than one selection and they all work independently. In fact, you can use as many selections as you wish. In theory, that is. When using selections, you make different clients communicate with each other. This means that those clients have to agree on which selections to use. You can’t just invent your own selection and then expect Firefox to be compatible with it.
事实上,您可以使用任意多的选项。理论上是这样的。当使用选择时,您将使不同的客户机彼此通信。这意味着这些客户机必须就使用哪些选择达成一致。你不能发明自己的选项,然后期望Firefox与之兼容。
Looking at it from a very high altitude, it goes like this:
从很高的海拔看,它是这样的:
(1) means every client can claim ownership of any selection at any time. It only informs the X server about that – no data is transferred yet. This is an important thing to understand. The X server is nothing more but a broker. It takes a note of which client owns which selection.
(1)意味着每个客户都可以在任何时间声称对任何选择的所有权。它只通知X服务器——还没有传输数据。理解这一点很重要。X服务器只不过是一个代理。它记录哪个客户机拥有哪个选择。
In (2), another client asks the X server to send it the content of selection “FOO”. The X server simply relays that request to the current owner of that selection. Client A is then responsible for actually transmitting the data to client B.
在(2)中,另一个客户端请求X服务器发送选择“FOO”的内容。X服务器只是将该请求转发给该选择的当前所有者。然后,客户机A负责将数据实际传输到客户机B。
How are selections identified?
选择是如何被识别的?
Above, I just called it “selection FOO”, meaning it’s a rather arbitrary identifier that you can choose. If you have worked with X11 before, this won’t be surprising to you: Selections are identified by atoms.
上面,我把它叫做“选择FOO”,意思是它是一个你可以选择的任意标识符。如果您以前使用过X11,这不会让您感到惊讶:选择是由原子标识的。
Quick recap: Atoms are a way to identify something in X11 and they are basically strings. Internally, a number is allocated for each atom, but you rarely need to ask the X server, “what’s the name of atom number 42?”
快速回顾一下:原子是在X11中识别某些东西的一种方法,它们基本上是字符串。在内部,为每个atom分配一个编号,但您很少需要问X服务器“atom编号42的名称是什么?”
There are three “standard” selection names:
有三个“标准”的选择名称:
PRIMARY: The “middle mouse clipboard”
主要:鼠标中间剪贴板SECONDARY: Virtually unused these days
次要的:这些天几乎没有使用CLIPBOARD: The “Ctrl+C clipboard”
剪贴板:“Ctrl+C剪贴板”
“Standard” means that they are specified by ICCCM 2.6.1. Yes, it’s confusing that one of the selections is named “clipboard”.
“标准”指ICCCM 2.6.1规定的标准。是的,其中一个选项被命名为“剪贴板”,这让人感到困惑。
Program 1: Query selection owners
程序1:查询选择所有者
Knowing what we know now, we can ask the X server to tell us who owns which selection. This is xowners.c:
知道了我们现在知道的,我们可以要求X服务器告诉我们谁拥有哪个选择。这是xowners.c:
#include <stdio.h>#include <X11/Xlib.h>int main() {Display *dpy;Window owner;Atom sel;char *selections[] = { "PRIMARY", "SECONDARY", "CLIPBOARD", "FOOBAR" };size_t i;dpy = XOpenDisplay(NULL);if (!dpy) {fprintf(stderr, "Could not open X display\n");return 1;}for (i = 0; i < sizeof selections / sizeof selections[0]; i++) {sel = XInternAtom(dpy, selections[i], False);owner = XGetSelectionOwner(dpy, sel);printf("Owner of '%s': 0x%lX\n", selections[i], owner);}return 0;}
Compilation this program (and all of the following ones in a similar manner):
编译这个程序(以类似的方式编译以下所有程序):
cc -Wall -Wextra -o xowners xowners.c -lX11
FOOBAR is a non-standard selection name. It’s perfectly valid to use it, but don’t expect it to work with all clients. :-)
FOOBAR是一个非标准的选择名。使用它是完全有效的,但不要期望它对所有客户端都有效。: -)
As you can see, the program prints IDs of windows:
如你所见,程序打印windows的id:
$ ./xownersOwner of 'PRIMARY': 0x60080FOwner of 'SECONDARY': 0x0Owner of 'CLIPBOARD': 0x1E00024Owner of 'FOOBAR': 0x0
Windows are another basic form of communication between clients, meaning they not necessarily work as “boxes of pixels”. Unmapped windows can exist in an X11 session just fine (and there usually are many of them).
Windows是客户端之间通信的另一种基本形式,这意味着它们不必像“像素盒”一样工作。未映射的窗口可以很好地存在于X11会话中(通常有很多这样的窗口)。
We can use the xwininfo tool to find out more about those two windows:
我们可以使用xwininfo工具来了解这两个窗口的更多信息:
Aha, so xiate is holding the PRIMARY selection, while lariza owns CLIPBOARD.
啊哈,所以xiate 拿着主要的选择,而lariza 拥有剪贴板。
Let’s have a look at the full output of one of these commands:
让我们来看看这些命令的完整输出:
$ xwininfo -id 0x60080Fxwininfo: Window id: 0x60080f "xiate"Absolute upper-left X: -100Absolute upper-left Y: -100Relative upper-left X: -100Relative upper-left Y: -100Width: 10Height: 10Depth: 0...Map State: IsUnMapped...
his is, in fact, an unmapped window. Clients often do this. They create a window with the sole purpose of managing selections. Clients could use their visible window, but that’s problematic. Sometimes, visible windows are short-lived and ownership of a selection is lost when the window dies.
事实上,他是一个未映射的窗口。客户经常这样做。它们创建窗口的唯一目的是管理选择。客户可以使用他们的可见窗口,但这是有问题的。有时,可见窗口是短暂的,当窗口死亡时,选择的所有权将丢失。
Content type and conversion
内容类型和转换
So far, so good. And so simple.
到目前为止,一切顺利。所以简单。
Things start to get complicated once you realize that some clients might use clipboards for text, others might use it for images, some might use it for audio data, and some other client might use it for some form of data that you have never heard of.
一旦你意识到有些客户端可能使用剪贴板来处理文本,有些客户端可能使用剪贴板来处理图像,有些客户端可能使用剪贴板来处理音频数据,还有一些客户端可能使用剪贴板来处理你从未听说过的某种形式的数据,事情就开始变得复杂了。
And then there are situations where you can provide the same data in different forms. To illustrate this, just select some text in a web browser. Copy it and paste it into Vim. You’ll get plain text. But if you paste the same selection into a program like LibreOffice Writer, you’ll not only get text but also text attributes, like “this is bold, this is a code block”, and so on.
还有一些情况,您可以以不同的形式提供相同的数据。为了演示这一点,只需在web浏览器中选择一些文本。复制并粘贴到Vim中。你会得到纯文本。但是如果你把相同的选择粘贴到像LibreOffice Writer这样的程序中,你不仅会得到文本,还会得到文本属性,比如“这是粗体,这是一个代码块”,等等。
Recall the diagram from above. Step 2 said: Client B tells the X server to write selection “FOO” to “BAR”. (We have not yet covered what “BAR” is, but we’ll get there soon.) Actually, it’s more like this: “Write selection ‘FOO’ to ‘BAR’ as content type ‘BAZ’.” In other words, client B can request the current content of selection “FOO” as text. Or as an image. Or as something else.
回想一下上面的图表。步骤2:客户端B告诉X服务器将选项“FOO”写入“BAR”。(我们还没有讲到“BAR”是什么,但是我们很快就会讲到。)实际上,它更像这样:“将选择FOO写入BAR作为内容类型BAZ。换句话说,客户端B可以请求选择“FOO”的当前内容作为文本。或者作为一个图像。或者作为其他东西。
That’s why the library call to “get” the current content of a selection is called XConvertSelection() instead of XGetSelection().
这就是为什么调用“获取”选择的当前内容的库称为XConvertSelection()而不是XGetSelection()。
Program 2: Get clipboard as UTF-8
程序2:得到剪贴板作为UTF-8
This is an example of “client B”:
这是“客户B”的一个例子:
#include <stdio.h>#include <X11/Xlib.h>void show_utf8_prop(Display *dpy, Window w, Atom p){Atom da, incr, type;int di;unsigned long size, dul;unsigned char *prop_ret = NULL;/* Dummy call to get type and size. */XGetWindowProperty(dpy, w, p, 0, 0, False, AnyPropertyType,&type, &di, &dul, &size, &prop_ret);XFree(prop_ret);incr = XInternAtom(dpy, "INCR", False);if (type == incr){printf("Data too large and INCR mechanism not implemented\n");return;}/* Read the data in one go. */printf("Property size: %lu\n", size);XGetWindowProperty(dpy, w, p, 0, size, False, AnyPropertyType,&da, &di, &dul, &dul, &prop_ret);printf("%s", prop_ret);fflush(stdout);XFree(prop_ret);/* Signal the selection owner that we have successfully read the* data. */XDeleteProperty(dpy, w, p);}int main(){Display *dpy;Window owner, target_window, root;int screen;Atom sel, target_property, utf8;XEvent ev;XSelectionEvent *sev;dpy = XOpenDisplay(NULL);if (!dpy){fprintf(stderr, "Could not open X display\n");return 1;}screen = DefaultScreen(dpy);root = RootWindow(dpy, screen);sel = XInternAtom(dpy, "CLIPBOARD", False);utf8 = XInternAtom(dpy, "UTF8_STRING", False);owner = XGetSelectionOwner(dpy, sel);if (owner == None){printf("'CLIPBOARD' has no owner\n");return 1;}printf("0x%lX\n", owner);/* The selection owner will store the data in a property on this* window: */target_window = XCreateSimpleWindow(dpy, root, -10, -10, 1, 1, 0, 0, 0);/* That's the property used by the owner. Note that it's completely* arbitrary. */target_property = XInternAtom(dpy, "PENGUIN", False);/* Request conversion to UTF-8. Not all owners will be able to* fulfill that request. */XConvertSelection(dpy, sel, utf8, target_property, target_window,CurrentTime);for (;;){XNextEvent(dpy, &ev);switch (ev.type){case SelectionNotify:sev = (XSelectionEvent*)&ev.xselection;if (sev->property == None){printf("Conversion could not be performed.\n");return 1;}else{show_utf8_prop(dpy, target_window, target_property);return 0;}break;}}}
This is more code than you expected? Yup. But bear with me. We’ll go through it step by step.
这比你预期的代码要多吗?是的。但请耐心听我说。我们将一步一步地进行。
First, let’s uncover what “BAR” is. You see that the code above creates a target_window and an atom target_property. These two things together are “BAR”. When client A sends the content of a selection to client B, it does so by writing the data to a property on a window. This is virtually the only way two X11 clients can communicate arbitrary data through the X server.
首先,让我们来揭示什么是“BAR”。您可以看到上面的代码创建了一个target_window和一个atom target_property。这两个东西加起来就是“BAR”。当客户机A将选择的内容发送给客户机B时,它是通过将数据写入窗口上的属性来实现的。这实际上是两个X11客户机通过X服务器通信任意数据的唯一方法。
Remember that X11 is network transparent. Clients A and B need not run on the same host. They need not even use the same network protocols. One might use TCP/IP, the other might use … whatever. ICCCM uses DECnet as an example, which nobody uses anymore today, probably. As a result, they must not communicate directly, but only through the X server.
客户端A和客户端B不必运行在同一台主机上。它们甚至不需要使用相同的网络协议。一个可能使用TCP/IP,另一个可能使用……ICCCM以DECnet为例,现在可能没人再用它了。因此,它们不能直接通信,而只能通过X服务器。
Okay. Our target “BAR” is a window and a property.
好吧。我们的目标“BAR”是一个窗口和一个属性。
We also need a content type. Here, I used UTF8_STRING. You won’t find this atom name in ICCCM. UTF-8 did not even exist when ICCCM was first published. Newer clients support it, though.
这里,我使用UTF8_STRING。在ICCCM中找不到这个原子名称。在ICCCM首次出版时,UTF-8甚至还不存在。不过,新的客户端支持它。
We then ask the X server to “perform” the conversion: XConvertSelection(). Now look closely at the first diagram at the top of this article. There is no immediate response to XConvertSelection(). The X server must first relay that request to client A, provided that there even is a selection owner right now. Then, at some point in the future, client A decides to do its work – or maybe not. This means that we can only wait for some X event to happen. That’s what the loop at the bottom of the code is for. The event SelectionNotify tells us that a conversion has happened or failed. We can then go ahead and read the property on our very own window; client A should have written its data to that property.
然后我们要求X服务器“执行”转换:XConvertSelection()。现在仔细看看本文顶部的第一个图表。没有立即响应XConvertSelection()。X服务器必须首先将该请求转发给客户机A,即使现在还有一个选择所有者。然后,在未来的某个时刻,客户A决定做它的工作——或者不做。这意味着我们只能等待某个X事件发生。这就是代码底部的循环的作用。事件SelectionNotify告诉我们转换已经发生或失败。然后我们可以在自己的窗口上读取属性;客户端A应该已经将其数据写入该属性。
Some things to note:
- Client A might fail to deliver its data. It may have crashed. Or whatever. Client B must not block and wait for the data transfer to be finished.
客户端A可能无法交付数据。它可能坠毁了。之类的。客户端B不能阻塞并等待数据传输完成。 - lient A might fail to convert the data. This happens, for example, when you ask The GIMP to give you UTF-8 from the clipboard, when GIMP has actually stored image data.
客户端A可能无法转换数据。例如,当您要求GIMP从剪贴板向您提供UTF-8,而GIMP实际上已经存储了图像数据时,就会发生这种情况。 - he call to
XDeleteProperty()tells client A that we have successfully read the data.
对XDeleteProperty()的调用告诉客户机A我们已经成功地读取了数据。 - It’s not required to ask for the current owner of a selection before asking for a conversion. I only did that to check if there is a selection owner right now. (If you don’t do that, you just get a “conversion failed”.)
在要求转换之前,不需要要求选择的当前所有者。我这样做只是为了检查现在是否有一个选择所有者。(如果你不这么做,你只会得到一个“转换失败”。)
Program 3: Owning a selection
计划3:拥有一个选择
This is the other direction. A client that claims ownership of CLIPBOARD and provides data if asked for type UTF8_STRING. So, this is client A:
这是另一个方向。声明剪贴板所有权的客户端,并在请求类型UTF8_STRING时提供数据。这是客户A:
#include <stdio.h>#include <string.h>#include <time.h>#include <X11/Xlib.h>void send_no(Display *dpy, XSelectionRequestEvent *sev) {XSelectionEvent ssev;char *an;an = XGetAtomName(dpy, sev->target);printf("Denying request of type '%s'\n", an);if (an)XFree(an);/* All of these should match the values of the request. */ssev.type = SelectionNotify;ssev.requestor = sev->requestor;ssev.selection = sev->selection;ssev.target = sev->target;ssev.property = None; /* signifies "nope" */ssev.time = sev->time;XSendEvent(dpy, sev->requestor, True, NoEventMask, (XEvent *)&ssev);}void send_utf8(Display *dpy, XSelectionRequestEvent *sev, Atom utf8) {XSelectionEvent ssev;time_t now_tm;char *now, *an;now_tm = time(NULL);now = ctime(&now_tm);an = XGetAtomName(dpy, sev->property);printf("Sending data to window 0x%lx, property '%s'\n", sev->requestor, an);if (an)XFree(an);XChangeProperty(dpy, sev->requestor, sev->property, utf8, 8, PropModeReplace,(unsigned char *)now, strlen(now));ssev.type = SelectionNotify;ssev.requestor = sev->requestor;ssev.selection = sev->selection;ssev.target = sev->target;ssev.property = sev->property;ssev.time = sev->time;XSendEvent(dpy, sev->requestor, True, NoEventMask, (XEvent *)&ssev);}int main() {Display *dpy;Window owner, root;int screen;Atom sel, utf8;XEvent ev;XSelectionRequestEvent *sev;dpy = XOpenDisplay(NULL);if (!dpy) {fprintf(stderr, "Could not open X display\n");return 1;}screen = DefaultScreen(dpy);root = RootWindow(dpy, screen);/* We need a window to receive messages from other clients. */owner = XCreateSimpleWindow(dpy, root, -10, -10, 1, 1, 0, 0, 0);sel = XInternAtom(dpy, "CLIPBOARD", False);utf8 = XInternAtom(dpy, "UTF8_STRING", False);/* Claim ownership of the clipboard. */XSetSelectionOwner(dpy, sel, owner, CurrentTime);for ( ; ; ) {XNextEvent(dpy, &ev);switch (ev.type) {case SelectionClear:printf("Lost selection ownership\n");return 1;break;case SelectionRequest:sev = (XSelectionRequestEvent*)&ev.xselectionrequest;printf("Requestor: 0x%lx\n", sev->requestor);/* Property is set to None by "obsolete" clients. */if (sev->target != utf8 || sev->property == None)send_no(dpy, sev);elsesend_utf8(dpy, sev, utf8);break;}}}
It creates an invisible window and then claims ownership of CLIPBOARD. As you can see, not “the client” owns a selection, but a window does.
它创建了一个看不见的窗口,然后声称拥有CLIPBOARD。如您所见,不是“客户端”拥有一个选择,而是窗口拥有。
The program then waits for events. SelectionClear is simple: Some other client has claimed ownership of the clipboard. Yes, that can happen at any time.
SelectionClear很简单:其他一些客户机已经声明了剪贴板的所有权。是的,那随时都可能发生。
SelectionRequest is sent to client A by the X server. It’s the event that the X server generates due to a call to XConvertSelection() by client B. We now simply check if target is UTF8_STRING. If it’s not, we deny the request. But if it is, we call XChangeProperty() to alter the given property on the given target window. Once we’ve done that, we generate a SelectionNotify event and send it to client B.
SelectionRequest由X服务器发送到客户机A。它是X服务器由于客户机b调用XConvertSelection()而生成的事件。我们现在简单地检查target是否为UTF8_STRING。如果不是,我们就拒绝这个请求。但如果是,我们调用XChangeProperty()来改变给定目标窗口上的给定属性。完成之后,我们生成一个SelectionNotify事件并将其发送给客户端B。
This client sends the current date and time to requestors. I did this to illustrate further how selections don’t store data in the X server. Data is converted (and possibly generated) only when another client asks for it.
此客户机将当前日期和时间发送给请求者。我这样做是为了进一步说明select如何不在X服务器中存储数据。只有当另一个客户机请求数据时,才转换(可能生成)数据。
Program 4: Content type TARGETS
程序4:内容类型目标
There are some special content types. You can ask the owner of a selection to convert the selection into the type TARGETS. This sounds a bit weird, but it’s simple. Client A will not respond with the actual data but with a list of atoms. Each atom is a valid target for the current data.
您可以要求选择的所有者将选择转换为类型目标。这听起来有点奇怪,但其实很简单。客户机A不会用实际数据响应,而是用原子列表响应。每个原子都是当前数据的有效目标。
#include <stdio.h>#include <X11/Xatom.h>#include <X11/Xlib.h>void show_targets(Display *dpy, Window w, Atom p) {Atom type, *targets;int di;unsigned long i, nitems, dul;unsigned char *prop_ret = NULL;char *an = NULL;/* Read the first 1024 atoms from this list of atoms. We don't* expect the selection owner to be able to convert to more than* 1024 different targets. :-) */XGetWindowProperty(dpy, w, p, 0, 1024 * sizeof (Atom), False, XA_ATOM,&type, &di, &nitems, &dul, &prop_ret);printf("Targets:\n");targets = (Atom *)prop_ret;for (i = 0; i < nitems; i++) {an = XGetAtomName(dpy, targets[i]);printf(" '%s'\n", an);if (an)XFree(an);}XFree(prop_ret);XDeleteProperty(dpy, w, p);}int main() {Display *dpy;Window target_window, root;int screen;Atom sel, targets, target_property;XEvent ev;XSelectionEvent *sev;dpy = XOpenDisplay(NULL);if (!dpy) {fprintf(stderr, "Could not open X display\n");return 1;}screen = DefaultScreen(dpy);root = RootWindow(dpy, screen);sel = XInternAtom(dpy, "CLIPBOARD", False);targets = XInternAtom(dpy, "TARGETS", False);target_property = XInternAtom(dpy, "PENGUIN", False);target_window = XCreateSimpleWindow(dpy, root, -10, -10, 1, 1, 0, 0, 0);XConvertSelection(dpy, sel, targets, target_property, target_window,CurrentTime);for (;;) {XNextEvent(dpy, &ev);switch (ev.type) {case SelectionNotify:sev = (XSelectionEvent*)&ev.xselection;if (sev->property == None) {printf("Conversion could not be performed.\n");return 1;} else {show_targets(dpy, target_window, target_property);return 0;}break;}}}
Running this when a typical GTK client currently owns a simple text selection reveals something interesting:
当一个典型的GTK客户端当前拥有一个简单的文本选择时,运行这个程序会发现一些有趣的事情:
$ ./xtargetsTargets:'TIMESTAMP''TARGETS''MULTIPLE''SAVE_TARGETS''UTF8_STRING''COMPOUND_TEXT''TEXT''STRING''text/plain;charset=utf-8''text/plain'
X11 is old and many conventions exist on how to specify data types. Some of them are legacy, some are ambiguous, many not even mentioned by ICCCM. MIME types are fine today, but ICCCM does not talk about MIME types in any way.
X11很旧,并且存在许多关于如何指定数据类型的约定。有些是遗留的,有些是模棱两可的,许多甚至没有被ICCCM提及。MIME类型现在很好,但是ICCCM并没有以任何方式讨论MIME类型。
This feels a little messy, yes. Being compatible with today’s clients and clients from 30 years ago isn’t easy.
这感觉有点乱,是的。要与今天的客户以及30年前的客户兼容并不容易。
Handling binary data using xclip
使用xclip处理二进制数据
I’ve been wondering for a long time why I’m unable to paste an image using xclip. It should be simple: xclip -o >foo.img. Well, no. Knowing what I know now, it finally is simple. :-)
它应该很简单:xclip -o >foo.img。嗯,没有。知道了我现在知道的一切,一切终于变得简单了。: -)
First, copy an image using a tool like The GIMP.
首先,使用GIMP等工具复制图像。
xclip can query TARGETS:
xclip可以查询目标:
$ xclip -o -target TARGETS -selection clipboardTIMESTAMPTARGETSMULTIPLESAVE_TARGETSimage/pngimage/tiffimage/x-iconimage/x-icoimage/x-win-bitmapimage/vnd.microsoft.iconapplication/icoimage/icoimage/icontext/icoimage/bmpimage/x-bmpimage/x-MS-bmpimage/jpeg
Choose something that you like. And then ask for the data:
选择你喜欢的东西。然后询问数据:
$ xclip -o -target image/png -selection clipboard >foo.png$ file foo.pngfoo.png: PNG image data, 373 x 309, 8-bit/color RGBA, non-interlaced
使用xclip复制图像数据的方式与此相同,只需使用-t指定MIME类型即可。
Large amounts of data
You might have noticed that program 2 aborts if there’s something involved called INCR. This is one of the many hacks in the world of X11 selections.
您可能已经注意到,如果涉及到称为INCR的东西,程序2就会中止。这是X11选择的众多技巧之一。
Properties on windows can only hold a limited amount of data, because they live in the memory of the X server. If you want to transfer several megabytes by using selections, you can still do that. You just have to chunk your data and client B must read data in chunks. Usually, the size of each chunk is about 256 kB. Not that much, but sufficient in most cases. It makes clients more complicated, though, because each client must implement that chunking mechanism.
windows上的属性只能保存有限数量的数据,因为它们存在于X服务器的内存中。如果您想通过使用选择来传输几兆字节,您仍然可以这样做。您只需要将数据分块,客户机B必须以块的形式读取数据。通常,每个块的大小约为256kb。不是很多,但在大多数情况下已经足够了。但是,它使客户端更加复杂,因为每个客户端都必须实现这种分块机制。
- The clipboard manager shall claim ownership of a selection.剪贴板管理人员应该声明一个选择的所有权。
Once it loses ownership, it will: 一旦失去所有权,它将:
- Ask the current owner for the content.向当前所有者询问内容。
- Provide the content itself.提供内容本身。
- Re-claim ownership.重新打所有权。
This feels like there are many race conditions involved. It will also break when a client does not support the TARGETS target. Yes, supporting this target is required by ICCCM, so it “should” work.
这感觉像是有很多竞争条件。当客户机不支持TARGETS目标时,它也会中断。是的,ICCCM需要支持这个目标,所以它“应该”起作用。
Summary
I think it’s important to understand that the X server is just a broker. Clients talk to each other (via the server), exchanging content. There is no clipboard “inside” of the server. Data is converted on the fly. You can have as many selections as you like, but not all clients support all of them.
客户端彼此通信(通过服务器),交换内容。服务器“内部”没有剪贴板。动态地转换数据。您可以选择任意多的选项,但并非所有客户机都支持所有选项。
One final thing to note: At first sight, selections in X11 appear to be simple. I fear, though, that they are almost as complicated as time zones. Even the “standard” utility xclip isn’t strictly ICCCM-compliant and contains the occasional “FIXME”. There are many race conditions and many corner cases.
最后要注意的一点是:乍一看,X11中的选择似乎很简单。不过,我担心它们几乎和时区一样复杂。即使是“标准”实用程序xclip也不是严格兼容icccm的,并且偶尔包含“FIXME”。有许多竞争条件和许多极端情况。
tl; dr: If possible, use a library.
https://www.uninformativ.de/blog/postings/2017-04-02/0/POSTING-en.html
