Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Paste clipboard to a window
Qt 6.11 is out! See what's new in the release blog

Paste clipboard to a window

Scheduled Pinned Locked Moved Unsolved General and Desktop
1 Posts 1 Posters 352 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    hbatalha
    wrote on last edited by hbatalha
    #1

    I am writing an over-simplified clipboard manager for personal use and I am trying to implement a feature where I will click in a text from clipboard history and it will paste to the window I am working.

    CopyQ has this feature so I am trying to see how it is done there.
    I took the code from there that I believe it does what I want:

    sleeptimer.h

    /*
        Copyright (c) 2020, Lukas Holecek <hluk@email.cz>
    
        This file is part of CopyQ.
    
        CopyQ is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        CopyQ is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
    
        You should have received a copy of the GNU General Public License
        along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
    */
    
    #ifndef SLEEPTIMER_H
    #define SLEEPTIMER_H
    
    #include <QCoreApplication>
    #include <QElapsedTimer>
    
    #include <cmath>
    
    class SleepTimer final
    {
    public:
        explicit SleepTimer(int timeoutMs)
            : m_timeoutMs(timeoutMs)
        {
            m_timer.start();
        }
    
        bool sleep()
        {
            QCoreApplication::processEvents(QEventLoop::AllEvents, 5);
            return m_timer.elapsed() < m_timeoutMs;
        }
    
        int remaining() const
        {
            const auto remaining = static_cast<int>(m_timeoutMs - m_timer.elapsed());
            return std::max(0, remaining);
        }
    
    private:
        QElapsedTimer m_timer;
        int m_timeoutMs;
    };
    
    inline void waitFor(int ms)
    {
        SleepTimer t(ms);
        while (t.sleep()) {}
    }
    
    #endif // SLEEPTIMER_H
    

    x11platformwindow.h

    /*
        Copyright (c) 2020, Lukas Holecek <hluk@email.cz>
    
        This file is part of CopyQ.
    
        CopyQ is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        CopyQ is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
    
        You should have received a copy of the GNU General Public License
        along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
    */
    
    #ifndef X11PLATFORMWINDOW_H
    #define X11PLATFORMWINDOW_H
    
    
    #include <X11/Xlib.h>
    
    #include <memory>
    
    class AppConfig;
    class QWidget;
    
    class X11PlatformWindow
    {
    public:
    
        explicit X11PlatformWindow(Window winId);
    
        void raise() ;
    
        void pasteClipboard() ;
    
        void copy();
    
        bool isValid() const;
    
    private:
        bool waitForFocus(int ms);
    
        void sendKeyPress(int modifier, int key);
    
        Window m_window;
    };
    
    Window getCurrentWindow();
    
    #endif // X11PLATFORMWINDOW_H
    

    x11platformwindow.cpp

    /*
        Copyright (c) 2020, Lukas Holecek <hluk@email.cz>
    
        This file is part of CopyQ.
    
        CopyQ is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        CopyQ is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
    
        You should have received a copy of the GNU General Public License
        along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
    */
    
    #include "sleeptimer.h"
    #include "x11platformwindow.h"
    
    #include <unistd.h>
    #include <QX11Info>
    #include <QTimer>
    
    #include <X11/Xlib.h>
    #include <X11/Xatom.h>
    #include <X11/keysym.h>
    
    #ifdef HAS_X11TEST
    #   include <X11/extensions/XTest.h>
    #endif
    
    void waitMs(int msec)
    {
        if (msec <= 0)
            return;
    
        QEventLoop loop;
        QTimer t;
        QObject::connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
        t.start(msec);
        loop.exec();
    }
    
    void simulateKeyPress(Display *display, Window window, unsigned int modifiers, unsigned int key)
    {
        XKeyEvent event;
        XEvent *xev = reinterpret_cast<XEvent *>(&event);
        event.display     = display;
        event.window      = window;
        event.root        = DefaultRootWindow(display);
        event.subwindow   = None;
        event.time        = CurrentTime;
        event.x           = 1;
        event.y           = 1;
        event.x_root      = 1;
        event.y_root      = 1;
        event.same_screen = True;
        event.keycode     = XKeysymToKeycode(display, key);
        event.state       = modifiers;
    
        event.type = KeyPress;
        XSendEvent(display, window, True, KeyPressMask, xev);
        XSync(display, False);
    
        event.type = KeyRelease;
        XSendEvent(display, window, True, KeyPressMask, xev);
        XSync(display, False);
    }
    
    class X11WindowProperty final
    {
    public:
        X11WindowProperty(Display *display, Window w, Atom property, long longOffset,
                          long longLength, Atom reqType)
        {
            if ( XGetWindowProperty(display, w, property, longOffset, longLength, false,
                                    reqType, &type, &format, &len, &remain, &data) != Success )
            {
                data = nullptr;
            }
        }
    
        ~X11WindowProperty()
        {
            if (data != nullptr)
                XFree(data);
        }
    
        bool isValid() const
        {
            return data != nullptr;
        }
    
        X11WindowProperty(const X11WindowProperty &) = delete;
        X11WindowProperty &operator=(const X11WindowProperty &) = delete;
    
        Atom type{};
        int format{};
        unsigned long len{};
        unsigned long remain{};
        unsigned char *data;
    };
    
    Window getCurrentWindow()
    {
        if (!QX11Info::isPlatformX11())
            return 0L;
    
        auto display = QX11Info::display();
        XSync(display, False);
    
        static Atom atomWindow = XInternAtom(display, "_NET_ACTIVE_WINDOW", true);
    
        X11WindowProperty property(display, DefaultRootWindow(display), atomWindow, 0l, 1l, XA_WINDOW);
    
        if ( property.isValid() && property.type == XA_WINDOW && property.format == 32 && property.len == 1)
            return *reinterpret_cast<Window *>(property.data);
    
        return 0L;
    }
    
    
    
    X11PlatformWindow::X11PlatformWindow(Window winId)
        : m_window(winId)
    {
    }
    
    
    void X11PlatformWindow::raise()
    {
        Q_ASSERT( isValid() );
    
        if (!QX11Info::isPlatformX11())
            return;
    
    
        auto display = QX11Info::display();
    
        XEvent e{};
        memset(&e, 0, sizeof(e));
        e.type = ClientMessage;
        e.xclient.display = display;
        e.xclient.window = m_window;
        e.xclient.message_type = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
        e.xclient.format = 32;
        e.xclient.data.l[0] = 2;
        e.xclient.data.l[1] = CurrentTime;
        e.xclient.data.l[2] = 0;
        e.xclient.data.l[3] = 0;
        e.xclient.data.l[4] = 0;
    
        XWindowAttributes wattr{};
        XGetWindowAttributes(display, m_window, &wattr);
    
        if (wattr.map_state == IsViewable)
        {
            XSendEvent(display, wattr.screen->root, False,
                       SubstructureNotifyMask | SubstructureRedirectMask,
                       &e);
            XSync(display, False);
            XRaiseWindow(display, m_window);
            XSetInputFocus(display, m_window, RevertToPointerRoot, CurrentTime);
            XSync(display, False);
        }
    }
    
    void X11PlatformWindow::pasteClipboard()
    {
        sendKeyPress(XK_Shift_L, XK_Insert);
    //    sendKeyPress(XK_Shift_L, XK_Insert);
    }
    
    void X11PlatformWindow::copy()
    {
    
    }
    
    bool X11PlatformWindow::isValid() const
    {
        return m_window != 0L;
    }
    
    bool X11PlatformWindow::waitForFocus(int ms)
    {
        Q_ASSERT( isValid() );
    
        if (ms >= 0)
        {
            SleepTimer t(ms);
            while (t.sleep())
            {
                const auto currentWindow = getCurrentWindow();
                if (currentWindow == m_window)
                    return true;
            }
        }
    
        return m_window == getCurrentWindow();
    }
    
    void X11PlatformWindow::sendKeyPress(int modifier, int key)
    {
        Q_ASSERT( isValid() );
    
        if ( !waitForFocus(50) )
        {
            raise();
            if ( !waitForFocus(150) )
            {
                return;
            }
        }
    
        waitMs(500);
    
        if (!QX11Info::isPlatformX11())
            return;
    
        auto display = QX11Info::display();
    
        const int modifierMask = (modifier == XK_Control_L) ? ControlMask : ShiftMask;
        simulateKeyPress(display, m_window, modifierMask, key);
    
    }
    

    here is a test code from mainwindow.cpp

    void MainWindow::on_button_clicked()
    {
        clipboard = QApplication::clipboard();
        
        clipboard->setText("TEST");
        
        this->hide();
    
        X11PlatformWindow window(lastWindow); // the code I use to get the lastwindow is a little lengthy so I am omitting it
    
        window.raise();
    
        qDebug() << lastWindow;
    
        window.pasteClipboard();
    }
    

    This code is pasting the text to the Qt Creator editor and the browser's search bar.
    My guess is that the window is being given input focus. X11PlatformWindow::raise() is supposed to do that but I am not sure it's working.

    I can't find how to know if XSetInputFocus() succeeded.

    I am not sure this topic is suited for this forum but if anyone could help I would appreciate it.
    Thank you.

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved