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.
This commit is contained in:
Chase Geigle 2018-09-01 00:09:48 -05:00
parent 15e444abe7
commit 42fda5b105
No known key found for this signature in database
GPG Key ID: 1020EF3A7626F7DC
16 changed files with 242 additions and 64 deletions

View File

@ -1,5 +1,6 @@
#pragma once
#include <list>
#include <moodycamel/blockingconcurrentqueue.h>
#include <thread>
@ -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<SIGN_PRIORITY_CONTROLLER, signals::eve
* @brief Misc threads
*/
vector<std::thread> m_threads;
/**
* @brief Loaded plugins
*/
std::list<plugin_handle> m_plugins;
};
POLYBAR_NS_END

View File

@ -7,23 +7,25 @@ POLYBAR_NS
namespace modules {
using factory_map = std::unordered_map<string, std::function<module_interface*(const bar_settings&, string)>>;
using factory_function = module_interface* (*)(const bar_settings&, string);
using factory_map = std::unordered_map<string, factory_function>;
factory_map& get_factory_map();
template <class Module>
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> 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> MODULE_TYPE##_registration(MODULE_NAME, &MODULE_TYPE##_create)
POLYBAR_NS_END

37
include/utils/plugin.hpp Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <array>
#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<const char*, 8> 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

38
include/utils/restack.hpp Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <unordered_map>
#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<std::string, std::unique_ptr<wm_restacker>>;
restacker_map& get_restacker_map();
const wm_restacker* get_restacker(const std::string& name);
template <class Restacker>
struct restacker_registration {
restacker_registration(std::string name) {
get_restacker_map()[std::move(name)] = std::make_unique<Restacker>();
}
};
#define POLYBAR_RESTACKER(RESTACKER_TYPE, RESTACKER_NAME) \
restacker_registration<RESTACKER_TYPE> RESTACKER_TYPE##_registration(RESTACKER_NAME)
} // namespace restack
POLYBAR_NS_END

View File

@ -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)

@ -1 +1 @@
Subproject commit d4e4786be35b48d72dc7e59cf85ec34a90d129b5
Subproject commit 21ce9060ac7c502225fdbd2f200b1cbdd8eca08d

View File

@ -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

View File

@ -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)

View File

@ -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");
}
}

View File

@ -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};

View File

@ -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)

View File

@ -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()

View File

@ -1,8 +1,10 @@
#include <sys/un.h>
#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

View File

@ -2,8 +2,10 @@
#include <i3ipc++/ipc.hpp>
#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

35
src/utils/plugin.cpp Normal file
View File

@ -0,0 +1,35 @@
#include <dlfcn.h>
#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

24
src/utils/restack.cpp Normal file
View File

@ -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