vendredi 25 décembre 2020

Xlib XSelectionRequest - sending an image to the requestor


I want my C++ program to act like clipboard storing a png image and sending this image to X11 clients requesting the current clipboard selection. The image data is stored as an array of bytes in the const std::string clipboardContent variable. After reading this I'm trying to implement it in my program:

#include <iostream>
#include <string>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>

int main()
{
    //...
    Display* display = XOpenDisplay(NULL);
    int screen = XDefaultScreen(display);
    Window rootWindow = XRootWindow(display, screen);
    Atom selection = XInternAtom(display, "CLIPBOARD", false);
    Atom image = XInternAtom(display, "image/png", false);
    Atom targets = XInternAtom(display, "TARGETS", false);
    Window newOwnerWindow = XCreateSimpleWindow(display, rootWindow,
            -10, -10, 1, 1, 0, 0, 0);
    XSetSelectionOwner(display, selection, newOwnerWindow, CurrentTime);
    XEvent event;
    while (true)
    {
        XNextEvent(display, &event);
        if (event.type == SelectionRequest)
        {
            Atom targetRequsted = event.xselectionrequest.target;
            std::cout << "Requested target: \"" <<
                XGetAtomName(display, targetRequsted) << "\"" << '\n';
            if (targetRequsted == targets)
            {
                XChangeProperty(display,
                        event.xselectionrequest.requestor,
                        event.xselectionrequest.property,
                        XA_ATOM,
                        32,
                        PropModeReplace,
                        reinterpret_cast<unsigned char*>(&image),
                        sizeof(image));
                XEvent selectionRespond;
                selectionRespond.type = SelectionNotify;
                selectionRespond.xselection.requestor =
                    event.xselectionrequest.requestor;
                selectionRespond.xselection.property =
                    event.xselectionrequest.property;
                selectionRespond.xselection.display =
                    event.xselectionrequest.display;
                selectionRespond.xselection.selection =
                    event.xselectionrequest.selection;
                selectionRespond.xselection.target =
                    event.xselectionrequest.target;
                selectionRespond.xselection.time =
                    event.xselectionrequest.time;
                XSendEvent(display, event.xselectionrequest.requestor, true,
                        NoEventMask, &selectionRespond);
            }
            else if (targetRequsted == image)
            {
                // clipboardContent is a const std::string variable storing a
                // png image
                XChangeProperty(display,
                        event.xselectionrequest.requestor,
                        event.xselectionrequest.property,
                        image,
                        8,
                        PropModeReplace,
                        reinterpret_cast<const unsigned char*>(&clipboardContent[0]),
                        clipboardContent.size());
                XEvent selectionRespond;
                selectionRespond.type = SelectionNotify;
                selectionRespond.xselection.requestor =
                    event.xselectionrequest.requestor;
                selectionRespond.xselection.property =
                    event.xselectionrequest.property;
                selectionRespond.xselection.display =
                    event.xselectionrequest.display;
                selectionRespond.xselection.selection =
                    event.xselectionrequest.selection;
                selectionRespond.xselection.target =
                    event.xselectionrequest.target;
                selectionRespond.xselection.time =
                    event.xselectionrequest.time;
                XSendEvent(display, event.xselectionrequest.requestor, true,
                        NoEventMask, &selectionRespond);
                break;
            }
            else
            {
                //...
            }
        }
    }
    //...
    return 0;
}

So, there is some png image data stored in the const std::string clipboardContent variable. The program creates a new simple window, claims this window as a new clipboard selection owner and waits for selection requests. If some client asks for available selection targets, the program responds with the image Atom which refers to the "image/png" string, and if the requestor is interested in "image/png" data type, it can request and get this data.

In general this code works and does what intented, but there is one small problem with it. When I paste some image into the Firefox window, for example, the program output is:

REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "TARGETS"
REQUESTED TARGET "application/x-moz-nativehtml"
REQUESTED TARGET "text/html"
REQUESTED TARGET "application/x-moz-file"
REQUESTED TARGET "image/png"
REQUESTED TARGET "TARGETS"

As you can see, the requestor performs a lot of redundant(or not?) requests before actually requesting the image, and it causes a notable(1-2 sec) lag between pressing "Ctrl+V" and pasting the image. On the other hand, there is no any lag when I paste the same image into the same window with my program not being run(using X native clipboard mechanism).
Why doesn't the client request the image right after requesting targets, but does request unavailable data types such as "text/html" instead? And why is there so many same requests for targets?

Aucun commentaire:

Enregistrer un commentaire