我想将一个字符串或字节放入X11选择中,以便当我切换到其他应用程序时,我可以直接进行
ctrl+p
粘贴。
我尝试遵循 X11 剪贴板机制的文档。如果我理解正确的话,我需要使用
XSetSelectionOwner
来获取 XA_CLIPBOARD
选择,然后使用 XChangeProperty
将数据放入剪贴板。
这是一个简单的片段,但不幸的是它不起作用:
// main.c
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
int main() {
// try to write `hello` to the clipboard
const char *in = "hello\0";
const int n = 5;
Display* d = XOpenDisplay(0);
Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 1, 1, 0, 0, 0);
Atom XA_CLIPBOARD = XInternAtom(d, "CLIPBOARD", True);
XSetSelectionOwner(d, XA_CLIPBOARD, w, CurrentTime);
XEvent event;
XNextEvent(d, &event);
if (event.type != SelectionRequest) {
XCloseDisplay(d);
return 0;
}
if (event.xselectionrequest.selection != XA_CLIPBOARD) {
XCloseDisplay(d);
return 0;
}
XSelectionRequestEvent* req = &event.xselectionrequest;
XChangeProperty(d, req->requestor, req->property, XA_STRING, 8, PropModeReplace, (unsigned char *)in, n);
XEvent re = {0};
re.xselection.type = SelectionNotify;
re.xselection.display = req->display;
re.xselection.requestor = req->requestor;
re.xselection.selection = req->selection;
re.xselection.property = req->property;
re.xselection.target = req->target;
XSendEvent(d, req->requestor, 0, 0, &re); // event is sent, but data is not in my clipboard
XFlush(d);
XCloseDisplay(d);
return 0;
}
编译:
clang -o main main.c -lX11 -lXmu
我做错了什么,如何解决?
XNextEvent 检索发生的任何事件(输入、指针、配置、属性更改等)。
因此,第一个活动不太可能是您想要的活动。
您必须迭代事件队列以检查是否发生了 SelectionRequest 事件。
因此,您必须循环调用 XNextEvent 并每次检查 event.type 是否有所需的事件。
编辑:
如果您检索 ClientMessage 事件并且客户端数据等于 Atom WM_DELETE,则表明用户请求关闭窗口(按 X)。除此之外,您可以随时退出。
XEvent evt;
while (i_want_to_receive_and_process_events) {
XNextEvent(dpy, &evt);
switch (evt.type) {
case SelectionRequest:
if (evt.xselectionrequest.selection == XA_CLIPBOARD) {
// what you were looking for, do your thing
// i_want_to_receive_and_process_events = false (?)
}
break;
case ClientMessage:
if (evt.xclient.data.l[0] == WM_DELETE) {
i_want_to_receive_and_process_events = false;
}
break;
}
}
我做了一些挖掘,试图获得一个熊最小程序,让我可以通过 X11 在剪贴板中设置我想要的任何值。根据我有限的理解,X11 是一个客户端-服务器设置。就像其他一些技术的功能一样,例如 MySql,要获取您想要的数据结构,您需要循环遍历各种队列。
不可否认,我并没有完全掌握幕后发生的事情,并且这段代码很可能是不可靠的;但你仍然可以用它来学习和弄清楚。
在 Linux Mint Cinnamon 上测试
// g++ -o clipboard test.cc -lX11 -lXmu
// |-->libxmu-dev
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
#include <string>
bool writeX11Clipboard(std::string dest){
if(dest.length() <= 0){
return false;
}
// init X11 display and window.
Display *display = XOpenDisplay(NULL);
unsigned long color = BlackPixel(display, DefaultScreen(display));
Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0, 1,1, 0, color, color);
// Copy string into an unsigned char array, may be able to just typecast.
size_t destSize = dest.length();
unsigned char *copyData = new unsigned char[destSize];
int copyDataLen = destSize;
for(int i=0; i<copyDataLen; i++){
copyData[i] = (unsigned char)dest[i];
}
// Pre-iteration preperations...
XEvent event;
Atom selection = XA_CLIPBOARD(display);
Atom target = XA_UTF8_STRING(display);
XSelectInput(display, window, PropertyChangeMask);
XSetSelectionOwner(display, selection, window, CurrentTime);
Window requestor_id;
/*
* Loop over a few events.
* The first 3 appears to require sending information regarding the previous event to the X11 Server.
* On the 4th iteration the event that allows you to write into the clipboard appears to become usable.
* */
for(int i=0; i<4; i++){
XNextEvent(display, &event);
if(event.type == SelectionRequest){
requestor_id = event.xselectionrequest.requestor;
}else{
XDestroyWindow(display, window);
XCloseDisplay(display);
return false;
}
XEvent eventResponse;
Atom inc;
Atom targets;
targets = XInternAtom(display, "TARGETS", False);
inc = XInternAtom(display, "INCR", False);
if (event.xselectionrequest.target == targets) {
// These operations will allow the code to continue iterating through events.
Atom types[2] = { targets, XA_UTF8_STRING(display) };
XChangeProperty(display,
event.xselectionrequest.requestor,
event.xselectionrequest.property,
XA_ATOM,
32, PropModeReplace, (unsigned char *) types,
(int) (sizeof(types) / sizeof(Atom))
);
}else{
// This is what actually sets the clipboard data.
XChangeProperty(display,
event.xselectionrequest.requestor,
event.xselectionrequest.property,
XA_UTF8_STRING(display),
8, PropModeReplace, (unsigned char *) copyData,
(int) copyDataLen);
}
// Create a X11 event packet and send it to the server.
eventResponse.xselection.property = event.xselectionrequest.property;
eventResponse.xselection.type = SelectionNotify;
eventResponse.xselection.display = event.xselectionrequest.display;
eventResponse.xselection.requestor = event.xselectionrequest.requestor;
eventResponse.xselection.selection = event.xselectionrequest.selection;
eventResponse.xselection.target = event.xselectionrequest.target;
eventResponse.xselection.time = event.xselectionrequest.time;
XSendEvent(display, event.xselectionrequest.requestor, 0, 0, &eventResponse);
XFlush(display);
}
// Clean up.
XDestroyWindow(display, window);
XCloseDisplay(display);
return true;
}
int main(int argc, char *argv[]){
if(argc != 2)
return 1;
writeX11Clipboard(argv[1]);
return 0;
}
用途:
morningstar@starmorning:: ~/Documents/tests 8=D~ ./clipboard "my test"
Ctrl+Shift+V
morningstar@starmorning:: ~/Documents/tests 8=D~ my test