feat: got library system loading working
This commit is contained in:
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -502,6 +502,7 @@ dependencies = [
|
||||
"gtk4",
|
||||
"gtk4-layer-shell",
|
||||
"itertools",
|
||||
"libloading",
|
||||
"log",
|
||||
"nix",
|
||||
"notify",
|
||||
@@ -1264,6 +1265,16 @@ version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
@@ -2331,6 +2342,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
||||
@@ -50,6 +50,8 @@ tokio = { workspace = true, features = ["full"] }
|
||||
unescape.workspace = true
|
||||
wait-timeout.workspace = true
|
||||
rhai.workspace = true
|
||||
# Plugin loading
|
||||
libloading = "0.8.9"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
|
||||
@@ -21,14 +21,15 @@ use crate::{
|
||||
window_initiator::WindowInitiator,
|
||||
*,
|
||||
};
|
||||
use rhai_impl::parser::ParseConfig;
|
||||
use anyhow::{anyhow, bail};
|
||||
use gdk::Monitor;
|
||||
use gtk4::Window;
|
||||
use gtk4::{gdk, glib};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::OnceCell;
|
||||
use rhai::Dynamic;
|
||||
use rhai_impl::ast::WidgetNode;
|
||||
use rhai_impl::parser::ParseConfig;
|
||||
use serde::{de::Error as SerdeError, Deserialize, Deserializer};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
@@ -39,6 +40,13 @@ use std::{
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
static ACTIVE_PLUGIN: OnceCell<libloading::Library> = OnceCell::new();
|
||||
|
||||
fn set_active_plugin(lib: libloading::Library) {
|
||||
// will panic if called more than once
|
||||
ACTIVE_PLUGIN.set(lib).unwrap();
|
||||
}
|
||||
|
||||
/// A command for the ewwii daemon.
|
||||
/// While these are mostly generated from ewwii CLI commands (see [`opts::ActionWithServer`]),
|
||||
/// they may also be generated from other places internally.
|
||||
@@ -90,6 +98,10 @@ pub enum DaemonCommand {
|
||||
print: bool,
|
||||
sender: DaemonResponseSender,
|
||||
},
|
||||
SetPlugin {
|
||||
file_path: String,
|
||||
sender: DaemonResponseSender,
|
||||
},
|
||||
}
|
||||
|
||||
/// An opened window.
|
||||
@@ -366,6 +378,12 @@ impl<B: DisplayBackend> App<B> {
|
||||
Err(e) => sender.send_failure(e.to_string())?,
|
||||
};
|
||||
}
|
||||
DaemonCommand::SetPlugin { file_path, sender } => {
|
||||
match self.set_ewwii_plugin(file_path) {
|
||||
Ok(_) => sender.send_success(String::new())?,
|
||||
Err(e) => sender.send_failure(e.to_string())?,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -483,7 +501,7 @@ impl<B: DisplayBackend> App<B> {
|
||||
// so I guess that I will just let it stay right here.
|
||||
let config_path = self.paths.get_rhai_path();
|
||||
let compiled_ast = self.ewwii_config.get_owned_compiled_ast();
|
||||
|
||||
|
||||
{
|
||||
let mut stored_parser = self.config_parser.borrow_mut();
|
||||
stored_parser.set_opt_level(get_opt_level_from(
|
||||
@@ -799,6 +817,30 @@ impl<B: DisplayBackend> App<B> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_ewwii_plugin(&mut self, file_path: String) -> Result<()> {
|
||||
let lib = unsafe {
|
||||
libloading::Library::new(file_path)
|
||||
.map_err(|e| anyhow!("Failed to load plugin: {}", e))?
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Each plugin exposes: extern "C" fn create_plugin() -> Box<dyn Plugin>
|
||||
let constructor: libloading::Symbol<
|
||||
unsafe extern "C" fn() -> Box<dyn ewwii_plugin_api::Plugin>,
|
||||
> = lib
|
||||
.get(b"create_plugin")
|
||||
.map_err(|e| anyhow!("Failed to find create_plugin: {}", e))?;
|
||||
|
||||
let plugin = constructor(); // instantiate plugin
|
||||
let host = crate::plugin::EwwiiImpl;
|
||||
plugin.init(&host); // call init immediately
|
||||
|
||||
set_active_plugin(lib); // keep library alive
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
|
||||
@@ -15,7 +15,10 @@ use rhai_impl::{ast::WidgetNode, parser::ParseConfig};
|
||||
|
||||
/// Load an [`EwwiiConfig`] from the config dir of the given [`crate::EwwiiPaths`],
|
||||
/// resetting and applying the global YuckFiles object in [`crate::error_handling_ctx`].
|
||||
pub fn read_from_ewwii_paths(eww_paths: &EwwiiPaths, parser: &mut ParseConfig) -> Result<EwwiiConfig> {
|
||||
pub fn read_from_ewwii_paths(
|
||||
eww_paths: &EwwiiPaths,
|
||||
parser: &mut ParseConfig,
|
||||
) -> Result<EwwiiConfig> {
|
||||
EwwiiConfig::read_from_dir(eww_paths, parser)
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ mod gen_diagnostic_macro;
|
||||
mod ipc_server;
|
||||
mod opts;
|
||||
mod paths;
|
||||
mod plugin;
|
||||
mod server;
|
||||
mod util;
|
||||
mod widgets;
|
||||
|
||||
@@ -217,6 +217,14 @@ pub enum ActionWithServer {
|
||||
#[arg(long = "sprint", short = 'p')]
|
||||
print: bool,
|
||||
},
|
||||
|
||||
/// Set a plugin (.so) to the ewwii binary
|
||||
#[command(name = "set-plugin")]
|
||||
SetPlugin {
|
||||
/// The .so file to load
|
||||
#[arg(value_parser = absolute_file_path_parser)]
|
||||
file_path: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Opt {
|
||||
@@ -355,6 +363,12 @@ impl ActionWithServer {
|
||||
sender,
|
||||
})
|
||||
}
|
||||
ActionWithServer::SetPlugin { file_path } => {
|
||||
return with_response_channel(|sender| app::DaemonCommand::SetPlugin {
|
||||
file_path,
|
||||
sender,
|
||||
})
|
||||
}
|
||||
};
|
||||
(command, None)
|
||||
}
|
||||
@@ -431,3 +445,10 @@ fn parse_inject_var_map(s: &str) -> Result<HashMap<String, String>, String> {
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
fn absolute_file_path_parser(s: &str) -> Result<String, String> {
|
||||
let p = std::path::Path::new(s);
|
||||
std::fs::canonicalize(p)
|
||||
.map_err(|e| format!("Failed to canonicalize '{}': {}", s, e))
|
||||
.map(|abs_path| abs_path.to_string_lossy().into_owned())
|
||||
}
|
||||
@@ -1,19 +1,13 @@
|
||||
use ewwii_plugin_api::{EwwiiAPI, Plugin};
|
||||
use ewwii_plugin_api::EwwiiAPI;
|
||||
use rhai::Engine;
|
||||
use rhai_impl::parser::ParseConfig;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub struct EwwiiImpl;
|
||||
|
||||
impl HostAPI for EwwiiImpl {
|
||||
// General
|
||||
impl EwwiiAPI for EwwiiImpl {
|
||||
// General
|
||||
fn log(&self, msg: &str) {
|
||||
println!("[HOST LOG] {}", msg);
|
||||
}
|
||||
|
||||
// Rhai Engine Stuff
|
||||
fn get_rhai_engine(&self) {
|
||||
|
||||
}
|
||||
|
||||
fn set_rhai_engine(&self, engine: &Engine) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ pub fn initialize_server<B: DisplayBackend>(
|
||||
widget_reg_store: std::rc::Rc::new(std::sync::Mutex::new(None)),
|
||||
pl_handler_store: None,
|
||||
rt_engine_config: EngineConfValues::default(),
|
||||
config_parser: config_parser,
|
||||
config_parser,
|
||||
paths,
|
||||
gtk_main_loop: main_loop.clone(),
|
||||
phantom: PhantomData,
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
use rhai::Engine;
|
||||
|
||||
/// The shared trait defining the Ewwii plugin API
|
||||
pub trait EwwiiAPI: Send + Sync {
|
||||
// General stuff
|
||||
fn log(&self, msg: &str);
|
||||
|
||||
// Rhai Manipulation Stuff
|
||||
fn get_rhai_engine(&self) -> &mut Engine;
|
||||
fn set_rhai_engine(&self, engine: &Engine);
|
||||
}
|
||||
|
||||
/// The API format that the plugin should follow
|
||||
|
||||
Reference in New Issue
Block a user