Compare commits
18 Commits
793ed4d730
...
rust_rewri
| Author | SHA1 | Date | |
|---|---|---|---|
|
50520bb1b8
|
|||
|
1edaf643c9
|
|||
|
026a68364b
|
|||
|
9f53240807
|
|||
|
6dd22fe0c3
|
|||
|
5b3b89ec4c
|
|||
|
50782cd512
|
|||
|
bd40dfe44a
|
|||
|
8349316169
|
|||
|
7f6920507c
|
|||
|
19aee664c2
|
|||
|
43a07debde
|
|||
|
8693e10979
|
|||
|
7f22334f0e
|
|||
|
3b107f09cd
|
|||
|
08eca1a2d0
|
|||
|
208eed348b
|
|||
|
85891abba0
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
/target
|
/target
|
||||||
/plugins
|
/plugins
|
||||||
/.cache
|
/.cache
|
||||||
|
/config/*
|
||||||
|
!/config/main.template.toml
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|||||||
190
Cargo.lock
generated
190
Cargo.lock
generated
@@ -2,6 +2,15 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -84,6 +93,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dim_sdk",
|
"dim_sdk",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -177,6 +187,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"dim_sdk",
|
"dim_sdk",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -198,18 +209,24 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"dim_sdk",
|
"dim_sdk",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dim"
|
name = "dim"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"dlopen",
|
"dlopen",
|
||||||
"dlopen_derive",
|
"dlopen_derive",
|
||||||
|
"env_logger",
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
"x11",
|
"x11",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -221,6 +238,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -246,20 +264,62 @@ dependencies = [
|
|||||||
"syn 0.15.44",
|
"syn 0.15.44",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "example_rust"
|
name = "example_rust"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dim_sdk",
|
"dim_sdk",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.60"
|
version = "0.1.60"
|
||||||
@@ -283,6 +343,16 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.0"
|
version = "1.70.0"
|
||||||
@@ -319,6 +389,12 @@ version = "0.4.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -376,6 +452,64 @@ dependencies = [
|
|||||||
"proc-macro2 1.0.85",
|
"proc-macro2 1.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.85",
|
||||||
|
"quote 1.0.36",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -410,6 +544,40 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
@@ -428,6 +596,19 @@ 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",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.92"
|
version = "0.2.92"
|
||||||
@@ -586,6 +767,15 @@ version = "0.52.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.6.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x11"
|
name = "x11"
|
||||||
version = "2.21.0"
|
version = "2.21.0"
|
||||||
|
|||||||
16
Cargo.toml
16
Cargo.toml
@@ -1,4 +1,13 @@
|
|||||||
workspace = { members = [ "sdk/rust","dim_plugins/example_rust", "dim_plugins/clock", "dim_plugins/counter", "dim_plugins/battery"] }
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"sdk/rust/dim_sdk",
|
||||||
|
"dim_plugins/example_rust",
|
||||||
|
"dim_plugins/clock",
|
||||||
|
"dim_plugins/counter",
|
||||||
|
"dim_plugins/battery",
|
||||||
|
"dim_plugins/volume",
|
||||||
|
]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "dim"
|
name = "dim"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -7,10 +16,15 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.86"
|
||||||
camino = "1.1.7"
|
camino = "1.1.7"
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
clap = { version = "4.5.7", features = ["derive"] }
|
clap = { version = "4.5.7", features = ["derive"] }
|
||||||
dlopen = "0.1.8"
|
dlopen = "0.1.8"
|
||||||
dlopen_derive = "0.1.4"
|
dlopen_derive = "0.1.4"
|
||||||
|
env_logger = "0.11.3"
|
||||||
libc = "0.2.155"
|
libc = "0.2.155"
|
||||||
|
log = "0.4.21"
|
||||||
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
toml = "0.8.14"
|
||||||
x11 = { version = "2.21.0", features = ["xlib"] }
|
x11 = { version = "2.21.0", features = ["xlib"] }
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"directory": "/home/xomf/git-stuff/dim2/dim/dim_plugins/example_c",
|
"directory": "/home/mcorange/.dwm/sources/dim/dim_plugins/example_c",
|
||||||
"arguments": [
|
"arguments": [
|
||||||
"gcc",
|
"gcc",
|
||||||
"-c",
|
"-c",
|
||||||
"-o",
|
"-o",
|
||||||
"/home/xomf/git-stuff/dim2/dim/target/objects/example_c/main.o",
|
"/home/mcorange/.dwm/sources/dim/target/objects/example_c/main.o",
|
||||||
"src/main.c",
|
"src/main.c",
|
||||||
"-pie",
|
"-pie",
|
||||||
"-I/home/xomf/git-stuff/dim2/dim/sdk/c_cxx",
|
"-I/home/mcorange/.dwm/sources/dim/sdk/c_cxx",
|
||||||
"-fPIC"
|
"-fPIC"
|
||||||
],
|
],
|
||||||
"file": "src/main.c"
|
"file": "src/main.c"
|
||||||
|
|||||||
9
config/main.template.toml
Normal file
9
config/main.template.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
refresh_ms=100
|
||||||
|
sock_path="~/dim.sock" # must be full path
|
||||||
|
seperator=" | "
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
path="./plugins"
|
||||||
|
blacklist=["example_c", "example_rust"]
|
||||||
|
as_whitelist=false
|
||||||
|
template=["counter", "battery", "clock"]
|
||||||
9
config/main.toml
Normal file
9
config/main.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
refresh_ms=100
|
||||||
|
sock_path="~/dim.sock" # must be full path
|
||||||
|
seperator=" | "
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
path="./plugins"
|
||||||
|
blacklist=["example_c", "example_rust"]
|
||||||
|
as_whitelist=false
|
||||||
|
template=["counter", "battery", "clock"]
|
||||||
@@ -8,5 +8,6 @@ crate-type=["cdylib"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dim_sdk = {path="../../sdk/rust/"}
|
dim_sdk = {path="../../sdk/rust/dim_sdk"}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
log = "0.4.21"
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
# Must match package name in Cargo.toml
|
# Must match package name in Cargo.toml
|
||||||
PLUGIN_NAME=battery
|
PLUGIN_NAME=battery
|
||||||
|
# `release` or `debug`
|
||||||
|
TYPE=release
|
||||||
|
|
||||||
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
||||||
|
|
||||||
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so
|
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so
|
||||||
cp $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
@mkdir -p $(dir $@)
|
||||||
|
cp $^ $@
|
||||||
|
|
||||||
$(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so:
|
$(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so:
|
||||||
mkdir -p $(OBJECT_DIR)/$(PLUGIN_NAME)
|
@mkdir -p $(dir $@)
|
||||||
cargo build --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
cargo build --release --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,48 @@
|
|||||||
use std::fmt::Write;
|
use std::{fmt::Write, path::PathBuf};
|
||||||
use dim_sdk::{plugin_info, 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!(
|
||||||
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 {
|
||||||
|
ctx: Context
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plug {
|
const BATT0: &'static str = "/sys/class/power_supply/BAT0/capacity";
|
||||||
pub fn new() -> Self {
|
const BATT1: &'static str = "/sys/class/power_supply/BAT1/capacity";
|
||||||
Self {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DimPlugin for Plug {
|
impl DimPlugin for Plug {
|
||||||
fn init(&mut self) {
|
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<()> {
|
||||||
let contents = read_to_string("/sys/class/power_supply/BAT0/capacity")?;
|
self.ctx.buf.reset();
|
||||||
let cleaned_contents: String = contents.chars().filter(|c| c.is_digit(10)).collect();
|
match msg.name.as_str() {
|
||||||
|
"poll" => {
|
||||||
|
//TODO: Quick fix, make this better, more portable
|
||||||
|
let path = if PathBuf::from(BATT0).exists() { BATT0 } else { BATT1 };
|
||||||
|
|
||||||
write!(f, "Battery: {}%", cleaned_contents)?;
|
let contents = read_to_string(path)?;
|
||||||
|
let cleaned_contents: String = contents.chars().filter(|c| c.is_digit(10)).collect();
|
||||||
|
|
||||||
|
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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ crate-type=["cdylib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
dim_sdk = {path="../../sdk/rust/"}
|
dim_sdk = {path="../../sdk/rust/dim_sdk"}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
log = "0.4.21"
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
# Must match package name in Cargo.toml
|
# Must match package name in Cargo.toml
|
||||||
PLUGIN_NAME=clock
|
PLUGIN_NAME=clock
|
||||||
|
# `release` or `debug`
|
||||||
|
TYPE=release
|
||||||
|
|
||||||
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
||||||
|
|
||||||
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so
|
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so
|
||||||
cp $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
@mkdir -p $(dir $@)
|
||||||
|
cp $^ $@
|
||||||
$(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so:
|
|
||||||
mkdir -p $(OBJECT_DIR)/$(PLUGIN_NAME)
|
|
||||||
cargo build --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so:
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cargo build --release --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use dim_sdk::{plugin_info, 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`
|
||||||
"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 {
|
||||||
|
ctx: Context
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plug {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DimPlugin for Plug {
|
impl DimPlugin for Plug {
|
||||||
fn init(&mut self) {
|
fn init(ctx: Context) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
ctx
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
fn on_message(&mut self, msg: dim_sdk::Message) -> Result<()> {
|
||||||
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> {
|
self.ctx.buf.reset();
|
||||||
let local: DateTime<Local> = Local::now();
|
match msg.name.as_str() {
|
||||||
let formatted_time = local.format("%H:%M (%Y-%m-%d)").to_string();
|
"poll" => {
|
||||||
|
let local: DateTime<Local> = Local::now();
|
||||||
write!(f, "Time: {}", formatted_time)?;
|
let formatted_time = local.format("%H:%M (%Y-%m-%d)").to_string();
|
||||||
|
write!(self.ctx, "Time: {}", formatted_time)?;
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn pre_reload(&mut self) {
|
fn pre_reload(&mut self) -> Result<()> { Ok(()) }
|
||||||
|
fn post_reload(&mut self) -> Result<()> { Ok(()) }
|
||||||
}
|
fn free(&mut self) -> Result<()> { Ok(()) }
|
||||||
fn post_reload(&mut self) {
|
|
||||||
|
|
||||||
}
|
|
||||||
fn free(&mut self) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ crate-type=["cdylib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
dim_sdk = {path="../../sdk/rust/"}
|
dim_sdk = {path="../../sdk/rust/dim_sdk"}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
log = "0.4.21"
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
# Must match package name in Cargo.toml
|
# Must match package name in Cargo.toml
|
||||||
PLUGIN_NAME=counter
|
PLUGIN_NAME=counter
|
||||||
|
# `release` or `debug`
|
||||||
|
TYPE=release
|
||||||
|
|
||||||
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
||||||
|
|
||||||
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so
|
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so
|
||||||
cp $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
@mkdir -p $(dir $@)
|
||||||
|
cp $^ $@
|
||||||
$(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so:
|
|
||||||
mkdir -p $(OBJECT_DIR)/$(PLUGIN_NAME)
|
|
||||||
cargo build --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so:
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cargo build --release --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
||||||
|
|||||||
@@ -1,43 +1,42 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use dim_sdk::{plugin_info, DimPlugin};
|
use dim_sdk::{plugin_info, Context, DimPlugin, Message, Result};
|
||||||
use chrono::{NaiveDate, Local};
|
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 {
|
||||||
}
|
ctx: Context
|
||||||
|
|
||||||
impl Plug {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DimPlugin for Plug {
|
impl DimPlugin for Plug {
|
||||||
fn init(&mut self) {
|
fn init(ctx: Context) -> Result<Self> {
|
||||||
|
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" => {
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2024, 3, 8).expect("Invalid date");
|
||||||
|
let current_date = Local::now().date_naive();
|
||||||
|
let duration = current_date.signed_duration_since(start_date);
|
||||||
|
|
||||||
let start_date = NaiveDate::from_ymd_opt(2024, 3, 8).expect("Invalid date");
|
write!(self.ctx, "{} days <3", duration.num_days())?;
|
||||||
let current_date = Local::now().date_naive();
|
}
|
||||||
let duration = current_date.signed_duration_since(start_date);
|
_ => ()
|
||||||
|
}
|
||||||
write!(f, "{} days <3", duration.num_days())?;
|
|
||||||
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) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,51 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
// Only use this define once, cause it defines funcitons
|
||||||
|
#define PLUG_IMPL
|
||||||
#include "dim_sdk.h"
|
#include "dim_sdk.h"
|
||||||
|
|
||||||
PLUG_INFO("Example plugin", "0.0.1", "GPLv3")
|
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() {
|
// If any function returns not 0 the plugin will be disabled
|
||||||
|
// Except for `pre_reload`, the plugin will be disabled if it is NULL
|
||||||
|
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++);
|
if (strcmp(msg->name, "poll") == 0) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ crate-type=["cdylib"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dim_sdk = {path="../../sdk/rust/"}
|
dim_sdk = {path="../../sdk/rust/dim_sdk"}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
log = "0.4.21"
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
# Must match package name in Cargo.toml
|
# Must match package name in Cargo.toml
|
||||||
PLUGIN_NAME=example_rust
|
PLUGIN_NAME=example_rust
|
||||||
|
# `release` or `debug`
|
||||||
|
TYPE=release
|
||||||
|
|
||||||
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
build: $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
||||||
|
|
||||||
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so
|
$(PLUGIN_DIR)/$(PLUGIN_NAME).dim: $(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so
|
||||||
cp $(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so $(PLUGIN_DIR)/$(PLUGIN_NAME).dim
|
@mkdir -p $(dir $@)
|
||||||
|
cp $^ $@
|
||||||
$(OBJECT_DIR)/$(PLUGIN_NAME)/debug/lib$(PLUGIN_NAME).so:
|
|
||||||
mkdir -p $(OBJECT_DIR)/$(PLUGIN_NAME)
|
|
||||||
cargo build --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(OBJECT_DIR)/$(PLUGIN_NAME)/$(TYPE)/lib$(PLUGIN_NAME).so:
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cargo build --release --target-dir $(OBJECT_DIR)/$(PLUGIN_NAME)
|
||||||
|
|||||||
@@ -1,45 +1,54 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use dim_sdk::{plugin_info, 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`
|
||||||
"Example rust project", // Plugin name
|
"example_rust", // Plugin name
|
||||||
"0.0.0", // Plugin Version (leave empty for none)
|
"0.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 {
|
||||||
|
ctx: Context,
|
||||||
counter: usize,
|
counter: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plug {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
counter: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DimPlugin for Plug {
|
impl DimPlugin for Plug {
|
||||||
fn init(&mut self) {
|
fn init(ctx: Context) -> dim_sdk::Result<Self> {
|
||||||
// Initialise data, this will run once, it will not run again after reload
|
// Initialise data, this will run once, it will not run again after reload
|
||||||
|
// log::info!("hewo from rust :3");
|
||||||
|
Ok(Self {
|
||||||
|
ctx,
|
||||||
|
counter: 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn poll(&mut self, f: &mut dim_sdk::CBuffer) -> dim_sdk::Result<()> {
|
fn on_message(&mut self, msg: Message) -> dim_sdk::Result<()> {
|
||||||
// Write to buffer the text you want to display, keep this short
|
self.ctx.buf.reset();
|
||||||
write!(f, "Hello from rust! ({})", self.counter)?;
|
match msg.name.as_str() {
|
||||||
self.counter += 1;
|
"poll" => {
|
||||||
|
// Write to buffer the text you want to display, keep this short
|
||||||
|
write!(self.ctx, "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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
dim_plugins/volume/Cargo.toml
Normal file
17
dim_plugins/volume/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[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"
|
||||||
|
log = "0.4.21"
|
||||||
|
regex = "1.10.5"
|
||||||
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
toml = "0.8.14"
|
||||||
17
dim_plugins/volume/Makefile
Normal file
17
dim_plugins/volume/Makefile
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3
dim_plugins/volume/config.template.toml
Normal file
3
dim_plugins/volume/config.template.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[commands]
|
||||||
|
get_volume="pactl get-sink-volume @DEFAULT_SINK@"
|
||||||
|
get_volume_regex=".* (?<vol>[0-9]{1,3})%.*"
|
||||||
37
dim_plugins/volume/src/config.rs
Normal file
37
dim_plugins/volume/src/config.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
dim_plugins/volume/src/lib.rs
Normal file
71
dim_plugins/volume/src/lib.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use std::{fmt::Write, process::Stdio};
|
||||||
|
use anyhow::bail;
|
||||||
|
use dim_sdk::{plugin_info, Context, DimPlugin, Message, Result};
|
||||||
|
|
||||||
|
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,
|
||||||
|
ctx: Context
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DimPlugin for Plug {
|
||||||
|
fn init(ctx: Context) -> Result<Self> {
|
||||||
|
let cfg;
|
||||||
|
match config::Config::parse(&ctx.config_dir.join("config.toml").clone()) {
|
||||||
|
Ok(c) => cfg = c,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to open config file: {e}");
|
||||||
|
// TODO: Add function to disable the plugin
|
||||||
|
bail!("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { ctx, cfg })
|
||||||
|
}
|
||||||
|
fn on_message(&mut self, msg: Message) -> Result<()> {
|
||||||
|
self.ctx.buf.reset();
|
||||||
|
match msg.name.as_str() {
|
||||||
|
"poll" => {
|
||||||
|
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!(self.ctx, "Vol: {volume}%")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn pre_reload(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn post_reload(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn free(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nightly
|
||||||
@@ -4,28 +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;
|
||||||
|
|
||||||
#define PLUG_INFO(_name, _version, _license) \
|
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 {
|
||||||
|
char* config_dir;
|
||||||
|
plug_funcs_ctx_t funcs;
|
||||||
|
char* buf;
|
||||||
|
} 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) \
|
||||||
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(); // 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
|
||||||
|
|||||||
@@ -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"] }
|
||||||
@@ -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;
|
||||||
46
sdk/rust/dim_sdk/src/context.rs
Normal file
46
sdk/rust/dim_sdk/src/context.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use std::{ffi::{c_char, CStr}, path::PathBuf};
|
||||||
|
|
||||||
|
use crate::CBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ContextRaw {
|
||||||
|
pub config_dir: *const c_char,
|
||||||
|
pub funcs: PluginContextFuncs,
|
||||||
|
pub buf: *mut u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Context {
|
||||||
|
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 {
|
||||||
|
pub fn new(cr: ContextRaw) -> Self {
|
||||||
|
let config_dir = unsafe {
|
||||||
|
let v = CStr::from_ptr(cr.config_dir);
|
||||||
|
PathBuf::from(v.to_string_lossy().to_string().clone())
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
58
sdk/rust/dim_sdk/src/lib.rs
Normal file
58
sdk/rust/dim_sdk/src/lib.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod magic;
|
||||||
|
mod c_buffer;
|
||||||
|
mod plugin_info;
|
||||||
|
mod context;
|
||||||
|
mod logger;
|
||||||
|
|
||||||
|
use std::ffi::{c_char, CStr};
|
||||||
|
|
||||||
|
pub use c_buffer::*;
|
||||||
|
pub use plugin_info::*;
|
||||||
|
pub use anyhow::Result;
|
||||||
|
pub use context::*;
|
||||||
|
pub use logger::*;
|
||||||
|
|
||||||
|
pub const BUF_SZ: usize = 2048;
|
||||||
|
|
||||||
|
pub trait DimPlugin: Clone{
|
||||||
|
fn init(ctx: Context) -> Result<Self>;
|
||||||
|
fn pre_reload(&mut self) -> Result<()>;
|
||||||
|
fn post_reload(&mut self) -> Result<()>;
|
||||||
|
fn on_message(&mut self, msg: Message) -> Result<()>;
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
37
sdk/rust/dim_sdk/src/logger.rs
Normal file
37
sdk/rust/dim_sdk/src/logger.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
101
sdk/rust/dim_sdk/src/magic.rs
Normal file
101
sdk/rust/dim_sdk/src/magic.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// 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: Option<$typ> = None as Option<$typ>;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn plug_get_info() -> *const std::ffi::c_void {
|
||||||
|
PLUGIN_INFO.get_raw_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $typ {
|
||||||
|
pub fn get_plugin_info(&self) -> $crate::PluginInfo {
|
||||||
|
$crate::PluginInfo::new(
|
||||||
|
PLUGIN_INFO.name().as_str(),
|
||||||
|
PLUGIN_INFO.version().as_str(),
|
||||||
|
PLUGIN_INFO.license().as_str()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]
|
||||||
|
unsafe extern "C" fn plug_pre_reload() -> *mut $typ {
|
||||||
|
//TODO: Untested
|
||||||
|
if let Some(p) = &mut PLUG {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn plug_post_reload(state: *mut $typ) -> i32 {
|
||||||
|
PLUG = Some((*state).clone());
|
||||||
|
|
||||||
|
if let Some(p) = &mut PLUG {
|
||||||
|
if let Err(e) = p.post_reload() {
|
||||||
|
log::error!("Had an error on pre reload: {e}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
//TODO: Untested
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn plug_on_msg(msg: *const $crate::MessageRaw) -> i32 {
|
||||||
|
let msg = $crate::Message::new((*msg).clone());
|
||||||
|
if let Some(p) = &mut PLUG {
|
||||||
|
if let Err(e) = p.on_message(msg) {
|
||||||
|
log::error!("Had error on `on_msg`: {e}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn plug_free() -> i32 {
|
||||||
|
if let Some(p) = &mut PLUG {
|
||||||
|
// std::alloc::dealloc(PLUG as *mut u8, std::alloc::Layout::new::<$typ>());
|
||||||
|
if let Err(e) = p.free() {
|
||||||
|
log::error!("Had error on `free`: {e}");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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 {}
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod magic;
|
|
||||||
mod c_buffer;
|
|
||||||
mod plugin_info;
|
|
||||||
|
|
||||||
pub use c_buffer::*;
|
|
||||||
pub use plugin_info::*;
|
|
||||||
|
|
||||||
pub use anyhow::Result;
|
|
||||||
|
|
||||||
pub trait DimPlugin {
|
|
||||||
fn init(&mut self);
|
|
||||||
fn pre_reload(&mut self);
|
|
||||||
fn post_reload(&mut self);
|
|
||||||
fn poll(&mut self, f: &mut CBuffer) -> Result<()>;
|
|
||||||
fn free(&mut self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// 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() {
|
|
||||||
PLUG = std::alloc::alloc(std::alloc::Layout::new::<$typ>()) as *mut $typ;
|
|
||||||
*PLUG = Plug::new();
|
|
||||||
(&mut *PLUG).init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
46
src/cli.rs
Normal file
46
src/cli.rs
Normal 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>
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/config.rs
Normal file
61
src/config.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::cli::CliArgs;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub seperator: String,
|
||||||
|
pub refresh_ms: usize,
|
||||||
|
pub sock_path: String,
|
||||||
|
pub plugins: ConfigPlugins,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub config_dir: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct ConfigPlugins {
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub blacklist: Vec<String>,
|
||||||
|
pub as_whitelist: bool,
|
||||||
|
pub template: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new(cli: CliArgs) -> Result<Self, ConfigError> {
|
||||||
|
let cfg_path = cli.config_dir.join("main.toml");
|
||||||
|
|
||||||
|
if !cfg_path.exists() {
|
||||||
|
println!("ERROR: {:?} doesnt exist", cli.config_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cfg: Self = toml::from_str(
|
||||||
|
&std::fs::read_to_string(&cfg_path)?
|
||||||
|
)?;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
IoError(std::io::Error),
|
||||||
|
TomlDeserialiseError(toml::de::Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ConfigError {
|
||||||
|
fn from(e: std::io::Error) -> Self {
|
||||||
|
Self::IoError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<toml::de::Error> for ConfigError {
|
||||||
|
fn from(e: toml::de::Error) -> Self {
|
||||||
|
Self::TomlDeserialiseError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::ffi::{CString, NulError};
|
use std::{collections::HashMap, ffi::{CString, NulError}};
|
||||||
|
|
||||||
use x11::xlib;
|
use x11::xlib;
|
||||||
|
|
||||||
|
use crate::{config::Config, plugman::PolledText};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Display {
|
pub struct Display {
|
||||||
@@ -16,7 +18,7 @@ impl Display {
|
|||||||
let display = xlib::XOpenDisplay(std::ptr::null());
|
let display = xlib::XOpenDisplay(std::ptr::null());
|
||||||
|
|
||||||
if display.is_null() {
|
if display.is_null() {
|
||||||
eprintln!("ERROR: Could not open x11 display");
|
log::error!("Could not open x11 display");
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,4 +43,63 @@ impl Display {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_with_vec(&mut self, cfg: &Config, v: HashMap<String, PolledText>) -> Result<(), NulError> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
let mut v = if cfg.plugins.as_whitelist {
|
||||||
|
let mut vv = HashMap::new();
|
||||||
|
for (n, pt) in v {
|
||||||
|
if cfg.plugins.blacklist.contains(&n) {
|
||||||
|
vv.insert(n, pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vv
|
||||||
|
} else {
|
||||||
|
let mut vv = HashMap::new();
|
||||||
|
for (n, pt) in v {
|
||||||
|
if !cfg.plugins.blacklist.contains(&n) {
|
||||||
|
vv.insert(n, pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vv
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut used_values = Vec::new();
|
||||||
|
for tv in &cfg.plugins.template {
|
||||||
|
let mut found = false;
|
||||||
|
for (pname, pt) in &v {
|
||||||
|
if *pname == *tv {
|
||||||
|
if !pt.disabled() {
|
||||||
|
buf.push(pt.text().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
used_values.push(pname.clone());
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
// log::warn!("Plugin '{}' was not found even though it was included in the template", tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for val in used_values {
|
||||||
|
v.remove(&val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut kv: Vec<(&String, &PolledText)> = v.iter().collect();
|
||||||
|
kv.sort_by(|a, b| a.0.cmp(b.0));
|
||||||
|
|
||||||
|
for (_, pt) in &kv {
|
||||||
|
buf.push(pt.text().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = buf.join(&cfg.seperator);
|
||||||
|
|
||||||
|
self.write_display_name(&text)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
src/ipc/client/mod.rs
Normal file
0
src/ipc/client/mod.rs
Normal file
63
src/ipc/mod.rs
Normal file
63
src/ipc/mod.rs
Normal 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
27
src/ipc/server/mod.rs
Normal 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
14
src/logger.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
51
src/main.rs
51
src/main.rs
@@ -1,40 +1,55 @@
|
|||||||
|
#![allow(internal_features)]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
use std::{path::PathBuf, 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 util;
|
||||||
|
mod cli;
|
||||||
|
mod logger;
|
||||||
|
mod ipc;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
// TODO: Make function available in ctx that disables the plugin
|
||||||
struct CliArgs {
|
// TODO: Set up ipc with unix sockets
|
||||||
#[arg(long, short, default_value="./plugins")]
|
// TODO: Allow sending messages command -> running DIM instance -> plugin with ipc
|
||||||
plugin_dir: camino::Utf8PathBuf,
|
// TODO: Clickable bar: https://dwm.suckless.org/patches/statuscmd/
|
||||||
}
|
// TODO: Run code through clippy
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
// let ca = CliArgs::parse();
|
let ca = cli::CliArgs::parse();
|
||||||
|
|
||||||
|
logger::init(&ca);
|
||||||
|
|
||||||
|
let config;
|
||||||
|
match config::Config::new(ca) {
|
||||||
|
Ok(v) => config = v,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to parse config: {e:?}");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
}
|
||||||
|
};
|
||||||
let Ok(mut disp) = display::Display::new() else {
|
let Ok(mut disp) = display::Display::new() else {
|
||||||
return ExitCode::from(1);
|
return ExitCode::from(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let mut pm = plugman::PlugMan::new(1024); // idk tbh
|
let mut pm = plugman::PlugMan::new(1024); // idk tbh
|
||||||
if let Err(e) = pm.load(PathBuf::from("./plugins")
|
if let Err(e) = pm.load(&config.plugins.path){
|
||||||
//ca.plugin_dir.as_std_path().to_path_buf()
|
log::error!("Failed to load plugins: {e:?}");
|
||||||
){
|
return ExitCode::from(3);
|
||||||
eprintln!("ERROR: Failed to load plugins: {e:?}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.init_plugins();
|
pm.init_plugins(&config);
|
||||||
|
|
||||||
// TODO: Sort them from a config or something
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let vals: Vec<String> = pm.poll_plugins().into_iter().map(|f| f.1).collect();
|
let vals = pm.poll_plugins();
|
||||||
|
|
||||||
|
log::debug!("vals: {vals:?}");
|
||||||
let _ = disp.write_display_name(&vals.join(" | "));
|
if let Err(e) = disp.write_with_vec(&config, vals) {
|
||||||
|
log::warn!("Failed to write too bar: {e}");
|
||||||
|
}
|
||||||
std::thread::sleep(Duration::from_millis(250));
|
std::thread::sleep(Duration::from_millis(250));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use std::path::PathBuf;
|
use std::{collections::HashMap, path::{Path, PathBuf}};
|
||||||
|
|
||||||
|
use crate::ipc::Message;
|
||||||
|
|
||||||
use self::plugin::Plugin;
|
use self::plugin::Plugin;
|
||||||
|
|
||||||
@@ -19,14 +21,17 @@ 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);
|
||||||
|
|
||||||
let files = std::fs::read_dir(dir)?;
|
let files = std::fs::read_dir(dir)?;
|
||||||
|
|
||||||
for file in files {
|
for file in files {
|
||||||
let entry = file?;
|
let entry = file?;
|
||||||
if entry.file_type()?.is_file() {
|
if entry.file_type()?.is_file() {
|
||||||
let plugin = Plugin::load(entry.path().to_path_buf())?;
|
let plugin = Plugin::load(entry.path().to_path_buf())?;
|
||||||
println!("INFO: Loaded plugin {} {} licensed with {}",
|
log::info!("Loaded plugin '{}' ({}) licensed with {}",
|
||||||
plugin.name().clone(),
|
plugin.name().clone(),
|
||||||
plugin.version().clone().unwrap_or("None".to_string()),
|
plugin.version().clone().unwrap_or("None".to_string()),
|
||||||
plugin.license().clone().unwrap_or("None".to_string())
|
plugin.license().clone().unwrap_or("None".to_string())
|
||||||
@@ -37,14 +42,19 @@ impl PlugMan {
|
|||||||
self.load(entry.path().to_path_buf())?;
|
self.load(entry.path().to_path_buf())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("INFO: Loaded {} plugins", self.plugins.len());
|
if self.plugins.len() == 0 {
|
||||||
|
log::error!("No plugins found");
|
||||||
|
return Err(PluginError::NoPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Loaded {} plugins", self.plugins.len());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_plugins(&mut self) {
|
pub fn init_plugins(&mut self, cfg: &crate::config::Config) {
|
||||||
for plugin in &self.plugins {
|
for plugin in &mut self.plugins {
|
||||||
plugin.init();
|
plugin.init(cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +62,7 @@ impl PlugMan {
|
|||||||
pub fn reload_plugins(&mut self) {
|
pub fn reload_plugins(&mut self) {
|
||||||
for plugin in &mut self.plugins {
|
for plugin in &mut self.plugins {
|
||||||
if let Err(e) = plugin.reload() {
|
if let Err(e) = plugin.reload() {
|
||||||
eprintln!("ERROR: Failed to reload plugin {:?}: {e}", plugin.name());
|
log::error!("Failed to reload plugin {:?}: {e}", plugin.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,31 +74,65 @@ impl PlugMan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_plugins(&mut self) -> Vec<(String, String)> {
|
pub fn poll_plugins(&mut self) -> HashMap<String, PolledText> {
|
||||||
let mut answers = Vec::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) => {
|
||||||
if !v.is_empty() {
|
let pth = (plugin.name().clone(), PolledText::new(v));
|
||||||
answers.push((plugin.name().clone(), v));
|
if !pth.1.text().is_empty() {
|
||||||
|
answers.insert(pth.0, pth.1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => eprintln!("Failed to poll plugin: {e}"),
|
Err(e) => log::error!("Failed to poll plugin: {e}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
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)]
|
||||||
|
pub struct PolledText {
|
||||||
|
text: String,
|
||||||
|
disabled: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolledText {
|
||||||
|
pub fn new(text: String) -> Self {
|
||||||
|
Self { text, disabled: false }
|
||||||
|
}
|
||||||
|
pub fn text(&self) -> &String {
|
||||||
|
&self.text
|
||||||
|
}
|
||||||
|
pub fn disabled(&self) -> bool {
|
||||||
|
self.disabled
|
||||||
|
}
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.disabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PluginError {
|
pub enum PluginError {
|
||||||
DlOpenError,
|
DlOpenError,
|
||||||
IoError
|
IoError,
|
||||||
|
NoPlugins
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<dlopen::Error> for PluginError {
|
impl From<dlopen::Error> for PluginError {
|
||||||
|
|||||||
@@ -1,148 +0,0 @@
|
|||||||
use std::{alloc::Layout, ffi::{c_char, c_uint, CStr, CString}, path::PathBuf};
|
|
||||||
|
|
||||||
use dlopen::raw::Library;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy,)]
|
|
||||||
pub struct PluginSyms {
|
|
||||||
init: unsafe extern "C" fn(),
|
|
||||||
pre_reload: unsafe extern "C" fn() -> *const (),
|
|
||||||
post_reload: unsafe extern "C" fn(state: *const ()),
|
|
||||||
poll: unsafe extern "C" fn(buf: *mut i8, len: usize),
|
|
||||||
free: unsafe extern "C" fn(),
|
|
||||||
get_info: unsafe extern "C" fn() -> *const PluginInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PluginInfo {
|
|
||||||
name: *const c_char,
|
|
||||||
version: *const c_char,
|
|
||||||
license: *const c_char,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Plugin {
|
|
||||||
syms: Option<PluginSyms>,
|
|
||||||
path: PathBuf,
|
|
||||||
lib: Option<Library>,
|
|
||||||
enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Plugin {
|
|
||||||
pub fn load(path: PathBuf) -> Result<Self, dlopen::Error> {
|
|
||||||
let mut s = Self::default();
|
|
||||||
s.enabled = true;
|
|
||||||
s.path = path;
|
|
||||||
s.reload_symbols()?;
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reload_symbols(&mut self) -> Result<(), dlopen::Error> {
|
|
||||||
println!("INFO: Loading {:?}", self.path);
|
|
||||||
let lib = Library::open(&self.path)?;
|
|
||||||
let symbols = PluginSyms {
|
|
||||||
init: unsafe { lib.symbol("plug_init")? },
|
|
||||||
pre_reload: unsafe { lib.symbol("plug_pre_reload")? },
|
|
||||||
post_reload: unsafe { lib.symbol("plug_post_reload")? },
|
|
||||||
poll: unsafe { lib.symbol("plug_poll")? },
|
|
||||||
free: unsafe { lib.symbol("plug_free")? },
|
|
||||||
|
|
||||||
get_info: unsafe { lib.symbol("plug_get_info")? },
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
if (symbols.get_info)().is_null() {
|
|
||||||
eprintln!("ERROR: Info fields for plugin {:?} are null", self.path);
|
|
||||||
self.disable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.syms = Some(symbols);
|
|
||||||
self.lib = Some(lib);
|
|
||||||
println!("INFO: Loading OK");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn syms(&self) -> PluginSyms {
|
|
||||||
self.syms.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(&self) {
|
|
||||||
unsafe {
|
|
||||||
(self.syms().init)()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn reload(&mut self) -> Result<(), dlopen::Error> {
|
|
||||||
unsafe {
|
|
||||||
let state = (self.syms().pre_reload)();
|
|
||||||
if let Err(e) = self.reload_symbols() {
|
|
||||||
self.disable();
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
(self.syms().post_reload)(state);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll(&self, cap: usize) -> Result<String, std::string::FromUtf8Error> {
|
|
||||||
let layout = Layout::from_size_align(cap, 1).unwrap();
|
|
||||||
let buf = unsafe {
|
|
||||||
std::alloc::alloc_zeroed(layout)
|
|
||||||
};
|
|
||||||
let s = unsafe {
|
|
||||||
(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)
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Polled: {}", s);
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free(self) {
|
|
||||||
unsafe {
|
|
||||||
(self.syms().free)();
|
|
||||||
}
|
|
||||||
drop(self.lib.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enabled(&self) -> bool {
|
|
||||||
self.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable(&mut self) {
|
|
||||||
self.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
|
||||||
unsafe {
|
|
||||||
let info = (self.syms().get_info)();
|
|
||||||
CStr::from_ptr((*info).name).to_string_lossy().to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version(&self) -> Option<String> {
|
|
||||||
unsafe {
|
|
||||||
let info = (self.syms().get_info)();
|
|
||||||
let v = (*info).version;
|
|
||||||
if v.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(CStr::from_ptr(v).to_string_lossy().to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn license(&self) -> Option<String> {
|
|
||||||
unsafe {
|
|
||||||
let info = (self.syms().get_info)();
|
|
||||||
let v = (*info).license;
|
|
||||||
if v.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(CStr::from_ptr(v).to_string_lossy().to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
50
src/plugman/plugin/context.rs
Normal file
50
src/plugman/plugin/context.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use std::ffi::{c_char, CStr, CString};
|
||||||
|
use super::Plugin;
|
||||||
|
|
||||||
|
pub struct PluginContextContainer {
|
||||||
|
pub inner: PluginContext,
|
||||||
|
_config_dir: CString
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PluginContext {
|
||||||
|
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 {
|
||||||
|
pub fn new(plug: &Plugin, config_dir: &CString) -> Self {
|
||||||
|
Self {
|
||||||
|
config_dir: config_dir.as_ptr(),
|
||||||
|
funcs: PluginContextFuncs {
|
||||||
|
log: plug_log,
|
||||||
|
},
|
||||||
|
buf: plug.buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl PluginContextContainer {
|
||||||
|
pub fn new(plug: &Plugin, config_dir: &String) -> Self {
|
||||||
|
let _config_dir = CString::new(config_dir.clone()).unwrap();
|
||||||
|
Self {
|
||||||
|
inner: PluginContext::new(plug, &_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}");
|
||||||
|
}
|
||||||
41
src/plugman/plugin/loader.rs
Normal file
41
src/plugman/plugin/loader.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use dlopen::raw::Library;
|
||||||
|
use crate::ipc::MessageFFI;
|
||||||
|
|
||||||
|
use super::{context::PluginContext, Plugin, PluginInfo};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct PluginSyms {
|
||||||
|
pub(super) init: unsafe extern "C" fn(ctx: *const PluginContext) -> i32,
|
||||||
|
pub(super) pre_reload: unsafe extern "C" fn() -> *const (),
|
||||||
|
pub(super) post_reload: unsafe extern "C" fn(state: *const ()) -> i32,
|
||||||
|
pub(super) on_msg: unsafe extern "C" fn(msg: *const MessageFFI) -> i32,
|
||||||
|
pub(super) free: unsafe extern "C" fn() -> i32,
|
||||||
|
pub(super) get_info: unsafe extern "C" fn() -> *const PluginInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin {
|
||||||
|
pub fn reload_symbols(&mut self) -> Result<(), dlopen::Error> {
|
||||||
|
log::debug!("Loading {:?}", self.path);
|
||||||
|
let lib = Library::open(&self.path)?;
|
||||||
|
let symbols = PluginSyms {
|
||||||
|
init: unsafe { lib.symbol("plug_init")? },
|
||||||
|
pre_reload: unsafe { lib.symbol("plug_pre_reload")? },
|
||||||
|
post_reload: unsafe { lib.symbol("plug_post_reload")? },
|
||||||
|
on_msg: unsafe { lib.symbol("plug_on_msg")? },
|
||||||
|
free: unsafe { lib.symbol("plug_free")? },
|
||||||
|
|
||||||
|
get_info: unsafe { lib.symbol("plug_get_info")? },
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
if (symbols.get_info)().is_null() {
|
||||||
|
log::error!("Info fields for plugin {:?} are null", self.path);
|
||||||
|
self.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.syms = Some(symbols);
|
||||||
|
self.lib = Some(lib);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/plugman/plugin/mod.rs
Normal file
159
src/plugman/plugin/mod.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
use std::{alloc::Layout, ffi::{c_char, CStr}, path::PathBuf};
|
||||||
|
|
||||||
|
use dlopen::raw::Library;
|
||||||
|
|
||||||
|
use crate::{config::Config, ipc::{Message, MessageFFI}};
|
||||||
|
|
||||||
|
use self::context::{PluginContext, PluginContextContainer};
|
||||||
|
|
||||||
|
pub mod loader;
|
||||||
|
pub mod context;
|
||||||
|
|
||||||
|
const MEM_LAYOUT: Result<Layout, std::alloc::LayoutError> = Layout::from_size_align(2048, 1);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PluginInfo {
|
||||||
|
name: *const c_char,
|
||||||
|
version: *const c_char,
|
||||||
|
license: *const c_char,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Plugin {
|
||||||
|
syms: Option<loader::PluginSyms>,
|
||||||
|
path: PathBuf,
|
||||||
|
lib: Option<Library>,
|
||||||
|
enabled: bool,
|
||||||
|
buf: *mut u8
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl Plugin {
|
||||||
|
pub fn load(path: PathBuf) -> Result<Self, dlopen::Error> {
|
||||||
|
let buf = unsafe {
|
||||||
|
std::alloc::alloc_zeroed(MEM_LAYOUT.unwrap())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut s = Self {
|
||||||
|
enabled: true,
|
||||||
|
path,
|
||||||
|
syms: None,
|
||||||
|
lib: None,
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
s.reload_symbols()?;
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syms(&self) -> loader::PluginSyms {
|
||||||
|
self.syms.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut 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(self, conf_dir);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
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> {
|
||||||
|
unsafe {
|
||||||
|
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() {
|
||||||
|
self.disable();
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
if (self.syms().post_reload)(state) != 0 {
|
||||||
|
log::warn!("`post_reload` had an error, disabling");
|
||||||
|
self.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_msg(&mut self, msg: Message) {
|
||||||
|
unsafe {
|
||||||
|
// log::debug!("Sending message to '{}': {:?}", self.name(), msg);
|
||||||
|
if (self.syms().on_msg)(&msg.ffi as *const MessageFFI) != 0 {
|
||||||
|
log::warn!("`on_msg` had an error, disabling");
|
||||||
|
self.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
// log::debug!("Polled: {}", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn free(mut self) {
|
||||||
|
unsafe {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enabled(&self) -> bool {
|
||||||
|
self.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
unsafe {
|
||||||
|
let info = (self.syms().get_info)();
|
||||||
|
CStr::from_ptr((*info).name).to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(&self) -> Option<String> {
|
||||||
|
unsafe {
|
||||||
|
let info = (self.syms().get_info)();
|
||||||
|
let v = (*info).version;
|
||||||
|
if v.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(CStr::from_ptr(v).to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn license(&self) -> Option<String> {
|
||||||
|
unsafe {
|
||||||
|
let info = (self.syms().get_info)();
|
||||||
|
let v = (*info).license;
|
||||||
|
if v.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(CStr::from_ptr(v).to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
0
src/util.rs
Normal file
0
src/util.rs
Normal file
Reference in New Issue
Block a user