KWindowSystem

kselectionwatcher.cpp
1 /*
2  SPDX-FileCopyrightText: 2003 Lubos Lunak <[email protected]>
3 
4  SPDX-License-Identifier: MIT
5 */
6 
7 #include "kselectionwatcher.h"
8 
9 #include "kwindowsystem.h"
10 #include <config-kwindowsystem.h>
11 
12 #include <QAbstractNativeEventFilter>
13 #include <QCoreApplication>
14 
15 #include <qx11info_x11.h>
16 
17 static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
18 {
19  xcb_window_t owner = XCB_NONE;
20  xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr);
21 
22  if (reply) {
23  owner = reply->owner;
24  free(reply);
25  }
26 
27  return owner;
28 }
29 
30 static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name)
31 {
32  xcb_atom_t atom = XCB_NONE;
33  xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr);
34 
35  if (reply) {
36  atom = reply->atom;
37  free(reply);
38  }
39 
40  return atom;
41 }
42 
43 //*******************************************
44 // KSelectionWatcher
45 //*******************************************
46 
47 class Q_DECL_HIDDEN KSelectionWatcher::Private : public QAbstractNativeEventFilter
48 {
49 public:
50  Private(KSelectionWatcher *watcher_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
51  : connection(c)
52  , root(root)
53  , selection(selection_P)
54  , selection_owner(XCB_NONE)
55  , watcher(watcher_P)
56  {
58  }
59 
60  xcb_connection_t *connection;
61  xcb_window_t root;
62  const xcb_atom_t selection;
63  xcb_window_t selection_owner;
64  static xcb_atom_t manager_atom;
65 
66  static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P);
67  static Private *create(KSelectionWatcher *watcher, const char *selection_P, int screen_P);
68  static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
69  static Private *create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root);
70 
71 protected:
72  bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override
73  {
74  Q_UNUSED(result);
75  if (eventType != "xcb_generic_event_t") {
76  return false;
77  }
78  watcher->filterEvent(message);
79  return false;
80  }
81 
82 private:
83  KSelectionWatcher *watcher;
84 };
85 
86 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P)
87 {
89  return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
90  }
91  return nullptr;
92 }
93 
94 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
95 {
96  return new Private(watcher, selection_P, c, root);
97 }
98 
99 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, int screen_P)
100 {
102  return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
103  }
104  return nullptr;
105 }
106 
107 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root)
108 {
109  return new Private(watcher, intern_atom(c, selection_P), c, root);
110 }
111 
112 KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection_P, int screen_P, QObject *parent_P)
113  : QObject(parent_P)
114  , d(Private::create(this, selection_P, screen_P))
115 {
116  init();
117 }
118 
119 KSelectionWatcher::KSelectionWatcher(const char *selection_P, int screen_P, QObject *parent_P)
120  : QObject(parent_P)
121  , d(Private::create(this, selection_P, screen_P))
122 {
123  init();
124 }
125 
126 KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
127  : QObject(parent)
128  , d(Private::create(this, selection, c, root))
129 {
130  init();
131 }
132 
133 KSelectionWatcher::KSelectionWatcher(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
134  : QObject(parent)
135  , d(Private::create(this, selection, c, root))
136 {
137  init();
138 }
139 
140 KSelectionWatcher::~KSelectionWatcher()
141 {
142  delete d;
143 }
144 
145 void KSelectionWatcher::init()
146 {
147  if (!d) {
148  return;
149  }
150  if (Private::manager_atom == XCB_NONE) {
151  xcb_connection_t *c = d->connection;
152 
153  xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(c, false, strlen("MANAGER"), "MANAGER");
154  xcb_get_window_attributes_cookie_t attr_cookie = xcb_get_window_attributes(c, d->root);
155 
156  xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(c, atom_cookie, nullptr);
157  Private::manager_atom = atom_reply->atom;
158  free(atom_reply);
159 
160  xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(c, attr_cookie, nullptr);
161  uint32_t event_mask = attr->your_event_mask;
162  free(attr);
163 
164  if (!(event_mask & XCB_EVENT_MASK_STRUCTURE_NOTIFY)) {
165  // We need XCB_EVENT_MASK_STRUCTURE_NORITY on the root window
166  event_mask |= XCB_EVENT_MASK_STRUCTURE_NOTIFY;
167  xcb_change_window_attributes(c, d->root, XCB_CW_EVENT_MASK, &event_mask);
168  }
169  }
170 
171  owner(); // trigger reading of current selection status
172 }
173 
175 {
176  if (!d) {
177  return XCB_WINDOW_NONE;
178  }
179  xcb_connection_t *c = d->connection;
180 
181  xcb_window_t current_owner = get_selection_owner(c, d->selection);
182  if (current_owner == XCB_NONE) {
183  return XCB_NONE;
184  }
185 
186  if (current_owner == d->selection_owner) {
187  return d->selection_owner;
188  }
189 
190  // We have a new selection owner - select for structure notify events
191  uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
192  xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(c, current_owner, XCB_CW_EVENT_MASK, &mask);
193 
194  // Verify that the owner didn't change again while selecting for events
195  xcb_window_t new_owner = get_selection_owner(c, d->selection);
196  xcb_generic_error_t *err = xcb_request_check(c, cookie);
197 
198  if (!err && current_owner == new_owner) {
199  d->selection_owner = current_owner;
200  Q_EMIT newOwner(d->selection_owner);
201  } else {
202  // ### This doesn't look right - the selection could have an owner
203  d->selection_owner = XCB_NONE;
204  }
205 
206  if (err) {
207  free(err);
208  }
209 
210  return d->selection_owner;
211 }
212 
214 {
215  if (!d) {
216  return;
217  }
218  xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P);
219  const uint response_type = event->response_type & ~0x80;
220  if (response_type == XCB_CLIENT_MESSAGE) {
221  xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event);
222 
223  if (cm_event->type != Private::manager_atom || cm_event->data.data32[1] != d->selection) {
224  return;
225  }
226  // owner() checks whether the owner changed and emits newOwner()
227  owner();
228  return;
229  }
230  if (response_type == XCB_DESTROY_NOTIFY) {
231  xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event);
232  if (d->selection_owner == XCB_NONE || ev->window != d->selection_owner) {
233  return;
234  }
235 
236  d->selection_owner = XCB_NONE; // in case the exactly same ID gets reused as the owner
237 
238  if (owner() == XCB_NONE) {
239  Q_EMIT lostOwner(); // it must be safe to delete 'this' in a slot
240  }
241  return;
242  }
243 }
244 
245 xcb_atom_t KSelectionWatcher::Private::manager_atom = XCB_NONE;
QAction * create(StandardAction id, const QObject *recvr, Func slot, QObject *parent)
void filterEvent(void *ev_P)
virtual bool event(QEvent *e)
void lostOwner()
This signal is emitted when the selection is given up, i.e.
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result)=0
This class implements watching manager selections, as described in the ICCCM section 2...
static bool isPlatformX11()
Convenience method to check whether the Platform is X11.
QCoreApplication * instance()
xcb_window_t owner()
Return the current owner of the manager selection, if any.
void installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
KSelectionWatcher(xcb_atom_t selection, int screen=-1, QObject *parent=nullptr)
This constructor initializes the object, but doesn&#39;t perform any operation on the selection...
void newOwner(xcb_window_t owner)
This signal is emitted when the selection is successfully claimed by a new owner. ...
QObject * parent() const const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Fri Oct 15 2021 22:41:49 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.