mirror of
https://github.com/polybar/polybar.git
synced 2025-04-14 17:33:17 -04:00
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:
parent
15e444abe7
commit
42fda5b105
16 changed files with 242 additions and 64 deletions
|
@ -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
|
||||
|
|
|
@ -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
37
include/utils/plugin.hpp
Normal 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
38
include/utils/restack.hpp
Normal 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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
35
src/utils/plugin.cpp
Normal 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
24
src/utils/restack.cpp
Normal 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
|
Loading…
Add table
Reference in a new issue