diff --git a/src/DateTimeSources.cpp b/src/DateTimeSources.cpp index 72d9e95..0ded7e5 100644 --- a/src/DateTimeSources.cpp +++ b/src/DateTimeSources.cpp @@ -17,7 +17,9 @@ std::shared_ptr DateSource::Create() { } void DateSource::Evaluate() { - // TODO: Check dirty + // TODO: To save some redraws the redraw flag should check if date has changed since last draw + m_published = true; // No need to publish + m_drawn = false; } std::shared_ptr TimeSource::Create(MainLoop& mainLoop, @@ -53,7 +55,8 @@ bool TimeSource::OnRead() { // Either block or no events return false; } - m_sourceDirtyFlag = true; + m_published = true; // No need to publish + m_drawn = false; m_dateSource->Evaluate(); - return m_sourceDirtyFlag; + return !m_drawn; } diff --git a/src/DateTimeSources.h b/src/DateTimeSources.h index ce8bd55..e135e1d 100644 --- a/src/DateTimeSources.h +++ b/src/DateTimeSources.h @@ -8,6 +8,7 @@ class DateSource : public Source { static std::shared_ptr Create(); void Evaluate(); virtual ~DateSource() {} + void Publish(const std::string_view, ScriptContext&) override {} private: DateSource() {} @@ -19,6 +20,7 @@ class TimeSource : public Source, public IoHandler { std::shared_ptr dateSource); virtual ~TimeSource(); virtual bool OnRead() override; + void Publish(const std::string_view, ScriptContext&) override {} private: TimeSource(int fd, std::shared_ptr dateSource) diff --git a/src/MainLoop.cpp b/src/MainLoop.cpp index 628b244..6cd4d78 100644 --- a/src/MainLoop.cpp +++ b/src/MainLoop.cpp @@ -52,10 +52,8 @@ void MainLoop::Run() { } } } - if (anyDirty) { - for (auto& handler : m_batchHandlers) { - handler->OnBatchProcessed(); - } + if (anyDirty && m_batchHandler) { + m_batchHandler->OnBatchProcessed(); } } while (m_polls.size() > 0); } @@ -66,8 +64,11 @@ void MainLoop::Register(int fd, const std::string_view name, std::shared_ptr ioBatchHandler) { - m_batchHandlers.push_back(ioBatchHandler); +void MainLoop::RegisterBatchHandler(std::shared_ptr batchHandler) { + if (m_batchHandler) { + spdlog::error("Only one batch handler supported"); + } + m_batchHandler = batchHandler; } void MainLoop::Wakeup() { diff --git a/src/MainLoop.h b/src/MainLoop.h index cbdc351..f308502 100644 --- a/src/MainLoop.h +++ b/src/MainLoop.h @@ -38,5 +38,5 @@ class MainLoop { std::mutex m_wakupMutex; std::vector m_polls; std::map> m_handlers; - std::vector> m_batchHandlers; + std::shared_ptr m_batchHandler; }; diff --git a/src/Manager.cpp b/src/Manager.cpp index ca11251..94f5217 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -3,16 +3,8 @@ #include "spdlog/spdlog.h" #include "src/Registry.h" -std::shared_ptr Manager::Create(std::shared_ptr registry, - std::string_view sourceName, MainLoop& mainLoop, - std::unique_ptr sources, - std::shared_ptr scriptContext) { - auto manager = std::shared_ptr( - new Manager(registry, sourceName, std::move(sources), scriptContext)); - // Register as a source - manager->m_sources->Register(sourceName, manager); - // Register source batch handler - mainLoop.RegisterBatchHandler(manager); +std::shared_ptr Manager::Create(std::shared_ptr registry) { + auto manager = std::shared_ptr(new Manager(registry)); // Register click handler if (!registry->seat) { spdlog::error("No seat in registry"); @@ -29,39 +21,32 @@ void Manager::ClickSurface(wl_surface* surface, int x, int y) { } void Manager::OnBatchProcessed() { + // Always publish sources + m_sources->PublishAll(); // No need to redraw when not visible and not in transition if (!m_visibilityChanged && !m_isVisible) return; - spdlog::debug("Processing batch of dirty sources"); if (m_visibilityChanged) { m_visibilityChanged = false; if (m_isVisible) { - m_sources->DirtyAll(); + m_sources->ForceRedraw(); } else { m_registry->BorrowOutputs().Hide(*m_registry); return; } } m_registry->BorrowOutputs().Draw(*m_registry, *m_sources); - m_sources->CleanAll(); -} - -void Manager::Publish(const Displays& displays) { - m_scriptContext->Publish(m_sourceName, displays); - m_sourceDirtyFlag = true; - OnBatchProcessed(); + m_sources->SetAllDrawn(); } void Manager::Hide() { m_isVisible = false; m_visibilityChanged = true; - m_sourceDirtyFlag = true; OnBatchProcessed(); } void Manager::Show() { m_isVisible = true; m_visibilityChanged = true; - m_sourceDirtyFlag = true; OnBatchProcessed(); } diff --git a/src/Manager.h b/src/Manager.h index 58ead52..fdb2cbb 100644 --- a/src/Manager.h +++ b/src/Manager.h @@ -5,35 +5,26 @@ #include "src/ScriptContext.h" #include "src/Sources.h" -class Manager : public IoBatchHandler, public Source { +class Manager : public IoBatchHandler { public: - static std::shared_ptr Create(std::shared_ptr registry, - std::string_view sourceName, MainLoop& mainLoop, - std::unique_ptr sources, - std::shared_ptr scriptContext); + static std::shared_ptr Create(std::shared_ptr registry); + void SetSources(std::unique_ptr sources) { m_sources = std::move(sources); } virtual ~Manager() {} - // When a batch of IO events has been processed and sources are potentially dirty + // When a batch of IO events has been processed and sources needs to be published and/or needs + // to redrawn void OnBatchProcessed() override; - // Used by compositor implementation + // Compositors tells manager when the overlays should be visible void Show(); void Hide(); - void Publish(const Displays& displays); void ClickSurface(wl_surface* surface, int x, int y); private: - Manager(std::shared_ptr registry, std::string_view sourceName, - std::unique_ptr sources, std::shared_ptr scriptContext) - : m_registry(registry), - m_sourceName(sourceName), - m_sources(std::move(sources)), - m_scriptContext(scriptContext) {} + Manager(std::shared_ptr registry) : m_registry(registry) {} std::shared_ptr m_registry; - std::string m_sourceName; bool m_isVisible; bool m_visibilityChanged; std::unique_ptr m_sources; - std::shared_ptr m_scriptContext; }; diff --git a/src/NetworkSource.cpp b/src/NetworkSource.cpp index d0d45b5..4a0d3a8 100644 --- a/src/NetworkSource.cpp +++ b/src/NetworkSource.cpp @@ -30,8 +30,7 @@ static char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen) { return s; } -std::shared_ptr NetworkSource::Create(std::string_view name, MainLoop &mainLoop, - std::shared_ptr scriptContext) { +std::shared_ptr NetworkSource::Create(MainLoop &mainLoop) { auto sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { return nullptr; @@ -48,8 +47,8 @@ std::shared_ptr NetworkSource::Create(std::string_view name, Main spdlog::error("Failed to set timer: {}", strerror(errno)); return nullptr; } - auto source = std::shared_ptr(new NetworkSource(name, sock, fd, scriptContext)); - mainLoop.Register(fd, name, source); + auto source = std::shared_ptr(new NetworkSource(sock, fd)); + mainLoop.Register(fd, "NetworkSource", source); return source; } @@ -90,10 +89,9 @@ void NetworkSource::ReadState() { network.address = get_ip_str(addr, ip, sizeof(ip)); networks.push_back(std::move(network)); } - m_sourceDirtyFlag = true; // m_networks != networks; - if (m_sourceDirtyFlag) { + if (m_networks != networks) { + m_drawn = m_published = false; m_networks = networks; - m_scriptContext->Publish(m_name, m_networks); } } @@ -105,6 +103,11 @@ bool NetworkSource::OnRead() { // Either block or no events return false; } - ReadState(); - return m_sourceDirtyFlag; + return !m_published; +} + +void NetworkSource::Publish(const std::string_view sourceName, ScriptContext &scriptContext) { + if (m_published) return; + scriptContext.Publish(sourceName, m_networks); + m_published = true; } diff --git a/src/NetworkSource.h b/src/NetworkSource.h index 2254b48..27031ac 100644 --- a/src/NetworkSource.h +++ b/src/NetworkSource.h @@ -8,19 +8,16 @@ class NetworkSource : public Source, public IoHandler { public: - static std::shared_ptr Create(std::string_view name, MainLoop& mainLoop, - std::shared_ptr scriptContext); + static std::shared_ptr Create(MainLoop& mainLoop); void Initialize(); void ReadState(); virtual bool OnRead() override; + void Publish(const std::string_view sourceName, ScriptContext& scriptContext) override; virtual ~NetworkSource() { close(m_timerfd); } private: - NetworkSource(std::string_view name, int socket, int timerfd, - std::shared_ptr scriptContext) - : m_name(name), m_socket(socket), m_timerfd(timerfd), m_scriptContext(scriptContext) {} + NetworkSource(int socket, int timerfd) : m_socket(socket), m_timerfd(timerfd) {} - const std::string m_name; int m_socket; int m_timerfd; std::shared_ptr m_scriptContext; diff --git a/src/Output.cpp b/src/Output.cpp index ee4da0a..16582b2 100644 --- a/src/Output.cpp +++ b/src/Output.cpp @@ -132,7 +132,7 @@ void Outputs::Draw(const Registry ®istry, const Sources &sources) { for (const auto &panelConfig : m_config->panels) { bool dirty = false; for (const auto &widgetConfig : panelConfig.widgets) { - if (sources.IsDirty(widgetConfig.sources)) { + if (sources.NeedsRedraw(widgetConfig.sources)) { dirty = true; break; } diff --git a/src/PowerSource.cpp b/src/PowerSource.cpp index 418d860..501430f 100644 --- a/src/PowerSource.cpp +++ b/src/PowerSource.cpp @@ -8,8 +8,7 @@ #include #include -std::shared_ptr PowerSource::Create(std::string_view name, MainLoop& mainLoop, - std::shared_ptr scriptContext) { +std::shared_ptr PowerSource::Create(MainLoop& mainLoop) { // Use non blocking to make sure we never hang on read auto fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); if (fd == -1) { @@ -23,15 +22,14 @@ std::shared_ptr PowerSource::Create(std::string_view name, MainLoop spdlog::error("Failed to set timer: {}", strerror(errno)); return nullptr; } - auto source = std::shared_ptr(new PowerSource(name, fd, scriptContext)); - mainLoop.Register(fd, name, source); + auto source = std::shared_ptr(new PowerSource(fd)); + mainLoop.Register(fd, "PowerSource", source); return source; } bool PowerSource::Initialize() { - // Publish initial value to make sure that something is published - m_scriptContext->Publish(m_name, - PowerState{.IsPluggedIn = false, .IsCharging = false, .Capacity = 0}); + m_drawn = m_published = false; + m_sourceState = PowerState{.IsPluggedIn = false, .IsCharging = false, .Capacity = 0}; // TODO: Probe that these exists m_ac = "/sys/class/power_supply/AC/online"; // TODO: Always BAT0? @@ -91,12 +89,11 @@ void PowerSource::ReadState() { if (maybeString) { state.IsPluggedIn = std::stoi(*maybeString) != 0; } - m_sourceDirtyFlag = state != m_sourceState; - if (m_sourceDirtyFlag) { - m_sourceState = state; - m_scriptContext->Publish(m_name, m_sourceState); + if (state != m_sourceState) { spdlog::info("Power status changed, capacity {}, charging {}, plugged in {}", state.Capacity, state.IsCharging, state.IsPluggedIn); + m_sourceState = state; + m_drawn = m_published = false; } } @@ -111,5 +108,11 @@ bool PowerSource::OnRead() { return false; } ReadState(); - return m_sourceDirtyFlag; + return !m_published; +} + +void PowerSource::Publish(const std::string_view sourceName, ScriptContext& scriptContext) { + if (m_published) return; + scriptContext.Publish(sourceName, m_sourceState); + m_published = true; } diff --git a/src/PowerSource.h b/src/PowerSource.h index 7f299a9..d691756 100644 --- a/src/PowerSource.h +++ b/src/PowerSource.h @@ -8,21 +8,18 @@ class PowerSource : public Source, public IoHandler { public: - static std::shared_ptr Create(std::string_view name, MainLoop& mainLoop, - std::shared_ptr scriptContext); + static std::shared_ptr Create(MainLoop& mainLoop); bool Initialize(); void ReadState(); virtual bool OnRead() override; + void Publish(const std::string_view sourceName, ScriptContext& scriptContext) override; virtual ~PowerSource(); private: - PowerSource(std::string_view name, int fd, std::shared_ptr scriptContext) - : m_name(name), m_timerfd(fd), m_scriptContext(scriptContext) {} - const std::string m_name; + PowerSource(int fd) : m_timerfd(fd) {} std::filesystem::path m_batteryCapacity; std::filesystem::path m_batteryStatus; std::filesystem::path m_ac; int m_timerfd; - std::shared_ptr m_scriptContext; PowerState m_sourceState; }; diff --git a/src/PulseAudioSource.cpp b/src/PulseAudioSource.cpp index 56555be..16b0e9d 100644 --- a/src/PulseAudioSource.cpp +++ b/src/PulseAudioSource.cpp @@ -41,9 +41,7 @@ static void on_subscribe(pa_context* ctx, pa_subscription_event_type_t event_and } } -std::unique_ptr PulseAudioSource::Create( - std::string_view name, std::shared_ptr zenMainloop, - std::shared_ptr scriptContext) { +std::unique_ptr PulseAudioSource::Create(std::shared_ptr zenMainloop) { auto mainloop = pa_threaded_mainloop_new(); if (!mainloop) return nullptr; pa_threaded_mainloop_lock(mainloop); @@ -59,7 +57,7 @@ std::unique_ptr PulseAudioSource::Create( return nullptr; } auto backend = std::unique_ptr( - new PulseAudioSource(name, zenMainloop, mainloop, scriptContext, api, context)); + new PulseAudioSource(zenMainloop, mainloop, api, context)); // From now on the backend will free on error if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) { @@ -144,10 +142,20 @@ void PulseAudioSource::OnSinkChange(const pa_sink_info* info) { break; } } - if (newState == m_sourceState) return; - spdlog::debug("Audio source is dirty"); - m_sourceDirtyFlag = true; - m_sourceState = newState; - m_scriptContext->Publish(m_name, m_sourceState); - m_zenMainloop->Wakeup(); + { + std::lock_guard lock(m_mutex); + if (newState == m_sourceState) return; + spdlog::debug("Audio source is dirty"); + m_drawn = m_published = false; + m_sourceState = newState; + m_zenMainloop->Wakeup(); + } +} + +// This is invoked on main thread. Can not publish on another thread +void PulseAudioSource::Publish(const std::string_view sourceName, ScriptContext& scriptContext) { + std::lock_guard lock(m_mutex); + if (m_published) return; + scriptContext.Publish(sourceName, m_sourceState); + m_published = true; } diff --git a/src/PulseAudioSource.h b/src/PulseAudioSource.h index ab7270d..795eafe 100644 --- a/src/PulseAudioSource.h +++ b/src/PulseAudioSource.h @@ -11,34 +11,25 @@ struct pa_threaded_mainloop; struct pa_mainloop_api; struct pa_server_info; struct pa_sink_info; -struct pa_source_info; class PulseAudioSource : public Source { public: - static std::unique_ptr Create(std::string_view name, - std::shared_ptr zenMainloop, - std::shared_ptr scriptContext); + static std::unique_ptr Create(std::shared_ptr zenMainloop); virtual ~PulseAudioSource(); void OnStateChange(); void OnServerChange(const pa_server_info*); void OnSinkChange(const pa_sink_info*); + void Publish(const std::string_view sourceName, ScriptContext& scriptContext) override; private: - PulseAudioSource(std::string_view name, std::shared_ptr mainloop, - pa_threaded_mainloop* mainLoop, std::shared_ptr scriptContext, + PulseAudioSource(std::shared_ptr mainloop, pa_threaded_mainloop* mainLoop, pa_mainloop_api* api, pa_context* ctx) - : m_name(name), - m_zenMainloop(mainloop), - m_mainLoop(mainLoop), - m_scriptContext(scriptContext), - m_api(api), - m_ctx(ctx) {} - const std::string m_name; + : m_zenMainloop(mainloop), m_mainLoop(mainLoop), m_api(api), m_ctx(ctx) {} std::shared_ptr m_zenMainloop; pa_threaded_mainloop* m_mainLoop; - std::shared_ptr m_scriptContext; pa_mainloop_api* m_api; pa_context* m_ctx; AudioState m_sourceState; + std::mutex m_mutex; }; diff --git a/src/Seat.cpp b/src/Seat.cpp index 087a840..03d5425 100644 --- a/src/Seat.cpp +++ b/src/Seat.cpp @@ -45,6 +45,7 @@ static const wl_pointer_listener pointer_listener = { .axis_source = nullptr, .axis_stop = nullptr, .axis_discrete = nullptr, + // Version > supported //.axis_value120 = nullptr, //.axis_relative_direction = nullptr, }; @@ -102,18 +103,17 @@ std::unique_ptr Keyboard::Create(std::shared_ptr mainloop, w } void Keyboard::SetLayout(const char* layout) { - m_sourceDirtyFlag = m_sourceState.layout != layout; - if (m_sourceDirtyFlag) { + if (m_sourceState.layout != layout) { + m_drawn = m_published = false; m_sourceState.layout = layout; - if (m_scriptContext) m_scriptContext->Publish(m_sourceName, m_sourceState); m_mainloop->Wakeup(); } } -void Keyboard::SetScriptContext(const std::string_view sourceName, - std::shared_ptr scriptContext) { - m_sourceName = sourceName; - m_scriptContext = scriptContext; - m_scriptContext->Publish(m_sourceName, m_sourceState); + +void Keyboard::Publish(const std::string_view sourceName, ScriptContext& scriptContext) { + if (m_published) return; + scriptContext.Publish(sourceName, m_sourceState); + m_published = true; } std::unique_ptr Seat::Create(std::shared_ptr mainLoop, wl_seat* wlseat) { diff --git a/src/Seat.h b/src/Seat.h index 9140744..00c0e96 100644 --- a/src/Seat.h +++ b/src/Seat.h @@ -21,15 +21,12 @@ class Keyboard : public Source { } static std::unique_ptr Create(std::shared_ptr mainloop, wl_seat* seat); void SetLayout(const char* layout); - void SetScriptContext(const std::string_view name, - std::shared_ptr scriptContext); + void Publish(const std::string_view sourceName, ScriptContext& scriptContext) override; private: std::shared_ptr m_mainloop; wl_keyboard* m_wlkeyboard; KeyboardState m_sourceState; - std::string m_sourceName; - std::shared_ptr m_scriptContext; }; class Pointer { diff --git a/src/Sources.cpp b/src/Sources.cpp index ad90d5d..8ff3aab 100644 --- a/src/Sources.cpp +++ b/src/Sources.cpp @@ -2,30 +2,38 @@ #include "spdlog/spdlog.h" -std::unique_ptr Sources::Create() { return std::unique_ptr(new Sources()); } +std::unique_ptr Sources::Create(std::unique_ptr scriptContext) { + return std::unique_ptr(new Sources(std::move(scriptContext))); +} void Sources::Register(std::string_view name, std::shared_ptr source) { m_sources[std::string(name)] = source; } -bool Sources::IsDirty(const std::set sources) const { +bool Sources::NeedsRedraw(const std::set sources) const { for (const auto& name : sources) { - if (m_sources.contains(name) && m_sources.at(name)->IsSourceDirty()) { - spdlog::trace("Source {} is dirty", name); + if (m_sources.contains(name) && !m_sources.at(name)->IsDrawn()) { + spdlog::trace("Source {} needs render", name); return true; } } return false; } -void Sources::CleanAll() { +void Sources::SetAllDrawn() { + for (auto const& source : m_sources) { + source.second->SetDrawn(); + } +} + +void Sources::ForceRedraw() { for (auto const& source : m_sources) { - source.second->CleanDirtySource(); + source.second->ClearDrawn(); } } -void Sources::DirtyAll() { +void Sources::PublishAll() { for (auto const& source : m_sources) { - source.second->ForceDirtySource(); + source.second->Publish(source.first, *m_scriptContext); } } diff --git a/src/Sources.h b/src/Sources.h index 90c6501..7a739e5 100644 --- a/src/Sources.h +++ b/src/Sources.h @@ -5,27 +5,36 @@ #include #include +#include "src/ScriptContext.h" + class Source { public: - virtual bool IsSourceDirty() const { return m_sourceDirtyFlag; } - virtual void CleanDirtySource() { m_sourceDirtyFlag = false; } - virtual void ForceDirtySource() { m_sourceDirtyFlag = true; } + void SetDrawn() { m_drawn = true; } + void ClearDrawn() { m_drawn = false; } + bool IsDrawn() { return m_drawn; } + + virtual void Publish(const std::string_view sourceName, ScriptContext& scriptContext) = 0; virtual ~Source() {} protected: - bool m_sourceDirtyFlag; + bool m_drawn; // Set to false to indicate that source needs to be redrawn + bool m_published; // Set to false to indicate that there is a new state to be published }; // Maintains set of sources class Sources { public: - static std::unique_ptr Create(); + static std::unique_ptr Create(std::unique_ptr scriptContext); void Register(std::string_view name, std::shared_ptr source); bool IsRegistered(const std::string& name) { return m_sources.find(name) != m_sources.end(); } - void CleanAll(); - void DirtyAll(); - bool IsDirty(const std::set sources) const; + void SetAllDrawn(); + void ForceRedraw(); + bool NeedsRedraw(const std::set sources) const; + void PublishAll(); private: + Sources(std::unique_ptr scriptContext) + : m_scriptContext(std::move(scriptContext)) {} std::map> m_sources; + std::unique_ptr m_scriptContext; }; diff --git a/src/SwayCompositor.cpp b/src/SwayCompositor.cpp index d887d32..cdfd8cc 100644 --- a/src/SwayCompositor.cpp +++ b/src/SwayCompositor.cpp @@ -132,20 +132,20 @@ static void ParseApplication(Workspace &workspace, nlohmann::basic_json<> applic workspace.applications.push_back(std::move(application)); } -static void ParseTree(const std::string &payload, Manager &manager) { +static std::optional ParseTree(const std::string &payload) { auto rootNode = json::parse(payload, Filter, false /*ignoring exceptions*/); if (rootNode.is_discarded()) { spdlog::error("Failed to parse Sway tree"); - return; + return {}; } if (!IsNodeType(rootNode, "root")) { spdlog::error("Sway tree root node should be of value root"); - return; + return {}; } auto outputNodes = rootNode["nodes"]; if (!IsArray(outputNodes)) { spdlog::error("Sway tree has invalid nodes"); - return; + return {}; } // Iterate over displays/outputs Displays displays; @@ -191,11 +191,10 @@ static void ParseTree(const std::string &payload, Manager &manager) { } displays.push_back(std::move(display)); } - manager.Publish(displays); + return displays; } -std::shared_ptr SwayCompositor::Connect(MainLoop &mainLoop, - std::shared_ptr manager) { +std::shared_ptr SwayCompositor::Connect(MainLoop &mainLoop, Visibility visibility) { auto path = getenv("SWAYSOCK"); spdlog::debug("Connecting to sway at {}", path); auto fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -211,7 +210,7 @@ std::shared_ptr SwayCompositor::Connect(MainLoop &mainLoop, close(fd); return nullptr; } - auto t = std::shared_ptr(new SwayCompositor(fd, manager)); + auto t = std::shared_ptr(new SwayCompositor(fd, visibility)); mainLoop.Register(fd, "Sway", t); t->Initialize(); return t; @@ -250,13 +249,16 @@ bool SwayCompositor::OnRead() { m_payload.resize(len + 1); auto msg = Message(*((uint32_t *)(hdr + MAGIC_LENGTH + 4))); read(m_fd, m_payload.data(), len); - bool isDirty = false; switch (msg) { - case Message::GET_TREE: + case Message::GET_TREE: { spdlog::trace("Received sway tree"); - ParseTree(m_payload, *m_manager); - isDirty = true; + auto maybeDisplays = ParseTree(m_payload); + if (maybeDisplays) { + m_displays = std::move(*maybeDisplays); + m_drawn = m_published = false; + } // else, error! break; + } case Message::SUBSCRIBE: spdlog::debug("Sway subscriptions confirmed"); break; @@ -274,12 +276,8 @@ bool SwayCompositor::OnRead() { bool visible; ParseBarStateUpdateEvent(m_payload, visible); spdlog::debug("Sway bar state event, visible: {}", visible); - if (visible) { - m_manager->Show(); - } else { - m_manager->Hide(); - } - isDirty = true; + m_visibility(visible); + m_drawn = m_published = false; break; case Message::EVENT_SHUTDOWN: spdlog::trace("Sway shutdown event"); @@ -288,5 +286,11 @@ bool SwayCompositor::OnRead() { spdlog::error("Received unhandled sway message: {}", (uint32_t)msg); break; } - return isDirty; + // True if state changed + return !m_published; +} + +void SwayCompositor::Publish(const std::string_view sourceName, ScriptContext &scriptContext) { + scriptContext.Publish(sourceName, m_displays); + m_published = true; } diff --git a/src/SwayCompositor.h b/src/SwayCompositor.h index 627d760..f4f911c 100644 --- a/src/SwayCompositor.h +++ b/src/SwayCompositor.h @@ -3,20 +3,23 @@ #include #include "MainLoop.h" -#include "src/Manager.h" +#include "src/Sources.h" -class SwayCompositor : public IoHandler { +using Visibility = std::function; + +class SwayCompositor : public IoHandler, public Source { public: - static std::shared_ptr Connect(MainLoop& mainLoop, - std::shared_ptr manager); + static std::shared_ptr Connect(MainLoop& mainLoop, Visibility visibility); virtual ~SwayCompositor(); virtual bool OnRead() override; + void Publish(const std::string_view sourceName, ScriptContext& scriptContext) override; private: void Initialize(); - SwayCompositor(int fd, std::shared_ptr manager) : m_fd(fd), m_manager(manager) {} + SwayCompositor(int fd, Visibility visibility) : m_fd(fd), m_visibility(visibility) {} int m_fd; - std::shared_ptr m_manager; std::string m_payload; + Displays m_displays; + Visibility m_visibility; }; diff --git a/src/main.cpp b/src/main.cpp index ed4600c..bb64299 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,8 +42,7 @@ static const std::optional ProbeForConfig(int argc, char* } static void InitializeSource(const std::string& source, Sources& sources, - std::shared_ptr mainLoop, - std::shared_ptr scriptContext, const Registry& registry, + std::shared_ptr mainLoop, const Registry& registry, const Configuration config) { if (source == "date" || source == "time") { // Date time sources @@ -58,7 +57,7 @@ static void InitializeSource(const std::string& source, Sources& sources, return; } if (source == "networks") { - auto networkSource = NetworkSource::Create(source, *mainLoop, scriptContext); + auto networkSource = NetworkSource::Create(*mainLoop); if (!networkSource) { spdlog::error("Failed to initialize network source"); return; @@ -70,7 +69,7 @@ static void InitializeSource(const std::string& source, Sources& sources, if (source == "audio") { switch (config.audio.soundServer) { case SoundServer::PulseAudio: { - auto audioSource = PulseAudioSource::Create(source, mainLoop, scriptContext); + auto audioSource = PulseAudioSource::Create(mainLoop); if (!audioSource) { spdlog::error("Failed to initialize PulseAudio source"); return; @@ -84,7 +83,7 @@ static void InitializeSource(const std::string& source, Sources& sources, } } if (source == "power") { - auto powerSource = PowerSource::Create(source, *mainLoop, scriptContext); + auto powerSource = PowerSource::Create(*mainLoop); if (!powerSource) { spdlog::error("Failed to initialize battery source"); return; @@ -97,7 +96,6 @@ static void InitializeSource(const std::string& source, Sources& sources, if (source == "keyboard") { if (registry.seat && registry.seat->keyboard) { sources.Register(source, registry.seat->keyboard); - registry.seat->keyboard->SetScriptContext(source, scriptContext); } else { spdlog::warn("No keyboard source"); } @@ -110,7 +108,7 @@ int main(int argc, char* argv[]) { // Environment variable configurable logging spdlog::cfg::load_env_levels(); // Initialize Lua context - auto scriptContext = std::shared_ptr(ScriptContext::Create()); + auto scriptContext = ScriptContext::Create(); if (!scriptContext) { spdlog::error("Failed to create script context"); return -1; @@ -143,36 +141,48 @@ int main(int argc, char* argv[]) { return -1; } // Initialize sources - auto sources = Sources::Create(); + auto sources = Sources::Create(std::move(scriptContext)); + scriptContext = nullptr; for (auto panelConfig : config->panels) { // Check what sources are needed for the widgets in the panel for (const auto& widgetConfig : panelConfig.widgets) { for (const auto& source : widgetConfig.sources) { // Initialize source if not already done if (!sources->IsRegistered(source)) { - InitializeSource(source, *sources, mainLoop, scriptContext, *registry, *config); + InitializeSource(source, *sources, mainLoop, *registry, *config); } } } } // Manager handles displays and redrawing - auto manager = - Manager::Create(registry, "displays", *mainLoop, std::move(sources), scriptContext); + std::shared_ptr manager = Manager::Create(registry); // Initialize compositor switch (config->displays.compositor) { case Compositor::Sway: { - auto sway = SwayCompositor::Connect(*mainLoop, manager); + auto sway = SwayCompositor::Connect(*mainLoop, [manager](bool visible) { + if (visible) { + manager->Show(); + } else { + manager->Hide(); + } + }); if (!sway) { spdlog::error("Failed to connect to Sway"); return -1; } - // Leave the rest to main loop - mainLoop->Run(); + sources->Register("displays", sway); break; } default: spdlog::error("Unsupported window manager"); break; } + // Make sure that an initial state of all sources are published + sources->PublishAll(); + // Let over control to mainloop and manager + manager->SetSources(std::move(sources)); + sources = nullptr; + mainLoop->RegisterBatchHandler(manager); + mainLoop->Run(); return 0; }