File indexing completed on 2024-05-05 12:24:28
0001 /* 0002 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0003 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "kselectionwatcher.h" 0008 0009 #include "kwindowsystem.h" 0010 #include <config-kwindowsystem.h> 0011 0012 #include <QAbstractNativeEventFilter> 0013 #include <QCoreApplication> 0014 0015 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0016 #include <private/qtx11extras_p.h> 0017 #else 0018 #include <QX11Info> 0019 #endif 0020 0021 static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection) 0022 { 0023 xcb_window_t owner = XCB_NONE; 0024 xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr); 0025 0026 if (reply) { 0027 owner = reply->owner; 0028 free(reply); 0029 } 0030 0031 return owner; 0032 } 0033 0034 static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name) 0035 { 0036 xcb_atom_t atom = XCB_NONE; 0037 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr); 0038 0039 if (reply) { 0040 atom = reply->atom; 0041 free(reply); 0042 } 0043 0044 return atom; 0045 } 0046 0047 //******************************************* 0048 // KSelectionWatcher 0049 //******************************************* 0050 0051 class Q_DECL_HIDDEN KSelectionWatcher::Private : public QAbstractNativeEventFilter 0052 { 0053 public: 0054 Private(KSelectionWatcher *watcher_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root) 0055 : connection(c) 0056 , root(root) 0057 , selection(selection_P) 0058 , selection_owner(XCB_NONE) 0059 , watcher(watcher_P) 0060 { 0061 QCoreApplication::instance()->installNativeEventFilter(this); 0062 } 0063 0064 xcb_connection_t *connection; 0065 xcb_window_t root; 0066 const xcb_atom_t selection; 0067 xcb_window_t selection_owner; 0068 static xcb_atom_t manager_atom; 0069 0070 static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P); 0071 static Private *create(KSelectionWatcher *watcher, const char *selection_P, int screen_P); 0072 static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root); 0073 static Private *create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root); 0074 0075 protected: 0076 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0077 bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override 0078 #else 0079 bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override 0080 #endif 0081 { 0082 if (eventType != "xcb_generic_event_t") { 0083 return false; 0084 } 0085 watcher->filterEvent(message); 0086 return false; 0087 } 0088 0089 private: 0090 KSelectionWatcher *watcher; 0091 }; 0092 0093 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P) 0094 { 0095 if (KWindowSystem::isPlatformX11()) { 0096 return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P)); 0097 } 0098 return nullptr; 0099 } 0100 0101 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root) 0102 { 0103 return new Private(watcher, selection_P, c, root); 0104 } 0105 0106 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, int screen_P) 0107 { 0108 if (KWindowSystem::isPlatformX11()) { 0109 return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P)); 0110 } 0111 return nullptr; 0112 } 0113 0114 KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root) 0115 { 0116 return new Private(watcher, intern_atom(c, selection_P), c, root); 0117 } 0118 0119 KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection_P, int screen_P, QObject *parent_P) 0120 : QObject(parent_P) 0121 , d(Private::create(this, selection_P, screen_P)) 0122 { 0123 init(); 0124 } 0125 0126 KSelectionWatcher::KSelectionWatcher(const char *selection_P, int screen_P, QObject *parent_P) 0127 : QObject(parent_P) 0128 , d(Private::create(this, selection_P, screen_P)) 0129 { 0130 init(); 0131 } 0132 0133 KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent) 0134 : QObject(parent) 0135 , d(Private::create(this, selection, c, root)) 0136 { 0137 init(); 0138 } 0139 0140 KSelectionWatcher::KSelectionWatcher(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent) 0141 : QObject(parent) 0142 , d(Private::create(this, selection, c, root)) 0143 { 0144 init(); 0145 } 0146 0147 KSelectionWatcher::~KSelectionWatcher() 0148 { 0149 delete d; 0150 } 0151 0152 void KSelectionWatcher::init() 0153 { 0154 if (!d) { 0155 return; 0156 } 0157 if (Private::manager_atom == XCB_NONE) { 0158 xcb_connection_t *c = d->connection; 0159 0160 xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(c, false, strlen("MANAGER"), "MANAGER"); 0161 xcb_get_window_attributes_cookie_t attr_cookie = xcb_get_window_attributes(c, d->root); 0162 0163 xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(c, atom_cookie, nullptr); 0164 Private::manager_atom = atom_reply->atom; 0165 free(atom_reply); 0166 0167 xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(c, attr_cookie, nullptr); 0168 uint32_t event_mask = attr->your_event_mask; 0169 free(attr); 0170 0171 if (!(event_mask & XCB_EVENT_MASK_STRUCTURE_NOTIFY)) { 0172 // We need XCB_EVENT_MASK_STRUCTURE_NORITY on the root window 0173 event_mask |= XCB_EVENT_MASK_STRUCTURE_NOTIFY; 0174 xcb_change_window_attributes(c, d->root, XCB_CW_EVENT_MASK, &event_mask); 0175 } 0176 } 0177 0178 owner(); // trigger reading of current selection status 0179 } 0180 0181 xcb_window_t KSelectionWatcher::owner() 0182 { 0183 if (!d) { 0184 return XCB_WINDOW_NONE; 0185 } 0186 xcb_connection_t *c = d->connection; 0187 0188 xcb_window_t current_owner = get_selection_owner(c, d->selection); 0189 if (current_owner == XCB_NONE) { 0190 return XCB_NONE; 0191 } 0192 0193 if (current_owner == d->selection_owner) { 0194 return d->selection_owner; 0195 } 0196 0197 // We have a new selection owner - select for structure notify events 0198 uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY; 0199 xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(c, current_owner, XCB_CW_EVENT_MASK, &mask); 0200 0201 // Verify that the owner didn't change again while selecting for events 0202 xcb_window_t new_owner = get_selection_owner(c, d->selection); 0203 xcb_generic_error_t *err = xcb_request_check(c, cookie); 0204 0205 if (!err && current_owner == new_owner) { 0206 d->selection_owner = current_owner; 0207 Q_EMIT newOwner(d->selection_owner); 0208 } else { 0209 // ### This doesn't look right - the selection could have an owner 0210 d->selection_owner = XCB_NONE; 0211 } 0212 0213 if (err) { 0214 free(err); 0215 } 0216 0217 return d->selection_owner; 0218 } 0219 0220 void KSelectionWatcher::filterEvent(void *ev_P) 0221 { 0222 if (!d) { 0223 return; 0224 } 0225 xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P); 0226 const uint response_type = event->response_type & ~0x80; 0227 if (response_type == XCB_CLIENT_MESSAGE) { 0228 xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event); 0229 0230 if (cm_event->type != Private::manager_atom || cm_event->data.data32[1] != d->selection) { 0231 return; 0232 } 0233 // owner() checks whether the owner changed and emits newOwner() 0234 owner(); 0235 return; 0236 } 0237 if (response_type == XCB_DESTROY_NOTIFY) { 0238 xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event); 0239 if (d->selection_owner == XCB_NONE || ev->window != d->selection_owner) { 0240 return; 0241 } 0242 0243 d->selection_owner = XCB_NONE; // in case the exactly same ID gets reused as the owner 0244 0245 if (owner() == XCB_NONE) { 0246 Q_EMIT lostOwner(); // it must be safe to delete 'this' in a slot 0247 } 0248 return; 0249 } 0250 } 0251 0252 xcb_atom_t KSelectionWatcher::Private::manager_atom = XCB_NONE; 0253 0254 #include "moc_kselectionwatcher.cpp"