File indexing completed on 2024-04-21 03:59:22
0001 /* 0002 SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org> 0003 SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: MIT 0006 */ 0007 0008 #include "kxmessages.h" 0009 #include "cptr_p.h" 0010 #include "kxutils_p.h" 0011 0012 #if KWINDOWSYSTEM_HAVE_X11 0013 0014 #include <QAbstractNativeEventFilter> 0015 #include <QCoreApplication> 0016 #include <QDebug> 0017 #include <QWindow> // WId 0018 0019 #include <X11/Xlib.h> 0020 0021 #include <private/qtx11extras_p.h> 0022 0023 class XcbAtom 0024 { 0025 public: 0026 explicit XcbAtom(const QByteArray &name, bool onlyIfExists = false) 0027 : m_name(name) 0028 , m_atom(XCB_ATOM_NONE) 0029 , m_connection(nullptr) 0030 , m_retrieved(false) 0031 , m_onlyIfExists(onlyIfExists) 0032 { 0033 m_cookie.sequence = 0; 0034 } 0035 explicit XcbAtom(xcb_connection_t *c, const QByteArray &name, bool onlyIfExists = false) 0036 : m_name(name) 0037 , m_atom(XCB_ATOM_NONE) 0038 , m_cookie(xcb_intern_atom_unchecked(c, onlyIfExists, name.length(), name.constData())) 0039 , m_connection(c) 0040 , m_retrieved(false) 0041 , m_onlyIfExists(onlyIfExists) 0042 { 0043 } 0044 0045 ~XcbAtom() 0046 { 0047 if (!m_retrieved && m_cookie.sequence && m_connection) { 0048 xcb_discard_reply(m_connection, m_cookie.sequence); 0049 } 0050 } 0051 0052 operator xcb_atom_t() 0053 { 0054 getReply(); 0055 return m_atom; 0056 } 0057 0058 inline const QByteArray &name() const 0059 { 0060 return m_name; 0061 } 0062 0063 inline void setConnection(xcb_connection_t *c) 0064 { 0065 m_connection = c; 0066 } 0067 0068 inline void fetch() 0069 { 0070 if (!m_connection || m_name.isEmpty()) { 0071 return; 0072 } 0073 m_cookie = xcb_intern_atom_unchecked(m_connection, m_onlyIfExists, m_name.length(), m_name.constData()); 0074 } 0075 0076 private: 0077 void getReply() 0078 { 0079 if (m_retrieved || !m_cookie.sequence || !m_connection) { 0080 return; 0081 } 0082 UniqueCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr)); 0083 if (reply) { 0084 m_atom = reply->atom; 0085 } 0086 m_retrieved = true; 0087 } 0088 QByteArray m_name; 0089 xcb_atom_t m_atom; 0090 xcb_intern_atom_cookie_t m_cookie; 0091 xcb_connection_t *m_connection; 0092 bool m_retrieved; 0093 bool m_onlyIfExists; 0094 }; 0095 0096 class KXMessagesPrivate : public QAbstractNativeEventFilter 0097 { 0098 public: 0099 KXMessagesPrivate(KXMessages *parent, const char *acceptBroadcast, xcb_connection_t *c, xcb_window_t root) 0100 : accept_atom1(acceptBroadcast ? QByteArray(acceptBroadcast) + QByteArrayLiteral("_BEGIN") : QByteArray()) 0101 , accept_atom2(acceptBroadcast ? QByteArray(acceptBroadcast) : QByteArray()) 0102 , handle(new QWindow) 0103 , q(parent) 0104 , valid(c) 0105 , connection(c) 0106 , rootWindow(root) 0107 { 0108 if (acceptBroadcast) { 0109 accept_atom1.setConnection(c); 0110 accept_atom1.fetch(); 0111 accept_atom2.setConnection(c); 0112 accept_atom2.fetch(); 0113 QCoreApplication::instance()->installNativeEventFilter(this); 0114 } 0115 } 0116 XcbAtom accept_atom1; 0117 XcbAtom accept_atom2; 0118 QMap<WId, QByteArray> incoming_messages; 0119 std::unique_ptr<QWindow> handle; 0120 KXMessages *q; 0121 bool valid; 0122 xcb_connection_t *connection; 0123 xcb_window_t rootWindow; 0124 0125 bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override 0126 { 0127 // A faster comparison than eventType != "xcb_generic_event_t" 0128 if (eventType[0] != 'x') { 0129 return false; 0130 } 0131 xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(message); 0132 uint response_type = event->response_type & ~0x80; 0133 if (response_type != XCB_CLIENT_MESSAGE) { 0134 return false; 0135 } 0136 xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event); 0137 if (cm_event->format != 8) { 0138 return false; 0139 } 0140 if (cm_event->type != accept_atom1 && cm_event->type != accept_atom2) { 0141 return false; 0142 } 0143 char buf[21]; // can't be longer 0144 // Copy the data in order to null-terminate it 0145 qstrncpy(buf, reinterpret_cast<char *>(cm_event->data.data8), 21); 0146 // qDebug() << cm_event->window << "buf=\"" << buf << "\" atom=" << (cm_event->type == accept_atom1 ? "atom1" : "atom2"); 0147 if (incoming_messages.contains(cm_event->window)) { 0148 if (cm_event->type == accept_atom1) 0149 // two different messages on the same window at the same time shouldn't happen anyway 0150 { 0151 incoming_messages[cm_event->window] = QByteArray(); 0152 } 0153 incoming_messages[cm_event->window] += buf; 0154 } else { 0155 if (cm_event->type == accept_atom2) { 0156 return false; // middle of message, but we don't have the beginning 0157 } 0158 incoming_messages[cm_event->window] = buf; 0159 } 0160 if (strlen(buf) < 20) { // last message fragment 0161 Q_EMIT q->gotMessage(QString::fromUtf8(incoming_messages[cm_event->window].constData())); 0162 incoming_messages.remove(cm_event->window); 0163 } 0164 return false; // lets other KXMessages instances get the event too 0165 } 0166 }; 0167 0168 static void 0169 send_message_internal(xcb_window_t w, const QString &msg, xcb_connection_t *c, xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle); 0170 0171 KXMessages::KXMessages(const char *accept_broadcast_P, QObject *parent_P) 0172 : QObject(parent_P) 0173 , d(new KXMessagesPrivate(this, 0174 accept_broadcast_P, 0175 QX11Info::isPlatformX11() ? QX11Info::connection() : nullptr, 0176 QX11Info::isPlatformX11() ? QX11Info::appRootWindow() : 0)) 0177 { 0178 } 0179 0180 KXMessages::KXMessages(xcb_connection_t *connection, xcb_window_t rootWindow, const char *accept_broadcast, QObject *parent) 0181 : QObject(parent) 0182 , d(new KXMessagesPrivate(this, accept_broadcast, connection, rootWindow)) 0183 { 0184 } 0185 0186 KXMessages::~KXMessages() 0187 { 0188 delete d; 0189 } 0190 0191 static xcb_screen_t *defaultScreen(xcb_connection_t *c, int screen) 0192 { 0193 for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(c)); it.rem; --screen, xcb_screen_next(&it)) { 0194 if (screen == 0) { 0195 return it.data; 0196 } 0197 } 0198 return nullptr; 0199 } 0200 0201 void KXMessages::broadcastMessage(const char *msg_type_P, const QString &message_P, int screen_P) 0202 { 0203 if (!d->valid) { 0204 qWarning() << "KXMessages used on non-X11 platform! This is an application bug."; 0205 return; 0206 } 0207 const QByteArray msg(msg_type_P); 0208 XcbAtom a2(d->connection, msg); 0209 XcbAtom a1(d->connection, msg + QByteArrayLiteral("_BEGIN")); 0210 xcb_window_t root = screen_P == -1 ? d->rootWindow : defaultScreen(d->connection, screen_P)->root; 0211 send_message_internal(root, message_P, d->connection, a1, a2, d->handle->winId()); 0212 } 0213 0214 bool KXMessages::broadcastMessageX(xcb_connection_t *c, const char *msg_type_P, const QString &message, int screenNumber) 0215 { 0216 if (!c) { 0217 return false; 0218 } 0219 const QByteArray msg(msg_type_P); 0220 XcbAtom a2(c, msg); 0221 XcbAtom a1(c, msg + QByteArrayLiteral("_BEGIN")); 0222 const xcb_screen_t *screen = defaultScreen(c, screenNumber); 0223 if (!screen) { 0224 return false; 0225 } 0226 const xcb_window_t root = screen->root; 0227 const xcb_window_t win = xcb_generate_id(c); 0228 xcb_create_window(c, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr); 0229 send_message_internal(root, message, c, a1, a2, win); 0230 xcb_destroy_window(c, win); 0231 return true; 0232 } 0233 0234 static void 0235 send_message_internal(xcb_window_t w, const QString &msg_P, xcb_connection_t *c, xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle) 0236 { 0237 unsigned int pos = 0; 0238 QByteArray msg = msg_P.toUtf8(); 0239 const size_t len = msg.size(); 0240 0241 xcb_client_message_event_t event; 0242 event.response_type = XCB_CLIENT_MESSAGE; 0243 event.format = 8; 0244 event.sequence = 0; 0245 event.window = handle; 0246 event.type = leadingMessage; 0247 0248 do { 0249 unsigned int i; 0250 for (i = 0; i < 20 && i + pos < len; ++i) { 0251 event.data.data8[i] = msg[i + pos]; 0252 } 0253 for (; i < 20; ++i) { 0254 event.data.data8[i] = 0; 0255 } 0256 xcb_send_event(c, false, w, XCB_EVENT_MASK_PROPERTY_CHANGE, (const char *)&event); 0257 event.type = followingMessage; 0258 pos += i; 0259 } while (pos <= len); 0260 0261 xcb_flush(c); 0262 } 0263 0264 #endif 0265 0266 #include "moc_kxmessages.cpp"