Send update dispatcher though FFI
parent
088ca6b963
commit
d86059016e
|
@ -17,6 +17,7 @@ class AModule : public IModule {
|
||||||
operator Gtk::Widget &() override;
|
operator Gtk::Widget &() override;
|
||||||
auto doAction(const std::string &name) -> void override;
|
auto doAction(const std::string &name) -> void override;
|
||||||
|
|
||||||
|
/// Emitting on this dispatcher triggers a update() call
|
||||||
Glib::Dispatcher dp;
|
Glib::Dispatcher dp;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -22,21 +22,21 @@ class CFFI : public AModule {
|
||||||
CFFI(const std::string&, const std::string&, const Json::Value&);
|
CFFI(const std::string&, const std::string&, const Json::Value&);
|
||||||
virtual ~CFFI();
|
virtual ~CFFI();
|
||||||
|
|
||||||
// virtual auto update() -> void override;
|
|
||||||
// virtual operator Gtk::Widget&() override;
|
|
||||||
|
|
||||||
virtual auto refresh(int signal) -> void override;
|
virtual auto refresh(int signal) -> void override;
|
||||||
virtual auto doAction(const std::string& name) -> void override;
|
virtual auto doAction(const std::string& name) -> void override;
|
||||||
|
virtual auto update() -> void override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
///
|
///
|
||||||
void* cffi_instance_ = nullptr;
|
void* cffi_instance_ = nullptr;
|
||||||
|
|
||||||
typedef void*(InitFn)(GtkContainer*, const struct wbcffi_config_entry* config_entries,
|
typedef void*(InitFn)(GtkContainer* root_widget, const void (*trigger_update)(void*),
|
||||||
|
void* trigger_update_arg, const struct wbcffi_config_entry* config_entries,
|
||||||
size_t config_entries_len);
|
size_t config_entries_len);
|
||||||
typedef void(DenitFn)(void* instance);
|
typedef void(DenitFn)(void* instance);
|
||||||
typedef void(RefreshFn)(void* instance, int signal);
|
typedef void(RefreshFn)(void* instance, int signal);
|
||||||
typedef void(DoActionFn)(void* instance, const char* name);
|
typedef void(DoActionFn)(void* instance, const char* name);
|
||||||
|
typedef void(UpdateFn)(void* instance);
|
||||||
|
|
||||||
// FFI hooks
|
// FFI hooks
|
||||||
struct {
|
struct {
|
||||||
|
@ -44,6 +44,7 @@ class CFFI : public AModule {
|
||||||
std::function<DenitFn> deinit = nullptr;
|
std::function<DenitFn> deinit = nullptr;
|
||||||
std::function<RefreshFn> refresh = [](void*, int) {};
|
std::function<RefreshFn> refresh = [](void*, int) {};
|
||||||
std::function<DoActionFn> doAction = [](void*, const char*) {};
|
std::function<DoActionFn> doAction = [](void*, const char*) {};
|
||||||
|
std::function<UpdateFn> update = [](void*) {};
|
||||||
} hooks_;
|
} hooks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ Most language can implement the required functions and constants (C, C++, Rust,
|
||||||
Go, Python, ...), meaning you can develop custom modules using your language of
|
Go, Python, ...), meaning you can develop custom modules using your language of
|
||||||
choice, as long as there's GTK bindings.
|
choice, as long as there's GTK bindings.
|
||||||
|
|
||||||
|
Symbols to implement are documented in the
|
||||||
|
[waybar_cffi_module.h](waybar_cffi_module.h) file.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
## Building this module
|
## Building this module
|
||||||
|
|
|
@ -19,7 +19,8 @@ void onclicked(GtkButton* button) {
|
||||||
// You must
|
// You must
|
||||||
const size_t wbcffi_version = 1;
|
const size_t wbcffi_version = 1;
|
||||||
|
|
||||||
void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_entries,
|
void* wbcffi_init(GtkContainer* root_widget, void (*trigger_update)(void*),
|
||||||
|
void* trigger_update_arg, const struct wbcffi_config_entry* config_entries,
|
||||||
size_t config_entries_len) {
|
size_t config_entries_len) {
|
||||||
printf("cffi_example: init config:\n");
|
printf("cffi_example: init config:\n");
|
||||||
for (size_t i = 0; i < config_entries_len; i++) {
|
for (size_t i = 0; i < config_entries_len; i++) {
|
||||||
|
@ -28,7 +29,7 @@ void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_e
|
||||||
|
|
||||||
// Allocate the instance object
|
// Allocate the instance object
|
||||||
Instance* inst = malloc(sizeof(Instance));
|
Instance* inst = malloc(sizeof(Instance));
|
||||||
inst->root = root;
|
inst->root = root_widget;
|
||||||
|
|
||||||
// Add a container for displaying the next widgets
|
// Add a container for displaying the next widgets
|
||||||
inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5));
|
inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5));
|
||||||
|
@ -51,13 +52,18 @@ void* wbcffi_init(GtkContainer* root, const struct wbcffi_config_entry* config_e
|
||||||
printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count);
|
printf("cffi_example inst=%p: init success ! (%d total instances)\n", inst, ++instance_count);
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wbcffi_deinit(void* instance) {
|
void wbcffi_deinit(void* instance) {
|
||||||
printf("cffi_example inst=%p: free memory\n", instance);
|
printf("cffi_example inst=%p: free memory\n", instance);
|
||||||
free(instance);
|
free(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wbcffi_update(void* instance) { printf("cffi_example inst=%p: Update request\n", instance); }
|
||||||
|
|
||||||
void wbcffi_refresh(void* instance, int signal) {
|
void wbcffi_refresh(void* instance, int signal) {
|
||||||
printf("cffi_example inst=%p: Received refresh signal %d\n", instance, signal);
|
printf("cffi_example inst=%p: Received refresh signal %d\n", instance, signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wbcffi_doaction(void* instance, const char* name) {
|
void wbcffi_doaction(void* instance, const char* name) {
|
||||||
printf("cffi_example inst=%p: doAction(%s)\n", instance, name);
|
printf("cffi_example inst=%p: doAction(%s)\n", instance, name);
|
||||||
}
|
}
|
|
@ -23,12 +23,16 @@ struct wbcffi_config_entry {
|
||||||
/// MANDATORY CFFI function
|
/// MANDATORY CFFI function
|
||||||
///
|
///
|
||||||
/// @param root_widget Root GTK widget instantiated by Waybar
|
/// @param root_widget Root GTK widget instantiated by Waybar
|
||||||
|
/// @param trigger_update Call this function with trigger_update_arg as argument to trigger
|
||||||
|
/// wbcffi_update() on the next GTK main event loop iteration
|
||||||
|
/// @param trigger_update_arg Argument for trigger_update call
|
||||||
/// @param config_entries Flat representation of the module JSON config. The data only available
|
/// @param config_entries Flat representation of the module JSON config. The data only available
|
||||||
/// during wbcffi_init call.
|
/// during wbcffi_init call.
|
||||||
/// @param config_entries_len Number of entries in `config_entries`
|
/// @param config_entries_len Number of entries in `config_entries`
|
||||||
///
|
///
|
||||||
/// @return A untyped pointer to module data, NULL if the module failed to load.
|
/// @return A untyped pointer to module data, NULL if the module failed to load.
|
||||||
void* wbcffi_init(GtkContainer* root_widget, const struct wbcffi_config_entry* config_entries,
|
void* wbcffi_init(GtkContainer* root_widget, const void (*trigger_update)(void*),
|
||||||
|
void* trigger_update_arg, const struct wbcffi_config_entry* config_entries,
|
||||||
size_t config_entries_len);
|
size_t config_entries_len);
|
||||||
|
|
||||||
/// Module deinit/delete function, called when Waybar is closed or when the module is removed
|
/// Module deinit/delete function, called when Waybar is closed or when the module is removed
|
||||||
|
@ -38,8 +42,15 @@ void* wbcffi_init(GtkContainer* root_widget, const struct wbcffi_config_entry* c
|
||||||
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||||
void wbcffi_deinit(void* instance);
|
void wbcffi_deinit(void* instance);
|
||||||
|
|
||||||
/// When Waybar receives a POSIX signal, it forwards the signal to each module, calling this
|
/// Called from the GTK main event loop, to update the UI
|
||||||
/// function
|
///
|
||||||
|
/// Optional CFFI function
|
||||||
|
///
|
||||||
|
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||||
|
/// @param action_name Action name
|
||||||
|
void wbcffi_update(void* instance);
|
||||||
|
|
||||||
|
/// Called when Waybar receives a POSIX signal and forwards it to each module
|
||||||
///
|
///
|
||||||
/// Optional CFFI function
|
/// Optional CFFI function
|
||||||
///
|
///
|
||||||
|
@ -47,12 +58,14 @@ void wbcffi_deinit(void* instance);
|
||||||
/// @param signal Signal ID
|
/// @param signal Signal ID
|
||||||
void wbcffi_refresh(void* instance, int signal);
|
void wbcffi_refresh(void* instance, int signal);
|
||||||
|
|
||||||
|
/// Called on module action (see
|
||||||
|
/// https://github.com/Alexays/Waybar/wiki/Configuration#module-actions-config)
|
||||||
///
|
///
|
||||||
/// Optional CFFI function
|
/// Optional CFFI function
|
||||||
///
|
///
|
||||||
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
/// @param instance Module instance data (as returned by `wbcffi_init`)
|
||||||
/// @param name Action name
|
/// @param action_name Action name
|
||||||
void wbcffi_doaction(void* instance, const char* name);
|
void wbcffi_doaction(void* instance, const char* action_name);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,9 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
|
||||||
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
|
throw std::runtime_error{std::string{"Missing wbcffi_deinit function: "} + dlerror()};
|
||||||
}
|
}
|
||||||
// Optional functions
|
// Optional functions
|
||||||
|
if (auto fn = reinterpret_cast<UpdateFn*>(dlsym(handle, "wbcffi_update"))) {
|
||||||
|
hooks_.update = fn;
|
||||||
|
}
|
||||||
if (auto fn = reinterpret_cast<RefreshFn*>(dlsym(handle, "wbcffi_refresh"))) {
|
if (auto fn = reinterpret_cast<RefreshFn*>(dlsym(handle, "wbcffi_refresh"))) {
|
||||||
hooks_.refresh = fn;
|
hooks_.refresh = fn;
|
||||||
}
|
}
|
||||||
|
@ -70,8 +73,10 @@ CFFI::CFFI(const std::string& name, const std::string& id, const Json::Value& co
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call init
|
// Call init
|
||||||
cffi_instance_ = hooks_.init(dynamic_cast<Gtk::Container*>(&event_box_)->gobj(),
|
cffi_instance_ = hooks_.init(
|
||||||
config_entries.data(), config_entries.size());
|
dynamic_cast<Gtk::Container*>(&event_box_)->gobj(),
|
||||||
|
[](void* dp) { ((Glib::Dispatcher*)dp)->emit(); }, &dp, config_entries.data(),
|
||||||
|
config_entries.size());
|
||||||
|
|
||||||
// Handle init failures
|
// Handle init failures
|
||||||
if (cffi_instance_ == nullptr) {
|
if (cffi_instance_ == nullptr) {
|
||||||
|
@ -85,6 +90,14 @@ CFFI::~CFFI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto CFFI::update() -> void {
|
||||||
|
assert(cffi_instance_ != nullptr);
|
||||||
|
hooks_.update(cffi_instance_);
|
||||||
|
|
||||||
|
// Execute the on-update command set in config
|
||||||
|
AModule::update();
|
||||||
|
}
|
||||||
|
|
||||||
auto CFFI::refresh(int signal) -> void {
|
auto CFFI::refresh(int signal) -> void {
|
||||||
assert(cffi_instance_ != nullptr);
|
assert(cffi_instance_ != nullptr);
|
||||||
hooks_.refresh(cffi_instance_, signal);
|
hooks_.refresh(cffi_instance_, signal);
|
||||||
|
@ -93,15 +106,7 @@ auto CFFI::refresh(int signal) -> void {
|
||||||
auto CFFI::doAction(const std::string& name) -> void {
|
auto CFFI::doAction(const std::string& name) -> void {
|
||||||
assert(cffi_instance_ != nullptr);
|
assert(cffi_instance_ != nullptr);
|
||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
// TODO: Make a decision
|
|
||||||
// Calling AModule::doAction and hooks_.doAction will execute the action twice if it is
|
|
||||||
// configured in AModule::eventActionMap_ and implemented in the CFFI module.
|
|
||||||
//
|
|
||||||
// Should we block AModule::doAction() if the action is implemented in hooks_.doAction() ?
|
|
||||||
// (doAction could return true if the action has been processed)
|
|
||||||
//
|
|
||||||
hooks_.doAction(cffi_instance_, name.c_str());
|
hooks_.doAction(cffi_instance_, name.c_str());
|
||||||
AModule::doAction(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue