From c233b30a5256d1ff322266c546a1254728d99f7e Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Sat, 4 May 2024 15:00:58 +0200 Subject: [PATCH] fix(src): Ensure that the new c api can actually be used --- .cargo/config.toml | 7 ++- Cargo.lock | 12 ++--- Cargo.toml | 4 +- src/app/command_interface/command_list/mod.rs | 54 +++++++++++++++++-- src/app/events/handlers/command.rs | 24 ++++----- src/app/events/handlers/input.rs | 4 +- src/app/events/mod.rs | 10 +++- src/app/mod.rs | 28 +++++++++- src/cli.rs | 7 ++- src/main.rs | 6 +-- 10 files changed, 117 insertions(+), 39 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 7af97f1..d9819ae 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,5 @@ -[target.x86_64-unknown-linux-gnu] -rustflags = ["-C", "link-arg=-fuse-ld=mold"] +# [target.x86_64-unknown-linux-gnu] +# rustflags = ["-C", "link-arg=-fuse-ld=mold"] + +[build] +rustflags = ["-C", "link-args=-rdynamic -fuse-ld=mold"] diff --git a/Cargo.lock b/Cargo.lock index c5ed663..9b31627 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,9 +211,9 @@ checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backoff" @@ -1696,9 +1696,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -2863,9 +2863,9 @@ dependencies = [ [[package]] name = "trixy" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84becfd882a68c35ec1c2bc23aac6a0b0e10021d74b8675250c048621d633c1" +checksum = "73eef2c0a310a228e44c0d5aa8b264381407f221975bb909646edb08d3b722ab" dependencies = [ "convert_case", "libc", diff --git a/Cargo.toml b/Cargo.toml index 66a91de..58f3d4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ keymaps = {version = "0.1.1", features = ["crossterm"] } # c api libloading = "0.8.3" -trixy = {version = "0.1.0"} +trixy = {version = "0.1.1"} # lua stuff mlua = { version = "0.9.7", features = ["lua54", "async", "send", "serialize"] } @@ -43,7 +43,7 @@ directories = "5.0.1" pretty_assertions = "1.4.0" [build-dependencies] -trixy = { version = "0.1.0" } +trixy = { version = "0.1.1" } [profile.release] lto = true diff --git a/src/app/command_interface/command_list/mod.rs b/src/app/command_interface/command_list/mod.rs index 6906c46..04923f3 100644 --- a/src/app/command_interface/command_list/mod.rs +++ b/src/app/command_interface/command_list/mod.rs @@ -1,14 +1,58 @@ // Run the `api` bin to see the generated api +use std::{panic::catch_unwind, process, thread}; + +use cli_log::{debug, info}; + use crate::app::{events::Event, COMMAND_TRANSMITTER}; include!(concat!(env!("OUT_DIR"), "/api.rs")); pub fn handle_cmd(cmd: Commands) { - let tx = COMMAND_TRANSMITTER - .get() - .expect("The cell should always be populated, at this point"); + // Unwinding into the c code implicitly calling this function would be UB. Besides, rust would + // probably just abort the process, giving us a weird shutdown without an error message. + // Thus we catch the panic before the ffi boundary. This could also be moved to trixy. + let error = catch_unwind(|| { + let tx = COMMAND_TRANSMITTER + .get() + .expect("The cell should always be populated, at this point"); - // FIXME: The None here is definitely wrong <2024-05-03> - tx.send(Event::CommandEvent(cmd, None)); + info!("Received command: {:#?}", cmd); + + // NOTE: The extra future and tokio runtime here is necessary to have a sync api. Which is imho + // better than expecting c to work with a async one. <2024-05-04> + let future = async move { + debug!("Asyncly started to send cmd"); + + // FIXME: The None here is definitely wrong <2024-05-03> + tx.send(Event::CommandEvent(cmd, None)) + .await + .expect("I hope that this does not fail"); + + debug!("Done with the async send"); + }; + + debug!("Starting runtime"); + let handle = thread::spawn(|| { + // run the task a separate thread to avoid tokio having problems with the currently + // running runtime. + + // PERFORMANCE(@soispha): Stating a new thread for each command is very bad. <2024-05-04> + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(future); + }); + handle.join().expect("Shall never error"); + debug!("Handled command"); + }); + + if let Err(err) = error { + eprintln!("Catched a panic just before the c ffi: {:#?}", err); + + // This will leave the terminal in a horrendous shape (as no destructors are run), but what + // can we do at this point beside that? + process::abort(); + } } diff --git a/src/app/events/handlers/command.rs b/src/app/events/handlers/command.rs index 87e7e8d..c678fbb 100644 --- a/src/app/events/handlers/command.rs +++ b/src/app/events/handlers/command.rs @@ -1,4 +1,4 @@ -use std::{mem, str::FromStr}; +use std::str::FromStr; use crate::{ app::{ @@ -84,11 +84,12 @@ pub async fn handle( Trinitrix::Api(api) => match api { Api::exit => { send_status_output!("Terminating the application.."); + warn!("Terminating the application"); EventStatus::Terminate } Api::room_message_send { message } => { if let Some(room) = app.status.room_mut() { - room.send(message.clone()).await?; + room.send(message.to_string()).await?; send_status_output!("Sent message: `{}`", message); } else { // FIXME(@soispha): This should raise an error within lua, as it would @@ -99,8 +100,6 @@ pub async fn handle( ); } - // NOTE(@soispha): This is temporary, until trixy can do it automatically <2024-05-03> - mem::forget(message); EventStatus::Ok } Api::Ui(ui) => match ui { @@ -140,9 +139,11 @@ pub async fn handle( key, callback, } => { - mode.chars().for_each(|char| { + info!("Will add a keymapping for '{}' in mode '{}'", key, mode); + mode.as_str().chars().for_each(|char| { info!("Setting keymaping ('{}') for mode '{}'", key, char); let parsed_keys = key + .as_str() .parse::() .map_err(|err| { send_error_output!(err.to_string()); @@ -172,8 +173,6 @@ pub async fn handle( }; }); - mem::forget(key); - mem::forget(mode); EventStatus::Ok } // FIXME(@soispha): It would be nice to have these functions, but well.. @@ -183,22 +182,20 @@ pub async fn handle( }, Api::Raw(raw) => match raw { Raw::raise_error { error_message } => { - send_error_output!(error_message); - mem::forget(error_message); + send_error_output!(error_message.to_string()); EventStatus::Ok } Raw::display_output { output_message } => { // TODO(@Soispha): This is only used to show the Lua command output to the user. // Lua commands already receive the output. This should probably be communicated // better, should it? - send_status_output!(output_message); - mem::forget(output_message); + send_status_output!(output_message.to_string()); EventStatus::Ok } Raw::send_input_unprocessed { input } => { let output = match app.status.state() { State::Insert => { - let key = Key::from_str(&input)?; + let key = Key::from_str(input.as_str())?; let cross_input: Event = key.try_into()?; app.ui .message_compose @@ -206,7 +203,7 @@ pub async fn handle( EventStatus::Ok } State::Command => { - let key = Key::from_str(&input)?; + let key = Key::from_str(input.as_str())?; let cross_input: Event = key.try_into()?; app.ui .cli @@ -222,7 +219,6 @@ pub async fn handle( pending_keys: _, } => EventStatus::Ok, }; - mem::forget(input); output } Raw::Private(private) => { diff --git a/src/app/events/handlers/input.rs b/src/app/events/handlers/input.rs index 77d15fa..2447f46 100644 --- a/src/app/events/handlers/input.rs +++ b/src/app/events/handlers/input.rs @@ -32,7 +32,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result, input_event: &CrosstermEvent) -> Result Ok(EventStatus::Terminate), + Event::LuaCommand(lua_code) => { + warn!( + "Got lua code to execute, but no exectuter is available:\n{}", + lua_code + ); + Ok(EventStatus::Ok) + } // lua_command::handle(app, lua_code.to_owned()) // .await // .with_context(|| format!("Failed to handle lua code: `{}`", lua_code)), diff --git a/src/app/mod.rs b/src/app/mod.rs index 628d86f..98f8569 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -5,14 +5,21 @@ pub mod status; use std::{ collections::HashMap, + ffi::c_int, path::{Path, PathBuf}, sync::OnceLock, }; use anyhow::{Context, Error, Result}; use cli_log::{info, warn}; +use crossterm::{ + event::DisableMouseCapture, + execute, + terminal::{disable_raw_mode, LeaveAlternateScreen}, +}; use directories::ProjectDirs; use keymaps::trie::Node; +use libloading::{Library, Symbol}; use matrix_sdk::Client; use tokio::sync::mpsc::{self, Sender}; use tokio_util::sync::CancellationToken; @@ -86,7 +93,11 @@ impl App<'_> { }) } - pub async fn run(&mut self, cli_lua_config_file: Option) -> Result<()> { + pub async fn run( + &mut self, + cli_lua_config_file: Option, + plugin_path: Option, + ) -> Result<()> { // Spawn input event listener tokio::task::spawn(events::listeners::input::poll( self.tx.clone(), @@ -121,6 +132,21 @@ impl App<'_> { } } + if let Some(plugin) = plugin_path { + info!("Loading plugin_main() from {}", plugin.display()); + + unsafe { + let lib = Library::new(plugin).context("Failed to load plugin")?; + let func: Symbol c_int> = lib + .get(b"plugin_main") + .context("Plugin does not have a 'plugin_main' symbol")?; + + info!("Starting plugin"); + let out = func(); + info!("Plugin finished with: {}", out); + } + } + if self.account().is_err() { info!("No saved sessions found -> jumping into setup"); self.setup().await?; diff --git a/src/cli.rs b/src/cli.rs index deb5ac8..fec5171 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,9 +12,12 @@ pub struct Args { /// The subcommand to execute, default is start pub subcommand: Option, + // #[arg(long, short)] + // /// Path to the Lua config file, executed instead of the normal one + // pub lua_config_file: Option, #[arg(long, short)] - /// Path to the Lua config file, executed instead of the normal one - pub lua_config_file: Option, + /// Path to a plugin to load. It must be a shared-object (.so) + pub plugin_path: Option, } #[derive(Subcommand, Debug)] pub enum Command { diff --git a/src/main.rs b/src/main.rs index 0e05c49..3d3fe01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,9 @@ async fn main() -> anyhow::Result<()> { match command { Command::Start {} => { let mut app = app::App::new()?; - app.run(args.lua_config_file).await?; + + // NOTE(@soispha): The `None` is temporary <2024-05-03> + app.run(None, args.plugin_path).await?; } }; @@ -24,6 +26,4 @@ async fn main() -> anyhow::Result<()> { } // FIXME(@soispha): Re-exports for trixy, this should be configurable <2024-05-03> -pub use crate::app::command_interface::handle_cmd; -pub use crate::app::command_interface::Commands; pub use crate::app::command_interface::*;