1#![doc = concat!(include_str!("../WAYBAR_VERSION"), ".")]
9#![doc = include_str!("../examples/hello-world.rs")]
18#[doc(hidden)]
44pub mod config;
45mod error;
46mod info;
47
48#[doc(hidden)]
49pub use crate::error::Error;
50pub use crate::info::InitInfo;
51#[doc(hidden)]
52pub use serde;
53#[doc(hidden)]
54pub use waybar_cffi_sys as sys;
55pub use waybar_cffi_sys::gtk;
56
57use serde::de::DeserializeOwned;
58use waybar_cffi_sys::{
59 libc::{c_void, size_t},
60 wbcffi_config_entry, wbcffi_init_info,
61};
62
63#[allow(unused_variables)]
68pub trait Module {
69 type Config: DeserializeOwned;
74
75 fn init(info: &InitInfo, config: Self::Config) -> Self;
80
81 fn update(&mut self) {}
83
84 fn refresh(&mut self, signal: i32) {}
86
87 fn do_action(&mut self, action: &str) {}
89}
90
91#[macro_export]
96macro_rules! waybar_module {
97 ($ty:ty) => {
98 #[allow(non_upper_case_globals)]
99 #[unsafe(no_mangle)]
100 pub static wbcffi_version: $crate::sys::libc::size_t = 1;
101
102 #[allow(clippy::not_unsafe_ptr_arg_deref)]
103 #[unsafe(no_mangle)]
104 pub extern "C" fn wbcffi_init(
105 init_info: *const $crate::sys::wbcffi_init_info,
106 config_entries: *const $crate::sys::wbcffi_config_entry,
107 config_entries_len: $crate::sys::libc::size_t,
108 ) -> *mut $crate::sys::libc::c_void {
109 $crate::init::<$ty>(
110 stringify!($ty),
111 init_info,
112 config_entries,
113 config_entries_len,
114 )
115 }
116
117 #[allow(clippy::not_unsafe_ptr_arg_deref)]
118 #[unsafe(no_mangle)]
119 pub extern "C" fn wbcffi_deinit(instance: *mut $crate::sys::libc::c_void) {
120 let ptr = instance as *mut $ty;
121 let module = unsafe { Box::from_raw(ptr) };
122
123 }
125
126 #[allow(clippy::not_unsafe_ptr_arg_deref)]
127 #[unsafe(no_mangle)]
128 pub extern "C" fn wbcffi_update(instance: *mut $crate::sys::libc::c_void) {
129 let ptr = instance as *mut $ty;
130 let mut module = unsafe { Box::from_raw(ptr) };
131
132 module.update();
133
134 let _ = Box::into_raw(module);
135 }
136
137 #[allow(clippy::not_unsafe_ptr_arg_deref)]
138 #[unsafe(no_mangle)]
139 pub extern "C" fn wbcffi_refresh(
140 instance: *mut $crate::sys::libc::c_void,
141 signal: $crate::sys::libc::c_int,
142 ) {
143 let ptr = instance as *mut $ty;
144 let mut module = unsafe { Box::from_raw(ptr) };
145
146 module.refresh(signal as i32);
147
148 let _ = Box::into_raw(module);
149 }
150
151 #[allow(clippy::not_unsafe_ptr_arg_deref)]
152 #[unsafe(no_mangle)]
153 pub extern "C" fn wbcffi_doaction(
154 instance: *mut $crate::sys::libc::c_void,
155 action_name: *const $crate::sys::libc::c_char,
156 ) {
157 let ptr = instance as *mut $ty;
158 let mut module = unsafe { Box::from_raw(ptr) };
159
160 let action = unsafe { std::ffi::CStr::from_ptr(action_name) }
161 .to_str()
162 .expect("action name");
163 module.do_action(action);
164
165 let _ = Box::into_raw(module);
166 }
167 };
168}
169
170#[doc(hidden)]
171pub fn init<T: Module>(
172 name: &'static str,
173 init_info: *const wbcffi_init_info,
174 config_entries: *const wbcffi_config_entry,
175 config_entries_len: size_t,
176) -> *mut c_void {
177 match do_init::<T>(init_info, config_entries, config_entries_len) {
180 Ok(module) => Box::into_raw(Box::new(module)) as *mut c_void,
181 Err(e) => {
182 eprintln!("{name} init error: {e}");
183 std::ptr::null_mut()
184 }
185 }
186}
187
188fn do_init<T: Module>(
189 init_info: *const wbcffi_init_info,
190 config_entries: *const wbcffi_config_entry,
191 config_entries_len: size_t,
192) -> Result<T, Error> {
193 gtk::init().map_err(Error::Gtk)?;
196
197 let info = InitInfo::new(init_info)?;
198 let config = config::parse(config_entries, config_entries_len)?;
199
200 Ok(T::init(&info, config))
201}