fix(src): Ensure that the new c api can actually be used
This commit is contained in:
parent
a39a0875a3
commit
c233b30a52
|
@ -1,2 +1,5 @@
|
||||||
[target.x86_64-unknown-linux-gnu]
|
# [target.x86_64-unknown-linux-gnu]
|
||||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
rustflags = ["-C", "link-args=-rdynamic -fuse-ld=mold"]
|
||||||
|
|
|
@ -211,9 +211,9 @@ checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backoff"
|
name = "backoff"
|
||||||
|
@ -1696,9 +1696,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.18"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
@ -2863,9 +2863,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trixy"
|
name = "trixy"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f84becfd882a68c35ec1c2bc23aac6a0b0e10021d74b8675250c048621d633c1"
|
checksum = "73eef2c0a310a228e44c0d5aa8b264381407f221975bb909646edb08d3b722ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
|
@ -24,7 +24,7 @@ keymaps = {version = "0.1.1", features = ["crossterm"] }
|
||||||
|
|
||||||
# c api
|
# c api
|
||||||
libloading = "0.8.3"
|
libloading = "0.8.3"
|
||||||
trixy = {version = "0.1.0"}
|
trixy = {version = "0.1.1"}
|
||||||
|
|
||||||
# lua stuff
|
# lua stuff
|
||||||
mlua = { version = "0.9.7", features = ["lua54", "async", "send", "serialize"] }
|
mlua = { version = "0.9.7", features = ["lua54", "async", "send", "serialize"] }
|
||||||
|
@ -43,7 +43,7 @@ directories = "5.0.1"
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
trixy = { version = "0.1.0" }
|
trixy = { version = "0.1.1" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
|
@ -1,14 +1,58 @@
|
||||||
// Run the `api` bin to see the generated api
|
// 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};
|
use crate::app::{events::Event, COMMAND_TRANSMITTER};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
include!(concat!(env!("OUT_DIR"), "/api.rs"));
|
||||||
|
|
||||||
pub fn handle_cmd(cmd: Commands) {
|
pub fn handle_cmd(cmd: Commands) {
|
||||||
let tx = COMMAND_TRANSMITTER
|
// Unwinding into the c code implicitly calling this function would be UB. Besides, rust would
|
||||||
.get()
|
// probably just abort the process, giving us a weird shutdown without an error message.
|
||||||
.expect("The cell should always be populated, at this point");
|
// 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>
|
info!("Received command: {:#?}", cmd);
|
||||||
tx.send(Event::CommandEvent(cmd, None));
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{mem, str::FromStr};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
|
@ -84,11 +84,12 @@ pub async fn handle(
|
||||||
Trinitrix::Api(api) => match api {
|
Trinitrix::Api(api) => match api {
|
||||||
Api::exit => {
|
Api::exit => {
|
||||||
send_status_output!("Terminating the application..");
|
send_status_output!("Terminating the application..");
|
||||||
|
warn!("Terminating the application");
|
||||||
EventStatus::Terminate
|
EventStatus::Terminate
|
||||||
}
|
}
|
||||||
Api::room_message_send { message } => {
|
Api::room_message_send { message } => {
|
||||||
if let Some(room) = app.status.room_mut() {
|
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);
|
send_status_output!("Sent message: `{}`", message);
|
||||||
} else {
|
} else {
|
||||||
// FIXME(@soispha): This should raise an error within lua, as it would
|
// 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
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Api::Ui(ui) => match ui {
|
Api::Ui(ui) => match ui {
|
||||||
|
@ -140,9 +139,11 @@ pub async fn handle(
|
||||||
key,
|
key,
|
||||||
callback,
|
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);
|
info!("Setting keymaping ('{}') for mode '{}'", key, char);
|
||||||
let parsed_keys = key
|
let parsed_keys = key
|
||||||
|
.as_str()
|
||||||
.parse::<Keys>()
|
.parse::<Keys>()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
send_error_output!(err.to_string());
|
send_error_output!(err.to_string());
|
||||||
|
@ -172,8 +173,6 @@ pub async fn handle(
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
mem::forget(key);
|
|
||||||
mem::forget(mode);
|
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
// FIXME(@soispha): It would be nice to have these functions, but well..
|
// 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 {
|
Api::Raw(raw) => match raw {
|
||||||
Raw::raise_error { error_message } => {
|
Raw::raise_error { error_message } => {
|
||||||
send_error_output!(error_message);
|
send_error_output!(error_message.to_string());
|
||||||
mem::forget(error_message);
|
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Raw::display_output { output_message } => {
|
Raw::display_output { output_message } => {
|
||||||
// TODO(@Soispha): This is only used to show the Lua command output to the user.
|
// 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
|
// Lua commands already receive the output. This should probably be communicated
|
||||||
// better, should it?
|
// better, should it?
|
||||||
send_status_output!(output_message);
|
send_status_output!(output_message.to_string());
|
||||||
mem::forget(output_message);
|
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Raw::send_input_unprocessed { input } => {
|
Raw::send_input_unprocessed { input } => {
|
||||||
let output = match app.status.state() {
|
let output = match app.status.state() {
|
||||||
State::Insert => {
|
State::Insert => {
|
||||||
let key = Key::from_str(&input)?;
|
let key = Key::from_str(input.as_str())?;
|
||||||
let cross_input: Event = key.try_into()?;
|
let cross_input: Event = key.try_into()?;
|
||||||
app.ui
|
app.ui
|
||||||
.message_compose
|
.message_compose
|
||||||
|
@ -206,7 +203,7 @@ pub async fn handle(
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
State::Command => {
|
State::Command => {
|
||||||
let key = Key::from_str(&input)?;
|
let key = Key::from_str(input.as_str())?;
|
||||||
let cross_input: Event = key.try_into()?;
|
let cross_input: Event = key.try_into()?;
|
||||||
app.ui
|
app.ui
|
||||||
.cli
|
.cli
|
||||||
|
@ -222,7 +219,6 @@ pub async fn handle(
|
||||||
pending_keys: _,
|
pending_keys: _,
|
||||||
} => EventStatus::Ok,
|
} => EventStatus::Ok,
|
||||||
};
|
};
|
||||||
mem::forget(input);
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
Raw::Private(private) => {
|
Raw::Private(private) => {
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(
|
||||||
Commands::Trinitrix(Trinitrix::Api(Api::Raw(
|
Commands::Trinitrix(Trinitrix::Api(Api::Raw(
|
||||||
Raw::send_input_unprocessed {
|
Raw::send_input_unprocessed {
|
||||||
input: key.to_string_repr(),
|
input: key.to_string_repr().into(),
|
||||||
},
|
},
|
||||||
))),
|
))),
|
||||||
None,
|
None,
|
||||||
|
@ -45,7 +45,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(
|
||||||
Commands::Trinitrix(Trinitrix::Api(Api::Raw(Raw::send_input_unprocessed {
|
Commands::Trinitrix(Trinitrix::Api(Api::Raw(Raw::send_input_unprocessed {
|
||||||
input: converted_key.to_string_repr(),
|
input: converted_key.to_string_repr().into(),
|
||||||
}))),
|
}))),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
|
|
|
@ -4,7 +4,7 @@ pub mod listeners;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use crate::app::{command_interface::Commands, status::State, App};
|
use crate::app::{command_interface::Commands, status::State, App};
|
||||||
use cli_log::trace;
|
use cli_log::{trace, warn};
|
||||||
use crossterm::event::Event as CrosstermEvent;
|
use crossterm::event::Event as CrosstermEvent;
|
||||||
use handlers::{command, input, lua_command, matrix, setup};
|
use handlers::{command, input, lua_command, matrix, setup};
|
||||||
|
|
||||||
|
@ -29,7 +29,13 @@ impl Event {
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("Failed to handle command event: `{:#?}`", event)),
|
.with_context(|| format!("Failed to handle command event: `{:#?}`", event)),
|
||||||
|
|
||||||
Event::LuaCommand(lua_code) => 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())
|
// lua_command::handle(app, lua_code.to_owned())
|
||||||
// .await
|
// .await
|
||||||
// .with_context(|| format!("Failed to handle lua code: `{}`", lua_code)),
|
// .with_context(|| format!("Failed to handle lua code: `{}`", lua_code)),
|
||||||
|
|
|
@ -5,14 +5,21 @@ pub mod status;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
ffi::c_int,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::OnceLock,
|
sync::OnceLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Error, Result};
|
use anyhow::{Context, Error, Result};
|
||||||
use cli_log::{info, warn};
|
use cli_log::{info, warn};
|
||||||
|
use crossterm::{
|
||||||
|
event::DisableMouseCapture,
|
||||||
|
execute,
|
||||||
|
terminal::{disable_raw_mode, LeaveAlternateScreen},
|
||||||
|
};
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use keymaps::trie::Node;
|
use keymaps::trie::Node;
|
||||||
|
use libloading::{Library, Symbol};
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::Client;
|
||||||
use tokio::sync::mpsc::{self, Sender};
|
use tokio::sync::mpsc::{self, Sender};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
@ -86,7 +93,11 @@ impl App<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self, cli_lua_config_file: Option<PathBuf>) -> Result<()> {
|
pub async fn run(
|
||||||
|
&mut self,
|
||||||
|
cli_lua_config_file: Option<PathBuf>,
|
||||||
|
plugin_path: Option<PathBuf>,
|
||||||
|
) -> Result<()> {
|
||||||
// Spawn input event listener
|
// Spawn input event listener
|
||||||
tokio::task::spawn(events::listeners::input::poll(
|
tokio::task::spawn(events::listeners::input::poll(
|
||||||
self.tx.clone(),
|
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<unsafe fn() -> 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() {
|
if self.account().is_err() {
|
||||||
info!("No saved sessions found -> jumping into setup");
|
info!("No saved sessions found -> jumping into setup");
|
||||||
self.setup().await?;
|
self.setup().await?;
|
||||||
|
|
|
@ -12,9 +12,12 @@ pub struct Args {
|
||||||
/// The subcommand to execute, default is start
|
/// The subcommand to execute, default is start
|
||||||
pub subcommand: Option<Command>,
|
pub subcommand: Option<Command>,
|
||||||
|
|
||||||
|
// #[arg(long, short)]
|
||||||
|
// /// Path to the Lua config file, executed instead of the normal one
|
||||||
|
// pub lua_config_file: Option<PathBuf>,
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
/// Path to the Lua config file, executed instead of the normal one
|
/// Path to a plugin to load. It must be a shared-object (.so)
|
||||||
pub lua_config_file: Option<PathBuf>,
|
pub plugin_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
|
|
|
@ -16,7 +16,9 @@ async fn main() -> anyhow::Result<()> {
|
||||||
match command {
|
match command {
|
||||||
Command::Start {} => {
|
Command::Start {} => {
|
||||||
let mut app = app::App::new()?;
|
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>
|
// 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::*;
|
pub use crate::app::command_interface::*;
|
||||||
|
|
Reference in New Issue