Too lazy to make propper edit but i changed the

polling system to a message one so we can have
more ways we can interract with plugins
(reload/ reset/ on click events)
This commit is contained in:
Gvidas Juknevičius 2024-06-26 04:00:44 +03:00
parent 9f53240807
commit 026a68364b
Signed by: MCorange
GPG Key ID: 12B1346D720B7FBB
33 changed files with 694 additions and 271 deletions

5
Cargo.lock generated
View File

@ -93,6 +93,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"dim_sdk", "dim_sdk",
"lazy_static", "lazy_static",
"log",
] ]
[[package]] [[package]]
@ -208,6 +209,7 @@ dependencies = [
"chrono", "chrono",
"dim_sdk", "dim_sdk",
"lazy_static", "lazy_static",
"log",
] ]
[[package]] [[package]]
@ -236,6 +238,7 @@ dependencies = [
"bytes", "bytes",
"lazy_static", "lazy_static",
"libc", "libc",
"log",
] ]
[[package]] [[package]]
@ -296,6 +299,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"dim_sdk", "dim_sdk",
"lazy_static", "lazy_static",
"log",
] ]
[[package]] [[package]]
@ -599,6 +603,7 @@ dependencies = [
"anyhow", "anyhow",
"dim_sdk", "dim_sdk",
"lazy_static", "lazy_static",
"log",
"regex", "regex",
"serde", "serde",
"toml", "toml",

View File

@ -1,6 +1,9 @@
refresh_ms=100
sock_path="~/dim.sock" # must be full path
seperator=" | " seperator=" | "
[plugins] [plugins]
path="./plugins"
blacklist=["example_c", "example_rust"] blacklist=["example_c", "example_rust"]
as_whitelist=false as_whitelist=false
template=["counter", "battery", "clock"] template=["counter", "battery", "clock"]

View File

@ -1,6 +1,9 @@
refresh_ms=100
sock_path="~/dim.sock" # must be full path
seperator=" | " seperator=" | "
[plugins] [plugins]
path="./plugins"
blacklist=["example_c", "example_rust"] blacklist=["example_c", "example_rust"]
as_whitelist=false as_whitelist=false
template=["counter", "battery", "clock"] template=["counter", "battery", "clock"]

View File

@ -10,3 +10,4 @@ crate-type=["cdylib"]
[dependencies] [dependencies]
dim_sdk = {path="../../sdk/rust/dim_sdk"} dim_sdk = {path="../../sdk/rust/dim_sdk"}
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.21"

View File

@ -1,5 +1,5 @@
use std::{fmt::Write, path::PathBuf}; use std::{fmt::Write, path::PathBuf};
use dim_sdk::{plugin_info, Context, DimPlugin}; use dim_sdk::{plugin_info, Context, DimPlugin, Message, Result};
use std::fs::read_to_string; use std::fs::read_to_string;
plugin_info!( plugin_info!(
@ -11,39 +11,38 @@ plugin_info!(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Plug { struct Plug {
} ctx: Context
impl Plug {
pub fn new() -> Self {
Self {
}
}
} }
const BATT0: &'static str = "/sys/class/power_supply/BAT0/capacity"; const BATT0: &'static str = "/sys/class/power_supply/BAT0/capacity";
const BATT1: &'static str = "/sys/class/power_supply/BAT1/capacity"; const BATT1: &'static str = "/sys/class/power_supply/BAT1/capacity";
impl DimPlugin for Plug { impl DimPlugin for Plug {
fn init(&mut self, _ctx: Context) { fn init(ctx: Context) -> Result<Self> {
dim_sdk::DimLogger::new(&ctx).init();
Ok(Self {
ctx
})
} }
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> { fn on_message(&mut self, msg: Message) -> Result<()> {
self.ctx.buf.reset();
match msg.name.as_str() {
"poll" => {
//TODO: Quick fix, make this better, more portable //TODO: Quick fix, make this better, more portable
let path = if PathBuf::from(BATT0).exists() { BATT0 } else { BATT1 }; let path = if PathBuf::from(BATT0).exists() { BATT0 } else { BATT1 };
let contents = read_to_string(path)?; let contents = read_to_string(path)?;
let cleaned_contents: String = contents.chars().filter(|c| c.is_digit(10)).collect(); let cleaned_contents: String = contents.chars().filter(|c| c.is_digit(10)).collect();
write!(f, "Battery: {}%", cleaned_contents)?; write!(self.ctx, "Battery: {}%", cleaned_contents)?;
}
_ => ()
}
Ok(()) Ok(())
} }
fn pre_reload(&mut self) { fn pre_reload(&mut self) -> Result<()> { Ok(()) }
} fn post_reload(&mut self) -> Result<()> { Ok(()) }
fn post_reload(&mut self) { fn free(&mut self) -> Result<()> { Ok(()) }
}
fn free(&mut self) {
}
} }

View File

@ -1,6 +1,6 @@
use std::fmt::Write; use std::fmt::Write;
use chrono::prelude::*; use chrono::prelude::*;
use dim_sdk::{plugin_info, Context, DimPlugin}; use dim_sdk::{plugin_info, Context, DimPlugin, Result};
plugin_info!( plugin_info!(
Plug, // Your main global structs name that implements `DimPlugin` Plug, // Your main global structs name that implements `DimPlugin`
@ -11,27 +11,31 @@ plugin_info!(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Plug { struct Plug {
ctx: Context
} }
impl Plug {
pub fn new() -> Self {
Self {
}
}
}
impl DimPlugin for Plug { impl DimPlugin for Plug {
fn init(&mut self, _ctx: Context) {} fn init(ctx: Context) -> Result<Self> {
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> { Ok(Self {
ctx
})
}
fn on_message(&mut self, msg: dim_sdk::Message) -> Result<()> {
self.ctx.buf.reset();
match msg.name.as_str() {
"poll" => {
let local: DateTime<Local> = Local::now(); let local: DateTime<Local> = Local::now();
let formatted_time = local.format("%H:%M (%Y-%m-%d)").to_string(); let formatted_time = local.format("%H:%M (%Y-%m-%d)").to_string();
log::info!("Hello from clocky :3"); write!(self.ctx, "Time: {}", formatted_time)?;
write!(f, "Time: {}", formatted_time)?; }
_ => ()
}
Ok(()) Ok(())
} }
fn pre_reload(&mut self) {} fn pre_reload(&mut self) -> Result<()> { Ok(()) }
fn post_reload(&mut self) {} fn post_reload(&mut self) -> Result<()> { Ok(()) }
fn free(&mut self) {} fn free(&mut self) -> Result<()> { Ok(()) }
} }

View File

@ -11,3 +11,4 @@ crate-type=["cdylib"]
chrono = "0.4.38" chrono = "0.4.38"
dim_sdk = {path="../../sdk/rust/dim_sdk"} dim_sdk = {path="../../sdk/rust/dim_sdk"}
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.21"

View File

@ -1,5 +1,5 @@
use std::fmt::Write; use std::fmt::Write;
use dim_sdk::{plugin_info, Context, DimPlugin}; use dim_sdk::{plugin_info, Context, DimPlugin, Message, Result};
use chrono::{NaiveDate, Local}; use chrono::{NaiveDate, Local};
plugin_info!( plugin_info!(
@ -11,30 +11,32 @@ plugin_info!(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Plug { struct Plug {
} ctx: Context
impl Plug {
pub fn new() -> Self {
Self {
}
}
} }
impl DimPlugin for Plug { impl DimPlugin for Plug {
fn init(&mut self, _ctx: Context) {} fn init(ctx: Context) -> Result<Self> {
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> { Ok(Self {
ctx
})
}
fn on_message(&mut self, msg: Message) -> Result<()> {
self.ctx.buf.reset();
match msg.name.as_str() {
"poll" => {
let start_date = NaiveDate::from_ymd_opt(2024, 3, 8).expect("Invalid date"); let start_date = NaiveDate::from_ymd_opt(2024, 3, 8).expect("Invalid date");
let current_date = Local::now().date_naive(); let current_date = Local::now().date_naive();
let duration = current_date.signed_duration_since(start_date); let duration = current_date.signed_duration_since(start_date);
write!(f, "{} days <3", duration.num_days())?; write!(self.ctx, "{} days <3", duration.num_days())?;
}
_ => ()
}
Ok(()) Ok(())
} }
fn pre_reload(&mut self) {} fn pre_reload(&mut self) -> Result<()> { Ok(()) }
fn post_reload(&mut self) {} fn post_reload(&mut self) -> Result<()> { Ok(()) }
fn free(&mut self) {} fn free(&mut self) -> Result<()> { Ok(()) }
} }

View File

@ -1,7 +1,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
// Only use this define once, cause it defines funcitons
#define PLUG_IMPL
#include "dim_sdk.h" #include "dim_sdk.h"
PLUG_INFO("example_c", "0.0.1", "GPLv3") PLUG_INFO("example_c", "0.0.1", "GPLv3")
@ -9,31 +10,38 @@ PLUG_INFO("example_c", "0.0.1", "GPLv3")
typedef struct plug_t { typedef struct plug_t {
char* some_data; char* some_data;
int count; int count;
plug_ctx_t ctx;
} plug_t; } plug_t;
plug_t* p = {0}; plug_t* p = {0};
void plug_init(plug_ctx_t* ctx) { // If any function returns 0/NULL the plugin will be disabled
(void) ctx; int plug_init(plug_ctx_t* ctx) {
setup_ctx(ctx);
p = malloc(sizeof(plug_t)); p = malloc(sizeof(plug_t));
assert(p != NULL && "Buy more ram KEKW"); assert(p != NULL && "Buy more ram KEKW");
p->count = 0; p->count = 0;
p->ctx = *ctx;
printf("Hello from plugin"); // log(INFO, "Hewo fwom C :3");
return 0;
} }
void* plug_pre_reload() { void* plug_pre_reload() {
return p; return p;
} }
void plug_post_reload(void *state) { int plug_post_reload(void *state) {
p = state; p = state;
return 0;
} }
void plug_poll(char *buf, size_t len) { int plug_on_msg(plug_msg_t* msg) {
snprintf(buf, len, "Hello from C! (%d)", p->count++); snprintf(p->ctx.buf, BUF_SZ, "Hello from C! (%d)", p->count++);
return 0;
} }
void plug_free() { int plug_free() {
free(p); free(p);
return 0;
} }

View File

@ -10,3 +10,4 @@ crate-type=["cdylib"]
[dependencies] [dependencies]
dim_sdk = {path="../../sdk/rust/dim_sdk"} dim_sdk = {path="../../sdk/rust/dim_sdk"}
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.21"

View File

@ -1,5 +1,5 @@
use std::fmt::Write; use std::fmt::Write;
use dim_sdk::{plugin_info, Context, DimPlugin}; use dim_sdk::{plugin_info, Context, DimPlugin, Message, Result};
plugin_info!( plugin_info!(
Plug, // Your main global structs name that implements `DimPlugin` Plug, // Your main global structs name that implements `DimPlugin`
@ -10,37 +10,45 @@ plugin_info!(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Plug { struct Plug {
ctx: Context,
counter: usize, counter: usize,
} }
impl Plug { impl DimPlugin for Plug {
pub fn new() -> Self { fn init(ctx: Context) -> dim_sdk::Result<Self> {
Self { // Initialise data, this will run once, it will not run again after reload
// log::info!("hewo from rust :3");
Ok(Self {
ctx,
counter: 0 counter: 0
})
} }
fn on_message(&mut self, msg: Message) -> dim_sdk::Result<()> {
self.ctx.buf.reset();
match msg.name.as_str() {
"poll" => {
// Write to buffer the text you want to display, keep this short
write!(self.ctx, "Hello from rust! ({})", self.counter)?;
self.counter += 1;
} }
_ => ()
} }
impl DimPlugin for Plug {
fn init(&mut self, _ctx: Context) {
// Initialise data, this will run once, it will not run again after reload
}
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> {
// Write to buffer the text you want to display, keep this short
write!(f, "Hello from rust! ({})", self.counter)?;
self.counter += 1;
Ok(()) Ok(())
} }
fn pre_reload(&mut self) { fn pre_reload(&mut self) -> Result<()> {
// Do stuff before reload, probably save important things because theres a good chance // Do stuff before reload, probably save important things because theres a good chance
// (especially on rust) that if you change the data layout it will die // (especially on rust) that if you change the data layout it will die
Ok(())
} }
fn post_reload(&mut self) { fn post_reload(&mut self) -> Result<()>{
// Do stuff after reloading plugin, state a.k.a this struct has the same data, will crash // Do stuff after reloading plugin, state a.k.a this struct has the same data, will crash
// if the data layout changed // if the data layout changed
Ok(())
} }
fn free(&mut self) { fn free(&mut self) -> Result<()> {
// Yout probably dont need this but its for freeing things before the plugin gets unloaded // Yout probably dont need this but its for freeing things before the plugin gets unloaded
Ok(())
} }
} }

View File

@ -11,6 +11,7 @@ crate-type=["cdylib"]
anyhow = "1.0.86" anyhow = "1.0.86"
dim_sdk = {path="../../sdk/rust/dim_sdk"} dim_sdk = {path="../../sdk/rust/dim_sdk"}
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.21"
regex = "1.10.5" regex = "1.10.5"
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.203", features = ["derive"] }
toml = "0.8.14" toml = "0.8.14"

View File

@ -1,6 +1,6 @@
use std::{fmt::Write, path::PathBuf, process::Stdio}; use std::{fmt::Write, process::Stdio};
use dim_sdk::{plugin_info, Context, DimPlugin}; use anyhow::bail;
use std::fs::read_to_string; use dim_sdk::{plugin_info, Context, DimPlugin, Message, Result};
mod config; mod config;
@ -14,28 +14,27 @@ plugin_info!(
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Plug { struct Plug {
cfg: config::Config, cfg: config::Config,
} ctx: Context
impl Plug {
pub fn new() -> Self {
Self {
cfg: config::Config::default()
}
}
} }
impl DimPlugin for Plug { impl DimPlugin for Plug {
fn init(&mut self, ctx: Context) { fn init(ctx: Context) -> Result<Self> {
match config::Config::parse(&ctx.config_dir.join("config.toml")) { let cfg;
Ok(c) => self.cfg = c, match config::Config::parse(&ctx.config_dir.join("config.toml").clone()) {
Ok(c) => cfg = c,
Err(e) => { Err(e) => {
eprintln!("ERROR: Failed to open config file: {e}"); log::error!("Failed to open config file: {e}");
// TODO: Add function to disable the plugin // TODO: Add function to disable the plugin
return; bail!("")
} }
} }
Ok(Self { ctx, cfg })
} }
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> { fn on_message(&mut self, msg: Message) -> Result<()> {
self.ctx.buf.reset();
match msg.name.as_str() {
"poll" => {
let mut proc = { let mut proc = {
let mut p = std::process::Command::new("sh"); let mut p = std::process::Command::new("sh");
p.arg("-c"); p.arg("-c");
@ -49,17 +48,21 @@ impl DimPlugin for Plug {
if let Some(caps) = re.captures(&output) { if let Some(caps) = re.captures(&output) {
let volume = &caps["vol"]; let volume = &caps["vol"];
write!(f, "Vol: {volume}%")?; write!(self.ctx, "Vol: {volume}%")?;
}
}
_ => ()
} }
Ok(()) Ok(())
} }
fn pre_reload(&mut self) { fn pre_reload(&mut self) -> Result<()> {
Ok(())
} }
fn post_reload(&mut self) { fn post_reload(&mut self) -> Result<()> {
Ok(())
} }
fn free(&mut self) { fn free(&mut self) -> Result<()> {
Ok(())
} }
} }

View File

@ -4,32 +4,83 @@
#include <stdio.h> #include <stdio.h>
#define BUF_SZ 2048
#ifdef _WIN32
#define PATH_SEP "\\"
#else
#define PATH_SEP "/"
#endif
typedef enum log_level_t {
ERROR=1,
WARN=2,
INFO=3,
DEBUG=4,
} log_level_t;
typedef struct plug_info_t { typedef struct plug_info_t {
char* name; char* name;
char* version; char* version;
char* license; char* license;
} plug_info_t; } plug_info_t;
typedef struct plug_funcs_ctx_t {
void (*log)(char* module, log_level_t level, char* s);
} plug_funcs_ctx_t;
typedef struct plug_ctx_t { typedef struct plug_ctx_t {
char* config_dir; char* config_dir;
plug_funcs_ctx_t funcs;
char* buf;
} plug_ctx_t; } plug_ctx_t;
typedef struct plug_msg_t {
char* name;
char* value;
} plug_msg_t;
// Functions defs
// User defined funcs
int plug_init(plug_ctx_t*); // Loads when DIM initialises
void* plug_pre_reload(); // Return a pointer to save state
int plug_post_reload(void*); // returns the same pointer after reload
int plug_on_msg(plug_msg_t*); // Write the message to `buf` with max `size` characters
int plug_free(); // Free everything before being killed
// Lib funcs
void setup_ctx(plug_ctx_t* ctx);
void _log(char* module, log_level_t level, char* s);
// Macros
#define PLUG_INFO(_name, _version, _license) \ #define PLUG_INFO(_name, _version, _license) \
static plug_info_t PLUG_INFO_VAR = { .name=(_name), .version=(_version), .license=(_license)}; \ static plug_info_t PLUG_INFO_VAR = { .name=(_name), .version=(_version), .license=(_license)}; \
void* plug_get_info() { \ void* plug_get_info() { \
return &PLUG_INFO_VAR; \ return &PLUG_INFO_VAR; \
} }
#define log(level, ...) do { \
char* buf = (char*)malloc(BUF_SZ*sizeof(char)); \
char* mod = (char*)malloc(BUF_SZ*sizeof(char)); \
snprintf(mod, BUF_SZ*sizeof(char), \
"(plug) %s/%s", PLUG_INFO_VAR.name, __FILE__); \
snprintf(buf, BUF_SZ*sizeof(char), __VA_ARGS__); \
_log(mod, level, buf); \
free(buf); \
free(mod); \
} while (0)
#define PLUG_NAME(s) volatile char* PLUG_NAME = (s); #ifdef PLUG_IMPL
#define PLUG_VERSION(s) volatile char* PLUG_VERSION = (s); static plug_ctx_t CTX = {0};
#define PLUG_LICENSE(s) volatile char* PLUG_LICENSE = (s);
void setup_ctx(plug_ctx_t* ctx) {
CTX = *ctx;
}
void plug_init(plug_ctx_t*); // Loads when DIM initialises void _log(char* module, log_level_t level, char* s) {
void* plug_pre_reload(); // Return a pointer to save state (CTX.funcs.log)(module, level, s);
void plug_post_reload(void*); // returns the same pointer after reload }
void plug_poll(char*, size_t); // Write the message to `buf` with max `size` characters
void plug_free(); // Free everything before being killed
#endif #endif
#endif

View File

@ -11,3 +11,4 @@ anyhow = "1.0.86"
bytes = "1.6.0" bytes = "1.6.0"
lazy_static = { version = "1.4.0", features = ["spin"] } lazy_static = { version = "1.4.0", features = ["spin"] }
libc = "0.2.155" libc = "0.2.155"
log = { version = "0.4.21", features = ["std"] }

View File

@ -1,33 +1,40 @@
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct CBuffer<'a> { pub struct CBuffer {
inner: &'a mut [i8], inner: Vec<u8>,
count: usize, count: usize,
capacity: usize capacity: usize
} }
#[allow(dead_code)] // rust_analyzer too dumb to see my macro magic #[allow(dead_code)] // rust_analyzer too dumb to see my macro magic
// i hate that macro as much as you do // i hate that macro as much as you do
impl CBuffer<'_> { impl CBuffer {
pub fn from_raw_parts_mut(buf: *mut i8, capacity: usize) -> Self { pub fn from_raw_parts_mut(buf: *mut u8, capacity: usize) -> Self {
Self { Self {
inner: unsafe { inner: unsafe {
std::slice::from_raw_parts_mut(buf, capacity) Vec::from_raw_parts(buf, capacity, capacity)
}, },
capacity, capacity,
count: 0, count: 0,
} }
} }
pub fn reset(&mut self) {
for i in 0..self.capacity {
self.inner[i] = 0;
}
self.count = 0;
}
} }
impl std::fmt::Write for CBuffer<'_> { impl std::fmt::Write for CBuffer {
fn write_str(&mut self, buf: &str) -> Result<(), std::fmt::Error> { fn write_str(&mut self, buf: &str) -> Result<(), std::fmt::Error> {
for c in buf.as_bytes() { for c in buf.as_bytes() {
if self.count >= self.capacity - 1 { if self.count >= self.capacity - 1 {
return Ok(()); return Ok(());
} }
self.inner[self.count] = *c as i8; self.inner[self.count] = *c;
self.count += 1; self.count += 1;
} }
self.inner[self.count] = 0; self.inner[self.count] = 0;

View File

@ -1,25 +1,46 @@
use std::{ffi::{c_char, CStr}, path::PathBuf}; use std::{ffi::{c_char, CStr}, path::PathBuf};
use crate::CBuffer;
#[derive(Debug, Clone)]
#[repr(C)] #[repr(C)]
pub struct ContextRaw { pub struct ContextRaw {
pub config_dir: *const c_char pub config_dir: *const c_char,
pub funcs: PluginContextFuncs,
pub buf: *mut u8,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
pub config_dir: PathBuf pub config_dir: PathBuf,
pub funcs: PluginContextFuncs,
pub buf: CBuffer
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct PluginContextFuncs {
pub log: unsafe extern "C" fn(module: *const c_char, level: log::Level, s: *const c_char),
} }
impl Context { impl Context {
pub fn new(cr: *const ContextRaw) -> Self { pub fn new(cr: ContextRaw) -> Self {
let config_dir = unsafe { let config_dir = unsafe {
let v = CStr::from_ptr((*cr).config_dir); let v = CStr::from_ptr(cr.config_dir);
PathBuf::from(v.to_string_lossy().to_string()) PathBuf::from(v.to_string_lossy().to_string().clone())
}; };
Self { Self {
config_dir config_dir,
funcs: cr.funcs.clone(),
buf: CBuffer::from_raw_parts_mut(cr.buf, crate::BUF_SZ)
} }
} }
} }
impl std::fmt::Write for Context {
fn write_str(&mut self, buf: &str) -> Result<(), std::fmt::Error> {
self.buf.write_str(buf)
}
}

View File

@ -4,23 +4,55 @@ mod magic;
mod c_buffer; mod c_buffer;
mod plugin_info; mod plugin_info;
mod context; mod context;
mod logger;
use std::ffi::{c_char, CStr};
pub use c_buffer::*; pub use c_buffer::*;
pub use plugin_info::*; pub use plugin_info::*;
pub use anyhow::Result; pub use anyhow::Result;
pub use context::*; pub use context::*;
pub use logger::*;
pub const BUF_SZ: usize = 2048;
pub trait DimPlugin: Clone{ pub trait DimPlugin: Clone{
fn init(&mut self, ctx: Context); fn init(ctx: Context) -> Result<Self>;
fn pre_reload(&mut self); fn pre_reload(&mut self) -> Result<()>;
fn post_reload(&mut self); fn post_reload(&mut self) -> Result<()>;
fn poll(&mut self, f: &mut CBuffer) -> Result<()>; fn on_message(&mut self, msg: Message) -> Result<()>;
fn free(&mut self) {} fn free(&mut self) -> Result<()>;
}
#[derive(Debug)]
pub struct Message {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct MessageRaw {
pub name: *const c_char,
pub value: *const c_char,
}
impl Message {
pub fn new(msg: MessageRaw) -> Self {
unsafe {
Self {
name: CStr::from_ptr(msg.name.clone())
.to_string_lossy().to_string(),
value: CStr::from_ptr(msg.value.clone())
.to_string_lossy().to_string()
}
}
}
} }

View File

@ -0,0 +1,37 @@
use std::ffi::{c_char, CString};
use crate::Context;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DimLogger {
log_fn: unsafe extern "C" fn(module: *const c_char, level: log::Level, s: *const c_char),
}
impl log::Log for DimLogger {
fn enabled(&self, _: &log::Metadata) -> bool {true}
fn log(&self, record: &log::Record) {
unsafe {
let module = format!("(plug) {}", record.module_path().unwrap_or("UNKNOWN"));
let module = CString::new(module).unwrap();
let s = CString::new(format!("{}", record.args())).unwrap();
(self.log_fn)(module.as_ptr(), record.level(), s.as_ptr());
}
}
fn flush(&self) {}
}
impl DimLogger {
pub fn new(ctx: &Context) -> Self {
Self {
log_fn: ctx.funcs.log,
}
}
pub fn init(&self) {
let _ = log::set_boxed_logger(Box::new(self.clone()))
.map(|()| log::set_max_level(log::LevelFilter::Debug));
}
}

View File

@ -14,61 +14,87 @@ macro_rules! plugin_info {
PLUGIN_INFO.get_raw_ptr() PLUGIN_INFO.get_raw_ptr()
} }
#[no_mangle] impl $typ {
unsafe extern "C" fn plug_init(ctx: *const $crate::ContextRaw) { pub fn get_plugin_info(&self) -> $crate::PluginInfo {
//PLUG = std::alloc::alloc(std::alloc::Layout::new::<$typ>()) as *mut $typ; $crate::PluginInfo::new(
PLUG = Some(Plug::new()); PLUGIN_INFO.name().as_str(),
PLUGIN_INFO.version().as_str(),
let ctx = $crate::Context::new(ctx); PLUGIN_INFO.license().as_str()
if let Some(p) = &mut PLUG { )
p.init(ctx)
} else {
unreachable!();
} }
} }
#[no_mangle]
unsafe extern "C" fn plug_init(ctx: *const $crate::ContextRaw) -> i32 {
let ctx = $crate::Context::new((*ctx).clone());
$crate::DimLogger::new(&ctx).init();
match <$typ>::init(ctx) {
Ok(v) => {
PLUG = Some(v);
return 0;
}
Err(e) => {
log::error!("Had an error while initialising: {e}");
return 1;
}
};
}
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_pre_reload() -> *mut $typ { unsafe extern "C" fn plug_pre_reload() -> *mut $typ {
//TODO: Untested //TODO: Untested
if let Some(p) = &mut PLUG { if let Some(p) = &mut PLUG {
p.pre_reload(); if let Err(e) = p.pre_reload() {
log::error!("Had an error on pre reload: {e}");
return std::ptr::null_mut();
}
return p as *mut $typ; return p as *mut $typ;
} }
unreachable!(); unreachable!();
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_post_reload(state: *mut $typ) { unsafe extern "C" fn plug_post_reload(state: *mut $typ) -> i32 {
PLUG = Some((*state).clone()); PLUG = Some((*state).clone());
if let Some(p) = &mut PLUG { if let Some(p) = &mut PLUG {
p.post_reload(); if let Err(e) = p.post_reload() {
log::error!("Had an error on pre reload: {e}");
return 1;
}
} else { } else {
unreachable!(); unreachable!();
} }
//TODO: Untested //TODO: Untested
return 0;
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_poll(buf: *mut i8, len: std::ffi::c_uint) { unsafe extern "C" fn plug_on_msg(msg: *const $crate::MessageRaw) -> i32 {
let mut buf = $crate::CBuffer::from_raw_parts_mut(buf, len as usize); let msg = $crate::Message::new((*msg).clone());
if let Some(p) = &mut PLUG { if let Some(p) = &mut PLUG {
if let Err(_e) = p.poll(&mut buf) { if let Err(e) = p.on_message(msg) {
// TODO: Handle error maybe? log::error!("Had error on `on_msg`: {e}");
return 1;
} }
} else { } else {
unreachable!(); unreachable!();
} }
return 0;
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_free() { unsafe extern "C" fn plug_free() -> i32 {
if let Some(p) = &mut PLUG { if let Some(p) = &mut PLUG {
// std::alloc::dealloc(PLUG as *mut u8, std::alloc::Layout::new::<$typ>()); // std::alloc::dealloc(PLUG as *mut u8, std::alloc::Layout::new::<$typ>());
p.free(); if let Err(e) = p.free() {
log::error!("Had error on `free`: {e}");
return 1;
}
} else { } else {
unreachable!(); unreachable!();
} }
return 0;
} }
}; };

View File

@ -36,6 +36,16 @@ impl PluginInfo {
pub fn get_raw_ptr(&self) -> *const c_void { pub fn get_raw_ptr(&self) -> *const c_void {
&self.ptrs as *const _ as *const c_void &self.ptrs as *const _ as *const c_void
} }
pub fn name(&self) -> String {
self._name.to_string_lossy().to_string()
}
pub fn version(&self) -> String {
self._version.to_string_lossy().to_string()
}
pub fn license(&self) -> String {
self._license.to_string_lossy().to_string()
}
} }
unsafe impl Sync for PluginInfo {} unsafe impl Sync for PluginInfo {}

46
src/cli.rs Normal file
View File

@ -0,0 +1,46 @@
use clap::{Parser, Subcommand};
#[derive(Debug, Parser)]
pub struct CliArgs {
/// Directory where all *.dim plugin files are stored
#[arg(long, short, default_value="./plugins")]
pub plugin_dir: camino::Utf8PathBuf,
/// Directory where all configurations are stored
#[arg(long, short, default_value="./config")]
pub config_dir: camino::Utf8PathBuf,
/// Print more debug info
#[arg(long, short)]
pub debug: bool,
/// Socket path
#[arg(long, short)]
pub sock_path: Option<camino::Utf8PathBuf>,
#[command(subcommand)]
pub subcommand: Option<CliSubcommand>,
}
#[derive(Debug, Subcommand)]
pub enum CliSubcommand {
/// Send a message through ipc to the running DIM instance
Send {
/// Command name, eg. `clock:reset`,
/// `clock:` will be stripped and the command will be sent
/// only to plugin `clock`, dont include it to send it to all plugins
#[arg(long, short)]
name: String,
/// Value to send
#[arg(long, short, default_value="")]
value: String,
},
/// Reload one or all plugins
Reload {
/// (Optional) Which plugin to Reload
name: Option<String>
}
}

View File

@ -2,10 +2,14 @@ use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::cli::CliArgs;
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]
pub struct Config { pub struct Config {
pub seperator: String, pub seperator: String,
pub refresh_ms: usize,
pub sock_path: String,
pub plugins: ConfigPlugins, pub plugins: ConfigPlugins,
#[serde(skip)] #[serde(skip)]
@ -14,24 +18,26 @@ pub struct Config {
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]
pub struct ConfigPlugins { pub struct ConfigPlugins {
pub path: PathBuf,
pub blacklist: Vec<String>, pub blacklist: Vec<String>,
pub as_whitelist: bool, pub as_whitelist: bool,
pub template: Vec<String>, pub template: Vec<String>,
} }
impl Config { impl Config {
pub fn new(path: &Path) -> Result<Self, ConfigError> { pub fn new(cli: CliArgs) -> Result<Self, ConfigError> {
let cfg_path = path.join("main.toml"); let cfg_path = cli.config_dir.join("main.toml");
if !cfg_path.exists() { if !cfg_path.exists() {
println!("ERROR: {:?} doesnt exist", path); println!("ERROR: {:?} doesnt exist", cli.config_dir);
} }
let mut cfg: Self = toml::from_str( let mut cfg: Self = toml::from_str(
&std::fs::read_to_string(&cfg_path)? &std::fs::read_to_string(&cfg_path)?
)?; )?;
cfg.config_dir = path.to_path_buf(); cfg.config_dir = cli.config_dir.as_std_path().to_path_buf();
cfg.plugins.path = cli.plugin_dir.as_std_path().to_path_buf();
Ok(cfg) Ok(cfg)
} }
} }

View File

@ -80,7 +80,7 @@ impl Display {
} }
} }
if !found { if !found {
log::warn!("Plugin '{}' was not found even though it was included in the template", tv); // log::warn!("Plugin '{}' was not found even though it was included in the template", tv);
} }
} }

0
src/ipc/client/mod.rs Normal file
View File

63
src/ipc/mod.rs Normal file
View File

@ -0,0 +1,63 @@
use std::ffi::{c_char, CString};
pub mod client;
pub mod server;
#[derive(Debug)]
pub struct Message {
pub plugin: Option<String>,
pub name: CString,
pub value: CString,
pub ffi: MessageFFI
}
#[derive(Debug)]
#[repr(C)]
pub struct MessageFFI {
pub name: *const c_char,
pub value: *const c_char,
}
impl Message {
pub fn from_string_kv<S: ToString>(name: S, value: S) -> Self {
let mut name = name.to_string();
let value = value.to_string();
let plugin;
if name.contains(':') {
let ns: Vec<&str> = name.split(':').collect();
plugin = Some(ns[0].to_string());
name = ns[1..].join(":");
} else {
plugin = None;
}
let name = CString::new(name).unwrap();
let value = CString::new(value).unwrap();
Self {
ffi: MessageFFI {
name: name.as_ptr(),
value: value.as_ptr()
},
plugin,
name,
value,
}
}
pub fn from_string<S: ToString>(k: S) -> Self {
let k = k.to_string();
let mut v = String::new();
let mut name = k.clone();
if k.contains('=') {
let kv: Vec<&str> = k.split('=').collect();
name = kv[0].to_string();
v = kv[1..].join("=");
}
Self::from_string_kv(name, v)
}
}

27
src/ipc/server/mod.rs Normal file
View File

@ -0,0 +1,27 @@
use std::{io::Read, os::unix::net::UnixStream};
use crate::config::Config;
use super::Message;
pub struct IpcServer{
sock: UnixStream
}
impl IpcServer {
pub fn start(cfg: &Config) -> std::io::Result<Self> {
Ok(Self {
sock: UnixStream::connect(&cfg.sock_path)?,
})
}
pub fn poll(&mut self) -> Option<Message> {
let mut buf = String::new();
let _ = self.sock.read_to_string(&mut buf);
if buf.is_empty() {
return None;
}
Some(Message::from_string(buf))
}
}

14
src/logger.rs Normal file
View File

@ -0,0 +1,14 @@
pub use crate::cli::CliArgs;
pub fn init(ca: &CliArgs) {
if ca.debug {
env_logger::builder()
.filter(None, log::LevelFilter::Debug)
.init();
} else {
env_logger::builder()
.filter(None, log::LevelFilter::Info)
.init();
}
}

View File

@ -1,48 +1,29 @@
#![allow(internal_features)] #![allow(internal_features)]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
use std::{ffi::c_char, process::ExitCode, time::Duration}; use std::{process::ExitCode, time::Duration};
use clap::Parser; use clap::Parser;
mod display; mod display;
mod plugman; mod plugman;
mod config; mod config;
mod util; mod util;
mod cli;
mod logger;
mod ipc;
#[derive(Debug, Parser)] // TODO: Make function available in ctx that disables the plugin
struct CliArgs {
#[arg(long, short, default_value="./plugins")]
pub plugin_dir: camino::Utf8PathBuf,
#[arg(long, short, default_value="./config")]
pub config_dir: camino::Utf8PathBuf,
#[arg(long, short)]
pub debug: bool
}
#[allow(non_camel_case_types)]
pub type c_str = *const c_char;
// TODO: Clean up main function
// TODO: Make ffi safe abstraction for logger
// TODO: Set up ipc with unix sockets // TODO: Set up ipc with unix sockets
// TODO: Allow sending messages command -> running DIM instance -> plugin with ipc // TODO: Allow sending messages command -> running DIM instance -> plugin with ipc
// TODO: Clickable bar: https://dwm.suckless.org/patches/statuscmd/ // TODO: Clickable bar: https://dwm.suckless.org/patches/statuscmd/
// TODO: Run code through clippy // TODO: Run code through clippy
fn main() -> ExitCode { fn main() -> ExitCode {
let ca = CliArgs::parse(); let ca = cli::CliArgs::parse();
if ca.debug { logger::init(&ca);
env_logger::builder()
.filter(None, log::LevelFilter::Debug)
.init();
} else {
env_logger::builder()
.filter(None, log::LevelFilter::Info)
.init();
}
let config; let config;
match config::Config::new(ca.config_dir.as_std_path()) { match config::Config::new(ca) {
Ok(v) => config = v, Ok(v) => config = v,
Err(e) => { Err(e) => {
log::error!("Failed to parse config: {e:?}"); log::error!("Failed to parse config: {e:?}");
@ -55,7 +36,7 @@ fn main() -> ExitCode {
let mut pm = plugman::PlugMan::new(1024); // idk tbh let mut pm = plugman::PlugMan::new(1024); // idk tbh
if let Err(e) = pm.load(ca.plugin_dir.as_std_path().to_path_buf()){ if let Err(e) = pm.load(&config.plugins.path){
log::error!("Failed to load plugins: {e:?}"); log::error!("Failed to load plugins: {e:?}");
return ExitCode::from(3); return ExitCode::from(3);
} }
@ -64,6 +45,8 @@ fn main() -> ExitCode {
loop { loop {
let vals = pm.poll_plugins(); let vals = pm.poll_plugins();
log::debug!("vals: {vals:?}");
if let Err(e) = disp.write_with_vec(&config, vals) { if let Err(e) = disp.write_with_vec(&config, vals) {
log::warn!("Failed to write too bar: {e}"); log::warn!("Failed to write too bar: {e}");
} }

View File

@ -1,4 +1,6 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::{Path, PathBuf}};
use crate::ipc::Message;
use self::plugin::Plugin; use self::plugin::Plugin;
@ -19,7 +21,8 @@ impl PlugMan {
} }
} }
pub fn load(&mut self, dir: PathBuf) -> Result<(), PluginError>{ pub fn load<P: AsRef<Path>>(&mut self, dir: P) -> Result<(), PluginError>{
let dir: &Path = dir.as_ref();
log::debug!("Loading plugins from {:?}", dir); log::debug!("Loading plugins from {:?}", dir);
let files = std::fs::read_dir(dir)?; let files = std::fs::read_dir(dir)?;
@ -50,7 +53,7 @@ impl PlugMan {
} }
pub fn init_plugins(&mut self, cfg: &crate::config::Config) { pub fn init_plugins(&mut self, cfg: &crate::config::Config) {
for plugin in &self.plugins { for plugin in &mut self.plugins {
plugin.init(cfg); plugin.init(cfg);
} }
} }
@ -73,20 +76,16 @@ impl PlugMan {
pub fn poll_plugins(&mut self) -> HashMap<String, PolledText> { pub fn poll_plugins(&mut self) -> HashMap<String, PolledText> {
let mut answers = HashMap::new(); let mut answers = HashMap::new();
let len = self.len_cap / self.plugins.len(); for plugin in &mut self.plugins {
for plugin in &self.plugins {
if !plugin.enabled() { if !plugin.enabled() {
continue; continue;
} }
match plugin.poll(len) { match plugin.poll() {
Ok(v) => { Ok(v) => {
let mut pth = (plugin.name().clone(), PolledText::new(v)); let pth = (plugin.name().clone(), PolledText::new(v));
if pth.1.text().is_empty() { if !pth.1.text().is_empty() {
pth.1.disable()
}
answers.insert(pth.0, pth.1); answers.insert(pth.0, pth.1);
}
}, },
Err(e) => log::error!("Failed to poll plugin: {e}"), Err(e) => log::error!("Failed to poll plugin: {e}"),
} }
@ -94,6 +93,18 @@ impl PlugMan {
} }
answers answers
} }
pub fn send_msg(&mut self, msg: Message) {
for plugin in &mut self.plugins {
if let Some(pn) = &msg.plugin {
if *pn != plugin.name() {
continue;
}
}
plugin.send_msg(msg);
break;
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -1,4 +1,5 @@
use std::ffi::{c_char, CString}; use std::ffi::{c_char, CStr, CString};
use super::Plugin;
pub struct PluginContextContainer { pub struct PluginContextContainer {
pub inner: PluginContext, pub inner: PluginContext,
@ -8,24 +9,42 @@ pub struct PluginContextContainer {
#[repr(C)] #[repr(C)]
pub struct PluginContext { pub struct PluginContext {
pub config_dir: *const c_char pub config_dir: *const c_char,
funcs: PluginContextFuncs,
buf: *mut u8
}
#[repr(C)]
pub struct PluginContextFuncs {
log: unsafe extern "C" fn(module: *const c_char, level: log::Level, s: *const c_char),
} }
impl PluginContext { impl PluginContext {
pub fn new(config_dir: &CString) -> Self { pub fn new(plug: &Plugin, config_dir: &CString) -> Self {
Self { Self {
config_dir: config_dir.as_ptr() config_dir: config_dir.as_ptr(),
funcs: PluginContextFuncs {
log: plug_log,
},
buf: plug.buf
} }
} }
} }
impl PluginContextContainer { impl PluginContextContainer {
pub fn new(config_dir: &String) -> Self { pub fn new(plug: &Plugin, config_dir: &String) -> Self {
let _config_dir = CString::new(config_dir.clone()).unwrap(); let _config_dir = CString::new(config_dir.clone()).unwrap();
Self { Self {
inner: PluginContext::new(&_config_dir), inner: PluginContext::new(plug, &_config_dir),
_config_dir, _config_dir,
} }
} }
} }
pub unsafe extern "C" fn plug_log(module: *const c_char, level: log::Level, s: *const c_char) {
let module = CStr::from_ptr(module).to_string_lossy().to_string();
let s = CStr::from_ptr(s).to_string_lossy().to_string();
log::log!(target: &module, level, "{s}");
}

View File

@ -1,17 +1,16 @@
use std::ffi::c_char;
use dlopen::raw::Library; use dlopen::raw::Library;
use crate::ipc::MessageFFI;
use super::{context::PluginContext, Plugin, PluginInfo}; use super::{context::PluginContext, Plugin, PluginInfo};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct PluginSyms { pub struct PluginSyms {
pub(super) init: unsafe extern "C" fn(ctx: *const PluginContext), pub(super) init: unsafe extern "C" fn(ctx: *const PluginContext) -> i32,
pub(super) pre_reload: unsafe extern "C" fn() -> *const (), pub(super) pre_reload: unsafe extern "C" fn() -> *const (),
pub(super) post_reload: unsafe extern "C" fn(state: *const ()), pub(super) post_reload: unsafe extern "C" fn(state: *const ()) -> i32,
pub(super) poll: unsafe extern "C" fn(buf: *mut c_char, len: usize), pub(super) on_msg: unsafe extern "C" fn(msg: *const MessageFFI) -> i32,
pub(super) free: unsafe extern "C" fn(), pub(super) free: unsafe extern "C" fn() -> i32,
pub(super) get_info: unsafe extern "C" fn() -> *const PluginInfo, pub(super) get_info: unsafe extern "C" fn() -> *const PluginInfo,
} }
@ -23,7 +22,7 @@ impl Plugin {
init: unsafe { lib.symbol("plug_init")? }, init: unsafe { lib.symbol("plug_init")? },
pre_reload: unsafe { lib.symbol("plug_pre_reload")? }, pre_reload: unsafe { lib.symbol("plug_pre_reload")? },
post_reload: unsafe { lib.symbol("plug_post_reload")? }, post_reload: unsafe { lib.symbol("plug_post_reload")? },
poll: unsafe { lib.symbol("plug_poll")? }, on_msg: unsafe { lib.symbol("plug_on_msg")? },
free: unsafe { lib.symbol("plug_free")? }, free: unsafe { lib.symbol("plug_free")? },
get_info: unsafe { lib.symbol("plug_get_info")? }, get_info: unsafe { lib.symbol("plug_get_info")? },

View File

@ -2,35 +2,46 @@ use std::{alloc::Layout, ffi::{c_char, CStr}, path::PathBuf};
use dlopen::raw::Library; use dlopen::raw::Library;
use crate::{c_str, config::Config}; use crate::{config::Config, ipc::{Message, MessageFFI}};
use self::context::{PluginContext, PluginContextContainer}; use self::context::{PluginContext, PluginContextContainer};
pub mod loader; pub mod loader;
pub mod context; pub mod context;
const MEM_LAYOUT: Result<Layout, std::alloc::LayoutError> = Layout::from_size_align(2048, 1);
#[repr(C)] #[repr(C)]
pub struct PluginInfo { pub struct PluginInfo {
name: c_str, name: *const c_char,
version: c_str, version: *const c_char,
license: c_str, license: *const c_char,
} }
#[derive(Debug, Default)] #[derive(Debug)]
pub struct Plugin { pub struct Plugin {
syms: Option<loader::PluginSyms>, syms: Option<loader::PluginSyms>,
path: PathBuf, path: PathBuf,
lib: Option<Library>, lib: Option<Library>,
enabled: bool, enabled: bool,
buf: *mut u8
} }
impl Plugin { impl Plugin {
pub fn load(path: PathBuf) -> Result<Self, dlopen::Error> { pub fn load(path: PathBuf) -> Result<Self, dlopen::Error> {
let mut s = Self::default(); let buf = unsafe {
s.enabled = true; std::alloc::alloc_zeroed(MEM_LAYOUT.unwrap())
s.path = path; };
let mut s = Self {
enabled: true,
path,
syms: None,
lib: None,
buf
};
s.reload_symbols()?; s.reload_symbols()?;
Ok(s) Ok(s)
} }
@ -39,51 +50,71 @@ impl Plugin {
self.syms.unwrap() self.syms.unwrap()
} }
pub fn init(&self, cfg: &Config) { pub fn init(&mut self, cfg: &Config) {
let conf_dir = &cfg.config_dir.join(self.name()).to_string_lossy().to_string(); let conf_dir = &cfg.config_dir.join(self.name()).to_string_lossy().to_string();
let _ = std::fs::create_dir(&conf_dir); // dont care let _ = std::fs::create_dir(&conf_dir); // dont care
let ctx = PluginContextContainer::new(conf_dir); let ctx = PluginContextContainer::new(self, conf_dir);
unsafe { unsafe {
(self.syms().init)(&ctx.inner as *const PluginContext) if (self.syms().init)(&ctx.inner as *const PluginContext) != 0 {
log::warn!("`init` had an error, disabling");
self.disable();
}
} }
} }
pub fn reload(&mut self) -> Result<(), dlopen::Error> { pub fn reload(&mut self) -> Result<(), dlopen::Error> {
unsafe { unsafe {
let state = (self.syms().pre_reload)(); let state = (self.syms().pre_reload)();
if state.is_null() {
log::warn!("`pre_reload` had an error, disabling");
self.disable();
}
if let Err(e) = self.reload_symbols() { if let Err(e) = self.reload_symbols() {
self.disable(); self.disable();
return Err(e); return Err(e);
} }
(self.syms().post_reload)(state); if (self.syms().post_reload)(state) != 0 {
log::warn!("`post_reload` had an error, disabling");
self.disable();
}
} }
Ok(()) Ok(())
} }
pub fn poll(&self, cap: usize) -> Result<String, std::string::FromUtf8Error> { pub fn send_msg(&mut self, msg: Message) {
let layout = Layout::from_size_align(cap, 1).unwrap(); unsafe {
let buf = unsafe { // log::debug!("Sending message to '{}': {:?}", self.name(), msg);
std::alloc::alloc_zeroed(layout) if (self.syms().on_msg)(&msg.ffi as *const MessageFFI) != 0 {
}; log::warn!("`on_msg` had an error, disabling");
let s = unsafe { self.disable();
(self.syms().poll)(buf as *mut i8, cap); }
let len = libc::strlen(buf as *const i8); }
if len > cap {
panic!("String len is bigger than allocatd");
} }
String::from_raw_parts(buf, len, cap) pub fn poll(&mut self) -> Result<String, std::string::FromUtf8Error> {
self.send_msg(Message::from_string("poll"));
let s = unsafe {
CStr::from_ptr(self.buf as *const c_char).to_string_lossy().to_string()
}; };
if !s.is_empty() { if !s.is_empty() {
log::debug!("Polled: {}", s); // log::debug!("Polled: {}", s);
} }
Ok(s) Ok(s)
} }
pub fn free(self) {
pub fn free(mut self) {
unsafe { unsafe {
(self.syms().free)(); if (self.syms().free)() != 0 {
log::warn!("`free` had an error, disabling");
self.disable(); // Literaly useless but eh
}
std::alloc::dealloc(self.buf, MEM_LAYOUT.unwrap());
} }
drop(self.lib.unwrap()); drop(self.lib.unwrap());
} }