7#include "kselectionowner.h"
9#include "kwindowsystem.h"
10#include "kxcbevent_p.h"
11#include <config-kwindowsystem.h>
13#include <QAbstractNativeEventFilter>
16#include <QGuiApplication>
19#include <private/qtx11extras_p.h>
21static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
23 xcb_window_t owner = XCB_NONE;
24 xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection),
nullptr);
34static xcb_atom_t intern_atom(xcb_connection_t *c,
const char *name)
36 xcb_atom_t atom = XCB_NONE;
37 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c,
false, strlen(name), name),
nullptr);
50 enum State { Idle, WaitingForTimestamp, WaitingForPreviousOwner };
52 Private(
KSelectionOwner *owner_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
54 , selection(selection_P)
58 , prev_owner(XCB_NONE)
59 , timestamp(XCB_CURRENT_TIME)
68 void claimSucceeded();
73 const xcb_atom_t selection;
74 xcb_connection_t *connection;
77 xcb_window_t prev_owner;
78 xcb_timestamp_t timestamp;
79 uint32_t extra1, extra2;
82 static xcb_atom_t manager_atom;
83 static xcb_atom_t xa_multiple;
84 static xcb_atom_t xa_targets;
85 static xcb_atom_t xa_timestamp;
89 static Private *
create(
KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
90 static Private *
create(
KSelectionOwner *owner,
const char *selection_P, xcb_connection_t *c, xcb_window_t root);
95 if (eventType !=
"xcb_generic_event_t") {
98 return owner->filterEvent(message);
105KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner, xcb_atom_t selection_P,
int screen_P)
107 if (KWindowSystem::isPlatformX11()) {
108 return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
110 qWarning() <<
"Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
114KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
116 return new Private(owner, selection_P, c, root);
119KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner,
const char *selection_P,
int screen_P)
121 if (KWindowSystem::isPlatformX11()) {
122 return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
124 qWarning() <<
"Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
128KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner,
const char *selection_P, xcb_connection_t *c, xcb_window_t root)
130 return new Private(owner, intern_atom(c, selection_P), c, root);
135 , d(Private::create(this, selection_P, screen_P))
141 , d(Private::create(this, selection_P, screen_P))
147 , d(Private::create(this, selection, c, root))
153 , d(Private::create(this, selection, c, root))
161 if (d->window != XCB_WINDOW_NONE) {
162 xcb_destroy_window(d->connection, d->window);
168void KSelectionOwner::Private::claimSucceeded()
172 KXcbEvent<xcb_client_message_event_t> ev;
173 ev.response_type = XCB_CLIENT_MESSAGE;
176 ev.type = Private::manager_atom;
177 ev.data.data32[0] = timestamp;
178 ev.data.data32[1] = selection;
179 ev.data.data32[2] = window;
180 ev.data.data32[3] = extra1;
181 ev.data.data32[4] = extra2;
183 xcb_send_event(connection,
false, root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, ev.buffer());
190void KSelectionOwner::Private::gotTimestamp()
192 Q_ASSERT(state == WaitingForTimestamp);
196 xcb_connection_t *c = connection;
199 xcb_set_selection_owner(c, window, selection, timestamp);
200 xcb_window_t new_owner = get_selection_owner(c, selection);
202 if (new_owner != window) {
204 xcb_destroy_window(c, window);
205 timestamp = XCB_CURRENT_TIME;
212 if (prev_owner != XCB_NONE && force_kill) {
214 timer.
start(1000, owner);
215 state = WaitingForPreviousOwner;
225void KSelectionOwner::Private::timeout()
227 Q_ASSERT(state == WaitingForPreviousOwner);
233 xcb_connection_t *c = connection;
236 xcb_generic_error_t *err = xcb_request_check(c, xcb_kill_client_checked(c, prev_owner));
250 Q_ASSERT(d->state == Private::Idle);
252 if (Private::manager_atom == XCB_NONE) {
256 if (d->timestamp != XCB_CURRENT_TIME) {
260 xcb_connection_t *c = d->connection;
261 d->prev_owner = get_selection_owner(c, d->selection);
263 if (d->prev_owner != XCB_NONE) {
272 uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
273 xcb_change_window_attributes(c, d->prev_owner, XCB_CW_EVENT_MASK, &mask);
276 uint32_t values[] = {
true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
278 d->window = xcb_generate_id(c);
280 XCB_COPY_FROM_PARENT,
288 XCB_WINDOW_CLASS_INPUT_ONLY,
289 XCB_COPY_FROM_PARENT,
290 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
294 xcb_atom_t tmp = XCB_ATOM_ATOM;
295 xcb_change_property(c, XCB_PROP_MODE_REPLACE, d->window, XCB_ATOM_ATOM, XCB_ATOM_ATOM, 32, 1, (
const void *)&tmp);
298 d->force_kill = force_kill_P;
299 d->state = Private::WaitingForTimestamp;
308 if (d->timestamp == XCB_CURRENT_TIME) {
312 xcb_destroy_window(d->connection, d->window);
313 d->window = XCB_NONE;
317 d->timestamp = XCB_CURRENT_TIME;
323 return XCB_WINDOW_NONE;
325 if (d->timestamp == XCB_CURRENT_TIME) {
337 d->extra1 = extra1_P;
338 d->extra2 = extra2_P;
346 xcb_generic_event_t *
event =
reinterpret_cast<xcb_generic_event_t *
>(ev_P);
347 const uint response_type =
event->response_type & ~0x80;
352 if (d->timestamp != CurrentTime && ev_P->xany.window == d->window) {
353 if (handleMessage(ev_P)) {
358 switch (response_type) {
359 case XCB_SELECTION_CLEAR: {
360 xcb_selection_clear_event_t *ev =
reinterpret_cast<xcb_selection_clear_event_t *
>(
event);
361 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
365 d->timestamp = XCB_CURRENT_TIME;
368 xcb_window_t window = d->window;
372 uint32_t event_mask = XCB_NONE;
373 xcb_change_window_attributes(d->connection, window, XCB_CW_EVENT_MASK, &event_mask);
374 xcb_destroy_window(d->connection, window);
377 case XCB_DESTROY_NOTIFY: {
378 xcb_destroy_notify_event_t *ev =
reinterpret_cast<xcb_destroy_notify_event_t *
>(
event);
379 if (ev->window == d->prev_owner) {
380 if (d->state == Private::WaitingForPreviousOwner) {
387 d->prev_owner = XCB_NONE;
390 if (d->timestamp == XCB_CURRENT_TIME || ev->window != d->window) {
394 d->timestamp = XCB_CURRENT_TIME;
399 case XCB_SELECTION_NOTIFY: {
400 xcb_selection_notify_event_t *ev =
reinterpret_cast<xcb_selection_notify_event_t *
>(
event);
401 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
408 case XCB_SELECTION_REQUEST:
409 filter_selection_request(
event);
411 case XCB_PROPERTY_NOTIFY: {
412 xcb_property_notify_event_t *ev =
reinterpret_cast<xcb_property_notify_event_t *
>(
event);
413 if (ev->window == d->window && d->state == Private::WaitingForTimestamp) {
414 d->timestamp = ev->time;
441bool KSelectionOwner::handleMessage(XEvent *)
447void KSelectionOwner::filter_selection_request(
void *
event)
452 xcb_selection_request_event_t *ev =
reinterpret_cast<xcb_selection_request_event_t *
>(
event);
454 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
458 if (ev->time != XCB_CURRENT_TIME && ev->time - d->timestamp > 1U << 31) {
464 xcb_connection_t *c = d->connection;
465 bool handled =
false;
467 if (ev->target == Private::xa_multiple) {
468 if (ev->property != XCB_NONE) {
469 const int MAX_ATOMS = 100;
471 xcb_get_property_cookie_t cookie = xcb_get_property(c,
false, ev->requestor, ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, MAX_ATOMS);
472 xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie,
nullptr);
474 if (reply && reply->format == 32 && reply->value_len % 2 == 0) {
475 xcb_atom_t *atoms =
reinterpret_cast<xcb_atom_t *
>(xcb_get_property_value(reply));
476 bool handled_array[MAX_ATOMS];
478 for (uint i = 0; i < reply->value_len / 2; i++) {
479 handled_array[i] = handle_selection(atoms[i * 2], atoms[i * 2 + 1], ev->requestor);
482 bool all_handled =
true;
483 for (uint i = 0; i < reply->value_len / 2; i++) {
484 if (!handled_array[i]) {
486 atoms[i * 2 + 1] = XCB_NONE;
491 xcb_change_property(c,
496 XCB_PROP_MODE_REPLACE,
498 reinterpret_cast<const void *
>(atoms));
509 if (ev->property == XCB_NONE) {
510 ev->property = ev->target;
513 handled = handle_selection(ev->target, ev->property, ev->requestor);
516 KXcbEvent<xcb_selection_notify_event_t> xev;
517 xev.response_type = XCB_SELECTION_NOTIFY;
518 xev.selection = ev->selection;
519 xev.requestor = ev->requestor;
520 xev.target = ev->target;
521 xev.property = handled ? ev->property : XCB_NONE;
523 xcb_send_event(c,
false, ev->requestor, 0, xev.buffer());
526bool KSelectionOwner::handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P)
531 if (target_P == Private::xa_timestamp) {
533 xcb_change_property(d->connection,
538 XCB_PROP_MODE_REPLACE,
540 reinterpret_cast<const void *
>(&d->timestamp));
541 }
else if (target_P == Private::xa_targets) {
543 }
else if (
genericReply(target_P, property_P, requestor_P)) {
557 xcb_atom_t atoms[3] = {Private::xa_multiple, Private::xa_timestamp, Private::xa_targets};
559 xcb_change_property(d->connection,
564 XCB_PROP_MODE_REPLACE,
565 sizeof(atoms) /
sizeof(atoms[0]),
566 reinterpret_cast<const void *
>(atoms));
581 if (Private::manager_atom != XCB_NONE) {
585 xcb_connection_t *c = d->connection;
590 } atoms[] = {{
"MANAGER", &Private::manager_atom},
591 {
"MULTIPLE", &Private::xa_multiple},
592 {
"TARGETS", &Private::xa_targets},
593 {
"TIMESTAMP", &Private::xa_timestamp}};
595 const int count =
sizeof(atoms) /
sizeof(atoms[0]);
596 xcb_intern_atom_cookie_t cookies[count];
598 for (
int i = 0; i < count; i++) {
599 cookies[i] = xcb_intern_atom(c,
false, strlen(atoms[i].name), atoms[i].name);
602 for (
int i = 0; i < count; i++) {
603 if (xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, cookies[i],
nullptr)) {
604 *atoms[i].atom = reply->atom;
610xcb_atom_t KSelectionOwner::Private::manager_atom = XCB_NONE;
611xcb_atom_t KSelectionOwner::Private::xa_multiple = XCB_NONE;
612xcb_atom_t KSelectionOwner::Private::xa_targets = XCB_NONE;
613xcb_atom_t KSelectionOwner::Private::xa_timestamp = XCB_NONE;
615#include "moc_kselectionowner.cpp"
This class implements claiming and owning manager selections, as described in the ICCCM,...
virtual bool genericReply(xcb_atom_t target, xcb_atom_t property, xcb_window_t requestor)
Called for every X event received on the window used for owning the selection.
void timerEvent(QTimerEvent *event) override
void release()
If the selection is owned, the ownership is given up.
void setData(uint32_t extra1, uint32_t extra2)
Sets extra data to be sent in the message sent to root window after successfully claiming a selection...
xcb_window_t ownerWindow() const
If the selection is owned, returns the window used internally for owning the selection.
KSelectionOwner(xcb_atom_t selection, int screen=-1, QObject *parent=nullptr)
This constructor initializes the object, but doesn't perform any operation on the selection.
void failedToClaimOwnership()
This signal is emitted when claim() failed to claim ownership of the selection.
void claimedOwnership()
This signal is emitted when claim() was successful in claiming ownership of the selection.
void claim(bool force, bool force_kill=true)
Try to claim ownership of the manager selection using the current X timestamp.
void lostOwnership()
This signal is emitted if the selection was owned and the ownership has been lost due to another clie...
virtual void getAtoms()
Called to create atoms needed for claiming the selection and communication using the selection handli...
~KSelectionOwner() override
Destructor.
bool filterEvent(void *ev_P)
virtual void replyTargets(xcb_atom_t property, xcb_window_t requestor)
Called to announce the supported targets, as described in the ICCCM section 2.6.
QAction * create(StandardAction id, const Receiver *recvr, Func slot, QObject *parent, std::optional< Qt::ConnectionType > connectionType=std::nullopt)
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)=0
void start(int msec, QObject *object)
int timerId() const const
void installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
QCoreApplication * instance()
virtual bool event(QEvent *e)
virtual void timerEvent(QTimerEvent *event)