|
| 1 | +/* |
| 2 | + * Registers as a embed container |
| 3 | + * Copyright (C) 2015 <[email protected]> David Edmundson |
| 4 | + * |
| 5 | + * This library is free software; you can redistribute it and/or |
| 6 | + * modify it under the terms of the GNU Lesser General Public |
| 7 | + * License as published by the Free Software Foundation; either |
| 8 | + * version 2.1 of the License, or (at your option) any later version. |
| 9 | + * |
| 10 | + * This library is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | + * Lesser General Public License for more details. |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU Lesser General Public |
| 16 | + * License along with this library; if not, write to the Free Software |
| 17 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 18 | + * |
| 19 | + */ |
| 20 | +#include "fdoselectionmanager.h" |
| 21 | + |
| 22 | +#include "debug.h" |
| 23 | + |
| 24 | +#include <QCoreApplication> |
| 25 | +#include <QHash> |
| 26 | +#include <QTimer> |
| 27 | + |
| 28 | +#include <QTextDocument> |
| 29 | +#include <QX11Info> |
| 30 | + |
| 31 | +#include <KWindowSystem> |
| 32 | +#include <KSelectionOwner> |
| 33 | + |
| 34 | +#include <xcb/xcb.h> |
| 35 | +#include <xcb/xcb_atom.h> |
| 36 | +#include <xcb/xcb_event.h> |
| 37 | +#include <xcb/composite.h> |
| 38 | +#include <xcb/damage.h> |
| 39 | + |
| 40 | +#include "xcbutils.h" |
| 41 | +#include "sniproxy.h" |
| 42 | + |
| 43 | +#define SYSTEM_TRAY_REQUEST_DOCK 0 |
| 44 | +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 |
| 45 | +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 |
| 46 | + |
| 47 | +FdoSelectionManager::FdoSelectionManager(): |
| 48 | + QObject(), |
| 49 | + m_selectionOwner(new KSelectionOwner(Xcb::atoms->selectionAtom, -1, this)) |
| 50 | +{ |
| 51 | + qCDebug(SNIPROXY) << "starting"; |
| 52 | + |
| 53 | + //load damage extension |
| 54 | + xcb_connection_t *c = QX11Info::connection(); |
| 55 | + xcb_prefetch_extension_data(c, &xcb_damage_id); |
| 56 | + const auto *reply = xcb_get_extension_data(c, &xcb_damage_id); |
| 57 | + if (reply->present) { |
| 58 | + m_damageEventBase = reply->first_event; |
| 59 | + xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); |
| 60 | + } else { |
| 61 | + //no XDamage means |
| 62 | + qCCritical(SNIPROXY) << "could not load damage extension. Quitting"; |
| 63 | + qApp->exit(-1); |
| 64 | + } |
| 65 | + |
| 66 | + qApp->installNativeEventFilter(this); |
| 67 | + |
| 68 | + m_selectionOwner->claim(false); |
| 69 | + connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership); |
| 70 | + connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership); |
| 71 | + connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership); |
| 72 | +} |
| 73 | + |
| 74 | +FdoSelectionManager::~FdoSelectionManager() |
| 75 | +{ |
| 76 | + qCDebug(SNIPROXY) << "closing"; |
| 77 | + m_selectionOwner->release(); |
| 78 | +} |
| 79 | + |
| 80 | +void FdoSelectionManager::addDamageWatch(xcb_window_t client) |
| 81 | +{ |
| 82 | + qCDebug(SNIPROXY) << "adding damage watch for " << client; |
| 83 | + |
| 84 | + xcb_connection_t *c = QX11Info::connection(); |
| 85 | + const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client); |
| 86 | + |
| 87 | + const auto damageId = xcb_generate_id(c); |
| 88 | + m_damageWatches[client] = damageId; |
| 89 | + xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); |
| 90 | + |
| 91 | + QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> attr(xcb_get_window_attributes_reply(c, attribsCookie, Q_NULLPTR)); |
| 92 | + uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY; |
| 93 | + if (!attr.isNull()) { |
| 94 | + events = events | attr->your_event_mask; |
| 95 | + } |
| 96 | + // the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem). |
| 97 | + // if we would remove the event mask again, other areas will break. |
| 98 | + xcb_change_window_attributes(c, client, XCB_CW_EVENT_MASK, &events); |
| 99 | + |
| 100 | +} |
| 101 | + |
| 102 | +bool FdoSelectionManager::nativeEventFilter(const QByteArray& eventType, void* message, long int* result) |
| 103 | +{ |
| 104 | + Q_UNUSED(result); |
| 105 | + |
| 106 | + if (eventType != "xcb_generic_event_t") { |
| 107 | + return false; |
| 108 | + } |
| 109 | + |
| 110 | + xcb_generic_event_t* ev = static_cast<xcb_generic_event_t *>(message); |
| 111 | + |
| 112 | + const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev); |
| 113 | + if (responseType == XCB_CLIENT_MESSAGE) { |
| 114 | + const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev); |
| 115 | + if (ce->type == Xcb::atoms->opcodeAtom) { |
| 116 | + switch (ce->data.data32[1]) { |
| 117 | + case SYSTEM_TRAY_REQUEST_DOCK: |
| 118 | + dock(ce->data.data32[2]); |
| 119 | + return true; |
| 120 | + } |
| 121 | + } |
| 122 | + } else if (responseType == XCB_UNMAP_NOTIFY) { |
| 123 | + const auto unmappedWId = reinterpret_cast<xcb_unmap_notify_event_t *>(ev)->window; |
| 124 | + if (m_proxies[unmappedWId]) { |
| 125 | + undock(unmappedWId); |
| 126 | + } |
| 127 | + } else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) { |
| 128 | + const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable; |
| 129 | + const auto sniProx = m_proxies[damagedWId]; |
| 130 | + |
| 131 | + Q_ASSERT(sniProx); |
| 132 | + |
| 133 | + if(sniProx) { |
| 134 | + sniProx->update(); |
| 135 | + xcb_damage_subtract(QX11Info::connection(), m_damageWatches[damagedWId], XCB_NONE, XCB_NONE); |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + return false; |
| 140 | +} |
| 141 | + |
| 142 | +void FdoSelectionManager::dock(xcb_window_t winId) |
| 143 | +{ |
| 144 | + qCDebug(SNIPROXY) << "trying to dock window " << winId; |
| 145 | + |
| 146 | + if (m_proxies.contains(winId)) { |
| 147 | + return; |
| 148 | + } |
| 149 | + |
| 150 | + addDamageWatch(winId); |
| 151 | + m_proxies[winId] = new SNIProxy(winId, this); |
| 152 | +} |
| 153 | + |
| 154 | +void FdoSelectionManager::undock(xcb_window_t winId) |
| 155 | +{ |
| 156 | + qCDebug(SNIPROXY) << "trying to undock window " << winId; |
| 157 | + |
| 158 | + if (!m_proxies.contains(winId)) { |
| 159 | + return; |
| 160 | + } |
| 161 | + m_proxies[winId]->deleteLater(); |
| 162 | + m_proxies.remove(winId); |
| 163 | +} |
| 164 | + |
| 165 | +void FdoSelectionManager::onClaimedOwnership() |
| 166 | +{ |
| 167 | + qCDebug(SNIPROXY) << "Manager selection claimed"; |
| 168 | +} |
| 169 | + |
| 170 | +void FdoSelectionManager::onFailedToClaimOwnership() |
| 171 | +{ |
| 172 | + qCWarning(SNIPROXY) << "failed to claim ownership of Systray Manager"; |
| 173 | + qApp->exit(-1); |
| 174 | +} |
| 175 | + |
| 176 | +void FdoSelectionManager::onLostOwnership() |
| 177 | +{ |
| 178 | + qCWarning(SNIPROXY) << "lost ownership of Systray Manager"; |
| 179 | + qApp->exit(-1); |
| 180 | +} |
| 181 | + |
| 182 | + |
0 commit comments