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 }