diff --git a/cabi_example/main.c b/cabi_example/main.c new file mode 100644 index 00000000..babb3aea --- /dev/null +++ b/cabi_example/main.c @@ -0,0 +1,39 @@ +#include +#include + +typedef struct { + GtkContainer* root; + GtkBox* container; + GtkLabel* label; + GtkButton* button; +} Instance; + +// +const size_t wbcabi_version = 1; + +void onclicked() { printf("You clicked the button\n"); } + +void* wbcabi_init(GtkContainer* root) { + // Allocate the instance object + Instance* inst = malloc(sizeof(Instance)); + inst->root = root; + + // Add a container for displaying the next widgets + inst->container = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5)); + gtk_container_add(GTK_CONTAINER(inst->root), GTK_WIDGET(inst->container)); + + // Add a label + inst->label = GTK_LABEL(gtk_label_new("This is a button:")); + gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->label)); + + // Add a button + inst->button = GTK_BUTTON(gtk_button_new_with_label("click me !")); + g_signal_connect(inst->button, "clicked", G_CALLBACK(onclicked), NULL); + gtk_container_add(GTK_CONTAINER(inst->container), GTK_WIDGET(inst->button)); + + // Return instance object + return inst; +} +void wbcabi_deinit(void* instance) { free(instance); } + +char* wbcabi_last_error_str(void* instance) { return NULL; } \ No newline at end of file diff --git a/cabi_example/meson.build b/cabi_example/meson.build new file mode 100644 index 00000000..3f69d2b8 --- /dev/null +++ b/cabi_example/meson.build @@ -0,0 +1,21 @@ +project( + 'waybar_cabi_c_example', 'c', + version: '0.1.0', + license: 'MIT', +) + +compiler = meson.get_compiler('c') + + +shared_library('waybar_cabi_c_example', + [ + 'main.c', + ], + dependencies: [ + dependency('gtk+-3.0', version : ['>=3.22.0']) + ] + # include_directories: include_directories(['../../../include']), + # dependencies: [], + # install: true, + # install_dir: 'plugins', +) diff --git a/include/factory.hpp b/include/factory.hpp index cb25078d..068245e6 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -91,6 +91,7 @@ #include "modules/cava.hpp" #endif #include "bar.hpp" +#include "modules/cabi.hpp" #include "modules/custom.hpp" #include "modules/image.hpp" #include "modules/temperature.hpp" diff --git a/include/modules/cabi.hpp b/include/modules/cabi.hpp new file mode 100644 index 00000000..7eae48b7 --- /dev/null +++ b/include/modules/cabi.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include +#include + +#include "AModule.hpp" +#include "util/command.hpp" +#include "util/json.hpp" +#include "util/sleeper_thread.hpp" + +namespace waybar::modules { + +class CABI : public AModule { + public: + CABI(const std::string&, const std::string&, const Json::Value&); + virtual ~CABI(); + + private: + void* cabi_instance_ = nullptr; + + std::function wbcabi_init_ = nullptr; + std::function wbcabi_deinit_ = [](void*) {}; + std::function wbcabi_last_error_str_ = [](void*) { return nullptr; }; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index bb9abdeb..99e8b562 100644 --- a/meson.build +++ b/meson.build @@ -190,6 +190,7 @@ if is_linux add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp') src_files += files( 'src/modules/battery.cpp', + 'src/modules/cabi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/common.cpp', 'src/modules/cpu_frequency/linux.cpp', @@ -202,6 +203,7 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp') add_project_arguments('-DHAVE_MEMORY_BSD', language: 'cpp') src_files += files( + 'src/modules/cabi.cpp', 'src/modules/cpu.cpp', 'src/modules/cpu_frequency/bsd.cpp', 'src/modules/cpu_frequency/common.cpp', diff --git a/src/factory.cpp b/src/factory.cpp index aaf46036..73815842 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -201,6 +201,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), id, config_[name]); } + if (ref.compare(0, 5, "cabi/") == 0 && ref.size() > 5) { + return new waybar::modules::CABI(ref.substr(5), id, config_[name]); + } } catch (const std::exception& e) { auto err = fmt::format("Disabling module \"{}\", {}", name, e.what()); throw std::runtime_error(err); diff --git a/src/modules/cabi.cpp b/src/modules/cabi.cpp new file mode 100644 index 00000000..7fd0ad5c --- /dev/null +++ b/src/modules/cabi.cpp @@ -0,0 +1,50 @@ +#include "modules/cabi.hpp" + +#include + +namespace waybar::modules { + +CABI::CABI(const std::string& name, const std::string& id, const Json::Value& config) + : AModule(config, name, id, true, true) { + const auto dynlib_path = config_["path"].asString(); + + void* handle = dlopen(dynlib_path.c_str(), RTLD_LAZY); + if (handle == nullptr) { + throw std::runtime_error{dlerror()}; + } + + // Fetch ABI version + auto wbcabi_version = reinterpret_cast(dlsym(handle, "wbcabi_version")); + if (wbcabi_version == nullptr) { + throw std::runtime_error{"Missing wbcabi_version"}; + } + + // Fetch functions + if (*wbcabi_version == 1) { + wbcabi_init_ = reinterpret_cast(dlsym(handle, "wbcabi_init")); + if (wbcabi_init_ == nullptr) { + throw std::runtime_error{"Missing wbcabi_init function"}; + } + if (auto fn = reinterpret_cast(dlsym(handle, "wbcabi_deinit"))) + wbcabi_deinit_ = fn; + if (auto fn = reinterpret_cast(dlsym(handle, "wbcabi_last_error_str"))) + wbcabi_last_error_str_ = fn; + } else { + throw std::runtime_error{"Unknown wbcabi_version " + std::to_string(*wbcabi_version)}; + } + + cabi_instance_ = wbcabi_init_(dynamic_cast(&event_box_)->gobj()); + if (cabi_instance_ == nullptr) { + const auto err_str = wbcabi_last_error_str_(cabi_instance_); + throw std::runtime_error{std::string{"Failed to initialize C ABI plugin: "} + + (err_str != nullptr ? err_str : "unknown error")}; + } +} + +CABI::~CABI() { + if (cabi_instance_ != nullptr) { + wbcabi_deinit_(cabi_instance_); + } +} + +} // namespace waybar::modules