Plasma5Support

hotplugengine.cpp
1/*
2 SPDX-FileCopyrightText: 2007 Menard Alexis <darktears31@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "hotplugengine.h"
8#include "hotplugservice.h"
9
10#include <QDir>
11#include <QDirIterator>
12#include <QStandardPaths>
13#include <QTimer>
14
15#include <KConfigGroup>
16#include <KDesktopFile>
17#include <KDirWatch>
18#include <KService>
19#include <Plasma5Support/DataContainer>
20#include <QDebug>
21
22// solid specific includes
23#include <Solid/Device>
24#include <Solid/DeviceInterface>
25#include <Solid/DeviceNotifier>
26#include <Solid/OpticalDisc>
27#include <Solid/StorageDrive>
28#include <Solid/StorageVolume>
29
30// #define HOTPLUGENGINE_TIMING
31
32HotplugEngine::HotplugEngine(QObject *parent)
33 : Plasma5Support::DataEngine(parent)
34 , m_dirWatch(new KDirWatch(this))
35{
36 const QStringList folders =
38
39 for (const QString &folder : folders) {
40 m_dirWatch->addDir(folder, KDirWatch::WatchFiles);
41 }
42 connect(m_dirWatch, &KDirWatch::created, this, &HotplugEngine::updatePredicates);
43 connect(m_dirWatch, &KDirWatch::deleted, this, &HotplugEngine::updatePredicates);
44 connect(m_dirWatch, &KDirWatch::dirty, this, &HotplugEngine::updatePredicates);
45 init();
46}
47
48HotplugEngine::~HotplugEngine()
49{
50}
51
52void HotplugEngine::init()
53{
54 findPredicates();
55
56 Solid::Predicate p(Solid::DeviceInterface::StorageAccess);
57 p |= Solid::Predicate(Solid::DeviceInterface::StorageDrive);
58 p |= Solid::Predicate(Solid::DeviceInterface::StorageVolume);
59 p |= Solid::Predicate(Solid::DeviceInterface::OpticalDrive);
60 p |= Solid::Predicate(Solid::DeviceInterface::OpticalDisc);
61 p |= Solid::Predicate(Solid::DeviceInterface::PortableMediaPlayer);
62 p |= Solid::Predicate(Solid::DeviceInterface::Camera);
64 for (const Solid::Device &dev : devices) {
65 m_startList.insert(dev.udi(), dev);
66 }
67
68 connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, this, &HotplugEngine::onDeviceAdded);
69 connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, this, &HotplugEngine::onDeviceRemoved);
70
71 m_encryptedPredicate = Solid::Predicate(QStringLiteral("StorageVolume"), QStringLiteral("usage"), "Encrypted");
72
73 processNextStartupDevice();
74}
75
77{
78 return new HotplugService(this, source);
79}
80
81void HotplugEngine::processNextStartupDevice()
82{
83 if (!m_startList.isEmpty()) {
85 // Solid::Device dev = const_cast<Solid::Device &>(m_startList.takeFirst());
86 handleDeviceAdded(it.value(), false);
87 m_startList.erase(it);
88 }
89
90 if (m_startList.isEmpty()) {
91 m_predicates.clear();
92 } else {
93 QTimer::singleShot(0, this, &HotplugEngine::processNextStartupDevice);
94 }
95}
96
97void HotplugEngine::findPredicates()
98{
99 m_predicates.clear();
100 QStringList files;
102 for (const QString &dir : dirs) {
103 QDirIterator it(dir, QStringList() << QStringLiteral("*.desktop"));
104 while (it.hasNext()) {
105 files.prepend(it.next());
106 }
107 }
108 // qDebug() << files;
109 for (const QString &path : std::as_const(files)) {
110 KDesktopFile cfg(path);
111 const QString string_predicate = cfg.desktopGroup().readEntry("X-KDE-Solid-Predicate");
112 // qDebug() << path << string_predicate;
113 m_predicates.insert(QUrl(path).fileName(), Solid::Predicate::fromString(string_predicate));
114 }
115
116 if (m_predicates.isEmpty()) {
118 }
119}
120
121void HotplugEngine::updatePredicates(const QString &path)
122{
123 Q_UNUSED(path)
124
125 findPredicates();
126
128 while (it.hasNext()) {
129 it.next();
130 Solid::Device device(it.value());
131 QString udi(it.key());
132
133 const QStringList predicates = predicatesForDevice(device);
134 if (!predicates.isEmpty()) {
135 if (sources().contains(udi)) {
137 data.insert(QStringLiteral("predicateFiles"), predicates);
138 data.insert(QStringLiteral("actions"), actionsForPredicates(predicates));
139 setData(udi, data);
140 } else {
141 handleDeviceAdded(device, false);
142 }
143 } else if (!m_encryptedPredicate.matches(device) && sources().contains(udi)) {
144 removeSource(udi);
145 }
146 }
147}
148
149QStringList HotplugEngine::predicatesForDevice(Solid::Device &device) const
150{
151 QStringList interestingDesktopFiles;
152 // search in all desktop configuration file if the device inserted is a correct device
154 // qDebug() << "=================" << udi;
155 while (it.hasNext()) {
156 it.next();
157 if (it.value().matches(device)) {
158 // qDebug() << " hit" << it.key();
159 interestingDesktopFiles << it.key();
160 }
161 }
162
163 return interestingDesktopFiles;
164}
165
166QVariantList HotplugEngine::actionsForPredicates(const QStringList &predicates) const
167{
168 QVariantList actions;
169 actions.reserve(predicates.count());
170
171 for (const QString &desktop : predicates) {
172 const QString actionUrl = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "solid/actions/" + desktop);
173 auto services = KService(actionUrl).actions();
174 if (!services.isEmpty()) {
176 action.insert(QStringLiteral("predicate"), desktop);
177 action.insert(QStringLiteral("text"), services[0].text());
178 action.insert(QStringLiteral("icon"), services[0].icon());
179 actions << action;
180 }
181 }
182
183 return actions;
184}
185
186void HotplugEngine::onDeviceAdded(const QString &udi)
187{
188 Solid::Device device(udi);
189 handleDeviceAdded(device);
190}
191
192void HotplugEngine::handleDeviceAdded(Solid::Device &device, bool added)
193{
194 // qDebug() << "adding" << device.udi();
195#ifdef HOTPLUGENGINE_TIMING
196 QTime t;
197 t.start();
198#endif
199 // Skip things we know we don't care about
200 if (device.is<Solid::StorageDrive>()) {
201 Solid::StorageDrive *drive = device.as<Solid::StorageDrive>();
202 if (!drive->isHotpluggable()) {
203#ifdef HOTPLUGENGINE_TIMING
204 qDebug() << "storage, but not pluggable, returning" << t.restart();
205#endif
206 return;
207 }
208 } else if (device.is<Solid::StorageVolume>()) {
211 if ((type == Solid::StorageVolume::Unused || type == Solid::StorageVolume::PartitionTable) && !device.is<Solid::OpticalDisc>()) {
212#ifdef HOTPLUGENGINE_TIMING
213 qDebug() << "storage volume, but not of interest" << t.restart();
214#endif
215 return;
216 }
217 }
218
219 m_devices.insert(device.udi(), device);
220
221 if (m_predicates.isEmpty()) {
222 findPredicates();
223 }
224
225 const QStringList interestingDesktopFiles = predicatesForDevice(device);
226 const bool isEncryptedContainer = m_encryptedPredicate.matches(device);
227
228 if (!interestingDesktopFiles.isEmpty() || isEncryptedContainer) {
229 // qDebug() << device.product();
230 // qDebug() << device.vendor();
231 // qDebug() << "number of interesting desktop file : " << interestingDesktopFiles.size();
233 data.insert(QStringLiteral("added"), added);
234 data.insert(QStringLiteral("udi"), device.udi());
235
236 if (!device.description().isEmpty()) {
237 data.insert(QStringLiteral("text"), device.description());
238 } else {
239 data.insert(QStringLiteral("text"), QString(device.vendor() + u' ' + device.product()));
240 }
241 data.insert(QStringLiteral("icon"), device.icon());
242 data.insert(QStringLiteral("emblems"), device.emblems());
243 data.insert(QStringLiteral("predicateFiles"), interestingDesktopFiles);
244 data.insert(QStringLiteral("actions"), actionsForPredicates(interestingDesktopFiles));
245
246 data.insert(QStringLiteral("isEncryptedContainer"), isEncryptedContainer);
247
248 setData(device.udi(), data);
249 // qDebug() << "add hardware solid : " << udi;
250 }
251
252#ifdef HOTPLUGENGINE_TIMING
253 qDebug() << "total time" << t.restart();
254#endif
255}
256
257void HotplugEngine::onDeviceRemoved(const QString &udi)
258{
259 // qDebug() << "remove hardware:" << udi;
260
261 if (m_startList.contains(udi)) {
262 m_startList.remove(udi);
263 return;
264 }
265
266 m_devices.remove(udi);
267 removeSource(udi);
268}
269
270K_PLUGIN_CLASS_WITH_JSON(HotplugEngine, "plasma-dataengine-hotplug.json")
271
272#include "hotplugengine.moc"
This class is connected with solid, filter devices and provide signal with source for applet in Plasm...
Plasma5Support::Service * serviceForSource(const QString &source) override
void deleted(const QString &path)
void dirty(const QString &path)
void created(const QString &path)
#define K_PLUGIN_CLASS_WITH_JSON(classname, jsonFile)
QList< KServiceAction > actions() const
void removeSource(const QString &source)
Removes a data source.
virtual QStringList sources() const
void setData(const QString &source, const QVariant &value)
Sets a value for a data source.
This class provides a generic API for write access to settings or services.
Definition service.h:78
void deviceRemoved(const QString &udi)
void deviceAdded(const QString &udi)
QString description() const
QStringList emblems() const
QString udi() const
QString icon() const
QString vendor() const
bool is() const
static QList< Device > listFromQuery(const Predicate &predicate, const QString &parentUdi=QString())
QString product() const
DevIface * as()
bool matches(const Device &device) const
static Predicate fromString(const QString &predicate)
bool isHotpluggable() const
Type type(const QSqlDatabase &db)
Namespace for everything in libplasma.
Definition datamodel.cpp:15
QCA_EXPORT void init()
iterator begin()
void clear()
bool contains(const Key &key) const const
iterator erase(const_iterator pos)
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
bool remove(const Key &key)
qsizetype count() const const
bool isEmpty() const const
void prepend(parameter_type value)
iterator insert(const Key &key, const T &value)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:57 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.