File indexing completed on 2024-05-19 05:49:20

0001 /*
0002  * Copyright 2014  Rohan Garg <rohan@kde.org>
0003  * Copyright 2021  Harald Sitter <sitter@kde.org>
0004  *
0005  * This program is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU General Public License as
0007  * published by the Free Software Foundation; either version 2 of
0008  * the License or (at your option) version 3 or any later version
0009  * accepted by the membership of KDE e.V. (or its successor approved
0010  * by the membership of KDE e.V.), which shall act as a proxy
0011  * defined in Section 14 of version 3 of the license.
0012  *
0013  * This program is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016  * GNU General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU General Public License
0019  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0020  *
0021  */
0022 
0023 #include "driverevent.h"
0024 #include <drivermanager_interface.h>
0025 
0026 #include <QDBusConnection>
0027 #include <QDebug>
0028 
0029 #include <QApt/Backend>
0030 
0031 #include <KToolInvocation>
0032 #include <KConfig>
0033 #include <KConfigGroup>
0034 
0035 DriverEvent::DriverEvent(QObject *parent)
0036     : Event(parent, "Driver")
0037     , m_showNotification(false)
0038     , m_aptBackendInitialized(false)
0039 {
0040     qDBusRegisterMetaType<DeviceList>();
0041 
0042     show();
0043 }
0044 
0045 void DriverEvent::show()
0046 {
0047     if (isHidden()) {
0048         return;
0049     }
0050 
0051     if (!m_aptBackendInitialized) {
0052         m_aptBackend = new QApt::Backend(this);
0053         if (!m_aptBackend->init()) {
0054             qWarning() << m_aptBackend->initErrorMessage();
0055             m_aptBackendInitialized = false;
0056         } else {
0057             m_aptBackendInitialized = true;
0058         }
0059     }
0060     if (m_aptBackendInitialized) {
0061         if(m_aptBackend->xapianIndexNeedsUpdate()) {
0062             m_aptBackend->updateXapianIndex();
0063             connect(m_aptBackend, SIGNAL(xapianUpdateFinished()), SLOT(updateFinished()));
0064         } else {
0065             updateFinished();
0066         }
0067     }
0068 }
0069 
0070 void DriverEvent::updateFinished()
0071 {
0072     if (!m_aptBackend->openXapianIndex()) {
0073         qDebug() << "Xapian update could not be opened, probably broken.";
0074         return;
0075     }
0076 
0077     m_manager = new OrgKubuntuDriverManagerInterface("org.kubuntu.DriverManager", "/DriverManager", QDBusConnection::sessionBus());
0078 
0079     // Force no dbus timeout.
0080     // There is exactly one method we use and it must always return. The only
0081     // situations where it does not return are those when something is terribly
0082     // wrong, however we cannot necessarily detect that with an async connection
0083     // either, so instead of asyncyfing the connection, we simply force no timeout.
0084     // TODO: this may be practical and equivalent to async connection
0085     //       but is really shitty and should be replaced by async connections.
0086     //       alas, those have their own unique problems with timer handling in
0087     //       python...
0088     m_manager->setTimeout(INT_MAX);
0089 
0090     QDBusPendingReply<DeviceList> reply = m_manager->devices();
0091     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
0092     connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
0093             this, SLOT(onDevicesReady(QDBusPendingCallWatcher*)));
0094 }
0095 
0096 void DriverEvent::onDevicesReady(QDBusPendingCallWatcher *call)
0097 {
0098     QDBusPendingReply<DeviceList> reply = *call;
0099 
0100     if (reply.isError()) {
0101         qDebug() << "got dbus error; abort";
0102         return;
0103     }
0104 
0105     DeviceList devices = reply.value();
0106     call->deleteLater(); // deep copy, delete caller
0107 
0108     qDebug() << "data " << devices;
0109 
0110     KConfig driver_manager("kcmdrivermanagerrc");
0111     KConfigGroup pciGroup( &driver_manager, "PCI" );
0112 
0113     foreach (Device device, devices) {
0114         if (pciGroup.readEntry(device.id) != QLatin1String("true")) {
0115             // Not seen before, check whether we have recommended drivers.
0116             for (int i = 0; i < device.drivers.length(); ++i) {
0117                 // Supposedly Driver is not a pod due to ctor, so it can't
0118                 // be fully used by QList :'<
0119                 // Manually iter instead.
0120                 Driver driver = device.drivers.at(i);
0121                 // If there is only one driver listed, we consider it an option.
0122                 // This works around an issue with virtualbox where the one and
0123                 // only driver is not marked as recommended. However, from
0124                 // a notification point of view it makes sense to notify about
0125                 // single-option drivers all the same as even if not considered
0126                 // recommended they probably should be looked at by the user.
0127                 if (driver.recommended || device.drivers.length() == 1) {
0128                     QApt::Package *package = m_aptBackend->package(driver.packageName);
0129                     if (package) {
0130                         if (!package->isInstalled()) {
0131                             m_showNotification = true;
0132                             break;
0133                         }
0134                     } else {
0135                         qDebug() << "package" << driver.packageName << "could not be found";
0136                     }
0137                 }
0138             }
0139         } else {
0140             qDebug() << device.id << "has already been processed by the KCM";
0141         }
0142     }
0143 
0144     if (m_showNotification) {
0145         QString icon = QString("hwinfo");
0146         QString text(i18nc("Notification when additional packages are required for activating proprietary hardware",
0147                            "Proprietary drivers might be required to enable additional features"));
0148         QStringList actions;
0149         actions << i18nc("Launches KDE Control Module to manage drivers", "Manage Drivers");
0150         actions << i18nc("Button to dismiss this notification once", "Ignore for now");
0151         actions << i18nc("Button to make this notification never show up again",
0152                          "Never show again");
0153         Event::show(icon, text, actions);
0154     }
0155 }
0156 
0157 void DriverEvent::run()
0158 {
0159     KToolInvocation::kdeinitExec("kcmshell5", QStringList() << "kcm_driver_manager");
0160     Event::run();
0161 }
0162