mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
refactor(mainloop): cleaner approach via pollSource to absract as much service code as possible
This commit is contained in:
+16
-2
@@ -1,5 +1,6 @@
|
||||
#include "Application.hpp"
|
||||
|
||||
#include "app/PollSource.hpp"
|
||||
#include "core/Log.hpp"
|
||||
|
||||
#include <csignal>
|
||||
@@ -119,8 +120,21 @@ void Application::run() {
|
||||
}
|
||||
}
|
||||
|
||||
m_mainLoop = std::make_unique<MainLoop>(m_wayland, m_bar, m_bus.get(), m_notificationService.get(),
|
||||
&m_timeService, &m_configService, &m_stateService);
|
||||
// Build poll sources
|
||||
std::vector<PollSource*> sources;
|
||||
if (m_bus != nullptr) {
|
||||
m_busPollSource = std::make_unique<SessionBusPollSource>(*m_bus);
|
||||
sources.push_back(m_busPollSource.get());
|
||||
}
|
||||
if (m_notificationService != nullptr) {
|
||||
m_notificationPollSource = std::make_unique<NotificationPollSource>(*m_notificationService);
|
||||
sources.push_back(m_notificationPollSource.get());
|
||||
}
|
||||
sources.push_back(&m_timePollSource);
|
||||
sources.push_back(&m_configPollSource);
|
||||
sources.push_back(&m_statePollSource);
|
||||
|
||||
m_mainLoop = std::make_unique<MainLoop>(m_wayland, m_bar, std::move(sources));
|
||||
m_mainLoop->run();
|
||||
|
||||
logInfo("shutdown");
|
||||
|
||||
+14
-1
@@ -1,17 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/MainLoop.hpp"
|
||||
#include "config/ConfigPollSource.hpp"
|
||||
#include "config/ConfigService.hpp"
|
||||
#include "config/StatePollSource.hpp"
|
||||
#include "config/StateService.hpp"
|
||||
#include "debug/DebugService.hpp"
|
||||
#include "dbus/SessionBus.hpp"
|
||||
#include "dbus/SessionBusPollSource.hpp"
|
||||
#include "dbus/mpris/MprisService.hpp"
|
||||
#include "dbus/notification/NotificationPollSource.hpp"
|
||||
#include "dbus/notification/NotificationService.hpp"
|
||||
#include "debug/DebugService.hpp"
|
||||
#include "notification/InternalNotificationService.hpp"
|
||||
#include "notification/NotificationManager.hpp"
|
||||
#include "shell/Bar.hpp"
|
||||
#include "shell/Wallpaper.hpp"
|
||||
#include "system/SystemMonitorService.hpp"
|
||||
#include "time/TimePollSource.hpp"
|
||||
#include "time/TimeService.hpp"
|
||||
#include "wayland/WaylandConnection.hpp"
|
||||
|
||||
@@ -42,5 +47,13 @@ private:
|
||||
NotificationManager m_manager;
|
||||
InternalNotificationService m_internalNotifications;
|
||||
std::unique_ptr<NotificationService> m_notificationService;
|
||||
|
||||
// Poll sources (must outlive MainLoop)
|
||||
std::unique_ptr<SessionBusPollSource> m_busPollSource;
|
||||
std::unique_ptr<NotificationPollSource> m_notificationPollSource;
|
||||
TimePollSource m_timePollSource{m_timeService};
|
||||
ConfigPollSource m_configPollSource{m_configService};
|
||||
StatePollSource m_statePollSource{m_stateService};
|
||||
|
||||
std::unique_ptr<MainLoop> m_mainLoop;
|
||||
};
|
||||
|
||||
+18
-69
@@ -1,13 +1,9 @@
|
||||
#include "app/MainLoop.hpp"
|
||||
|
||||
#include "app/Application.hpp"
|
||||
#include "config/ConfigService.hpp"
|
||||
#include "config/StateService.hpp"
|
||||
#include "app/PollSource.hpp"
|
||||
#include "core/Log.hpp"
|
||||
#include "dbus/SessionBus.hpp"
|
||||
#include "dbus/notification/NotificationService.hpp"
|
||||
#include "shell/Bar.hpp"
|
||||
#include "time/TimeService.hpp"
|
||||
#include "wayland/WaylandConnection.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
@@ -16,16 +12,10 @@
|
||||
|
||||
#include <wayland-client-core.h>
|
||||
|
||||
MainLoop::MainLoop(WaylandConnection& wayland, Bar& bar, SessionBus* bus,
|
||||
NotificationService* notifications, TimeService* time,
|
||||
ConfigService* config, StateService* state)
|
||||
MainLoop::MainLoop(WaylandConnection& wayland, Bar& bar, std::vector<PollSource*> sources)
|
||||
: m_wayland(wayland)
|
||||
, m_bar(bar)
|
||||
, m_bus(bus)
|
||||
, m_notifications(notifications)
|
||||
, m_time(time)
|
||||
, m_config(config)
|
||||
, m_state(state) {}
|
||||
, m_sources(std::move(sources)) {}
|
||||
|
||||
void MainLoop::run() {
|
||||
while (m_bar.isRunning() && !Application::s_shutdown_requested) {
|
||||
@@ -36,46 +26,25 @@ void MainLoop::run() {
|
||||
throw std::runtime_error("failed to flush Wayland display");
|
||||
}
|
||||
|
||||
// Check if shutdown was requested since the last iteration
|
||||
if (Application::s_shutdown_requested) {
|
||||
break;
|
||||
}
|
||||
|
||||
int pollTimeout = -1;
|
||||
if (m_time != nullptr) {
|
||||
pollTimeout = m_time->pollTimeoutMs();
|
||||
}
|
||||
if (m_notifications != nullptr) {
|
||||
const int expiryTimeout = m_notifications->nextExpiryTimeoutMs();
|
||||
if (expiryTimeout >= 0 && (pollTimeout < 0 || expiryTimeout < pollTimeout)) {
|
||||
pollTimeout = expiryTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
// Build poll fd list
|
||||
// Collect poll fds and compute timeout from all sources
|
||||
std::vector<pollfd> pollFds;
|
||||
pollFds.push_back({.fd = wl_display_get_fd(m_wayland.display()), .events = POLLIN, .revents = 0});
|
||||
|
||||
if (m_bus != nullptr) {
|
||||
auto dbusPollData = m_bus->getPollData();
|
||||
const int dbusTimeout = dbusPollData.getPollTimeout();
|
||||
if (dbusTimeout >= 0 && (pollTimeout < 0 || dbusTimeout < pollTimeout)) {
|
||||
pollTimeout = dbusTimeout;
|
||||
int pollTimeout = -1;
|
||||
std::vector<std::size_t> sourceStartIndices;
|
||||
sourceStartIndices.reserve(m_sources.size());
|
||||
|
||||
for (auto* source : m_sources) {
|
||||
sourceStartIndices.push_back(source->addPollFds(pollFds));
|
||||
|
||||
const int t = source->pollTimeoutMs();
|
||||
if (t >= 0 && (pollTimeout < 0 || t < pollTimeout)) {
|
||||
pollTimeout = t;
|
||||
}
|
||||
pollFds.push_back({.fd = dbusPollData.fd, .events = dbusPollData.events, .revents = 0});
|
||||
pollFds.push_back({.fd = dbusPollData.eventFd, .events = POLLIN, .revents = 0});
|
||||
}
|
||||
|
||||
int configFdIdx = -1;
|
||||
if (m_config != nullptr && m_config->watchFd() >= 0) {
|
||||
configFdIdx = static_cast<int>(pollFds.size());
|
||||
pollFds.push_back({.fd = m_config->watchFd(), .events = POLLIN, .revents = 0});
|
||||
}
|
||||
|
||||
int stateFdIdx = -1;
|
||||
if (m_state != nullptr && m_state->watchFd() >= 0) {
|
||||
stateFdIdx = static_cast<int>(pollFds.size());
|
||||
pollFds.push_back({.fd = m_state->watchFd(), .events = POLLIN, .revents = 0});
|
||||
}
|
||||
|
||||
const int pollResult = ::poll(pollFds.data(), pollFds.size(), pollTimeout);
|
||||
@@ -97,36 +66,16 @@ void MainLoop::run() {
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch D-Bus events
|
||||
if (m_bus != nullptr) {
|
||||
m_bus->processPendingEvents();
|
||||
}
|
||||
|
||||
// Check config file changes
|
||||
if (configFdIdx >= 0 && (pollFds[static_cast<std::size_t>(configFdIdx)].revents & POLLIN) != 0) {
|
||||
m_config->checkReload();
|
||||
}
|
||||
|
||||
// Check state file changes
|
||||
if (stateFdIdx >= 0 && (pollFds[static_cast<std::size_t>(stateFdIdx)].revents & POLLIN) != 0) {
|
||||
m_state->checkReload();
|
||||
}
|
||||
|
||||
if (m_time != nullptr) {
|
||||
m_time->tick();
|
||||
}
|
||||
|
||||
if (m_notifications != nullptr) {
|
||||
m_notifications->processExpiredNotifications();
|
||||
// Dispatch all sources
|
||||
for (std::size_t i = 0; i < m_sources.size(); ++i) {
|
||||
m_sources[i]->dispatch(pollFds, sourceStartIndices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Close all UI surfaces immediately and flush Wayland to make them disappear
|
||||
// This happens while we're still in a valid Wayland/display context
|
||||
logDebug("closing bar surfaces for clean shutdown");
|
||||
m_bar.closeAllInstances();
|
||||
|
||||
// Dispatch any pending surface destroy messages and flush to compositor
|
||||
|
||||
if (wl_display_dispatch_pending(m_wayland.display()) < 0) {
|
||||
logWarn("failed to dispatch pending Wayland events during shutdown");
|
||||
}
|
||||
|
||||
+5
-13
@@ -1,27 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Bar;
|
||||
class ConfigService;
|
||||
class NotificationService;
|
||||
class SessionBus;
|
||||
class StateService;
|
||||
class TimeService;
|
||||
class PollSource;
|
||||
class WaylandConnection;
|
||||
|
||||
class MainLoop {
|
||||
public:
|
||||
MainLoop(WaylandConnection& wayland, Bar& bar, SessionBus* bus,
|
||||
NotificationService* notifications, TimeService* time,
|
||||
ConfigService* config, StateService* state);
|
||||
MainLoop(WaylandConnection& wayland, Bar& bar, std::vector<PollSource*> sources);
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
WaylandConnection& m_wayland;
|
||||
Bar& m_bar;
|
||||
SessionBus* m_bus = nullptr;
|
||||
NotificationService* m_notifications = nullptr;
|
||||
TimeService* m_time = nullptr;
|
||||
ConfigService* m_config = nullptr;
|
||||
StateService* m_state = nullptr;
|
||||
std::vector<PollSource*> m_sources;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <poll.h>
|
||||
#include <vector>
|
||||
|
||||
class PollSource {
|
||||
public:
|
||||
virtual ~PollSource() = default;
|
||||
|
||||
// Append any fds this source wants polled. Returns the starting index.
|
||||
std::size_t addPollFds(std::vector<pollfd>& fds) {
|
||||
auto start = fds.size();
|
||||
doAddPollFds(fds);
|
||||
return start;
|
||||
}
|
||||
|
||||
// Minimum timeout this source needs, or -1 for no timeout.
|
||||
[[nodiscard]] virtual int pollTimeoutMs() const { return -1; }
|
||||
|
||||
// Called after poll() returns. startIdx is what addPollFds returned.
|
||||
virtual void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) = 0;
|
||||
|
||||
protected:
|
||||
virtual void doAddPollFds(std::vector<pollfd>& fds) = 0;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/PollSource.hpp"
|
||||
#include "config/ConfigService.hpp"
|
||||
|
||||
class ConfigPollSource final : public PollSource {
|
||||
public:
|
||||
explicit ConfigPollSource(ConfigService& config) : m_config(config) {}
|
||||
|
||||
void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) override {
|
||||
if (m_config.watchFd() >= 0 && (fds[startIdx].revents & POLLIN) != 0) {
|
||||
m_config.checkReload();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void doAddPollFds(std::vector<pollfd>& fds) override {
|
||||
if (m_config.watchFd() >= 0) {
|
||||
fds.push_back({.fd = m_config.watchFd(), .events = POLLIN, .revents = 0});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ConfigService& m_config;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/PollSource.hpp"
|
||||
#include "config/StateService.hpp"
|
||||
|
||||
class StatePollSource final : public PollSource {
|
||||
public:
|
||||
explicit StatePollSource(StateService& state) : m_state(state) {}
|
||||
|
||||
void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) override {
|
||||
if (m_state.watchFd() >= 0 && (fds[startIdx].revents & POLLIN) != 0) {
|
||||
m_state.checkReload();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void doAddPollFds(std::vector<pollfd>& fds) override {
|
||||
if (m_state.watchFd() >= 0) {
|
||||
fds.push_back({.fd = m_state.watchFd(), .events = POLLIN, .revents = 0});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
StateService& m_state;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/PollSource.hpp"
|
||||
#include "dbus/SessionBus.hpp"
|
||||
|
||||
class SessionBusPollSource final : public PollSource {
|
||||
public:
|
||||
explicit SessionBusPollSource(SessionBus& bus) : m_bus(bus) {}
|
||||
|
||||
[[nodiscard]] int pollTimeoutMs() const override {
|
||||
const int t = m_bus.getPollData().getPollTimeout();
|
||||
return t;
|
||||
}
|
||||
|
||||
void dispatch(const std::vector<pollfd>& /*fds*/, std::size_t /*startIdx*/) override {
|
||||
m_bus.processPendingEvents();
|
||||
}
|
||||
|
||||
protected:
|
||||
void doAddPollFds(std::vector<pollfd>& fds) override {
|
||||
auto pd = m_bus.getPollData();
|
||||
fds.push_back({.fd = pd.fd, .events = pd.events, .revents = 0});
|
||||
fds.push_back({.fd = pd.eventFd, .events = POLLIN, .revents = 0});
|
||||
}
|
||||
|
||||
private:
|
||||
SessionBus& m_bus;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/PollSource.hpp"
|
||||
#include "dbus/notification/NotificationService.hpp"
|
||||
|
||||
class NotificationPollSource final : public PollSource {
|
||||
public:
|
||||
explicit NotificationPollSource(NotificationService& notifications)
|
||||
: m_notifications(notifications) {}
|
||||
|
||||
[[nodiscard]] int pollTimeoutMs() const override {
|
||||
return m_notifications.nextExpiryTimeoutMs();
|
||||
}
|
||||
|
||||
void dispatch(const std::vector<pollfd>& /*fds*/, std::size_t /*startIdx*/) override {
|
||||
m_notifications.processExpiredNotifications();
|
||||
}
|
||||
|
||||
protected:
|
||||
void doAddPollFds(std::vector<pollfd>& /*fds*/) override {}
|
||||
|
||||
private:
|
||||
NotificationService& m_notifications;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/PollSource.hpp"
|
||||
#include "time/TimeService.hpp"
|
||||
|
||||
class TimePollSource final : public PollSource {
|
||||
public:
|
||||
explicit TimePollSource(TimeService& time) : m_time(time) {}
|
||||
|
||||
[[nodiscard]] int pollTimeoutMs() const override {
|
||||
return m_time.pollTimeoutMs();
|
||||
}
|
||||
|
||||
void dispatch(const std::vector<pollfd>& /*fds*/, std::size_t /*startIdx*/) override {
|
||||
m_time.tick();
|
||||
}
|
||||
|
||||
protected:
|
||||
void doAddPollFds(std::vector<pollfd>& /*fds*/) override {}
|
||||
|
||||
private:
|
||||
TimeService& m_time;
|
||||
};
|
||||
Reference in New Issue
Block a user