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