Compare commits

...

5 Commits

Author SHA1 Message Date
6dd22fe0c3
extra stuff 2024-06-23 14:16:27 +03:00
5b3b89ec4c
Volume plugin 2024-06-23 14:16:03 +03:00
50782cd512
Fixed a bug that crashes the program when doing something very specific
It crashed while trying to do Config::default() aka probabbly allocate
into the very manually allocated struct (which is bad in rust)
2024-06-23 14:15:51 +03:00
bd40dfe44a
Normalised versions 2024-06-23 14:14:11 +03:00
8349316169
more todos and create config dir per plugin 2024-06-21 00:04:11 +03:00
17 changed files with 257 additions and 22 deletions

2
.gitignore vendored
View File

@ -1,4 +1,6 @@
/target /target
/plugins /plugins
/.cache /.cache
/config/*
!/config/main.template.toml
compile_commands.json compile_commands.json

12
Cargo.lock generated
View File

@ -592,6 +592,18 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "volume"
version = "0.1.0"
dependencies = [
"anyhow",
"dim_sdk",
"lazy_static",
"regex",
"serde",
"toml",
]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.92" version = "0.2.92"

View File

@ -5,6 +5,7 @@ members = [
"dim_plugins/clock", "dim_plugins/clock",
"dim_plugins/counter", "dim_plugins/counter",
"dim_plugins/battery", "dim_plugins/battery",
"dim_plugins/volume",
] ]
[package] [package]

55
\ Normal file
View File

@ -0,0 +1,55 @@
// Lord have mercy on me
#[macro_export]
macro_rules! plugin_info {
($typ:ty, $name:literal, $version:literal, $license:literal) => {
lazy_static::lazy_static!(
static ref PLUGIN_INFO: $crate::PluginInfo = $crate::PluginInfo::new($name, $version, $license);
);
static mut PLUG: *mut $typ = std::ptr::null_mut() as *mut $typ;
#[no_mangle]
unsafe extern "C" fn plug_get_info() -> *const std::ffi::c_void {
PLUGIN_INFO.get_raw_ptr()
}
#[no_mangle]
unsafe extern "C" fn plug_init(ctx: *const $crate::ContextRaw) {
PLUG = std::alloc::alloc(std::alloc::Layout::new::<$typ>()) as *mut $typ;
*PLUG = <$typ>::new();
let ctx = $crate::Context::new(ctx);
(&mut *PLUG).init(ctx);
}
#[no_mangle]
unsafe extern "C" fn plug_pre_reload() -> *mut $typ {
//TODO: Untested
(&mut *PLUG).pre_reload();
return PLUG;
}
#[no_mangle]
unsafe extern "C" fn plug_post_reload(state: *mut $typ) {
//TODO: Untested
PLUG = state;
(&mut *PLUG).post_reload();
}
#[no_mangle]
unsafe extern "C" fn plug_poll(buf: *mut i8, len: std::ffi::c_uint) {
let mut buf = $crate::CBuffer::from_raw_parts_mut(buf, len as usize);
if let Err(_e) = (&mut *PLUG).poll(&mut buf) {
// TODO: Handle error maybe?
}
}
#[no_mangle]
unsafe extern "C" fn plug_free() {
std::alloc::dealloc(PLUG as *mut u8, std::alloc::Layout::new::<$typ>());
(&mut *PLUG).free();
}
};
}

View File

@ -5,10 +5,11 @@ use std::fs::read_to_string;
plugin_info!( plugin_info!(
Plug, // Your main global structs name that implements `DimPlugin` Plug, // Your main global structs name that implements `DimPlugin`
"battery", // Plugin name "battery", // Plugin name
"0.0.0", // Plugin Version (leave empty for none) "1.0.0", // Plugin Version (leave empty for none)
"GPLv3" // Plugin license (leave empty for none) "GPLv3" // Plugin license (leave empty for none)
); );
#[derive(Debug, Clone)]
struct Plug { struct Plug {
} }

View File

@ -5,11 +5,11 @@ use dim_sdk::{plugin_info, Context, DimPlugin};
plugin_info!( plugin_info!(
Plug, // Your main global structs name that implements `DimPlugin` Plug, // Your main global structs name that implements `DimPlugin`
"clock", // Plugin name "clock", // Plugin name
"0.0.0", // Plugin Version (leave empty for none) "1.0.0", // Plugin Version (leave empty for none)
"GPLv3" // Plugin license (leave empty for none) "GPLv3" // Plugin license (leave empty for none)
); );
#[derive(Debug, Clone)]
struct Plug { struct Plug {
} }

View File

@ -5,10 +5,11 @@ use chrono::{NaiveDate, Local};
plugin_info!( plugin_info!(
Plug, // Your main global structs name that implements `DimPlugin` Plug, // Your main global structs name that implements `DimPlugin`
"counter", // Plugin name "counter", // Plugin name
"0.0.0", // Plugin Version (leave empty for none) "1.0.0", // Plugin Version (leave empty for none)
"GPLv3" // Plugin license (leave empty for none) "GPLv3" // Plugin license (leave empty for none)
); );
#[derive(Debug, Clone)]
struct Plug { struct Plug {
} }

View File

@ -8,6 +8,7 @@ plugin_info!(
"GPLv3" // Plugin license (leave empty for none) "GPLv3" // Plugin license (leave empty for none)
); );
#[derive(Debug, Clone)]
struct Plug { struct Plug {
counter: usize, counter: usize,
} }

View File

@ -0,0 +1,16 @@
[package]
name = "volume"
version = "0.1.0"
edition = "2021"
[lib]
crate-type=["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.86"
dim_sdk = {path="../../sdk/rust/dim_sdk"}
lazy_static = "1.4.0"
regex = "1.10.5"
serde = { version = "1.0.203", features = ["derive"] }
toml = "0.8.14"

View File

@ -0,0 +1,17 @@
# Must match package name in Cargo.toml
PLUGIN_NAME=volume
# `release` or `debug`
TYPE=release
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so
@mkdir -p $(dir $@)
cp $^ $@
$(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so:
@mkdir -p $(dir $@)
cargo build --release --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)

View File

@ -0,0 +1,3 @@
[commands]
get_volume="pactl get-sink-volume @DEFAULT_SINK@"
get_volume_regex=".* (?<vol>[0-9]{1,3})%.*"

View File

@ -0,0 +1,37 @@
use std::path::Path;
use anyhow::Result;
use serde::Deserialize;
const DEFAULT_CFG: &'static str = include_str!("../config.template.toml");
#[derive(Debug, Deserialize, Clone)]
pub struct Config {
pub commands: ConfigCommands,
}
#[derive(Debug, Deserialize, Clone)]
pub struct ConfigCommands {
pub get_volume: String,
pub get_volume_regex: String,
}
impl Config {
pub fn parse(p: &Path) -> Result<Self> {
if !p.exists() {
std::fs::write(p, DEFAULT_CFG)?;
}
let c = std::fs::read_to_string(p)?;
Ok(toml::from_str(c.as_str())?)
}
pub fn default() -> Self {
Self {
commands: ConfigCommands {
get_volume: String::new(),
get_volume_regex: String::new()
}
}
}
}

View File

@ -0,0 +1,68 @@
use std::{fmt::Write, path::PathBuf, process::Stdio};
use dim_sdk::{plugin_info, Context, DimPlugin};
use std::fs::read_to_string;
mod config;
plugin_info!(
Plug, // Your main global structs name that implements `DimPlugin`
"volume", // Plugin name
"1.0.0", // Plugin Version (leave empty for none)
"GPLv3" // Plugin license (leave empty for none)
);
#[derive(Debug, Clone)]
struct Plug {
cfg: config::Config,
}
impl Plug {
pub fn new() -> Self {
Self {
cfg: config::Config::default()
}
}
}
impl DimPlugin for Plug {
fn init(&mut self, ctx: Context) {
match config::Config::parse(&ctx.config_dir.join("config.toml")) {
Ok(c) => self.cfg = c,
Err(e) => {
eprintln!("ERROR: Failed to open config file: {e}");
// TODO: Add function to disable the plugin
return;
}
}
}
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> {
let mut proc = {
let mut p = std::process::Command::new("sh");
p.arg("-c");
p.arg(&self.cfg.commands.get_volume);
p.stdout(Stdio::piped());
p
};
let output = String::from_utf8(proc.output()?.stdout)?;
let re = regex::Regex::new(&self.cfg.commands.get_volume_regex)?;
if let Some(caps) = re.captures(&output) {
let volume = &caps["vol"];
write!(f, "Vol: {volume}%")?;
}
Ok(())
}
fn pre_reload(&mut self) {
}
fn post_reload(&mut self) {
}
fn free(&mut self) {
}
}

View File

@ -1,5 +1,4 @@
#[macro_use] #[macro_use]
mod magic; mod magic;
mod c_buffer; mod c_buffer;
@ -11,7 +10,7 @@ pub use plugin_info::*;
pub use anyhow::Result; pub use anyhow::Result;
pub use context::*; pub use context::*;
pub trait DimPlugin { pub trait DimPlugin: Clone {
fn init(&mut self, ctx: Context); fn init(&mut self, ctx: Context);
fn pre_reload(&mut self); fn pre_reload(&mut self);
fn post_reload(&mut self); fn post_reload(&mut self);

View File

@ -7,7 +7,7 @@ macro_rules! plugin_info {
static ref PLUGIN_INFO: $crate::PluginInfo = $crate::PluginInfo::new($name, $version, $license); static ref PLUGIN_INFO: $crate::PluginInfo = $crate::PluginInfo::new($name, $version, $license);
); );
static mut PLUG: *mut $typ = std::ptr::null_mut() as *mut $typ; static mut PLUG: Option<$typ> = None as Option<$typ>;
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_get_info() -> *const std::ffi::c_void { unsafe extern "C" fn plug_get_info() -> *const std::ffi::c_void {
@ -16,39 +16,59 @@ macro_rules! plugin_info {
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_init(ctx: *const $crate::ContextRaw) { unsafe extern "C" fn plug_init(ctx: *const $crate::ContextRaw) {
PLUG = std::alloc::alloc(std::alloc::Layout::new::<$typ>()) as *mut $typ; //PLUG = std::alloc::alloc(std::alloc::Layout::new::<$typ>()) as *mut $typ;
*PLUG = Plug::new(); PLUG = Some(Plug::new());
let ctx = $crate::Context::new(ctx); let ctx = $crate::Context::new(ctx);
(&mut *PLUG).init(ctx); if let Some(p) = &mut PLUG {
p.init(ctx)
} else {
unreachable!();
}
} }
#[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
(&mut *PLUG).pre_reload(); if let Some(p) = &mut PLUG {
return PLUG; p.pre_reload();
return p as *mut $typ;
}
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) {
//TODO: Untested PLUG = Some((*state).clone());
PLUG = state;
(&mut *PLUG).post_reload(); if let Some(p) = &mut PLUG {
p.post_reload();
} else {
unreachable!();
}
//TODO: Untested
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_poll(buf: *mut i8, len: std::ffi::c_uint) { unsafe extern "C" fn plug_poll(buf: *mut i8, len: std::ffi::c_uint) {
let mut buf = $crate::CBuffer::from_raw_parts_mut(buf, len as usize); let mut buf = $crate::CBuffer::from_raw_parts_mut(buf, len as usize);
if let Err(_e) = (&mut *PLUG).poll(&mut buf) { if let Some(p) = &mut PLUG {
// TODO: Handle error maybe? if let Err(_e) = p.poll(&mut buf) {
// TODO: Handle error maybe?
}
} else {
unreachable!();
} }
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn plug_free() { unsafe extern "C" fn plug_free() {
std::alloc::dealloc(PLUG as *mut u8, std::alloc::Layout::new::<$typ>()); if let Some(p) = &mut PLUG {
(&mut *PLUG).free(); // std::alloc::dealloc(PLUG as *mut u8, std::alloc::Layout::new::<$typ>());
p.free();
} else {
unreachable!();
}
} }
}; };

View File

@ -26,6 +26,7 @@ pub type c_str = *const c_char;
// TODO: Make ffi safe abstraction for logger // 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: Run code through clippy // TODO: Run code through clippy
fn main() -> ExitCode { fn main() -> ExitCode {
let ca = CliArgs::parse(); let ca = CliArgs::parse();

View File

@ -40,10 +40,11 @@ impl Plugin {
} }
pub fn init(&self, cfg: &Config) { pub fn init(&self, cfg: &Config) {
let conf_dir = &cfg.config_dir.join(self.name()).to_string_lossy().to_string();
let _ = std::fs::create_dir(&conf_dir); // dont care
let ctx = PluginContextContainer::new(conf_dir);
unsafe { unsafe {
let conf_dir = &cfg.config_dir.join(self.name()).to_string_lossy().to_string();
let ctx = PluginContextContainer::new(conf_dir);
(self.syms().init)(&ctx.inner as *const PluginContext) (self.syms().init)(&ctx.inner as *const PluginContext)
} }
} }