From 42fda5b105a0cdcb47b06adcb98e263b1d44932f Mon Sep 17 00:00:00 2001 From: Chase Geigle Date: Sat, 1 Sep 2018 00:09:48 -0500 Subject: [PATCH] feat(plugin): Add initial draft plugin architecture. Modules and other features that require optional libraries are now dynamically loaded on the construction of the controller via dlopen(). This allows polybar to be built with support for all of the features on one machine, but gracefully fall back and disable those features on machines where the required optional shared libraries are not found. --- include/components/controller.hpp | 7 ++++++ include/modules/meta/factory.hpp | 16 +++++++------ include/utils/plugin.hpp | 37 ++++++++++++++++++++++++++++++ include/utils/restack.hpp | 38 +++++++++++++++++++++++++++++++ lib/CMakeLists.txt | 2 -- lib/i3ipcpp | 2 +- src/CMakeLists.txt | 2 +- src/components/CMakeLists.txt | 5 +--- src/components/bar.cpp | 26 +++------------------ src/components/controller.cpp | 12 +++++++++- src/modules/CMakeLists.txt | 33 +++++++++------------------ src/utils/CMakeLists.txt | 25 +++++++++++++++++++- src/utils/bspwm.cpp | 19 +++++++++++++++- src/utils/i3.cpp | 23 ++++++++++++++++++- src/utils/plugin.cpp | 35 ++++++++++++++++++++++++++++ src/utils/restack.cpp | 24 +++++++++++++++++++ 16 files changed, 242 insertions(+), 64 deletions(-) create mode 100644 include/utils/plugin.hpp create mode 100644 include/utils/restack.hpp create mode 100644 src/utils/plugin.cpp create mode 100644 src/utils/restack.cpp diff --git a/include/components/controller.hpp b/include/components/controller.hpp index 3b25b099..e6e14aa1 100644 --- a/include/components/controller.hpp +++ b/include/components/controller.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -9,6 +10,7 @@ #include "events/signal_receiver.hpp" #include "events/types.hpp" #include "utils/file.hpp" +#include "utils/plugin.hpp" #include "x11/types.hpp" POLYBAR_NS @@ -142,6 +144,11 @@ class controller : public signal_receiver m_threads; + + /** + * @brief Loaded plugins + */ + std::list m_plugins; }; POLYBAR_NS_END diff --git a/include/modules/meta/factory.hpp b/include/modules/meta/factory.hpp index 8d140e1c..9e8e3f89 100644 --- a/include/modules/meta/factory.hpp +++ b/include/modules/meta/factory.hpp @@ -7,23 +7,25 @@ POLYBAR_NS namespace modules { - using factory_map = std::unordered_map>; + using factory_function = module_interface* (*)(const bar_settings&, string); + using factory_map = std::unordered_map; factory_map& get_factory_map(); template class module_registration { public: - module_registration(string name) { - get_factory_map()[std::move(name)] = [](const bar_settings& bar, string module_name) { - return new Module(bar, std::move(module_name)); - }; + module_registration(string name, factory_function ff) { + get_factory_map()[std::move(name)] = ff; } }; module_interface* make_module(string&& name, const bar_settings& bar, string module_name); } // namespace modules -#define POLYBAR_MODULE(MODULE_TYPE, MODULE_NAME) \ - module_registration MODULE_TYPE ## _registration{MODULE_NAME} +#define POLYBAR_MODULE(MODULE_TYPE, MODULE_NAME) \ + module_interface* MODULE_TYPE##_create(const bar_settings& bar, std::string module_name) { \ + return new MODULE_TYPE(bar, std::move(module_name)); \ + } \ + module_registration MODULE_TYPE##_registration(MODULE_NAME, &MODULE_TYPE##_create) POLYBAR_NS_END diff --git a/include/utils/plugin.hpp b/include/utils/plugin.hpp new file mode 100644 index 00000000..853cdf58 --- /dev/null +++ b/include/utils/plugin.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include "common.hpp" +#include "components/logger.hpp" + +POLYBAR_NS + +class plugin_handle { + public: + plugin_handle(const char* libname); + ~plugin_handle(); + + plugin_handle(const plugin_handle&) = delete; + plugin_handle& operator=(const plugin_handle&) = delete; + + plugin_handle(plugin_handle&&); + plugin_handle& operator=(plugin_handle&&); + + private: + void* m_handle = nullptr; +}; + +// clang-format off +const static std::array plugin_names = { + "libpolybar-utils-i3.so", + "libpolybar-modules-alsa.so", + "libpolybar-modules-github.so", + "libpolybar-modules-i3.so", + "libpolybar-modules-mpd.so", + "libpolybar-modules-network.so", + "libpolybar-modules-pulseaudio.so", + "libpolybar-modules-xkeyboard.so" +}; +// clang-format on + +POLYBAR_NS_END diff --git a/include/utils/restack.hpp b/include/utils/restack.hpp new file mode 100644 index 00000000..067be38e --- /dev/null +++ b/include/utils/restack.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "common.hpp" +#include "components/types.hpp" +#include "x11/extensions/randr.hpp" + +POLYBAR_NS + +class connection; +class logger; + +namespace restack { + + struct wm_restacker { + virtual void operator()(connection& conn, const bar_settings& opts, const logger& log) const = 0; + }; + + using restacker_map = std::unordered_map>; + + restacker_map& get_restacker_map(); + + const wm_restacker* get_restacker(const std::string& name); + + template + struct restacker_registration { + restacker_registration(std::string name) { + get_restacker_map()[std::move(name)] = std::make_unique(); + } + }; + +#define POLYBAR_RESTACKER(RESTACKER_TYPE, RESTACKER_NAME) \ + restacker_registration RESTACKER_TYPE##_registration(RESTACKER_NAME) + +} // namespace restack + +POLYBAR_NS_END diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ed5c9204..a089a1aa 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,8 +39,6 @@ list(APPEND dirs ${XPP_INCLUDE_DIRS}) if(ENABLE_I3) add_subdirectory(i3ipcpp) - list(APPEND libs ${I3IPCPP_LIBRARIES}) - list(APPEND dirs ${I3IPCPP_INCLUDE_DIRS}) set_property(TARGET i3ipc++ PROPERTY POSITION_INDEPENDENT_CODE ON) set(I3IPCPP_LIBRARIES ${I3IPCPP_LIBRARIES} PARENT_SCOPE) set(I3IPCPP_INCLUDE_DIRS ${I3IPCPP_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/lib/i3ipcpp b/lib/i3ipcpp index d4e4786b..21ce9060 160000 --- a/lib/i3ipcpp +++ b/lib/i3ipcpp @@ -1 +1 @@ -Subproject commit d4e4786be35b48d72dc7e59cf85ec34a90d129b5 +Subproject commit 21ce9060ac7c502225fdbd2f200b1cbdd8eca08d diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 146644c1..35371390 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,7 +23,7 @@ make_executable(polybar INCLUDE_DIRS ${dirs} TARGET_DEPENDS - ${shared_modules} + polybar-modules-internal_shared polybar-components_static polybar-drawtypes_static polybar-events_static diff --git a/src/components/CMakeLists.txt b/src/components/CMakeLists.txt index a64403d6..efd4e70c 100644 --- a/src/components/CMakeLists.txt +++ b/src/components/CMakeLists.txt @@ -13,11 +13,8 @@ make_library(polybar-components renderer.cpp screen.cpp taskqueue.cpp - ../utils/i3.cpp INCLUDE_DIRS ${dirs} - ${I3IPCPP_INCLUDE_DIRS} RAW_DEPENDS ${libs} - polybar-cairo_static - ${I3IPCPP_LIBRARIES}) + polybar-cairo_static) diff --git a/src/components/bar.cpp b/src/components/bar.cpp index cbd9ad13..2c80a11c 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -9,7 +9,7 @@ #include "components/types.hpp" #include "events/signal.hpp" #include "events/signal_emitter.hpp" -#include "utils/bspwm.hpp" +#include "utils/restack.hpp" #include "utils/color.hpp" #include "utils/factory.hpp" #include "utils/math.hpp" @@ -25,10 +25,6 @@ #include "x11/cursor.hpp" #endif -#if ENABLE_I3 -#include "utils/i3.hpp" -#endif - POLYBAR_NS using namespace signals::ui; @@ -437,26 +433,10 @@ void bar::restack_window() { return; } - auto restacked = false; - - if (wm_restack == "bspwm") { - restacked = bspwm_util::restack_to_root(m_connection, m_opts.monitor, m_opts.window); -#if ENABLE_I3 - } else if (wm_restack == "i3" && m_opts.override_redirect) { - restacked = i3_util::restack_to_root(m_connection, m_opts.window); - } else if (wm_restack == "i3" && !m_opts.override_redirect) { - m_log.warn("Ignoring restack of i3 window (not needed when `override-redirect = false`)"); - wm_restack.clear(); -#endif + if (auto restacker = restack::get_restacker(wm_restack)) { + (*restacker)(m_connection, m_opts, m_log); } else { m_log.warn("Ignoring unsupported wm-restack option '%s'", wm_restack); - wm_restack.clear(); - } - - if (restacked) { - m_log.info("Successfully restacked bar window"); - } else if (!wm_restack.empty()) { - m_log.err("Failed to restack bar window"); } } diff --git a/src/components/controller.cpp b/src/components/controller.cpp index 6a9bdc25..dd55972d 100644 --- a/src/components/controller.cpp +++ b/src/components/controller.cpp @@ -8,13 +8,14 @@ #include "components/types.hpp" #include "events/signal.hpp" #include "events/signal_emitter.hpp" +#include "modules/ipc.hpp" #include "modules/meta/event_handler.hpp" #include "modules/meta/factory.hpp" #include "modules/meta/input_handler.hpp" -#include "modules/ipc.hpp" #include "utils/command.hpp" #include "utils/factory.hpp" #include "utils/inotify.hpp" +#include "utils/plugin.hpp" #include "utils/string.hpp" #include "utils/time.hpp" #include "x11/connection.hpp" @@ -76,6 +77,15 @@ controller::controller(connection& conn, signal_emitter& emitter, const logger& sigaction(SIGUSR1, &act, nullptr); sigaction(SIGALRM, &act, nullptr); + m_log.trace("controller: Load plugins"); + for (const auto name : plugin_names) { + try { + m_plugins.emplace_back(name); + } catch (const application_error& err) { + m_log.warn("Failed to load plugin '%s': %s", name, err.what()); + } + } + m_log.trace("controller: Setup user-defined modules"); size_t created_modules{0}; diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index b2b967ea..186f34e2 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -1,6 +1,6 @@ if(ENABLE_ALSA) make_library(polybar-modules-alsa - SHARED + MODULE SOURCES ../adapters/alsa/control.cpp ../adapters/alsa/mixer.cpp @@ -10,12 +10,11 @@ if(ENABLE_ALSA) ${alsa-dirs} RAW_DEPENDS ${alsa-libs}) - list(APPEND shared_modules polybar-modules-alsa_shared) endif() if(ENABLE_CURL) make_library(polybar-modules-github - SHARED + MODULE SOURCES ../utils/http.cpp github.cpp @@ -24,12 +23,11 @@ if(ENABLE_CURL) ${curl-dirs} RAW_DEPENDS ${curl-libs}) - list(APPEND shared_modules polybar-modules-github_shared) endif() if(ENABLE_MPD) make_library(polybar-modules-mpd - SHARED + MODULE SOURCES ../adapters/mpd.cpp mpd.cpp @@ -38,13 +36,12 @@ if(ENABLE_MPD) ${mpd-dirs} RAW_DEPENDS ${mpd-libs}) - list(APPEND shared_modules polybar-modules-mpd_shared) endif() if(ENABLE_NETWORK) list(APPEND net-src ../adapters/net.cpp network.cpp) make_library(polybar-modules-network - SHARED + MODULE SOURCES ${net-src} INCLUDE_DIRS @@ -52,26 +49,21 @@ if(ENABLE_NETWORK) ${net-dirs} RAW_DEPENDS ${net-libs}) - list(APPEND shared_modules polybar-modules-network_shared) endif() if(ENABLE_I3) make_library(polybar-modules-i3 - SHARED + MODULE SOURCES - ../utils/i3.cpp i3.cpp INCLUDE_DIRS ${dirs} - ${I3IPCPP_INCLUDE_DIRS} - RAW_DEPENDS - ${I3IPCPP_LIBRARIES}) - list(APPEND shared_modules polybar-modules-i3_shared) + ${I3IPCPP_INCLUDE_DIRS}) endif() if(ENABLE_PULSEAUDIO) make_library(polybar-modules-pulseaudio - SHARED + MODULE SOURCES ../adapters/pulseaudio.cpp pulseaudio.cpp @@ -80,12 +72,11 @@ if(ENABLE_PULSEAUDIO) ${pulse-dirs} RAW_DEPENDS ${pulse-libs}) - list(APPEND shared_modules polybar-modules-pulseaudio_shared) endif() if(WITH_XKB) make_library(polybar-modules-xkeyboard - SHARED + MODULE SOURCES ../x11/extensions/xkb.cpp xkeyboard.cpp @@ -94,7 +85,6 @@ if(WITH_XKB) ${xkb-dirs} RAW_DEPENDS ${xkb-libs}) - list(APPEND shared_modules polybar-modules-xkeyboard_shared) endif() make_library(polybar-modules-internal @@ -120,7 +110,6 @@ make_library(polybar-modules-internal xwindow.cpp xworkspaces.cpp INCLUDE_DIRS - ${dirs}) -list(APPEND shared_modules polybar-modules-internal_shared) - -set(shared_modules ${shared_modules} PARENT_SCOPE) + ${dirs} + RAW_DEPENDS + polybar-utils-bspwm_shared) diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 2cd955d6..0f64482a 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -2,7 +2,6 @@ make_library(polybar-utils STATIC INTERNAL SOURCES - bspwm.cpp command.cpp concurrency.cpp env.cpp @@ -10,11 +9,35 @@ make_library(polybar-utils file.cpp inotify.cpp io.cpp + plugin.cpp process.cpp + restack.cpp socket.cpp string.cpp throttle.cpp INCLUDE_DIRS ${dirs} + RAW_DEPENDS + ${libs} + ${CMAKE_DL_LIBS}) + +make_library(polybar-utils-bspwm + SHARED + SOURCES + bspwm.cpp + INCLUDE_DIRS + ${dirs} RAW_DEPENDS ${libs}) + +if(ENABLE_I3) + make_library(polybar-utils-i3 + SHARED + SOURCES + i3.cpp + INCLUDE_DIRS + ${dirs} + ${I3IPCPP_INCLUDE_DIRS} + RAW_DEPENDS + ${I3IPCPP_LIBRARIES}) +endif() diff --git a/src/utils/bspwm.cpp b/src/utils/bspwm.cpp index 570b1753..783a3838 100644 --- a/src/utils/bspwm.cpp +++ b/src/utils/bspwm.cpp @@ -1,8 +1,10 @@ #include +#include "components/logger.hpp" #include "errors.hpp" #include "utils/bspwm.hpp" #include "utils/env.hpp" +#include "utils/restack.hpp" #include "x11/connection.hpp" POLYBAR_NS @@ -146,6 +148,21 @@ namespace bspwm_util { } return conn; } -} +} // namespace bspwm_util + +namespace restack { + struct bspwm_restacker : public wm_restacker { + void operator()(connection& conn, const bar_settings& opts, const logger& log) const override { + auto restacked = bspwm_util::restack_to_root(conn, opts.monitor, opts.window); + if (restacked) { + log.info("Successfully restacked bar window"); + } else { + log.err("Failed to restack bar window"); + } + } + }; + + POLYBAR_RESTACKER(bspwm_restacker, "bspwm"); +} // namespace restack POLYBAR_NS_END diff --git a/src/utils/i3.cpp b/src/utils/i3.cpp index d78f2fb2..32a4af72 100644 --- a/src/utils/i3.cpp +++ b/src/utils/i3.cpp @@ -2,8 +2,10 @@ #include #include "common.hpp" +#include "components/logger.hpp" #include "settings.hpp" #include "utils/i3.hpp" +#include "utils/restack.hpp" #include "utils/socket.hpp" #include "utils/string.hpp" #include "x11/connection.hpp" @@ -78,6 +80,25 @@ namespace i3_util { } return false; } -} +} // namespace i3_util + +namespace restack { + struct i3_restacker : public wm_restacker { + void operator()(connection& conn, const bar_settings& opts, const logger& log) const override { + if (opts.override_redirect) { + auto restacked = i3_util::restack_to_root(conn, opts.window); + if (restacked) { + log.info("Successfully restacked bar window"); + } else { + log.err("Failed to restack bar window"); + } + } else { + log.warn("Ignoring restack of i3 window (not needed when `override-redirect = false`)"); + } + } + }; + + POLYBAR_RESTACKER(i3_restacker, "i3"); +} // namespace restack POLYBAR_NS_END diff --git a/src/utils/plugin.cpp b/src/utils/plugin.cpp new file mode 100644 index 00000000..f078e937 --- /dev/null +++ b/src/utils/plugin.cpp @@ -0,0 +1,35 @@ +#include + +#include "components/logger.hpp" +#include "errors.hpp" +#include "utils/plugin.hpp" + +POLYBAR_NS + +plugin_handle::plugin_handle(const char* libname) { + m_handle = ::dlopen(libname, RTLD_NOW | RTLD_GLOBAL); + if (!m_handle) + throw application_error(::dlerror()); +} + +plugin_handle::~plugin_handle() { + if (m_handle) { + ::dlclose(m_handle); + } +} + +plugin_handle::plugin_handle(plugin_handle&& ph) { + m_handle = ph.m_handle; + ph.m_handle = nullptr; +} + +plugin_handle& plugin_handle::operator=(plugin_handle&& ph) { + if (m_handle) { + ::dlclose(m_handle); + } + m_handle = ph.m_handle; + ph.m_handle = nullptr; + return *this; +} + +POLYBAR_NS_END diff --git a/src/utils/restack.cpp b/src/utils/restack.cpp new file mode 100644 index 00000000..822337ec --- /dev/null +++ b/src/utils/restack.cpp @@ -0,0 +1,24 @@ +#include "utils/restack.hpp" + +POLYBAR_NS + +namespace restack { + + restacker_map& get_restacker_map() { + static restacker_map rmap; + return rmap; + } + + const wm_restacker* get_restacker(const std::string& name) { + const auto& rmap = get_restacker_map(); + auto it = rmap.find(name); + if (it != rmap.end()) { + return it->second.get(); + } else { + return nullptr; + } + } + +} // namespace restack + +POLYBAR_NS_END