Send update dispatcher though FFI

pull/2573/head
Crom (Thibaut CHARLES) 2023-10-17 18:11:48 +02:00
parent 088ca6b963
commit d86059016e
No known key found for this signature in database
GPG Key ID: 45A3D5F880B9E6D0
6 changed files with 50 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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