miracle_plugin/
lib.rs

1#![doc(html_root_url = "https://docs.miracle-wm.org/miracle_plugin/")]
2//! # miracle-plugin
3//!
4//! A Rust SDK for writing [miracle-wm](https://github.com/miracle-wm-org/miracle-wm) plugins.
5//!
6//! Miracle's plugin system runs each plugin as a WebAssembly module. This crate provides
7//! idiomatic Rust types and traits that map to the compositor's C ABI, so you can write
8//! plugins without touching raw FFI.
9//!
10//! ## Quick start
11//!
12//! Create a new library crate and add `miracle-plugin` as a dependency:
13//!
14//! ```bash
15//! cargo new --lib my-plugin
16//! cd my-plugin
17//! cargo add miracle-plugin
18//! ```
19//!
20//! Set the crate type to `cdylib` in `Cargo.toml`:
21//!
22//! ```toml
23//! # Cargo.toml
24//! [lib]
25//! crate-type = ["cdylib"]
26//! ```
27//!
28//! If you would like to configure from your plugin, enable the configure feature, e.g.:
29//! ```toml
30//! # Cargo.toml
31//! [dependencies]
32//! miracle-plugin = { version = "0.0.5", features = ["configure"]}
33//! ```
34//!
35//! Add the `wasm32-wasip1` target if you haven't already:
36//!
37//! ```bash
38//! rustup target add wasm32-wasip1
39//! ```
40//!
41//! Implement the [`plugin::Plugin`] trait and register your type with the
42//! [`miracle_plugin!`] macro:
43//!
44//! ```rust,ignore
45//! use miracle_plugin::plugin::Plugin;
46//!
47//! #[derive(Default)]
48//! struct MyPlugin;
49//!
50//! impl Plugin for MyPlugin {
51//!     // Your plugin implementation here.
52//! }
53//!
54//! miracle_plugin::miracle_plugin!(MyPlugin);
55//! ```
56//!
57//! Build the plugin as a `.wasm` module:
58//!
59//! ```bash
60//! cargo build --target wasm32-wasip1 --release
61//! ```
62//!
63//! The compiled `.wasm` file will be at
64//! `target/wasm32-wasip1/release/my_plugin.wasm`.
65//!
66//! ## Modules
67//!
68//! | Module | Contents |
69//! |---|---|
70//! | [`plugin`] | [`plugin::Plugin`] trait, [`miracle_plugin!`] macro, helper functions |
71//! | [`window`] | [`window::WindowInfo`], [`window::PluginWindow`], window-state enums |
72//! | [`placement`] | [`placement::Placement`] |
73//! | [`animation`] | [`animation::AnimationFrameData`], [`animation::AnimationFrameResult`] |
74//! | [`input`] | [`input::KeyboardEvent`], [`input::PointerEvent`], modifier/button flags |
75//! | [`core`] | Geometric primitives: [`core::Rect`], [`core::Point`], [`core::Size`], [`core::Rectangle`] |
76//! | [`container`] | [`container::Container`], [`container::ContainerType`], [`container::LayoutScheme`] |
77//! | [`workspace`] | [`workspace::Workspace`] |
78//! | [`output`] | [`output::Output`] |
79//! | [`application`] | [`application::ApplicationInfo`] |
80//! | [`config`] | [`config::Configuration`] and all supporting types ([`config::Modifier`], [`config::Key`], [`config::BindingAction`], …) |
81
82pub mod animation;
83pub mod application;
84#[doc(hidden)]
85pub mod bindings;
86pub mod config;
87pub mod container;
88pub mod core;
89#[doc(hidden)]
90pub mod host;
91pub mod input;
92pub mod output;
93pub mod placement;
94pub mod plugin;
95pub mod window;
96pub mod workspace;
97
98#[doc(hidden)]
99pub mod __private {
100    #[cfg(feature = "configure")]
101    pub use serde_json;
102
103    /// Called by the `miracle_plugin!` macro to serialize the plugin's configuration.
104    ///
105    /// Keeping the `#[cfg(feature = "configure")]` here (in the defining crate) means
106    /// the feature flag is resolved against miracle-plugin's own Cargo features, not
107    /// against the calling plugin crate's features — which avoids the
108    /// `unexpected_cfgs` warning that would fire if `#[cfg(feature = "configure")]`
109    /// appeared directly inside the macro body.
110    #[cfg(feature = "configure")]
111    pub fn run_configure<P: crate::plugin::Plugin>(
112        plugin: &mut P,
113        buf_ptr: i32,
114        buf_len: i32,
115    ) -> i32 {
116        match plugin.configure() {
117            None => 0,
118            Some(config_data) => {
119                let json = match serde_json::to_string(&config_data) {
120                    Ok(s) => s,
121                    Err(_) => return 0,
122                };
123                let bytes = json.as_bytes();
124                if bytes.len() > buf_len as usize {
125                    return -1;
126                }
127                unsafe {
128                    let buf = core::slice::from_raw_parts_mut(buf_ptr as *mut u8, buf_len as usize);
129                    buf[..bytes.len()].copy_from_slice(bytes);
130                }
131                bytes.len() as i32
132            }
133        }
134    }
135
136    #[cfg(not(feature = "configure"))]
137    pub fn run_configure<P: crate::plugin::Plugin>(
138        _plugin: &mut P,
139        _buf_ptr: i32,
140        _buf_len: i32,
141    ) -> i32 {
142        0
143    }
144}
145
146pub use config::{BindingAction, Key, Modifier};
147pub use plugin::{
148    get_active_workspace, get_output_at, get_outputs, get_userdata_json, managed_windows,
149    num_outputs, queue_custom_animation, request_workspace,
150};