File indexing completed on 2024-04-28 03:59:24
0001 /* 0002 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0003 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "kselectionowner.h" 0008 0009 #include "kwindowsystem.h" 0010 #include <config-kwindowsystem.h> 0011 0012 #include <QAbstractNativeEventFilter> 0013 #include <QBasicTimer> 0014 #include <QDebug> 0015 #include <QGuiApplication> 0016 #include <QTimerEvent> 0017 0018 #include <private/qtx11extras_p.h> 0019 0020 static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection) 0021 { 0022 xcb_window_t owner = XCB_NONE; 0023 xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr); 0024 0025 if (reply) { 0026 owner = reply->owner; 0027 free(reply); 0028 } 0029 0030 return owner; 0031 } 0032 0033 static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name) 0034 { 0035 xcb_atom_t atom = XCB_NONE; 0036 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr); 0037 0038 if (reply) { 0039 atom = reply->atom; 0040 free(reply); 0041 } 0042 0043 return atom; 0044 } 0045 0046 class Q_DECL_HIDDEN KSelectionOwner::Private : public QAbstractNativeEventFilter 0047 { 0048 public: 0049 enum State { Idle, WaitingForTimestamp, WaitingForPreviousOwner }; 0050 0051 Private(KSelectionOwner *owner_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root) 0052 : state(Idle) 0053 , selection(selection_P) 0054 , connection(c) 0055 , root(root) 0056 , window(XCB_NONE) 0057 , prev_owner(XCB_NONE) 0058 , timestamp(XCB_CURRENT_TIME) 0059 , extra1(0) 0060 , extra2(0) 0061 , force_kill(false) 0062 , owner(owner_P) 0063 { 0064 QCoreApplication::instance()->installNativeEventFilter(this); 0065 } 0066 0067 void claimSucceeded(); 0068 void gotTimestamp(); 0069 void timeout(); 0070 0071 State state; 0072 const xcb_atom_t selection; 0073 xcb_connection_t *connection; 0074 xcb_window_t root; 0075 xcb_window_t window; 0076 xcb_window_t prev_owner; 0077 xcb_timestamp_t timestamp; 0078 uint32_t extra1, extra2; 0079 QBasicTimer timer; 0080 bool force_kill; 0081 static xcb_atom_t manager_atom; 0082 static xcb_atom_t xa_multiple; 0083 static xcb_atom_t xa_targets; 0084 static xcb_atom_t xa_timestamp; 0085 0086 static Private *create(KSelectionOwner *owner, xcb_atom_t selection_P, int screen_P); 0087 static Private *create(KSelectionOwner *owner, const char *selection_P, int screen_P); 0088 static Private *create(KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root); 0089 static Private *create(KSelectionOwner *owner, const char *selection_P, xcb_connection_t *c, xcb_window_t root); 0090 0091 protected: 0092 bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override 0093 { 0094 if (eventType != "xcb_generic_event_t") { 0095 return false; 0096 } 0097 return owner->filterEvent(message); 0098 } 0099 0100 private: 0101 KSelectionOwner *owner; 0102 }; 0103 0104 KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, xcb_atom_t selection_P, int screen_P) 0105 { 0106 if (KWindowSystem::isPlatformX11()) { 0107 return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P)); 0108 } 0109 qWarning() << "Trying to use KSelectionOwner on a non-X11 platform! This is an application bug."; 0110 return nullptr; 0111 } 0112 0113 KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root) 0114 { 0115 return new Private(owner, selection_P, c, root); 0116 } 0117 0118 KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, const char *selection_P, int screen_P) 0119 { 0120 if (KWindowSystem::isPlatformX11()) { 0121 return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P)); 0122 } 0123 qWarning() << "Trying to use KSelectionOwner on a non-X11 platform! This is an application bug."; 0124 return nullptr; 0125 } 0126 0127 KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, const char *selection_P, xcb_connection_t *c, xcb_window_t root) 0128 { 0129 return new Private(owner, intern_atom(c, selection_P), c, root); 0130 } 0131 0132 KSelectionOwner::KSelectionOwner(xcb_atom_t selection_P, int screen_P, QObject *parent_P) 0133 : QObject(parent_P) 0134 , d(Private::create(this, selection_P, screen_P)) 0135 { 0136 } 0137 0138 KSelectionOwner::KSelectionOwner(const char *selection_P, int screen_P, QObject *parent_P) 0139 : QObject(parent_P) 0140 , d(Private::create(this, selection_P, screen_P)) 0141 { 0142 } 0143 0144 KSelectionOwner::KSelectionOwner(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent) 0145 : QObject(parent) 0146 , d(Private::create(this, selection, c, root)) 0147 { 0148 } 0149 0150 KSelectionOwner::KSelectionOwner(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent) 0151 : QObject(parent) 0152 , d(Private::create(this, selection, c, root)) 0153 { 0154 } 0155 0156 KSelectionOwner::~KSelectionOwner() 0157 { 0158 if (d) { 0159 release(); 0160 if (d->window != XCB_WINDOW_NONE) { 0161 xcb_destroy_window(d->connection, d->window); // also makes the selection not owned 0162 } 0163 delete d; 0164 } 0165 } 0166 0167 void KSelectionOwner::Private::claimSucceeded() 0168 { 0169 state = Idle; 0170 0171 xcb_client_message_event_t ev; 0172 ev.response_type = XCB_CLIENT_MESSAGE; 0173 ev.format = 32; 0174 ev.window = root; 0175 ev.type = Private::manager_atom; 0176 ev.data.data32[0] = timestamp; 0177 ev.data.data32[1] = selection; 0178 ev.data.data32[2] = window; 0179 ev.data.data32[3] = extra1; 0180 ev.data.data32[4] = extra2; 0181 0182 xcb_send_event(connection, false, root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *)&ev); 0183 0184 // qDebug() << "Claimed selection"; 0185 0186 Q_EMIT owner->claimedOwnership(); 0187 } 0188 0189 void KSelectionOwner::Private::gotTimestamp() 0190 { 0191 Q_ASSERT(state == WaitingForTimestamp); 0192 0193 state = Idle; 0194 0195 xcb_connection_t *c = connection; 0196 0197 // Set the selection owner and immediately verify that the claim was successful 0198 xcb_set_selection_owner(c, window, selection, timestamp); 0199 xcb_window_t new_owner = get_selection_owner(c, selection); 0200 0201 if (new_owner != window) { 0202 // qDebug() << "Failed to claim selection : " << new_owner; 0203 xcb_destroy_window(c, window); 0204 timestamp = XCB_CURRENT_TIME; 0205 window = XCB_NONE; 0206 0207 Q_EMIT owner->failedToClaimOwnership(); 0208 return; 0209 } 0210 0211 if (prev_owner != XCB_NONE && force_kill) { 0212 // qDebug() << "Waiting for previous owner to disown"; 0213 timer.start(1000, owner); 0214 state = WaitingForPreviousOwner; 0215 0216 // Note: We've already selected for structure notify events 0217 // on the previous owner window 0218 } else { 0219 // If there was no previous owner, we're done 0220 claimSucceeded(); 0221 } 0222 } 0223 0224 void KSelectionOwner::Private::timeout() 0225 { 0226 Q_ASSERT(state == WaitingForPreviousOwner); 0227 0228 state = Idle; 0229 0230 if (force_kill) { 0231 // qDebug() << "Killing previous owner"; 0232 xcb_connection_t *c = connection; 0233 0234 // Ignore any errors from the kill request 0235 xcb_generic_error_t *err = xcb_request_check(c, xcb_kill_client_checked(c, prev_owner)); 0236 free(err); 0237 0238 claimSucceeded(); 0239 } else { 0240 Q_EMIT owner->failedToClaimOwnership(); 0241 } 0242 } 0243 0244 void KSelectionOwner::claim(bool force_P, bool force_kill_P) 0245 { 0246 if (!d) { 0247 return; 0248 } 0249 Q_ASSERT(d->state == Private::Idle); 0250 0251 if (Private::manager_atom == XCB_NONE) { 0252 getAtoms(); 0253 } 0254 0255 if (d->timestamp != XCB_CURRENT_TIME) { 0256 release(); 0257 } 0258 0259 xcb_connection_t *c = d->connection; 0260 d->prev_owner = get_selection_owner(c, d->selection); 0261 0262 if (d->prev_owner != XCB_NONE) { 0263 if (!force_P) { 0264 // qDebug() << "Selection already owned, failing"; 0265 Q_EMIT failedToClaimOwnership(); 0266 return; 0267 } 0268 0269 // Select structure notify events so get an event when the previous owner 0270 // destroys the window 0271 uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY; 0272 xcb_change_window_attributes(c, d->prev_owner, XCB_CW_EVENT_MASK, &mask); 0273 } 0274 0275 uint32_t values[] = {true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY}; 0276 0277 d->window = xcb_generate_id(c); 0278 xcb_create_window(c, 0279 XCB_COPY_FROM_PARENT, 0280 d->window, 0281 d->root, 0282 0, 0283 0, 0284 1, 0285 1, 0286 0, 0287 XCB_WINDOW_CLASS_INPUT_ONLY, 0288 XCB_COPY_FROM_PARENT, 0289 XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, 0290 values); 0291 0292 // Trigger a property change event so we get a timestamp 0293 xcb_atom_t tmp = XCB_ATOM_ATOM; 0294 xcb_change_property(c, XCB_PROP_MODE_REPLACE, d->window, XCB_ATOM_ATOM, XCB_ATOM_ATOM, 32, 1, (const void *)&tmp); 0295 0296 // Now we have to return to the event loop and wait for the property change event 0297 d->force_kill = force_kill_P; 0298 d->state = Private::WaitingForTimestamp; 0299 } 0300 0301 // destroy resource first 0302 void KSelectionOwner::release() 0303 { 0304 if (!d) { 0305 return; 0306 } 0307 if (d->timestamp == XCB_CURRENT_TIME) { 0308 return; 0309 } 0310 0311 xcb_destroy_window(d->connection, d->window); // also makes the selection not owned 0312 d->window = XCB_NONE; 0313 0314 // qDebug() << "Releasing selection"; 0315 0316 d->timestamp = XCB_CURRENT_TIME; 0317 } 0318 0319 xcb_window_t KSelectionOwner::ownerWindow() const 0320 { 0321 if (!d) { 0322 return XCB_WINDOW_NONE; 0323 } 0324 if (d->timestamp == XCB_CURRENT_TIME) { 0325 return XCB_NONE; 0326 } 0327 0328 return d->window; 0329 } 0330 0331 void KSelectionOwner::setData(uint32_t extra1_P, uint32_t extra2_P) 0332 { 0333 if (!d) { 0334 return; 0335 } 0336 d->extra1 = extra1_P; 0337 d->extra2 = extra2_P; 0338 } 0339 0340 bool KSelectionOwner::filterEvent(void *ev_P) 0341 { 0342 if (!d) { 0343 return false; 0344 } 0345 xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P); 0346 const uint response_type = event->response_type & ~0x80; 0347 0348 #if 0 0349 // There's no generic way to get the window for an event in xcb, it depends on the type of event 0350 // This handleMessage virtual doesn't seem used anyway. 0351 if (d->timestamp != CurrentTime && ev_P->xany.window == d->window) { 0352 if (handleMessage(ev_P)) { 0353 return true; 0354 } 0355 } 0356 #endif 0357 switch (response_type) { 0358 case XCB_SELECTION_CLEAR: { 0359 xcb_selection_clear_event_t *ev = reinterpret_cast<xcb_selection_clear_event_t *>(event); 0360 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) { 0361 return false; 0362 } 0363 0364 d->timestamp = XCB_CURRENT_TIME; 0365 // qDebug() << "Lost selection"; 0366 0367 xcb_window_t window = d->window; 0368 Q_EMIT lostOwnership(); 0369 0370 // Unset the event mask before we destroy the window so we don't get a destroy event 0371 uint32_t event_mask = XCB_NONE; 0372 xcb_change_window_attributes(d->connection, window, XCB_CW_EVENT_MASK, &event_mask); 0373 xcb_destroy_window(d->connection, window); 0374 return true; 0375 } 0376 case XCB_DESTROY_NOTIFY: { 0377 xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event); 0378 if (ev->window == d->prev_owner) { 0379 if (d->state == Private::WaitingForPreviousOwner) { 0380 d->timer.stop(); 0381 d->claimSucceeded(); 0382 return true; 0383 } 0384 // It is possible for the previous owner to be destroyed 0385 // while we're waiting for the timestamp 0386 d->prev_owner = XCB_NONE; 0387 } 0388 0389 if (d->timestamp == XCB_CURRENT_TIME || ev->window != d->window) { 0390 return false; 0391 } 0392 0393 d->timestamp = XCB_CURRENT_TIME; 0394 // qDebug() << "Lost selection (destroyed)"; 0395 Q_EMIT lostOwnership(); 0396 return true; 0397 } 0398 case XCB_SELECTION_NOTIFY: { 0399 xcb_selection_notify_event_t *ev = reinterpret_cast<xcb_selection_notify_event_t *>(event); 0400 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) { 0401 return false; 0402 } 0403 0404 // ignore? 0405 return false; 0406 } 0407 case XCB_SELECTION_REQUEST: 0408 filter_selection_request(event); 0409 return false; 0410 case XCB_PROPERTY_NOTIFY: { 0411 xcb_property_notify_event_t *ev = reinterpret_cast<xcb_property_notify_event_t *>(event); 0412 if (ev->window == d->window && d->state == Private::WaitingForTimestamp) { 0413 d->timestamp = ev->time; 0414 d->gotTimestamp(); 0415 return true; 0416 } 0417 return false; 0418 } 0419 default: 0420 return false; 0421 } 0422 } 0423 0424 void KSelectionOwner::timerEvent(QTimerEvent *event) 0425 { 0426 if (!d) { 0427 QObject::timerEvent(event); 0428 return; 0429 } 0430 if (event->timerId() == d->timer.timerId()) { 0431 d->timer.stop(); 0432 d->timeout(); 0433 return; 0434 } 0435 0436 QObject::timerEvent(event); 0437 } 0438 0439 #if 0 0440 bool KSelectionOwner::handleMessage(XEvent *) 0441 { 0442 return false; 0443 } 0444 #endif 0445 0446 void KSelectionOwner::filter_selection_request(void *event) 0447 { 0448 if (!d) { 0449 return; 0450 } 0451 xcb_selection_request_event_t *ev = reinterpret_cast<xcb_selection_request_event_t *>(event); 0452 0453 if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) { 0454 return; 0455 } 0456 0457 if (ev->time != XCB_CURRENT_TIME && ev->time - d->timestamp > 1U << 31) { 0458 return; // too old or too new request 0459 } 0460 0461 // qDebug() << "Got selection request"; 0462 0463 xcb_connection_t *c = d->connection; 0464 bool handled = false; 0465 0466 if (ev->target == Private::xa_multiple) { 0467 if (ev->property != XCB_NONE) { 0468 const int MAX_ATOMS = 100; 0469 0470 xcb_get_property_cookie_t cookie = xcb_get_property(c, false, ev->requestor, ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, MAX_ATOMS); 0471 xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr); 0472 0473 if (reply && reply->format == 32 && reply->value_len % 2 == 0) { 0474 xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply)); 0475 bool handled_array[MAX_ATOMS]; 0476 0477 for (uint i = 0; i < reply->value_len / 2; i++) { 0478 handled_array[i] = handle_selection(atoms[i * 2], atoms[i * 2 + 1], ev->requestor); 0479 } 0480 0481 bool all_handled = true; 0482 for (uint i = 0; i < reply->value_len / 2; i++) { 0483 if (!handled_array[i]) { 0484 all_handled = false; 0485 atoms[i * 2 + 1] = XCB_NONE; 0486 } 0487 } 0488 0489 if (!all_handled) { 0490 xcb_change_property(c, 0491 ev->requestor, 0492 ev->property, 0493 XCB_ATOM_ATOM, 0494 32, 0495 XCB_PROP_MODE_REPLACE, 0496 reply->value_len, 0497 reinterpret_cast<const void *>(atoms)); 0498 } 0499 0500 handled = true; 0501 } 0502 0503 if (reply) { 0504 free(reply); 0505 } 0506 } 0507 } else { 0508 if (ev->property == XCB_NONE) { // obsolete client 0509 ev->property = ev->target; 0510 } 0511 0512 handled = handle_selection(ev->target, ev->property, ev->requestor); 0513 } 0514 0515 xcb_selection_notify_event_t xev; 0516 xev.response_type = XCB_SELECTION_NOTIFY; 0517 xev.selection = ev->selection; 0518 xev.requestor = ev->requestor; 0519 xev.target = ev->target; 0520 xev.property = handled ? ev->property : XCB_NONE; 0521 0522 xcb_send_event(c, false, ev->requestor, 0, (const char *)&xev); 0523 } 0524 0525 bool KSelectionOwner::handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P) 0526 { 0527 if (!d) { 0528 return false; 0529 } 0530 if (target_P == Private::xa_timestamp) { 0531 // qDebug() << "Handling timestamp request"; 0532 xcb_change_property(d->connection, 0533 requestor_P, 0534 property_P, 0535 XCB_ATOM_INTEGER, 0536 32, 0537 XCB_PROP_MODE_REPLACE, 0538 1, 0539 reinterpret_cast<const void *>(&d->timestamp)); 0540 } else if (target_P == Private::xa_targets) { 0541 replyTargets(property_P, requestor_P); 0542 } else if (genericReply(target_P, property_P, requestor_P)) { 0543 // handled 0544 } else { 0545 return false; // unknown 0546 } 0547 0548 return true; 0549 } 0550 0551 void KSelectionOwner::replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P) 0552 { 0553 if (!d) { 0554 return; 0555 } 0556 xcb_atom_t atoms[3] = {Private::xa_multiple, Private::xa_timestamp, Private::xa_targets}; 0557 0558 xcb_change_property(d->connection, 0559 requestor_P, 0560 property_P, 0561 XCB_ATOM_ATOM, 0562 32, 0563 XCB_PROP_MODE_REPLACE, 0564 sizeof(atoms) / sizeof(atoms[0]), 0565 reinterpret_cast<const void *>(atoms)); 0566 0567 // qDebug() << "Handling targets request"; 0568 } 0569 0570 bool KSelectionOwner::genericReply(xcb_atom_t, xcb_atom_t, xcb_window_t) 0571 { 0572 return false; 0573 } 0574 0575 void KSelectionOwner::getAtoms() 0576 { 0577 if (!d) { 0578 return; 0579 } 0580 if (Private::manager_atom != XCB_NONE) { 0581 return; 0582 } 0583 0584 xcb_connection_t *c = d->connection; 0585 0586 struct { 0587 const char *name; 0588 xcb_atom_t *atom; 0589 } atoms[] = {{"MANAGER", &Private::manager_atom}, 0590 {"MULTIPLE", &Private::xa_multiple}, 0591 {"TARGETS", &Private::xa_targets}, 0592 {"TIMESTAMP", &Private::xa_timestamp}}; 0593 0594 const int count = sizeof(atoms) / sizeof(atoms[0]); 0595 xcb_intern_atom_cookie_t cookies[count]; 0596 0597 for (int i = 0; i < count; i++) { 0598 cookies[i] = xcb_intern_atom(c, false, strlen(atoms[i].name), atoms[i].name); 0599 } 0600 0601 for (int i = 0; i < count; i++) { 0602 if (xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, cookies[i], nullptr)) { 0603 *atoms[i].atom = reply->atom; 0604 free(reply); 0605 } 0606 } 0607 } 0608 0609 xcb_atom_t KSelectionOwner::Private::manager_atom = XCB_NONE; 0610 xcb_atom_t KSelectionOwner::Private::xa_multiple = XCB_NONE; 0611 xcb_atom_t KSelectionOwner::Private::xa_targets = XCB_NONE; 0612 xcb_atom_t KSelectionOwner::Private::xa_timestamp = XCB_NONE; 0613 0614 #include "moc_kselectionowner.cpp"