File indexing completed on 2024-04-21 15:05:29

0001 /*
0002     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #ifndef NETTESTHELPER_H
0007 #define NETTESTHELPER_H
0008 
0009 #include <QByteArray>
0010 #include <memory>
0011 #include <xcb/xcb.h>
0012 
0013 #include "cptr_p.h"
0014 
0015 namespace KXUtils
0016 {
0017 
0018 /**
0019  * @brief Small helper class to fetch an intern atom through XCB.
0020  *
0021  * This class allows to request an intern atom and delay the retrieval of the reply
0022  * till it is needed. In case the reply is never retrieved the reply gets discarded
0023  * in the dtor. So there is no need to keep track manually about which atoms have been
0024  * retrieved.
0025  *
0026  * This class can be used as a drop-in replacement for everywhere where a xcb_atom_t is
0027  * needed as it implements the cast operator. The first time this operator is invoked it
0028  * will retrieve the reply. If the xcb request failed the value is @c XCB_ATOM_NONE, which
0029  * can be used to check whether the returned value is valid.
0030  *
0031  * This class has two modes of operations: a direct one which performs the request directly
0032  * during construction, and an indirect one which needs an explicit call to {@link fetch}.
0033  *
0034  * @code
0035  * Atom direct(QX11Info::connection(), QByteArrayLiteral("myAtomName"));
0036  * Atom indirect(QByteArrayLiteral("myAtomName"));
0037  * indirect.setConnection(QX11Info::connection());
0038  * indirect.fetch();
0039  *
0040  * if (direct == XCB_ATOM_NONE) {
0041  *     qWarning() << "Request failed";
0042  * }
0043  * if (indirect == XCB_ATOM_NONE) {
0044  *     qWarning() << "Request failed";
0045  * }
0046  * @endcode
0047  */
0048 class Atom
0049 {
0050 public:
0051     explicit Atom(const QByteArray &name)
0052         : m_connection(nullptr)
0053         , m_retrieved(false)
0054         , m_atom(XCB_ATOM_NONE)
0055         , m_name(name)
0056     {
0057         m_cookie.sequence = 0;
0058     }
0059     explicit Atom(xcb_connection_t *c, const QByteArray &name, bool onlyIfExists = false)
0060         : m_connection(c)
0061         , m_retrieved(false)
0062         , m_cookie(xcb_intern_atom_unchecked(m_connection, onlyIfExists, name.length(), name.constData()))
0063         , m_atom(XCB_ATOM_NONE)
0064         , m_name(name)
0065     {
0066     }
0067     Atom() Q_DECL_EQ_DELETE;
0068     Atom(const Atom &) Q_DECL_EQ_DELETE;
0069 
0070     ~Atom()
0071     {
0072         if (!m_retrieved && m_cookie.sequence) {
0073             xcb_discard_reply(m_connection, m_cookie.sequence);
0074         }
0075     }
0076 
0077     void setConnection(xcb_connection_t *c)
0078     {
0079         m_connection = c;
0080     }
0081 
0082     void fetch(bool onlyIfExists = false)
0083     {
0084         if (!m_connection) {
0085             // set connection first!
0086             return;
0087         }
0088         if (m_retrieved || m_cookie.sequence) {
0089             // already fetched, don't fetch again
0090             return;
0091         }
0092         m_cookie = xcb_intern_atom_unchecked(m_connection, onlyIfExists, m_name.length(), m_name.constData());
0093     }
0094 
0095     operator xcb_atom_t() const
0096     {
0097         (const_cast<Atom *>(this))->getReply();
0098         return m_atom;
0099     }
0100 
0101     const QByteArray &name() const
0102     {
0103         return m_name;
0104     }
0105 
0106 private:
0107     void getReply()
0108     {
0109         if (m_retrieved || !m_cookie.sequence) {
0110             return;
0111         }
0112         UniqueCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr));
0113         if (reply) {
0114             m_atom = reply->atom;
0115         }
0116         m_retrieved = true;
0117     }
0118     xcb_connection_t *m_connection;
0119     bool m_retrieved;
0120     xcb_intern_atom_cookie_t m_cookie;
0121     xcb_atom_t m_atom;
0122     QByteArray m_name;
0123 };
0124 
0125 inline xcb_window_t rootWindow(xcb_connection_t *c, int screen)
0126 {
0127     xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(c));
0128     for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(c)); it.rem; --screen, xcb_screen_next(&it)) {
0129         if (screen == 0) {
0130             return iter.data->root;
0131         }
0132     }
0133     return XCB_WINDOW_NONE;
0134 }
0135 
0136 }
0137 
0138 #endif