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