7#include "kselectionowner.h"
9#include "kwindowsystem.h"
10#include <config-kwindowsystem.h>
12#include <QAbstractNativeEventFilter>
15#include <QGuiApplication>
18#include <private/qtx11extras_p.h>
20static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
22 xcb_window_t owner = XCB_NONE;
23 xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection),
nullptr);
33static xcb_atom_t intern_atom(xcb_connection_t *c,
const char *name)
35 xcb_atom_t atom = XCB_NONE;
36 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c,
false, strlen(name), name),
nullptr);
49 enum State { Idle, WaitingForTimestamp, WaitingForPreviousOwner };
51 Private(
KSelectionOwner *owner_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
53 , selection(selection_P)
57 , prev_owner(XCB_NONE)
58 , timestamp(XCB_CURRENT_TIME)
67 void claimSucceeded();
72 const xcb_atom_t selection;
73 xcb_connection_t *connection;
76 xcb_window_t prev_owner;
77 xcb_timestamp_t timestamp;
78 uint32_t extra1, extra2;
81 static xcb_atom_t manager_atom;
82 static xcb_atom_t xa_multiple;
83 static xcb_atom_t xa_targets;
84 static xcb_atom_t xa_timestamp;
88 static Private *
create(
KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
89 static Private *
create(
KSelectionOwner *owner,
const char *selection_P, xcb_connection_t *c, xcb_window_t root);
94 if (eventType !=
"xcb_generic_event_t") {
97 return owner->filterEvent(message);
104KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner, xcb_atom_t selection_P,
int screen_P)
106 if (KWindowSystem::isPlatformX11()) {
107 return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
109 qWarning() <<
"Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
113KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
115 return new Private(owner, selection_P, c, root);
118KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner,
const char *selection_P,
int screen_P)
120 if (KWindowSystem::isPlatformX11()) {
121 return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
123 qWarning() <<
"Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
127KSelectionOwner::Private *KSelectionOwner::Private::create(
KSelectionOwner *owner,
const char *selection_P, xcb_connection_t *c, xcb_window_t root)
129 return new Private(owner, intern_atom(c, selection_P), c, root);
134 , d(Private::create(this, selection_P, screen_P))
140 , d(Private::create(this, selection_P, screen_P))
146 , d(Private::create(this, selection, c, root))
152 , d(Private::create(this, selection, c, root))
160 if (d->window != XCB_WINDOW_NONE) {
161 xcb_destroy_window(d->connection, d->window);
167void KSelectionOwner::Private::claimSucceeded()
171 xcb_client_message_event_t ev;
172 ev.response_type = XCB_CLIENT_MESSAGE;
175 ev.type = Private::manager_atom;
176 ev.data.data32[0] = timestamp;
177 ev.data.data32[1] = selection;
178 ev.data.data32[2] = window;
179 ev.data.data32[3] = extra1;
180 ev.data.data32[4] = extra2;
182 xcb_send_event(connection,
false, root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (
const char *)&ev);
189void KSelectionOwner::Private::gotTimestamp()
191 Q_ASSERT(state == WaitingForTimestamp);
195 xcb_connection_t *c = connection;
198 xcb_set_selection_owner(c, window, selection, timestamp);
199 xcb_window_t new_owner = get_selection_owner(c, selection);
201 if (new_owner != window) {
203 xcb_destroy_window(c, window);
204 timestamp = XCB_CURRENT_TIME;
211 if (prev_owner != XCB_NONE && force_kill) {
213 timer.
start(1000, owner);
214 state = WaitingForPreviousOwner;
224void KSelectionOwner::Private::timeout()
226 Q_ASSERT(state == WaitingForPreviousOwner);
232 xcb_connection_t *c = connection;
235 xcb_generic_error_t *err = xcb_request_check(c, xcb_kill_client_checked(c, prev_owner));
249 Q_ASSERT(d->state == Private::Idle);
251 if (Private::manager_atom == XCB_NONE) {
255 if (d->timestamp != XCB_CURRENT_TIME) {
259 xcb_connection_t *c = d->connection;
260 d->prev_owner = get_selection_owner(c, d->selection);
262 if (d->prev_owner != XCB_NONE) {
271 uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
272 xcb_change_window_attributes(c, d->prev_owner, XCB_CW_EVENT_MASK, &mask);
275 uint32_t values[] = {
true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
277 d->window = xcb_generate_id(c);
279 XCB_COPY_FROM_PARENT,
287 XCB_WINDOW_CLASS_INPUT_ONLY,
288 XCB_COPY_FROM_PARENT,
289 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
293 xcb_atom_t tmp = XCB_ATOM_ATOM;
294 xcb_change_property(c, XCB_PROP_MODE_REPLACE, d->window, XCB_ATOM_ATOM, XCB_ATOM_ATOM, 32, 1, (
const void *)&tmp);
297 d->force_kill = force_kill_P;
298 d->state = Private::WaitingForTimestamp;
307 if (d->timestamp == XCB_CURRENT_TIME) {
311 xcb_destroy_window(d->connection, d->window);
312 d->window = XCB_NONE;
316 d->timestamp = XCB_CURRENT_TIME;
322 return XCB_WINDOW_NONE;
324 if (d->timestamp == XCB_CURRENT_TIME) {
336 d->extra1 = extra1_P;
337 d->extra2 = extra2_P;
345 xcb_generic_event_t *
event =
reinterpret_cast<xcb_generic_event_t *
>(ev_P);
346 const uint response_type =
event->response_type & ~0x80;
351 if (d->timestamp != CurrentTime && ev_P->xany.window == d->window) {
352 if (handleMessage(ev_P)) {
357 switch (response_type) {
358 case XCB_SELECTION_CLEAR: {
359 xcb_selection_clear_event_t *ev =
reinterpret_cast<xcb_selection_clear_event_t *
>(
event);
360 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
364 d->timestamp = XCB_CURRENT_TIME;
367 xcb_window_t window = d->window;
371 uint32_t event_mask = XCB_NONE;
372 xcb_change_window_attributes(d->connection, window, XCB_CW_EVENT_MASK, &event_mask);
373 xcb_destroy_window(d->connection, window);
376 case XCB_DESTROY_NOTIFY: {
377 xcb_destroy_notify_event_t *ev =
reinterpret_cast<xcb_destroy_notify_event_t *
>(
event);
378 if (ev->window == d->prev_owner) {
379 if (d->state == Private::WaitingForPreviousOwner) {
386 d->prev_owner = XCB_NONE;
389 if (d->timestamp == XCB_CURRENT_TIME || ev->window != d->window) {
393 d->timestamp = XCB_CURRENT_TIME;
398 case XCB_SELECTION_NOTIFY: {
399 xcb_selection_notify_event_t *ev =
reinterpret_cast<xcb_selection_notify_event_t *
>(
event);
400 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
407 case XCB_SELECTION_REQUEST:
408 filter_selection_request(
event);
410 case XCB_PROPERTY_NOTIFY: {
411 xcb_property_notify_event_t *ev =
reinterpret_cast<xcb_property_notify_event_t *
>(
event);
412 if (ev->window == d->window && d->state == Private::WaitingForTimestamp) {
413 d->timestamp = ev->time;
440bool KSelectionOwner::handleMessage(XEvent *)
446void KSelectionOwner::filter_selection_request(
void *
event)
451 xcb_selection_request_event_t *ev =
reinterpret_cast<xcb_selection_request_event_t *
>(
event);
453 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
457 if (ev->time != XCB_CURRENT_TIME && ev->time - d->timestamp > 1U << 31) {
463 xcb_connection_t *c = d->connection;
464 bool handled =
false;
466 if (ev->target == Private::xa_multiple) {
467 if (ev->property != XCB_NONE) {
468 const int MAX_ATOMS = 100;
470 xcb_get_property_cookie_t cookie = xcb_get_property(c,
false, ev->requestor, ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, MAX_ATOMS);
471 xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie,
nullptr);
473 if (reply && reply->format == 32 && reply->value_len % 2 == 0) {
474 xcb_atom_t *atoms =
reinterpret_cast<xcb_atom_t *
>(xcb_get_property_value(reply));
475 bool handled_array[MAX_ATOMS];
477 for (uint i = 0; i < reply->value_len / 2; i++) {
478 handled_array[i] = handle_selection(atoms[i * 2], atoms[i * 2 + 1], ev->requestor);
481 bool all_handled =
true;
482 for (uint i = 0; i < reply->value_len / 2; i++) {
483 if (!handled_array[i]) {
485 atoms[i * 2 + 1] = XCB_NONE;
490 xcb_change_property(c,
495 XCB_PROP_MODE_REPLACE,
497 reinterpret_cast<const void *
>(atoms));
508 if (ev->property == XCB_NONE) {
509 ev->property = ev->target;
512 handled = handle_selection(ev->target, ev->property, ev->requestor);
515 xcb_selection_notify_event_t xev;
516 xev.response_type = XCB_SELECTION_NOTIFY;
517 xev.selection = ev->selection;
518 xev.requestor = ev->requestor;
519 xev.target = ev->target;
520 xev.property = handled ? ev->property : XCB_NONE;
522 xcb_send_event(c,
false, ev->requestor, 0, (
const char *)&xev);
525bool KSelectionOwner::handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P)
530 if (target_P == Private::xa_timestamp) {
532 xcb_change_property(d->connection,
537 XCB_PROP_MODE_REPLACE,
539 reinterpret_cast<const void *
>(&d->timestamp));
540 }
else if (target_P == Private::xa_targets) {
542 }
else if (
genericReply(target_P, property_P, requestor_P)) {
556 xcb_atom_t atoms[3] = {Private::xa_multiple, Private::xa_timestamp, Private::xa_targets};
558 xcb_change_property(d->connection,
563 XCB_PROP_MODE_REPLACE,
564 sizeof(atoms) /
sizeof(atoms[0]),
565 reinterpret_cast<const void *
>(atoms));
580 if (Private::manager_atom != XCB_NONE) {
584 xcb_connection_t *c = d->connection;
589 } atoms[] = {{
"MANAGER", &Private::manager_atom},
590 {
"MULTIPLE", &Private::xa_multiple},
591 {
"TARGETS", &Private::xa_targets},
592 {
"TIMESTAMP", &Private::xa_timestamp}};
594 const int count =
sizeof(atoms) /
sizeof(atoms[0]);
595 xcb_intern_atom_cookie_t cookies[count];
597 for (
int i = 0; i < count; i++) {
598 cookies[i] = xcb_intern_atom(c,
false, strlen(atoms[i].name), atoms[i].name);
601 for (
int i = 0; i < count; i++) {
602 if (xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, cookies[i],
nullptr)) {
603 *atoms[i].atom = reply->atom;
609xcb_atom_t KSelectionOwner::Private::manager_atom = XCB_NONE;
610xcb_atom_t KSelectionOwner::Private::xa_multiple = XCB_NONE;
611xcb_atom_t KSelectionOwner::Private::xa_targets = XCB_NONE;
612xcb_atom_t KSelectionOwner::Private::xa_timestamp = XCB_NONE;
614#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(GameStandardAction id, const QObject *recvr, const char *slot, QObject *parent)
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)