File indexing completed on 2024-05-12 05:29:23
0001 /* 0002 * SPDX-FileCopyrightText: 2010-2016 Ivan Cukic <ivan.cukic@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 // Self 0008 #include "Resources.h" 0009 #include "Resources_p.h" 0010 0011 // Qt 0012 #include <QDBusConnection> 0013 #include <QMutex> 0014 #include <QMutexLocker> 0015 #include <QThread> 0016 0017 // KDE 0018 #include <KX11Extras> 0019 0020 // Utils 0021 #include <utils/d_ptr_implementation.h> 0022 #include <utils/remove_if.h> 0023 0024 // System 0025 #include <time.h> 0026 0027 // Local 0028 #include "common/dbus/common.h" 0029 #include "resourcesadaptor.h" 0030 0031 Resources::Private::Private(Resources *parent) 0032 : QThread(parent) 0033 , focussedWindow(0) 0034 , q(parent) 0035 { 0036 } 0037 0038 Resources::Private::~Private() 0039 { 0040 requestInterruption(); 0041 wait(1500); // Enough time for the sleep(1) + processing in run() 0042 } 0043 0044 namespace 0045 { 0046 EventList events; 0047 QMutex events_mutex; 0048 } 0049 0050 void Resources::Private::run() 0051 { 0052 while (!isInterruptionRequested()) { 0053 // initial delay before processing the events 0054 sleep(1); 0055 0056 EventList currentEvents; 0057 0058 { 0059 QMutexLocker locker(&events_mutex); 0060 0061 if (events.count() == 0) { 0062 return; 0063 } 0064 0065 std::swap(currentEvents, events); 0066 } 0067 0068 Q_EMIT q->ProcessedResourceEvents(currentEvents); 0069 } 0070 } 0071 0072 void Resources::Private::insertEvent(const Event &newEvent) 0073 { 0074 if (lastEvent == newEvent) { 0075 return; 0076 } 0077 0078 lastEvent = newEvent; 0079 0080 { 0081 QMutexLocker locker(&events_mutex); 0082 events << newEvent; 0083 } 0084 0085 Q_EMIT q->RegisteredResourceEvent(newEvent); 0086 } 0087 0088 void Resources::Private::addEvent(const QString &application, WId wid, const QString &uri, int type) 0089 { 0090 Event newEvent(application, wid, uri, type); 0091 addEvent(newEvent); 0092 } 0093 0094 void Resources::Private::addEvent(const Event &newEvent) 0095 { 0096 // And now, for something completely delayed 0097 { 0098 QMutexLocker locker(&events_mutex); 0099 0100 // Deleting previously registered Accessed events if 0101 // the current one has the same application and uri 0102 if (newEvent.type != Event::Accessed) { 0103 kamd::utils::remove_if(events, [&newEvent](const Event &event) -> bool { 0104 return event.application == newEvent.application && event.uri == newEvent.uri; 0105 }); 0106 } 0107 } 0108 0109 // Process the windowing 0110 // Essentially, this is the brain of SLC. We need to track the 0111 // window focus changes to be able to generate the potential 0112 // missing events like FocussedOut before Closed and similar. 0113 // So, there is no point in having the same logic in SLC plugin 0114 // as well. 0115 0116 if (newEvent.wid != 0) { 0117 WindowData &window = windows[newEvent.wid]; 0118 const QString &uri = newEvent.uri; 0119 0120 window.application = newEvent.application; 0121 0122 switch (newEvent.type) { 0123 case Event::Opened: 0124 insertEvent(newEvent); 0125 0126 if (window.focussedResource.isEmpty()) { 0127 // This window haven't had anything focused, 0128 // assuming the new document is focused 0129 0130 window.focussedResource = newEvent.uri; 0131 insertEvent(newEvent.deriveWithType(Event::FocussedIn)); 0132 } 0133 0134 break; 0135 0136 case Event::FocussedIn: 0137 0138 if (!window.resources.contains(uri)) { 0139 // This window did not contain this resource before, 0140 // sending Opened event 0141 0142 insertEvent(newEvent.deriveWithType(Event::Opened)); 0143 } 0144 0145 window.focussedResource = newEvent.uri; 0146 insertEvent(newEvent); 0147 0148 break; 0149 0150 case Event::Closed: 0151 0152 if (window.focussedResource == uri) { 0153 // If we are closing a document that is in focus, 0154 // release focus first 0155 0156 insertEvent(newEvent.deriveWithType(Event::FocussedOut)); 0157 window.focussedResource.clear(); 0158 } 0159 0160 insertEvent(newEvent); 0161 0162 break; 0163 0164 case Event::FocussedOut: 0165 0166 if (window.focussedResource == uri) { 0167 window.focussedResource.clear(); 0168 } 0169 0170 insertEvent(newEvent); 0171 0172 break; 0173 0174 default: 0175 insertEvent(newEvent); 0176 break; 0177 } 0178 0179 } else { 0180 // If we haven't got a window, just pass the event on, 0181 // but only if it is not a focus event 0182 if (newEvent.type != Event::FocussedIn && newEvent.type != Event::FocussedOut) { 0183 insertEvent(newEvent); 0184 } 0185 } 0186 0187 start(); 0188 } 0189 0190 void Resources::Private::windowClosed(WId windowId) 0191 { 0192 // Testing whether the window is a registered one 0193 0194 if (!windows.contains(windowId)) { 0195 return; 0196 } 0197 0198 if (focussedWindow == windowId) { 0199 focussedWindow = 0; 0200 } 0201 0202 // Closing all the resources that the window registered 0203 0204 for (const QString &uri : std::as_const(windows[windowId].resources)) { 0205 q->RegisterResourceEvent(windows[windowId].application, windowId, uri, Event::Closed); 0206 } 0207 0208 windows.remove(windowId); 0209 } 0210 0211 void Resources::Private::activeWindowChanged(WId windowId) 0212 { 0213 // If the focused window has changed, we need to create a 0214 // FocussedOut event for the resource it contains, 0215 // and FocussedIn for the resource of the new active window. 0216 // The windows can do this manually, but if they are 0217 // SDI, we can do it on our own. 0218 0219 if (windowId == focussedWindow) { 0220 return; 0221 } 0222 0223 if (windows.contains(focussedWindow)) { 0224 const WindowData &data = windows[focussedWindow]; 0225 0226 if (!data.focussedResource.isEmpty()) { 0227 insertEvent(Event(data.application, focussedWindow, data.focussedResource, Event::FocussedOut)); 0228 } 0229 } 0230 0231 focussedWindow = windowId; 0232 0233 if (windows.contains(focussedWindow)) { 0234 const WindowData &data = windows[focussedWindow]; 0235 0236 if (!data.focussedResource.isEmpty()) { 0237 insertEvent(Event(data.application, windowId, data.focussedResource, Event::FocussedIn)); 0238 } 0239 } 0240 } 0241 0242 Resources::Resources(QObject *parent) 0243 : Module(QStringLiteral("resources"), parent) 0244 , d(this) 0245 { 0246 qRegisterMetaType<Event>("Event"); 0247 qRegisterMetaType<EventList>("EventList"); 0248 qRegisterMetaType<WId>("WId"); 0249 0250 new ResourcesAdaptor(this); 0251 QDBusConnection::sessionBus().registerObject(KAMD_DBUS_OBJECT_PATH("Resources"), this); 0252 0253 connect(KX11Extras::self(), &KX11Extras::windowRemoved, d.operator->(), &Resources::Private::windowClosed); 0254 connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, d.operator->(), &Resources::Private::activeWindowChanged); 0255 } 0256 0257 Resources::~Resources() 0258 { 0259 } 0260 0261 void Resources::RegisterResourceEvent(const QString &application, uint _windowId, const QString &uri, uint event) 0262 { 0263 if (event > Event::LastEventType || uri.isEmpty() || application.isEmpty()) { 0264 return; 0265 } 0266 0267 WId windowId = (WId)_windowId; 0268 0269 d->addEvent(application, windowId, uri, (Event::Type)event); 0270 } 0271 0272 void Resources::RegisterResourceMimetype(const QString &uri, const QString &mimetype) 0273 { 0274 if (!mimetype.isEmpty()) { 0275 return; 0276 } 0277 0278 Q_EMIT RegisteredResourceMimetype(uri, mimetype); 0279 } 0280 0281 void Resources::RegisterResourceTitle(const QString &uri, const QString &title) 0282 { 0283 // A dirty sanity check for the title 0284 if (title.length() < 3) { 0285 return; 0286 } 0287 0288 Q_EMIT RegisteredResourceTitle(uri, title); 0289 } 0290 0291 #include "moc_Resources_p.cpp" 0292 0293 #include "moc_Resources.cpp"