KWindowSystem

kxmessages.cpp
1 /*
2  SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <[email protected]>
3  SPDX-FileCopyrightText: 2012 David Faure <[email protected]>
4 
5  SPDX-License-Identifier: MIT
6 */
7 
8 #include "kxmessages.h"
9 #include "kxutils_p.h"
10 
11 #if KWINDOWSYSTEM_HAVE_X11
12 
13 #include <QCoreApplication>
14 #include <QDebug>
15 #include <QWindow> // WId
16 #include <QAbstractNativeEventFilter>
17 
18 #include <xcb/xcb.h>
19 #include <qx11info_x11.h>
20 #include <X11/Xlib.h>
21 
22 
23 class XcbAtom
24 {
25 public:
26  explicit XcbAtom(const QByteArray &name, bool onlyIfExists = false)
27  : m_name(name)
28  , m_atom(XCB_ATOM_NONE)
29  , m_connection(nullptr)
30  , m_retrieved(false)
31  , m_onlyIfExists(onlyIfExists)
32  {
33  m_cookie.sequence = 0;
34  }
35  explicit XcbAtom(xcb_connection_t *c, const QByteArray &name, bool onlyIfExists = false)
36  : m_name(name)
37  , m_atom(XCB_ATOM_NONE)
38  , m_cookie(xcb_intern_atom_unchecked(c, onlyIfExists, name.length(), name.constData()))
39  , m_connection(c)
40  , m_retrieved(false)
41  , m_onlyIfExists(onlyIfExists)
42  {
43  }
44 
45  ~XcbAtom() {
46  if (!m_retrieved && m_cookie.sequence && m_connection) {
47  xcb_discard_reply(m_connection, m_cookie.sequence);
48  }
49  }
50 
51  operator xcb_atom_t() {
52  getReply();
53  return m_atom;
54  }
55 
56  inline const QByteArray &name() const {
57  return m_name;
58  }
59 
60  inline void setConnection(xcb_connection_t *c) {
61  m_connection = c;
62  }
63 
64  inline void fetch() {
65  if (!m_connection || m_name.isEmpty()) {
66  return;
67  }
68  m_cookie = xcb_intern_atom_unchecked(m_connection, m_onlyIfExists, m_name.length(), m_name.constData());
69  }
70 
71 private:
72  void getReply() {
73  if (m_retrieved || !m_cookie.sequence || !m_connection) {
74  return;
75  }
76  KXUtils::ScopedCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr));
77  if (!reply.isNull()) {
78  m_atom = reply->atom;
79  }
80  m_retrieved = true;
81  }
82  QByteArray m_name;
83  xcb_atom_t m_atom;
84  xcb_intern_atom_cookie_t m_cookie;
85  xcb_connection_t *m_connection;
86  bool m_retrieved;
87  bool m_onlyIfExists;
88 };
89 
90 class KXMessagesPrivate
92 {
93 public:
94  KXMessagesPrivate(KXMessages *parent, const char *acceptBroadcast, xcb_connection_t *c, xcb_window_t root)
95  : accept_atom1(acceptBroadcast ? QByteArray(acceptBroadcast) + QByteArrayLiteral("_BEGIN") : QByteArray())
96  , accept_atom2(acceptBroadcast ? QByteArray(acceptBroadcast) : QByteArray())
97  , handle(new QWindow)
98  , q(parent)
99  , valid(c)
100  , connection(c)
101  , rootWindow(root)
102  {
103  if (acceptBroadcast) {
104  accept_atom1.setConnection(c);
105  accept_atom1.fetch();
106  accept_atom2.setConnection(c);
107  accept_atom2.fetch();
109  }
110  }
111  XcbAtom accept_atom1;
112  XcbAtom accept_atom2;
113  QMap< WId, QByteArray > incoming_messages;
115  KXMessages *q;
116  bool valid;
117  xcb_connection_t *connection;
118  xcb_window_t rootWindow;
119 
120  bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
121  {
122  Q_UNUSED(result);
123  // A faster comparison than eventType != "xcb_generic_event_t"
124  if (eventType[0] != 'x') {
125  return false;
126  }
127  xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(message);
128  uint response_type = event->response_type & ~0x80;
129  if (response_type != XCB_CLIENT_MESSAGE) {
130  return false;
131  }
132  xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event);
133  if (cm_event->format != 8) {
134  return false;
135  }
136  if (cm_event->type != accept_atom1 && cm_event->type != accept_atom2) {
137  return false;
138  }
139  char buf[ 21 ]; // can't be longer
140  // Copy the data in order to null-terminate it
141  qstrncpy(buf, reinterpret_cast<char *>(cm_event->data.data8), 21);
142  //qDebug() << cm_event->window << "buf=\"" << buf << "\" atom=" << (cm_event->type == accept_atom1 ? "atom1" : "atom2");
143  if (incoming_messages.contains(cm_event->window)) {
144  if (cm_event->type == accept_atom1)
145  // two different messages on the same window at the same time shouldn't happen anyway
146  {
147  incoming_messages[cm_event->window] = QByteArray();
148  }
149  incoming_messages[cm_event->window] += buf;
150  } else {
151  if (cm_event->type == accept_atom2) {
152  return false; // middle of message, but we don't have the beginning
153  }
154  incoming_messages[cm_event->window] = buf;
155  }
156  if (strlen(buf) < 20) { // last message fragment
157  emit q->gotMessage(QString::fromUtf8(incoming_messages[cm_event->window].constData()));
158  incoming_messages.remove(cm_event->window);
159  }
160  return false; // lets other KXMessages instances get the event too
161  }
162 };
163 
164 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 18)
165 static void send_message_internal(WId w_P, const QString &msg_P, long mask_P,
166  Display *disp, Atom atom1_P, Atom atom2_P, Window handle_P);
167 // for broadcasting
168 static const long BROADCAST_MASK = PropertyChangeMask;
169 // CHECKME
170 #endif
171 static void send_message_internal(xcb_window_t w, const QString &msg, xcb_connection_t *c,
172  xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle);
173 
174 KXMessages::KXMessages(const char *accept_broadcast_P, QObject *parent_P)
175  : QObject(parent_P)
176  , d(new KXMessagesPrivate(this, accept_broadcast_P, QX11Info::isPlatformX11() ? QX11Info::connection() : nullptr, QX11Info::isPlatformX11() ? QX11Info::appRootWindow() : 0))
177 {
178 }
179 
180 KXMessages::KXMessages(xcb_connection_t *connection, xcb_window_t rootWindow, const char *accept_broadcast, QObject *parent)
181  : QObject(parent)
182  , d(new KXMessagesPrivate(this, accept_broadcast, connection, rootWindow))
183 {
184 }
185 
186 KXMessages::~KXMessages()
187 {
188  delete d;
189 }
190 
191 static
192 xcb_screen_t *defaultScreen(xcb_connection_t *c, int screen)
193 {
194  for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(c));
195  it.rem;
196  --screen, xcb_screen_next(&it)) {
197  if (screen == 0) {
198  return it.data;
199  }
200  }
201  return nullptr;
202 }
203 
204 void KXMessages::broadcastMessage(const char *msg_type_P, const QString &message_P, int screen_P)
205 {
206  if (!d->valid) {
207  qWarning() << "KXMessages used on non-X11 platform! This is an application bug.";
208  return;
209  }
210  const QByteArray msg(msg_type_P);
211  XcbAtom a2(d->connection, msg);
212  XcbAtom a1(d->connection, msg + QByteArrayLiteral("_BEGIN"));
213  xcb_window_t root = screen_P == -1 ? d->rootWindow : defaultScreen(d->connection, screen_P)->root;
214  send_message_internal(root, message_P, d->connection,
215  a1, a2, d->handle->winId());
216 }
217 
218 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 18)
219 bool KXMessages::broadcastMessageX(Display *disp, const char *msg_type_P,
220  const QString &message_P, int screen_P)
221 {
222  if (disp == nullptr) {
223  return false;
224  }
225  Atom a2 = XInternAtom(disp, msg_type_P, false);
226  Atom a1 = XInternAtom(disp, QByteArray(QByteArray(msg_type_P) + "_BEGIN").constData(), false);
227  Window root = screen_P == -1 ? DefaultRootWindow(disp) : RootWindow(disp, screen_P);
228  Window win = XCreateSimpleWindow(disp, root, 0, 0, 1, 1,
229  0, BlackPixel(disp, screen_P == -1 ? DefaultScreen(disp) : screen_P),
230  BlackPixel(disp, screen_P == -1 ? DefaultScreen(disp) : screen_P));
231  send_message_internal(root, message_P, BROADCAST_MASK, disp,
232  a1, a2, win);
233  XDestroyWindow(disp, win);
234  return true;
235 }
236 #endif
237 
238 bool KXMessages::broadcastMessageX(xcb_connection_t *c, const char *msg_type_P, const QString &message, int screenNumber)
239 {
240  if (!c) {
241  return false;
242  }
243  const QByteArray msg(msg_type_P);
244  XcbAtom a2(c, msg);
245  XcbAtom a1(c, msg + QByteArrayLiteral("_BEGIN"));
246  const xcb_screen_t *screen = defaultScreen(c, screenNumber);
247  if (!screen) {
248  return false;
249  }
250  const xcb_window_t root = screen->root;
251  const xcb_window_t win = xcb_generate_id(c);
252  xcb_create_window(c, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1,
253  0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr);
254  send_message_internal(root, message, c, a1, a2, win);
255  xcb_destroy_window(c, win);
256  return true;
257 }
258 
259 #if 0 // currently unused
260 void KXMessages::sendMessage(WId w_P, const char *msg_type_P, const QString &message_P)
261 {
262  Atom a2 = XInternAtom(QX11Info::display(), msg_type_P, false);
263  Atom a1 = XInternAtom(QX11Info::display(), QByteArray(QByteArray(msg_type_P) + "_BEGIN").constData(), false);
264  send_message_internal(w_P, message_P, 0, QX11Info::display(), a1, a2, d->handle->winId());
265 }
266 
267 bool KXMessages::sendMessageX(Display *disp, WId w_P, const char *msg_type_P,
268  const QString &message_P)
269 {
270  if (disp == nullptr) {
271  return false;
272  }
273  Atom a2 = XInternAtom(disp, msg_type_P, false);
274  Atom a1 = XInternAtom(disp, QByteArray(QByteArray(msg_type_P) + "_BEGIN").constData(), false);
275  Window win = XCreateSimpleWindow(disp, DefaultRootWindow(disp), 0, 0, 1, 1,
276  0, BlackPixelOfScreen(DefaultScreenOfDisplay(disp)),
277  BlackPixelOfScreen(DefaultScreenOfDisplay(disp)));
278  send_message_internal(w_P, message_P, 0, disp, a1, a2, win);
279  XDestroyWindow(disp, win);
280  return true;
281 }
282 #endif
283 
284 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 18)
285 static void send_message_internal(WId w_P, const QString &msg_P, long mask_P,
286  Display *disp, Atom atom1_P, Atom atom2_P, Window handle_P)
287 {
288  //qDebug() << "send_message_internal" << w_P << msg_P << mask_P << atom1_P << atom2_P << handle_P;
289  unsigned int pos = 0;
290  QByteArray msg = msg_P.toUtf8();
291  unsigned int len = strlen(msg.constData());
292  XEvent e;
293  e.xclient.type = ClientMessage;
294  e.xclient.message_type = atom1_P; // leading message
295  e.xclient.display = disp;
296  e.xclient.window = handle_P;
297  e.xclient.format = 8;
298  do {
299  unsigned int i;
300  for (i = 0;
301  i < 20 && i + pos <= len;
302  ++i) {
303  e.xclient.data.b[ i ] = msg[ i + pos ];
304  }
305  XSendEvent(disp, w_P, false, mask_P, &e);
306  e.xclient.message_type = atom2_P; // following messages
307  pos += i;
308  } while (pos <= len);
309  XFlush(disp);
310 }
311 #endif
312 
313 static void send_message_internal(xcb_window_t w, const QString &msg_P, xcb_connection_t *c,
314  xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle)
315 {
316  unsigned int pos = 0;
317  QByteArray msg = msg_P.toUtf8();
318  const size_t len = strlen(msg.constData());
319 
320  xcb_client_message_event_t event;
321  event.response_type = XCB_CLIENT_MESSAGE;
322  event.format = 8;
323  event.sequence = 0;
324  event.window = handle;
325  event.type = leadingMessage;
326 
327  do {
328  unsigned int i;
329  for (i = 0;
330  i < 20 && i + pos <= len;
331  ++i) {
332  event.data.data8[i] = msg[ i + pos ];
333  }
334  for (unsigned int j = i; j < 20; ++j) {
335  event.data.data8[j] = 0;
336  }
337  xcb_send_event(c, false, w, XCB_EVENT_MASK_PROPERTY_CHANGE, (const char *) &event);
338  event.type = followingMessage;
339  pos += i;
340  } while (pos <= len);
341 
342  xcb_flush(c);
343 }
344 
345 #endif
QString name(const QVariant &location)
bool contains(const Key &key) const const
Sending string messages to other applications using the X Client Messages.
Definition: kxmessages.h:33
virtual bool event(QEvent *e)
QString fromUtf8(const char *str, int size)
const char * constData() const const
QCoreApplication * instance()
void installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
void broadcastMessage(const char *msg_type, const QString &message, int screen=-1)
Broadcasts the given message with the given message type.
Definition: kxmessages.cpp:204
void gotMessage(const QString &message)
Emitted when a message was received.
char * data()
KXMessages(const char *accept_broadcast=nullptr, QObject *parent=nullptr)
Creates an instance which will receive X messages.
Definition: kxmessages.cpp:174
static bool broadcastMessageX(Display *disp, const char *msg_type, const QString &message, int screen=-1)
Broadcasts the given message with the given message type.
Definition: kxmessages.cpp:219
int remove(const Key &key)
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Sep 21 2020 22:40:59 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.