File indexing completed on 2024-05-19 05:37:50
0001 /* 0002 SPDX-FileCopyrightText: 2007 Menard Alexis <darktears31@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "hotplugengine.h" 0008 #include "hotplugservice.h" 0009 0010 #include <QDir> 0011 #include <QDirIterator> 0012 #include <QStandardPaths> 0013 #include <QTimer> 0014 0015 #include <KConfigGroup> 0016 #include <KDesktopFile> 0017 #include <KDirWatch> 0018 #include <KService> 0019 #include <Plasma5Support/DataContainer> 0020 #include <QDebug> 0021 0022 // solid specific includes 0023 #include <Solid/Device> 0024 #include <Solid/DeviceInterface> 0025 #include <Solid/DeviceNotifier> 0026 #include <Solid/OpticalDisc> 0027 #include <Solid/StorageDrive> 0028 #include <Solid/StorageVolume> 0029 0030 // #define HOTPLUGENGINE_TIMING 0031 0032 HotplugEngine::HotplugEngine(QObject *parent) 0033 : Plasma5Support::DataEngine(parent) 0034 , m_dirWatch(new KDirWatch(this)) 0035 { 0036 const QStringList folders = 0037 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("solid/actions"), QStandardPaths::LocateDirectory); 0038 0039 for (const QString &folder : folders) { 0040 m_dirWatch->addDir(folder, KDirWatch::WatchFiles); 0041 } 0042 connect(m_dirWatch, &KDirWatch::created, this, &HotplugEngine::updatePredicates); 0043 connect(m_dirWatch, &KDirWatch::deleted, this, &HotplugEngine::updatePredicates); 0044 connect(m_dirWatch, &KDirWatch::dirty, this, &HotplugEngine::updatePredicates); 0045 init(); 0046 } 0047 0048 HotplugEngine::~HotplugEngine() 0049 { 0050 } 0051 0052 void HotplugEngine::init() 0053 { 0054 findPredicates(); 0055 0056 Solid::Predicate p(Solid::DeviceInterface::StorageAccess); 0057 p |= Solid::Predicate(Solid::DeviceInterface::StorageDrive); 0058 p |= Solid::Predicate(Solid::DeviceInterface::StorageVolume); 0059 p |= Solid::Predicate(Solid::DeviceInterface::OpticalDrive); 0060 p |= Solid::Predicate(Solid::DeviceInterface::OpticalDisc); 0061 p |= Solid::Predicate(Solid::DeviceInterface::PortableMediaPlayer); 0062 p |= Solid::Predicate(Solid::DeviceInterface::Camera); 0063 const QList<Solid::Device> devices = Solid::Device::listFromQuery(p); 0064 for (const Solid::Device &dev : devices) { 0065 m_startList.insert(dev.udi(), dev); 0066 } 0067 0068 connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceAdded, this, &HotplugEngine::onDeviceAdded); 0069 connect(Solid::DeviceNotifier::instance(), &Solid::DeviceNotifier::deviceRemoved, this, &HotplugEngine::onDeviceRemoved); 0070 0071 m_encryptedPredicate = Solid::Predicate(QStringLiteral("StorageVolume"), QStringLiteral("usage"), "Encrypted"); 0072 0073 processNextStartupDevice(); 0074 } 0075 0076 Plasma5Support::Service *HotplugEngine::serviceForSource(const QString &source) 0077 { 0078 return new HotplugService(this, source); 0079 } 0080 0081 void HotplugEngine::processNextStartupDevice() 0082 { 0083 if (!m_startList.isEmpty()) { 0084 QHash<QString, Solid::Device>::iterator it = m_startList.begin(); 0085 // Solid::Device dev = const_cast<Solid::Device &>(m_startList.takeFirst()); 0086 handleDeviceAdded(it.value(), false); 0087 m_startList.erase(it); 0088 } 0089 0090 if (m_startList.isEmpty()) { 0091 m_predicates.clear(); 0092 } else { 0093 QTimer::singleShot(0, this, &HotplugEngine::processNextStartupDevice); 0094 } 0095 } 0096 0097 void HotplugEngine::findPredicates() 0098 { 0099 m_predicates.clear(); 0100 QStringList files; 0101 const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("solid/actions"), QStandardPaths::LocateDirectory); 0102 for (const QString &dir : dirs) { 0103 QDirIterator it(dir, QStringList() << QStringLiteral("*.desktop")); 0104 while (it.hasNext()) { 0105 files.prepend(it.next()); 0106 } 0107 } 0108 // qDebug() << files; 0109 for (const QString &path : std::as_const(files)) { 0110 KDesktopFile cfg(path); 0111 const QString string_predicate = cfg.desktopGroup().readEntry("X-KDE-Solid-Predicate"); 0112 // qDebug() << path << string_predicate; 0113 m_predicates.insert(QUrl(path).fileName(), Solid::Predicate::fromString(string_predicate)); 0114 } 0115 0116 if (m_predicates.isEmpty()) { 0117 m_predicates.insert(QString(), Solid::Predicate::fromString(QString())); 0118 } 0119 } 0120 0121 void HotplugEngine::updatePredicates(const QString &path) 0122 { 0123 Q_UNUSED(path) 0124 0125 findPredicates(); 0126 0127 QHashIterator<QString, Solid::Device> it(m_devices); 0128 while (it.hasNext()) { 0129 it.next(); 0130 Solid::Device device(it.value()); 0131 QString udi(it.key()); 0132 0133 const QStringList predicates = predicatesForDevice(device); 0134 if (!predicates.isEmpty()) { 0135 if (sources().contains(udi)) { 0136 Plasma5Support::DataEngine::Data data; 0137 data.insert(QStringLiteral("predicateFiles"), predicates); 0138 data.insert(QStringLiteral("actions"), actionsForPredicates(predicates)); 0139 setData(udi, data); 0140 } else { 0141 handleDeviceAdded(device, false); 0142 } 0143 } else if (!m_encryptedPredicate.matches(device) && sources().contains(udi)) { 0144 removeSource(udi); 0145 } 0146 } 0147 } 0148 0149 QStringList HotplugEngine::predicatesForDevice(Solid::Device &device) const 0150 { 0151 QStringList interestingDesktopFiles; 0152 // search in all desktop configuration file if the device inserted is a correct device 0153 QHashIterator<QString, Solid::Predicate> it(m_predicates); 0154 // qDebug() << "=================" << udi; 0155 while (it.hasNext()) { 0156 it.next(); 0157 if (it.value().matches(device)) { 0158 // qDebug() << " hit" << it.key(); 0159 interestingDesktopFiles << it.key(); 0160 } 0161 } 0162 0163 return interestingDesktopFiles; 0164 } 0165 0166 QVariantList HotplugEngine::actionsForPredicates(const QStringList &predicates) const 0167 { 0168 QVariantList actions; 0169 actions.reserve(predicates.count()); 0170 0171 for (const QString &desktop : predicates) { 0172 const QString actionUrl = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "solid/actions/" + desktop); 0173 auto services = KService(actionUrl).actions(); 0174 if (!services.isEmpty()) { 0175 Plasma5Support::DataEngine::Data action; 0176 action.insert(QStringLiteral("predicate"), desktop); 0177 action.insert(QStringLiteral("text"), services[0].text()); 0178 action.insert(QStringLiteral("icon"), services[0].icon()); 0179 actions << action; 0180 } 0181 } 0182 0183 return actions; 0184 } 0185 0186 void HotplugEngine::onDeviceAdded(const QString &udi) 0187 { 0188 Solid::Device device(udi); 0189 handleDeviceAdded(device); 0190 } 0191 0192 void HotplugEngine::handleDeviceAdded(Solid::Device &device, bool added) 0193 { 0194 // qDebug() << "adding" << device.udi(); 0195 #ifdef HOTPLUGENGINE_TIMING 0196 QTime t; 0197 t.start(); 0198 #endif 0199 // Skip things we know we don't care about 0200 if (device.is<Solid::StorageDrive>()) { 0201 Solid::StorageDrive *drive = device.as<Solid::StorageDrive>(); 0202 if (!drive->isHotpluggable()) { 0203 #ifdef HOTPLUGENGINE_TIMING 0204 qDebug() << "storage, but not pluggable, returning" << t.restart(); 0205 #endif 0206 return; 0207 } 0208 } else if (device.is<Solid::StorageVolume>()) { 0209 Solid::StorageVolume *volume = device.as<Solid::StorageVolume>(); 0210 Solid::StorageVolume::UsageType type = volume->usage(); 0211 if ((type == Solid::StorageVolume::Unused || type == Solid::StorageVolume::PartitionTable) && !device.is<Solid::OpticalDisc>()) { 0212 #ifdef HOTPLUGENGINE_TIMING 0213 qDebug() << "storage volume, but not of interest" << t.restart(); 0214 #endif 0215 return; 0216 } 0217 } 0218 0219 m_devices.insert(device.udi(), device); 0220 0221 if (m_predicates.isEmpty()) { 0222 findPredicates(); 0223 } 0224 0225 const QStringList interestingDesktopFiles = predicatesForDevice(device); 0226 const bool isEncryptedContainer = m_encryptedPredicate.matches(device); 0227 0228 if (!interestingDesktopFiles.isEmpty() || isEncryptedContainer) { 0229 // qDebug() << device.product(); 0230 // qDebug() << device.vendor(); 0231 // qDebug() << "number of interesting desktop file : " << interestingDesktopFiles.size(); 0232 Plasma5Support::DataEngine::Data data; 0233 data.insert(QStringLiteral("added"), added); 0234 data.insert(QStringLiteral("udi"), device.udi()); 0235 0236 if (!device.description().isEmpty()) { 0237 data.insert(QStringLiteral("text"), device.description()); 0238 } else { 0239 data.insert(QStringLiteral("text"), QString(device.vendor() + QLatin1Char(' ') + device.product())); 0240 } 0241 data.insert(QStringLiteral("icon"), device.icon()); 0242 data.insert(QStringLiteral("emblems"), device.emblems()); 0243 data.insert(QStringLiteral("predicateFiles"), interestingDesktopFiles); 0244 data.insert(QStringLiteral("actions"), actionsForPredicates(interestingDesktopFiles)); 0245 0246 data.insert(QStringLiteral("isEncryptedContainer"), isEncryptedContainer); 0247 0248 setData(device.udi(), data); 0249 // qDebug() << "add hardware solid : " << udi; 0250 } 0251 0252 #ifdef HOTPLUGENGINE_TIMING 0253 qDebug() << "total time" << t.restart(); 0254 #endif 0255 } 0256 0257 void HotplugEngine::onDeviceRemoved(const QString &udi) 0258 { 0259 // qDebug() << "remove hardware:" << udi; 0260 0261 if (m_startList.contains(udi)) { 0262 m_startList.remove(udi); 0263 return; 0264 } 0265 0266 m_devices.remove(udi); 0267 removeSource(udi); 0268 } 0269 0270 K_PLUGIN_CLASS_WITH_JSON(HotplugEngine, "plasma-dataengine-hotplug.json") 0271 0272 #include "hotplugengine.moc"