XChangeProperty 无法写入剪贴板

问题描述 投票:0回答:2

我想将一个字符串或字节放入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

我做错了什么,如何解决?

c x11
2个回答
0
投票

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;

    }

}

0
投票

我做了一些挖掘,试图获得一个熊最小程序,让我可以通过 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
© www.soinside.com 2019 - 2024. All rights reserved.