From d9c05dbdbda5dfdabb259b08569d258327a35156 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Fri, 30 Jun 2023 23:49:02 -0600 Subject: [PATCH 01/19] Fix #280 --- applications/system-service/application.cpp | 2 +- applications/system-service/powerapi.h | 46 +++-- shared/liboxide/debug.h | 1 + shared/liboxide/liboxide.pro | 8 +- shared/liboxide/udev.cpp | 193 ++++++++++++++++++++ shared/liboxide/udev.h | 83 +++++++++ 6 files changed, 317 insertions(+), 16 deletions(-) create mode 100644 shared/liboxide/udev.cpp create mode 100644 shared/liboxide/udev.h diff --git a/applications/system-service/application.cpp b/applications/system-service/application.cpp index 6c3f55922..16d5804c7 100644 --- a/applications/system-service/application.cpp +++ b/applications/system-service/application.cpp @@ -515,7 +515,7 @@ void Application::showSplashScreen(){ EPFrameBuffer::sendUpdate(textRect, EPFrameBuffer::Grayscale, EPFrameBuffer::PartialUpdate, true); painter.end(); }); - qDebug() << "Waitng for screen to finish..."; + qDebug() << "Waiting for screen to finish..."; Oxide::Sentry::sentry_span(t, "wait", "Wait for screen finish updating", [](){ EPFrameBuffer::waitForLastUpdate(); }); diff --git a/applications/system-service/powerapi.h b/applications/system-service/powerapi.h index c3eabdf67..17947bbb8 100644 --- a/applications/system-service/powerapi.h +++ b/applications/system-service/powerapi.h @@ -2,6 +2,7 @@ #define BATTERYAPI_H #include +#include #include #include @@ -46,25 +47,45 @@ class PowerAPI : public APIBase { Oxide::Sentry::sentry_span(t, "update", "Update current state", [this]{ update(); }); - Oxide::Sentry::sentry_span(t, "timer", "Setup timer", [this]{ - timer = new QTimer(this); - timer->setSingleShot(false); - timer->setInterval(3 * 1000); // 3 seconds - timer->moveToThread(qApp->thread()); - connect(timer, &QTimer::timeout, this, QOverload<>::of(&PowerAPI::update)); - timer->start(); + Oxide::Sentry::sentry_span(t, "monitor", "Setup monitor", [this]{ + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ + udev = new UDev(this); + udev->addMonitor("power_supply", NULL); + connect(udev, &UDev::event, this, QOverload<>::of(&PowerAPI::update)); + udev->start(); + }else{ + timer = new QTimer(this); + timer->setSingleShot(false); + timer->setInterval(3 * 1000); // 3 seconds + timer->moveToThread(qApp->thread()); + connect(timer, &QTimer::timeout, this, QOverload<>::of(&PowerAPI::update)); + timer->start(); + } }); }); } ~PowerAPI(){ - qDebug() << "Killing timer"; - timer->stop(); - delete timer; + if(timer != nullptr){ + qDebug() << "Killing timer"; + timer->stop(); + delete timer; + } + if(udev != nullptr){ + qDebug() << "Killing UDev monitor"; + udev->stop(); + delete udev; + } } void setEnabled(bool enabled) override { if(enabled){ - timer->start(); + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ + udev->start(); + }else{ + timer->start(); + } + }else if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ + udev->stop(); }else{ timer->stop(); } @@ -152,7 +173,8 @@ class PowerAPI : public APIBase { void chargerWarning(); private: - QTimer* timer; + QTimer* timer = nullptr; + UDev* udev = nullptr; int m_state = Normal; int m_batteryState = BatteryUnknown; int m_batteryLevel = 0; diff --git a/shared/liboxide/debug.h b/shared/liboxide/debug.h index 84644e83c..873346fa4 100644 --- a/shared/liboxide/debug.h +++ b/shared/liboxide/debug.h @@ -13,6 +13,7 @@ * \brief Log a debug message if compiled with DEBUG mode, and debugging is enabled * \param msg Debug message to log */ +#define DEBUG #ifdef DEBUG #define O_DEBUG(msg) if(Oxide::debugEnabled()){ qDebug() << msg; } #else diff --git a/shared/liboxide/liboxide.pro b/shared/liboxide/liboxide.pro index d5526977f..8bee136c1 100644 --- a/shared/liboxide/liboxide.pro +++ b/shared/liboxide/liboxide.pro @@ -23,7 +23,8 @@ SOURCES += \ settingsfile.cpp \ slothandler.cpp \ sysobject.cpp \ - signalhandler.cpp + signalhandler.cpp \ + udev.cpp HEADERS += \ applications.h \ @@ -40,7 +41,8 @@ HEADERS += \ settingsfile.h \ slothandler.h \ sysobject.h \ - signalhandler.h + signalhandler.h \ + udev.h PRECOMPILED_HEADER = \ liboxide_stable.h @@ -59,7 +61,7 @@ DBUS_INTERFACES += \ ../../interfaces/notificationapi.xml \ ../../interfaces/notification.xml -LIBS += -lsystemd +LIBS += -lsystemd -ludev liboxide_liboxide_h.target = include/liboxide/liboxide.h liboxide_liboxide_h.commands = \ diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp new file mode 100644 index 000000000..d4d09d6be --- /dev/null +++ b/shared/liboxide/udev.cpp @@ -0,0 +1,193 @@ +#include "udev.h" +#include "debug.h" + +#include +#include + +#include +#include + +namespace Oxide { + + UDev::UDev(QObject *parent) : QObject(), _thread(this){ + qRegisterMetaType("UDev::Device"); + udevLib = udev_new(); + connect(parent, &QObject::destroyed, this, &QObject::deleteLater); + connect(&_thread, &QThread::started, this, &UDev::run); + connect(&_thread, &QThread::finished, [this]{ + running = false; + }); + moveToThread(&_thread); + } + + UDev::~UDev(){ + if(udevLib != nullptr){ + udev_unref(udevLib); + udevLib = nullptr; + } + } + + void UDev::start(){ + if(running){ + return; + } + wait(); + qDebug() << "UDev::Starting..."; + running = true; + _thread.start(); + qDebug() << "UDev::Started"; + } + + void UDev::stop(){ + qDebug() << "UDev::Stopping..."; + if(running){ + running = false; + wait(); + } + qDebug() << "UDev::Stopped"; + } + + bool UDev::isRunning(){ return running || _thread.isRunning(); } + + void UDev::wait(){ + qDebug() << "UDev::Waiting..."; + while(isRunning()){ + thread()->yieldCurrentThread(); + } + qDebug() << "UDev::Waited"; + } + + void UDev::addMonitor(QString subsystem, QString deviceType){ + QStringList* list; + if(monitors.contains(subsystem)){ + list = monitors[subsystem]; + }else{ + list = new QStringList(); + monitors.insert(subsystem, list); + } + if(!list->contains(deviceType)){ + list->append(deviceType); + } + } + void UDev::removeMonitor(QString subsystem, QString deviceType){ + if(monitors.contains(subsystem)){ + monitors[subsystem]->removeAll(deviceType); + } + } + + QList UDev::getDeviceList(const QString& subsystem){ + QList deviceList; + struct udev_enumerate* udevEnumeration = udev_enumerate_new(udevLib); + if(udevEnumeration == nullptr){ + return deviceList; + } + udev_enumerate_add_match_subsystem(udevEnumeration, subsystem.toUtf8().constData()); + udev_enumerate_scan_devices(udevEnumeration); + struct udev_list_entry* udevDeviceList = udev_enumerate_get_list_entry(udevEnumeration); + if(udevDeviceList != nullptr){ + struct udev_list_entry* entry = nullptr; + udev_list_entry_foreach(entry, udevDeviceList){ + if(entry == nullptr){ + continue; + } + const char* path = udev_list_entry_get_name(entry); + if(path == nullptr){ + continue; + } + Device device; + struct udev_device* udevDevice = udev_device_new_from_syspath(udevLib, path); + device.action = getActionType(udevDevice); + device.path = path; + device.subsystem = subsystem; + device.deviceType = QString(udev_device_get_devtype(udevDevice)); + udev_device_unref(udevDevice); + deviceList.append(device); + } + } + udev_enumerate_unref(udevEnumeration); + return deviceList; + } + + UDev::ActionType UDev::getActionType(udev_device* udevDevice){ + if(udevDevice == nullptr){ + return Unknown; + } + return getActionType(QString(udev_device_get_action(udevDevice)).trimmed().toUpper()); + } + + UDev::ActionType UDev::getActionType(const QString& actionType){ + if(actionType == "ADD"){ + return Add; + } + if(actionType == "REMOVE"){ + return Remove; + } + if(actionType == "Change"){ + return Change; + } + if(actionType == "OFFLINE"){ + return Offline; + } + if(actionType == "ONLINE"){ + return Online; + } + return Unknown; + } + + void UDev::run(){ + O_DEBUG("UDev::Monitor starting"); + udev_monitor* mon = udev_monitor_new_from_netlink(udevLib, "udev"); + if(!mon){ + O_WARNING("UDev::Monitor Unable to listen to UDev: Failed to create netlink monitor"); + O_DEBUG(strerror(errno)) + return; + } + for(QString subsystem : monitors.keys()){ + for(QString deviceType : *monitors[subsystem]){ + O_DEBUG("UDev::Monitor " << subsystem << deviceType); + int err = udev_monitor_filter_add_match_subsystem_devtype( + mon, + subsystem.toUtf8().constData(), + deviceType.isNull() || deviceType.isEmpty() ? deviceType.toUtf8().constData() : NULL + ); + if(err < 0){ + O_WARNING("UDev::Monitor Unable to add filter: " << strerror(err)); + } + } + } + int err = udev_monitor_enable_receiving(mon); + if(err < 0){ + O_WARNING("UDev::Monitor Unable to listen to UDev:" << strerror(err)); + udev_monitor_unref(mon); + return; + } + while(running){ + udev_device* dev = udev_monitor_receive_device(mon); + if(dev != nullptr){ + Device device; + device.action = getActionType(dev); + device.path = QString(udev_device_get_devnode(dev)); + device.subsystem = QString(udev_device_get_subsystem(dev)); + device.deviceType = QString(udev_device_get_devtype(dev)); + udev_device_unref(dev); + O_DEBUG("UDev::Monitor UDev event" << device); + emit event(device); + }else if(errno && errno != EAGAIN){ + O_WARNING("UDev::Monitor error checking event:" << strerror(errno)); + } + auto timestamp = QDateTime::currentMSecsSinceEpoch(); + QThread::yieldCurrentThread(); + if(QDateTime::currentMSecsSinceEpoch() - timestamp < 30){ + QThread::msleep(30); + } + } + O_DEBUG("UDev::Monitor stopping"); + udev_monitor_unref(mon); + } + QDebug operator<<(QDebug debug, const UDev::Device& device){ + QDebugStateSaver saver(debug); + Q_UNUSED(saver) + debug.nospace() << device.debugString().c_str(); + return debug.maybeSpace(); + } +} diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h new file mode 100644 index 000000000..86a3cf691 --- /dev/null +++ b/shared/liboxide/udev.h @@ -0,0 +1,83 @@ +/*! + * \addtogroup Oxide + * @{ + * \file + */ +#pragma once + +#include "liboxide_global.h" + +#include + +#include + +using namespace std; + +namespace Oxide { + class UDev : public QObject { + Q_OBJECT + + public: + static void ensureMaxThreads(); + explicit UDev(QObject *parent = nullptr); + ~UDev(); + + enum ActionType { + Add, + Remove, + Change, + Online, + Offline, + Unknown + }; + struct Device { + QString subsystem; + QString deviceType; + QString path; + ActionType action = Unknown; + QString actionString() const { + switch(action){ + case Add: + return "ADD"; + case Remove: + return "REMOVE"; + case Change: + return "CHANGE"; + case Online: + return "ONLINE"; + case Offline: + return "OFFLINE"; + case Unknown: + default: + return "UNKNOWN"; + } + } + string debugString() const { + return QString("").arg(subsystem, deviceType, actionString()).toStdString(); + } + }; + void start(); + void stop(); + bool isRunning(); + void wait(); + void addMonitor(QString subsystem, QString deviceType); + void removeMonitor(QString subsystem, QString deviceType); + QList getDeviceList(const QString& subsystem); + ActionType getActionType(udev_device* udevDevice); + ActionType getActionType(const QString& actionType); + + signals: + void event(Device device); + + private: + struct udev* udevLib = nullptr; + bool running = false; + QMap monitors; + QThread _thread; + + protected: + void run(); + }; + QDebug operator<<(QDebug debug, const UDev::Device& device); +} +Q_DECLARE_METATYPE(Oxide::UDev::Device) From 1ab39f5285d5b8158220e32f6af0bf7b0ac04a73 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sat, 1 Jul 2023 17:24:09 -0600 Subject: [PATCH 02/19] Ensure events are processed --- applications/system-service/fifohandler.h | 1 + shared/liboxide/udev.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/applications/system-service/fifohandler.h b/applications/system-service/fifohandler.h index 799856aed..4601faf07 100644 --- a/applications/system-service/fifohandler.h +++ b/applications/system-service/fifohandler.h @@ -77,6 +77,7 @@ class FifoHandler : public QObject { QString line(data.c_str()); line = line.trimmed(); emit dataRecieved(this, line); + qApp->processEvents(); thread()->yieldCurrentThread(); } if(in.eof()){ diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index d4d09d6be..be68f959b 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -52,6 +52,7 @@ namespace Oxide { void UDev::wait(){ qDebug() << "UDev::Waiting..."; while(isRunning()){ + qApp->processEvents(); thread()->yieldCurrentThread(); } qDebug() << "UDev::Waited"; @@ -176,6 +177,7 @@ namespace Oxide { O_WARNING("UDev::Monitor error checking event:" << strerror(errno)); } auto timestamp = QDateTime::currentMSecsSinceEpoch(); + qApp->processEvents(); QThread::yieldCurrentThread(); if(QDateTime::currentMSecsSinceEpoch() - timestamp < 30){ QThread::msleep(30); From 10a6dc455581a1fcd669fff25d8b5fcb393ae8f3 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sat, 1 Jul 2023 17:35:01 -0600 Subject: [PATCH 03/19] Fix udev thread quitting --- shared/liboxide/udev.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index be68f959b..82aa96d68 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -15,6 +15,7 @@ namespace Oxide { connect(parent, &QObject::destroyed, this, &QObject::deleteLater); connect(&_thread, &QThread::started, this, &UDev::run); connect(&_thread, &QThread::finished, [this]{ + qDebug() << "UDev::Stopped"; running = false; }); moveToThread(&_thread); @@ -35,7 +36,6 @@ namespace Oxide { qDebug() << "UDev::Starting..."; running = true; _thread.start(); - qDebug() << "UDev::Started"; } void UDev::stop(){ @@ -44,18 +44,14 @@ namespace Oxide { running = false; wait(); } - qDebug() << "UDev::Stopped"; } bool UDev::isRunning(){ return running || _thread.isRunning(); } void UDev::wait(){ - qDebug() << "UDev::Waiting..."; - while(isRunning()){ - qApp->processEvents(); - thread()->yieldCurrentThread(); + if(isRunning()){ + _thread.wait(); } - qDebug() << "UDev::Waited"; } void UDev::addMonitor(QString subsystem, QString deviceType){ @@ -185,6 +181,7 @@ namespace Oxide { } O_DEBUG("UDev::Monitor stopping"); udev_monitor_unref(mon); + _thread.quit(); } QDebug operator<<(QDebug debug, const UDev::Device& device){ QDebugStateSaver saver(debug); From 926fd66a6fd1ce0493ebc01d8bff1622862462df Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sat, 1 Jul 2023 18:06:26 -0600 Subject: [PATCH 04/19] Make udev a singleton --- applications/system-service/buttonhandler.cpp | 1 + .../system-service/digitizerhandler.h | 1 + applications/system-service/powerapi.h | 18 ++++---- shared/liboxide/udev.cpp | 42 ++++++++++++++++++- shared/liboxide/udev.h | 10 +++-- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/applications/system-service/buttonhandler.cpp b/applications/system-service/buttonhandler.cpp index 517c4a2fe..cfa39c984 100644 --- a/applications/system-service/buttonhandler.cpp +++ b/applications/system-service/buttonhandler.cpp @@ -86,6 +86,7 @@ void ButtonHandler::run(){ emit keyUp(map[ie.code].keyCode); } }else{ + qApp->processEvents(); yieldCurrentThread(); } } diff --git a/applications/system-service/digitizerhandler.h b/applications/system-service/digitizerhandler.h index f9fe4222f..319fa93db 100644 --- a/applications/system-service/digitizerhandler.h +++ b/applications/system-service/digitizerhandler.h @@ -146,6 +146,7 @@ class DigitizerHandler : public QThread { qDebug() << "Reading From : " << device.device.c_str() << " (" << name << ")"; qDebug() << "Listening for events..."; while(handle_events()){ + qApp->processEvents(); yieldCurrentThread(); } } diff --git a/applications/system-service/powerapi.h b/applications/system-service/powerapi.h index 17947bbb8..f1229f6a5 100644 --- a/applications/system-service/powerapi.h +++ b/applications/system-service/powerapi.h @@ -49,10 +49,9 @@ class PowerAPI : public APIBase { }); Oxide::Sentry::sentry_span(t, "monitor", "Setup monitor", [this]{ if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - udev = new UDev(this); - udev->addMonitor("power_supply", NULL); - connect(udev, &UDev::event, this, QOverload<>::of(&PowerAPI::update)); - udev->start(); + UDev::singleton()->subsystem("power_supply", [this]{ + update(); + }); }else{ timer = new QTimer(this); timer->setSingleShot(false); @@ -69,23 +68,21 @@ class PowerAPI : public APIBase { qDebug() << "Killing timer"; timer->stop(); delete timer; - } - if(udev != nullptr){ + }else{ qDebug() << "Killing UDev monitor"; - udev->stop(); - delete udev; + UDev::singleton()->stop(); } } void setEnabled(bool enabled) override { if(enabled){ if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - udev->start(); + UDev::singleton()->start(); }else{ timer->start(); } }else if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - udev->stop(); + UDev::singleton()->stop(); }else{ timer->stop(); } @@ -174,7 +171,6 @@ class PowerAPI : public APIBase { private: QTimer* timer = nullptr; - UDev* udev = nullptr; int m_state = Normal; int m_batteryState = BatteryUnknown; int m_batteryLevel = 0; diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 82aa96d68..b7d659622 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -9,10 +9,18 @@ namespace Oxide { - UDev::UDev(QObject *parent) : QObject(), _thread(this){ + UDev* UDev::singleton(){ + static UDev* instance; + if(instance == nullptr){ + instance = new UDev(); + instance->start(); + } + return instance; + } + + UDev::UDev() : QObject(), _thread(this){ qRegisterMetaType("UDev::Device"); udevLib = udev_new(); - connect(parent, &QObject::destroyed, this, &QObject::deleteLater); connect(&_thread, &QThread::started, this, &UDev::run); connect(&_thread, &QThread::finished, [this]{ qDebug() << "UDev::Stopped"; @@ -28,6 +36,30 @@ namespace Oxide { } } + void UDev::subsystem(const QString& subsystem, std::function callback){ + deviceType(subsystem, NULL, callback); + } + + void UDev::subsystem(const QString& subsystem, std::function callback){ + deviceType(subsystem, NULL, callback); + } + + void UDev::deviceType(const QString& subsystem, const QString& deviceType, std::function callback){ + connect(singleton(), &UDev::event, [callback, subsystem, deviceType](const Device& device){ + if(device.subsystem == subsystem && (deviceType == NULL || device.deviceType == deviceType)){ + callback(device); + } + }); + singleton()->addMonitor(subsystem, deviceType); + } + + void UDev::deviceType(const QString& subsystem, const QString& deviceType, std::function callback){ + UDev::deviceType(subsystem, deviceType, [callback](const Device& device){ + Q_UNUSED(device); + callback(); + }); + } + void UDev::start(){ if(running){ return; @@ -64,11 +96,17 @@ namespace Oxide { } if(!list->contains(deviceType)){ list->append(deviceType); + // TODO - update filter on the fly + stop(); + start(); } } void UDev::removeMonitor(QString subsystem, QString deviceType){ if(monitors.contains(subsystem)){ monitors[subsystem]->removeAll(deviceType); + // TODO - update filter on the fly + stop(); + start(); } } diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h index 86a3cf691..e3bcf2879 100644 --- a/shared/liboxide/udev.h +++ b/shared/liboxide/udev.h @@ -18,8 +18,8 @@ namespace Oxide { Q_OBJECT public: - static void ensureMaxThreads(); - explicit UDev(QObject *parent = nullptr); + static UDev* singleton(); + explicit UDev(); ~UDev(); enum ActionType { @@ -56,6 +56,10 @@ namespace Oxide { return QString("").arg(subsystem, deviceType, actionString()).toStdString(); } }; + static void subsystem(const QString& subsystem, std::function callback); + static void subsystem(const QString& subsystem, std::function callback); + static void deviceType(const QString& subsystem, const QString& deviceType, std::function callback); + static void deviceType(const QString& subsystem, const QString& deviceType, std::function callback); void start(); void stop(); bool isRunning(); @@ -67,7 +71,7 @@ namespace Oxide { ActionType getActionType(const QString& actionType); signals: - void event(Device device); + void event(const Device& device); private: struct udev* udevLib = nullptr; From dc8657eed6e11c5661e95da2794b7bea9e03260b Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sat, 1 Jul 2023 18:41:53 -0600 Subject: [PATCH 05/19] More logging and change how thread is triggered --- shared/liboxide/udev.cpp | 16 ++++++++++++---- shared/liboxide/udev.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index b7d659622..5acd55f42 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -21,12 +21,10 @@ namespace Oxide { UDev::UDev() : QObject(), _thread(this){ qRegisterMetaType("UDev::Device"); udevLib = udev_new(); - connect(&_thread, &QThread::started, this, &UDev::run); connect(&_thread, &QThread::finished, [this]{ qDebug() << "UDev::Stopped"; running = false; }); - moveToThread(&_thread); } UDev::~UDev(){ @@ -68,6 +66,14 @@ namespace Oxide { qDebug() << "UDev::Starting..."; running = true; _thread.start(); + QTimer* timer = new QTimer(); + timer->moveToThread(&_thread); + timer->setSingleShot(true); + QObject::connect(timer, &QTimer::timeout, [timer, this](){ + monitor(); + timer->deleteLater(); + }); + QMetaObject::invokeMethod(timer, "start", Qt::BlockingQueuedConnection, Q_ARG(int, 0)); } void UDev::stop(){ @@ -87,6 +93,7 @@ namespace Oxide { } void UDev::addMonitor(QString subsystem, QString deviceType){ + qDebug() << "UDev::Adding" << subsystem << deviceType; QStringList* list; if(monitors.contains(subsystem)){ list = monitors[subsystem]; @@ -102,6 +109,7 @@ namespace Oxide { } } void UDev::removeMonitor(QString subsystem, QString deviceType){ + qDebug() << "UDev::Removing" << subsystem << deviceType; if(monitors.contains(subsystem)){ monitors[subsystem]->removeAll(deviceType); // TODO - update filter on the fly @@ -169,8 +177,8 @@ namespace Oxide { return Unknown; } - void UDev::run(){ - O_DEBUG("UDev::Monitor starting"); + void UDev::monitor(){ + O_DEBUG("UDev::Started"); udev_monitor* mon = udev_monitor_new_from_netlink(udevLib, "udev"); if(!mon){ O_WARNING("UDev::Monitor Unable to listen to UDev: Failed to create netlink monitor"); diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h index e3bcf2879..c3fb7f5b9 100644 --- a/shared/liboxide/udev.h +++ b/shared/liboxide/udev.h @@ -80,7 +80,7 @@ namespace Oxide { QThread _thread; protected: - void run(); + void monitor(); }; QDebug operator<<(QDebug debug, const UDev::Device& device); } From 1ac596126ca0808684a26cc3a2f161dcd28687ff Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sat, 1 Jul 2023 20:32:27 -0600 Subject: [PATCH 06/19] More work on threading --- applications/system-service/powerapi.h | 1 + shared/liboxide/liboxide.cpp | 11 +-- shared/liboxide/liboxide.h | 5 ++ shared/liboxide/udev.cpp | 99 ++++++++++++++++---------- shared/liboxide/udev.h | 4 ++ 5 files changed, 77 insertions(+), 43 deletions(-) diff --git a/applications/system-service/powerapi.h b/applications/system-service/powerapi.h index f1229f6a5..a96154a1f 100644 --- a/applications/system-service/powerapi.h +++ b/applications/system-service/powerapi.h @@ -49,6 +49,7 @@ class PowerAPI : public APIBase { }); Oxide::Sentry::sentry_span(t, "monitor", "Setup monitor", [this]{ if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ + UDev::singleton()->addMonitor("platform", NULL); UDev::singleton()->subsystem("power_supply", [this]{ update(); }); diff --git a/shared/liboxide/liboxide.cpp b/shared/liboxide/liboxide.cpp index b8cb0dfe0..700da4469 100644 --- a/shared/liboxide/liboxide.cpp +++ b/shared/liboxide/liboxide.cpp @@ -96,17 +96,18 @@ namespace Oxide { } return pids; } - void dispatchToMainThread(std::function callback){ - if(QThread::currentThread() == qApp->thread()){ + void dispatchToMainThread(std::function callback){ dispatchToThread(qApp->thread(), callback); } + void dispatchToThread(QThread* thread, std::function callback){ + if(QThread::currentThread() == thread){ + // Already on the thread callback(); return; } - // any thread + // Send to the thread QTimer* timer = new QTimer(); - timer->moveToThread(qApp->thread()); + timer->moveToThread(thread); timer->setSingleShot(true); QObject::connect(timer, &QTimer::timeout, [=](){ - // main thread callback(); timer->deleteLater(); }); diff --git a/shared/liboxide/liboxide.h b/shared/liboxide/liboxide.h index 34c6396ff..b2ff4a870 100644 --- a/shared/liboxide/liboxide.h +++ b/shared/liboxide/liboxide.h @@ -102,6 +102,11 @@ namespace Oxide { * \snippet examples/oxide.cpp dispatchToMainThread */ LIBOXIDE_EXPORT void dispatchToMainThread(std::function callback); + /*! + * \brief Run code on a thread + * \param callback The code to run on the thread + */ + LIBOXIDE_EXPORT void dispatchToThread(QThread* thread, std::function callback); /*! * \brief Get the UID for a username * \param name Username to search for diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 5acd55f42..28901a442 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -1,5 +1,6 @@ #include "udev.h" #include "debug.h" +#include "liboxide.h" #include #include @@ -21,10 +22,14 @@ namespace Oxide { UDev::UDev() : QObject(), _thread(this){ qRegisterMetaType("UDev::Device"); udevLib = udev_new(); + connect(&_thread, &QThread::started, [this]{ + O_DEBUG("UDev::Thread started"); + }); connect(&_thread, &QThread::finished, [this]{ - qDebug() << "UDev::Stopped"; - running = false; + O_DEBUG("UDev::Thread finished"); }); + _thread.start(QThread::LowPriority); + moveToThread(&_thread); } UDev::~UDev(){ @@ -59,41 +64,43 @@ namespace Oxide { } void UDev::start(){ + statelock.lock(); + O_DEBUG("UDev::Starting..."); + exitRequested = false; if(running){ + statelock.unlock(); + O_DEBUG("UDev::Already running"); return; } - wait(); - qDebug() << "UDev::Starting..."; - running = true; - _thread.start(); - QTimer* timer = new QTimer(); - timer->moveToThread(&_thread); - timer->setSingleShot(true); - QObject::connect(timer, &QTimer::timeout, [timer, this](){ + QTimer::singleShot(0, [this](){ monitor(); - timer->deleteLater(); + statelock.unlock(); + O_DEBUG("UDev::Started"); }); - QMetaObject::invokeMethod(timer, "start", Qt::BlockingQueuedConnection, Q_ARG(int, 0)); } void UDev::stop(){ - qDebug() << "UDev::Stopping..."; + statelock.lock(); + O_DEBUG("UDev::Stopping..."); if(running){ - running = false; - wait(); + exitRequested = true; } + statelock.unlock(); } - bool UDev::isRunning(){ return running || _thread.isRunning(); } + bool UDev::isRunning(){ return running; } void UDev::wait(){ if(isRunning()){ - _thread.wait(); + O_DEBUG("UDev::Waiting to stop..."); + while(running){ + qApp->processEvents(); + } } } void UDev::addMonitor(QString subsystem, QString deviceType){ - qDebug() << "UDev::Adding" << subsystem << deviceType; + O_DEBUG("UDev::Adding" << subsystem << deviceType); QStringList* list; if(monitors.contains(subsystem)){ list = monitors[subsystem]; @@ -103,18 +110,14 @@ namespace Oxide { } if(!list->contains(deviceType)){ list->append(deviceType); - // TODO - update filter on the fly - stop(); - start(); + update = true; } } void UDev::removeMonitor(QString subsystem, QString deviceType){ - qDebug() << "UDev::Removing" << subsystem << deviceType; + O_DEBUG("UDev::Removing" << subsystem << deviceType); if(monitors.contains(subsystem)){ monitors[subsystem]->removeAll(deviceType); - // TODO - update filter on the fly - stop(); - start(); + update = true; } } @@ -178,16 +181,18 @@ namespace Oxide { } void UDev::monitor(){ - O_DEBUG("UDev::Started"); + running = true; + O_DEBUG("UDev::Monitor starting..."); udev_monitor* mon = udev_monitor_new_from_netlink(udevLib, "udev"); if(!mon){ O_WARNING("UDev::Monitor Unable to listen to UDev: Failed to create netlink monitor"); O_DEBUG(strerror(errno)) return; } + O_DEBUG("UDev::Monitor applying filters..."); for(QString subsystem : monitors.keys()){ for(QString deviceType : *monitors[subsystem]){ - O_DEBUG("UDev::Monitor " << subsystem << deviceType); + O_DEBUG("UDev::Monitor filter" << subsystem << deviceType); int err = udev_monitor_filter_add_match_subsystem_devtype( mon, subsystem.toUtf8().constData(), @@ -198,13 +203,36 @@ namespace Oxide { } } } + O_DEBUG("UDev::Monitor enabling..."); int err = udev_monitor_enable_receiving(mon); if(err < 0){ O_WARNING("UDev::Monitor Unable to listen to UDev:" << strerror(err)); udev_monitor_unref(mon); return; } - while(running){ + O_DEBUG("UDev::Monitor setting up timer..."); + auto timer = new QTimer(); + timer->setTimerType(Qt::PreciseTimer); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, [this, mon, timer]{ + if(exitRequested){ + O_DEBUG("UDev::Monitor stopping..."); + udev_monitor_unref(mon); + timer->deleteLater(); + running = false; + O_DEBUG("UDev::Stopped"); + return; + } + if(update || !mon){ + O_DEBUG("UDev::Monitor reloading..."); + update = false; + udev_monitor_unref(mon); + timer->deleteLater(); + QTimer::singleShot(0, [this]{ + monitor(); + }); + return; + } udev_device* dev = udev_monitor_receive_device(mon); if(dev != nullptr){ Device device; @@ -218,17 +246,12 @@ namespace Oxide { }else if(errno && errno != EAGAIN){ O_WARNING("UDev::Monitor error checking event:" << strerror(errno)); } - auto timestamp = QDateTime::currentMSecsSinceEpoch(); - qApp->processEvents(); - QThread::yieldCurrentThread(); - if(QDateTime::currentMSecsSinceEpoch() - timestamp < 30){ - QThread::msleep(30); - } - } - O_DEBUG("UDev::Monitor stopping"); - udev_monitor_unref(mon); - _thread.quit(); + timer->start(30); + }); + timer->start(30); + O_DEBUG("UDev::Monitor event loop started"); } + QDebug operator<<(QDebug debug, const UDev::Device& device){ QDebugStateSaver saver(debug); Q_UNUSED(saver) diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h index c3fb7f5b9..552982a12 100644 --- a/shared/liboxide/udev.h +++ b/shared/liboxide/udev.h @@ -10,6 +10,7 @@ #include #include +#include using namespace std; @@ -76,8 +77,11 @@ namespace Oxide { private: struct udev* udevLib = nullptr; bool running = false; + bool exitRequested = false; + bool update = false; QMap monitors; QThread _thread; + QMutex statelock; protected: void monitor(); From 3dd666a408a4c2b340c84e83a0170319ebe0c171 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sun, 14 Jan 2024 18:58:48 -0700 Subject: [PATCH 07/19] Fix up build --- shared/liboxide/liboxide.cpp | 17 +++-------------- shared/liboxide/liboxide.h | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/shared/liboxide/liboxide.cpp b/shared/liboxide/liboxide.cpp index e989116d6..8e7596330 100644 --- a/shared/liboxide/liboxide.cpp +++ b/shared/liboxide/liboxide.cpp @@ -98,22 +98,11 @@ namespace Oxide { } return pids; } - void dispatchToMainThread(std::function callback){ dispatchToThread(qApp->thread(), callback); } - void dispatchToThread(QThread* thread, std::function callback){ - if(QThread::currentThread() == thread){ - // Already on the thread + void dispatchToMainThread(std::function callback){ + dispatchToMainThread([callback]{ callback(); - return; - } - // Send to the thread - QTimer* timer = new QTimer(); - timer->moveToThread(thread); - timer->setSingleShot(true); - QObject::connect(timer, &QTimer::timeout, [=](){ - callback(); - timer->deleteLater(); + return 0; }); - QMetaObject::invokeMethod(timer, "start", Qt::BlockingQueuedConnection, Q_ARG(int, 0)); } uid_t getUID(const QString& name){ char buffer[1024]; diff --git a/shared/liboxide/liboxide.h b/shared/liboxide/liboxide.h index 0be192c53..3bf9ae3db 100644 --- a/shared/liboxide/liboxide.h +++ b/shared/liboxide/liboxide.h @@ -102,10 +102,29 @@ namespace Oxide { */ LIBOXIDE_EXPORT void dispatchToMainThread(std::function callback); /*! - * \brief Run code on a thread - * \param callback The code to run on the thread + * \brief Run code on the main Qt thread + * \param callback The code to run on the main thread + * \return Return value of callback + * + * \snippet examples/oxide.cpp dispatchToMainThread */ - LIBOXIDE_EXPORT void dispatchToThread(QThread* thread, std::function callback); + template LIBOXIDE_EXPORT T dispatchToMainThread(std::function callback){ + if(QThread::currentThread() == qApp->thread()){ + return callback(); + } + // any thread + QTimer* timer = new QTimer(); + timer->moveToThread(qApp->thread()); + timer->setSingleShot(true); + T result; + QObject::connect(timer, &QTimer::timeout, [timer, &result, callback](){ + // main thread + result = callback(); + timer->deleteLater(); + }); + QMetaObject::invokeMethod(timer, "start", Qt::BlockingQueuedConnection, Q_ARG(int, 0)); + return result; + } /*! * \brief Get the UID for a username * \param name Username to search for From 98b58d19a2ae5eca8b391743e5eef93b593c4a31 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 19:09:47 -0600 Subject: [PATCH 08/19] Fix compilation --- applications/system-service/powerapi.cpp | 12 +++++++----- applications/system-service/powerapi.h | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/applications/system-service/powerapi.cpp b/applications/system-service/powerapi.cpp index 24e91f993..95cecea59 100644 --- a/applications/system-service/powerapi.cpp +++ b/applications/system-service/powerapi.cpp @@ -1,5 +1,7 @@ #include "powerapi.h" +#include + PowerAPI* PowerAPI::singleton(PowerAPI* self){ static PowerAPI* instance; if(self != nullptr){ @@ -23,8 +25,8 @@ PowerAPI::PowerAPI(QObject* parent) }); Oxide::Sentry::sentry_span(t, "monitor", "Setup monitor", [this]{ if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - UDev::singleton()->addMonitor("platform", NULL); - UDev::singleton()->subsystem("power_supply", [this]{ + Oxide::UDev::singleton()->addMonitor("platform", NULL); + Oxide::UDev::singleton()->subsystem("power_supply", [this]{ update(); }); }else{ @@ -46,19 +48,19 @@ PowerAPI::~PowerAPI(){ delete timer; }else{ qDebug() << "Killing UDev monitor"; - UDev::singleton()->stop(); + Oxide::UDev::singleton()->stop(); } } void PowerAPI::setEnabled(bool enabled) { if(enabled){ if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - UDev::singleton()->start(); + Oxide::UDev::singleton()->start(); }else{ timer->start(); } }else if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - UDev::singleton()->stop(); + Oxide::UDev::singleton()->stop(); }else{ timer->stop(); } diff --git a/applications/system-service/powerapi.h b/applications/system-service/powerapi.h index 508b241eb..67ec17113 100644 --- a/applications/system-service/powerapi.h +++ b/applications/system-service/powerapi.h @@ -2,7 +2,6 @@ #define BATTERYAPI_H #include -#include #include #include From bc3bc68251e6caeec2627774753378a2c1a9378e Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 19:40:25 -0600 Subject: [PATCH 09/19] Code review changes --- applications/system-service/powerapi.cpp | 22 +++++++++------- shared/liboxide/udev.cpp | 33 +++++++++++++----------- shared/liboxide/udev.h | 6 ++--- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/applications/system-service/powerapi.cpp b/applications/system-service/powerapi.cpp index 95cecea59..9e5dfcd3a 100644 --- a/applications/system-service/powerapi.cpp +++ b/applications/system-service/powerapi.cpp @@ -52,18 +52,20 @@ PowerAPI::~PowerAPI(){ } } -void PowerAPI::setEnabled(bool enabled) { - if(enabled){ - if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - Oxide::UDev::singleton()->start(); - }else{ - timer->start(); - } - }else if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ - Oxide::UDev::singleton()->stop(); +void PowerAPI::setEnabled(bool enabled){ + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ + if(enabled){ + Oxide::UDev::singleton()->start(); }else{ - timer->stop(); + Oxide::UDev::singleton()->stop(); } + return; + } + if(enabled){ + timer->start(); + }else{ + timer->stop(); + } } int PowerAPI::state(){ diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 28901a442..3c03540ec 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -11,11 +11,12 @@ namespace Oxide { UDev* UDev::singleton(){ - static UDev* instance; - if(instance == nullptr){ - instance = new UDev(); - instance->start(); - } + static std::once_flag initFlag; + static UDev* instance = nullptr; + std::call_once(initFlag, [](){ + instance = new UDev(); + instance->start(); + }); return instance; } @@ -103,10 +104,10 @@ namespace Oxide { O_DEBUG("UDev::Adding" << subsystem << deviceType); QStringList* list; if(monitors.contains(subsystem)){ - list = monitors[subsystem]; + list = monitors[subsystem].data(); }else{ list = new QStringList(); - monitors.insert(subsystem, list); + monitors.insert(subsystem, QSharedPointer(list)); } if(!list->contains(deviceType)){ list->append(deviceType); @@ -145,7 +146,8 @@ namespace Oxide { device.action = getActionType(udevDevice); device.path = path; device.subsystem = subsystem; - device.deviceType = QString(udev_device_get_devtype(udevDevice)); + auto devType = udev_device_get_devtype(udevDevice); + device.deviceType = QString(devType ? devType : ""); udev_device_unref(udevDevice); deviceList.append(device); } @@ -168,7 +170,7 @@ namespace Oxide { if(actionType == "REMOVE"){ return Remove; } - if(actionType == "Change"){ + if(actionType == "CHANGE"){ return Change; } if(actionType == "OFFLINE"){ @@ -228,18 +230,19 @@ namespace Oxide { update = false; udev_monitor_unref(mon); timer->deleteLater(); - QTimer::singleShot(0, [this]{ - monitor(); - }); + QTimer::singleShot(0, this, &UDev::monitor); return; } udev_device* dev = udev_monitor_receive_device(mon); if(dev != nullptr){ Device device; device.action = getActionType(dev); - device.path = QString(udev_device_get_devnode(dev)); - device.subsystem = QString(udev_device_get_subsystem(dev)); - device.deviceType = QString(udev_device_get_devtype(dev)); + auto devNode = udev_device_get_devnode(dev); + device.path = QString(devNode ? devNode : ""); + auto devSubsystem = udev_device_get_subsystem(dev); + device.subsystem = QString(devSubsystem ? devSubsystem : ""); + auto devType = udev_device_get_devtype(dev); + device.deviceType = QString(devType ? devType : ""); udev_device_unref(dev); O_DEBUG("UDev::Monitor UDev event" << device); emit event(device); diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h index 552982a12..d8b0fd1fd 100644 --- a/shared/liboxide/udev.h +++ b/shared/liboxide/udev.h @@ -12,8 +12,6 @@ #include #include -using namespace std; - namespace Oxide { class UDev : public QObject { Q_OBJECT @@ -53,7 +51,7 @@ namespace Oxide { return "UNKNOWN"; } } - string debugString() const { + std::string debugString() const { return QString("").arg(subsystem, deviceType, actionString()).toStdString(); } }; @@ -79,7 +77,7 @@ namespace Oxide { bool running = false; bool exitRequested = false; bool update = false; - QMap monitors; + QMap> monitors; QThread _thread; QMutex statelock; From ef634c3d23b4e228eedf9697edc0f854ea3bf86b Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 19:46:28 -0600 Subject: [PATCH 10/19] Fix inverted logic --- shared/liboxide/udev.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 3c03540ec..1b49461b2 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -198,7 +198,9 @@ namespace Oxide { int err = udev_monitor_filter_add_match_subsystem_devtype( mon, subsystem.toUtf8().constData(), - deviceType.isNull() || deviceType.isEmpty() ? deviceType.toUtf8().constData() : NULL + deviceType.isNull() || deviceType.isEmpty() + ? NULL + : deviceType.toUtf8().constData() ); if(err < 0){ O_WARNING("UDev::Monitor Unable to add filter: " << strerror(err)); From 341ee3baabf2a28b18bae62ff74deb52f33e1b9c Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 19:48:20 -0600 Subject: [PATCH 11/19] Fix invalid compare --- shared/liboxide/udev.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 1b49461b2..920e76aa8 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -50,7 +50,14 @@ namespace Oxide { void UDev::deviceType(const QString& subsystem, const QString& deviceType, std::function callback){ connect(singleton(), &UDev::event, [callback, subsystem, deviceType](const Device& device){ - if(device.subsystem == subsystem && (deviceType == NULL || device.deviceType == deviceType)){ + if( + device.subsystem == subsystem + && ( + deviceType.isNull() + || deviceType.isEmpty() + || device.deviceType == deviceType + ) + ){ callback(device); } }); From 741dbb3f0f4270ee367d34ce35eb012c51fb9ece Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 20:10:24 -0600 Subject: [PATCH 12/19] Various code review feedback changes --- shared/liboxide/udev.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 920e76aa8..bde2c73ce 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -11,8 +11,8 @@ namespace Oxide { UDev* UDev::singleton(){ - static std::once_flag initFlag; static UDev* instance = nullptr; + static std::once_flag initFlag; std::call_once(initFlag, [](){ instance = new UDev(); instance->start(); @@ -104,17 +104,15 @@ namespace Oxide { while(running){ qApp->processEvents(); } + _thread.wait((); } } void UDev::addMonitor(QString subsystem, QString deviceType){ O_DEBUG("UDev::Adding" << subsystem << deviceType); - QStringList* list; - if(monitors.contains(subsystem)){ - list = monitors[subsystem].data(); - }else{ - list = new QStringList(); - monitors.insert(subsystem, QSharedPointer(list)); + auto& list = monitors[subsystem]; + if (!list) { + list = QSharedPointer::create(); } if(!list->contains(deviceType)){ list->append(deviceType); @@ -127,16 +125,32 @@ namespace Oxide { monitors[subsystem]->removeAll(deviceType); update = true; } + + auto it = monitors.find(subsystem); + if(it != monitors.end() && *it){ + (*it)->removeAll(deviceType); + update = true; + } } QList UDev::getDeviceList(const QString& subsystem){ QList deviceList; struct udev_enumerate* udevEnumeration = udev_enumerate_new(udevLib); if(udevEnumeration == nullptr){ + static std::once_flag onceFlag; + std::call_once(onceFlag, [](){ + O_WARNING("Failed to enumerate udev"); + }); + return deviceList; + } + if(udev_enumerate_add_match_subsystem(udevEnumeration, subsystem.toUtf8().constData()) < 0){ + O_WARNING("Failed to add subsystem"); + return deviceList; + } + if(udev_enumerate_scan_devices(udevEnumeration) < 0){ + O_WARNING("Failed to scan devices"); return deviceList; } - udev_enumerate_add_match_subsystem(udevEnumeration, subsystem.toUtf8().constData()); - udev_enumerate_scan_devices(udevEnumeration); struct udev_list_entry* udevDeviceList = udev_enumerate_get_list_entry(udevEnumeration); if(udevDeviceList != nullptr){ struct udev_list_entry* entry = nullptr; @@ -150,6 +164,10 @@ namespace Oxide { } Device device; struct udev_device* udevDevice = udev_device_new_from_syspath(udevLib, path); + if(udevDevice == nullptr) { + O_WARNING("Failed to create udev device from syspath"); + continue; + } device.action = getActionType(udevDevice); device.path = path; device.subsystem = subsystem; From 5e5e54fa362dedf194e42839964422bf7b53d2af Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 20:13:02 -0600 Subject: [PATCH 13/19] Optimize stop --- shared/liboxide/udev.cpp | 8 ++++---- shared/liboxide/udev.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index bde2c73ce..7c22d9db0 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -101,10 +101,9 @@ namespace Oxide { void UDev::wait(){ if(isRunning()){ O_DEBUG("UDev::Waiting to stop..."); - while(running){ - qApp->processEvents(); - } - _thread.wait((); + QEventLoop loop; + connect(this, &UDev::stopped, &loop, &QEventLoop::quit); + loop.exec(); } } @@ -250,6 +249,7 @@ namespace Oxide { timer->deleteLater(); running = false; O_DEBUG("UDev::Stopped"); + emit stopped(); return; } if(update || !mon){ diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h index d8b0fd1fd..8d75b3c6b 100644 --- a/shared/liboxide/udev.h +++ b/shared/liboxide/udev.h @@ -71,6 +71,7 @@ namespace Oxide { signals: void event(const Device& device); + void stopped(); private: struct udev* udevLib = nullptr; From 8091cd90a5bdbd60acd973d029d0dc6ce60bc149 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 21:03:03 -0600 Subject: [PATCH 14/19] Code review feedback --- shared/liboxide/udev.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 7c22d9db0..62792408a 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -41,11 +41,11 @@ namespace Oxide { } void UDev::subsystem(const QString& subsystem, std::function callback){ - deviceType(subsystem, NULL, callback); + deviceType(subsystem, "", callback); } void UDev::subsystem(const QString& subsystem, std::function callback){ - deviceType(subsystem, NULL, callback); + deviceType(subsystem, "", callback); } void UDev::deviceType(const QString& subsystem, const QString& deviceType, std::function callback){ @@ -144,10 +144,12 @@ namespace Oxide { } if(udev_enumerate_add_match_subsystem(udevEnumeration, subsystem.toUtf8().constData()) < 0){ O_WARNING("Failed to add subsystem"); + udev_enumerate_unref(udevEnumeration); return deviceList; } if(udev_enumerate_scan_devices(udevEnumeration) < 0){ O_WARNING("Failed to scan devices"); + udev_enumerate_unref(udevEnumeration); return deviceList; } struct udev_list_entry* udevDeviceList = udev_enumerate_get_list_entry(udevEnumeration); From c5a717774ff45f8a2e140b8e5b59907acfc35183 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 21:16:38 -0600 Subject: [PATCH 15/19] Code review feedback --- shared/liboxide/udev.cpp | 22 +++++++--------------- shared/liboxide/udev.h | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 62792408a..90fb1cf1c 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -109,25 +109,16 @@ namespace Oxide { void UDev::addMonitor(QString subsystem, QString deviceType){ O_DEBUG("UDev::Adding" << subsystem << deviceType); - auto& list = monitors[subsystem]; - if (!list) { - list = QSharedPointer::create(); - } - if(!list->contains(deviceType)){ - list->append(deviceType); + QStringList& list = monitors[subsystem]; + if(!list.contains(deviceType)){ + list.append(deviceType); update = true; } } void UDev::removeMonitor(QString subsystem, QString deviceType){ O_DEBUG("UDev::Removing" << subsystem << deviceType); if(monitors.contains(subsystem)){ - monitors[subsystem]->removeAll(deviceType); - update = true; - } - - auto it = monitors.find(subsystem); - if(it != monitors.end() && *it){ - (*it)->removeAll(deviceType); + monitors[subsystem].removeAll(deviceType); update = true; } } @@ -186,7 +177,8 @@ namespace Oxide { if(udevDevice == nullptr){ return Unknown; } - return getActionType(QString(udev_device_get_action(udevDevice)).trimmed().toUpper()); + auto devType = udev_device_get_action(udevDevice); + return getActionType(QString(devType ? devType : "").trimmed().toUpper()); } UDev::ActionType UDev::getActionType(const QString& actionType){ @@ -219,7 +211,7 @@ namespace Oxide { } O_DEBUG("UDev::Monitor applying filters..."); for(QString subsystem : monitors.keys()){ - for(QString deviceType : *monitors[subsystem]){ + for(QString deviceType : monitors[subsystem]){ O_DEBUG("UDev::Monitor filter" << subsystem << deviceType); int err = udev_monitor_filter_add_match_subsystem_devtype( mon, diff --git a/shared/liboxide/udev.h b/shared/liboxide/udev.h index 8d75b3c6b..5a18487ab 100644 --- a/shared/liboxide/udev.h +++ b/shared/liboxide/udev.h @@ -78,7 +78,7 @@ namespace Oxide { bool running = false; bool exitRequested = false; bool update = false; - QMap> monitors; + QMap monitors; QThread _thread; QMutex statelock; From 1d00a388bc91103ea30f5f1a82564cf5f7b3f69f Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 22:00:16 -0600 Subject: [PATCH 16/19] Update to python 3.12 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee22c7684..02d09a7a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install toltecmk run: pip install toltecmk requests==2.26.0 - name: Build packages From 08440dfe17a74405cd135a53a712efa1020b4aea Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 22:09:19 -0600 Subject: [PATCH 17/19] install the cli manually --- .github/workflows/build.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02d09a7a2..b5422aba3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -129,20 +129,22 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} SENTRY_URL: https://sentry.eeems.codes - name: Setup Sentry CLI - uses: mathieu-bour/setup-sentry-cli@v2 - with: - version: latest - url: https://sentry.eeems.codes - token: ${{ secrets.SENTRY_AUTH_TOKEN }} - organization: ${{ secrets.SENTRY_ORG }} - project: ${{ secrets.SENTRY_PROJECT }} - - name: Upload debug artifacts (debug) + run: curl -sL https://sentry.io/get-cli/ | bash + - name: Upload debug artifacts if: ${{ !runner.debug }} run: sentry-cli debug-files upload --include-sources . env: SENTRY_LOG_LEVEL: info - - name: Upload debug artifacts + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + SENTRY_URL: https://sentry.eeems.codes + - name: Upload debug artifacts (debug) if: ${{ runner.debug }} run: sentry-cli debug-files upload --include-sources . env: SENTRY_LOG_LEVEL: debug + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + SENTRY_URL: https://sentry.eeems.codes From 3db8a72466f0e760b44d592b55767180a9ea4005 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 2 Oct 2024 22:11:39 -0600 Subject: [PATCH 18/19] Remove subsystem from monitors if it's empty --- shared/liboxide/udev.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/shared/liboxide/udev.cpp b/shared/liboxide/udev.cpp index 90fb1cf1c..a29164128 100644 --- a/shared/liboxide/udev.cpp +++ b/shared/liboxide/udev.cpp @@ -117,10 +117,14 @@ namespace Oxide { } void UDev::removeMonitor(QString subsystem, QString deviceType){ O_DEBUG("UDev::Removing" << subsystem << deviceType); - if(monitors.contains(subsystem)){ - monitors[subsystem].removeAll(deviceType); - update = true; + if(!monitors.contains(subsystem)){ + return; + } + monitors[subsystem].removeAll(deviceType); + if(monitors[subsystem].isEmpty()){ + monitors.remove(subsystem); } + update = true; } QList UDev::getDeviceList(const QString& subsystem){ From 30fcf36ef7054bd3fcc3c07f8957ae76e6aa79eb Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 3 Oct 2024 20:35:11 -0600 Subject: [PATCH 19/19] Fix signal from the wrong thread --- applications/system-service/powerapi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/system-service/powerapi.cpp b/applications/system-service/powerapi.cpp index 41ad26650..3f4773f90 100644 --- a/applications/system-service/powerapi.cpp +++ b/applications/system-service/powerapi.cpp @@ -27,7 +27,7 @@ PowerAPI::PowerAPI(QObject* parent) if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){ Oxide::UDev::singleton()->addMonitor("platform", NULL); Oxide::UDev::singleton()->subsystem("power_supply", [this]{ - update(); + QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); }); }else{ timer = new QTimer(this);