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"