From 596839e4ed545a8fb2ca132e852c9fcefb4c41ae Mon Sep 17 00:00:00 2001 From: modeco80 Date: Sun, 4 Aug 2024 03:47:20 -0400 Subject: [PATCH] listen address + port are configurable. Also, libvncserver log messages now output via Tracing. --- Cargo.lock | 1 + crates/retro_frontend/src/libretro_vfs.rs | 14 +++++ crates/retrovnc/Cargo.toml | 3 ++ crates/retrovnc/build.rs | 12 +++++ crates/retrovnc/src/main.rs | 21 +++++--- crates/retrovnc/src/rfb.rs | 66 ++++++++++++++++++++--- crates/retrovnc/src/rfb_log_helper.cpp | 43 +++++++++++++++ 7 files changed, 146 insertions(+), 14 deletions(-) create mode 100644 crates/retro_frontend/src/libretro_vfs.rs create mode 100644 crates/retrovnc/build.rs create mode 100644 crates/retrovnc/src/rfb_log_helper.cpp diff --git a/Cargo.lock b/Cargo.lock index a1423f7..396f005 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,7 @@ name = "retrovnc" version = "0.1.0" dependencies = [ "anyhow", + "cc", "clap", "libvnc-sys", "retro_frontend", diff --git a/crates/retro_frontend/src/libretro_vfs.rs b/crates/retro_frontend/src/libretro_vfs.rs new file mode 100644 index 0000000..6ac0cbd --- /dev/null +++ b/crates/retro_frontend/src/libretro_vfs.rs @@ -0,0 +1,14 @@ +//! Libretro VFS + +use crate::libretro_sys_new::*; +use std::path; + + +/// A file handle +struct FileHandle { + /// The path used to open a file on the host + real_path: path::PathBuf, +} + +//unsafe extern "C" fn libretro_vfs_get_path(stream: *mut ) + diff --git a/crates/retrovnc/Cargo.toml b/crates/retrovnc/Cargo.toml index 5b89e86..b9f899a 100644 --- a/crates/retrovnc/Cargo.toml +++ b/crates/retrovnc/Cargo.toml @@ -11,3 +11,6 @@ libvnc-sys = "0.1.4" retro_frontend = { path = "../retro_frontend" } tracing = "0.1.40" tracing-subscriber = "0.3.18" + +[build-dependencies] +cc = "1.0.99" diff --git a/crates/retrovnc/build.rs b/crates/retrovnc/build.rs new file mode 100644 index 0000000..38a2e31 --- /dev/null +++ b/crates/retrovnc/build.rs @@ -0,0 +1,12 @@ +use cc; + +fn main() { + let mut build = cc::Build::new(); + + build + .emit_rerun_if_env_changed(true) + .cpp(true) + .std("c++20") + .file("src/rfb_log_helper.cpp") + .compile("rfb_log_helper"); +} diff --git a/crates/retrovnc/src/main.rs b/crates/retrovnc/src/main.rs index 3430aec..5d77363 100644 --- a/crates/retrovnc/src/main.rs +++ b/crates/retrovnc/src/main.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::{net::Ipv4Addr, path::Path}; use anyhow::Result; @@ -21,13 +21,10 @@ struct App { } impl App { - fn new() -> Result> { + fn new(rfb_config: RfbServerConfig) -> Result> { let mut boxed = Box::new(Self { frontend: None, - rfb_server: RfbServer::new(RfbServerConfig { - width: 640, - height: 480, - })?, + rfb_server: RfbServer::new(rfb_config)?, // nasty, but idk a better way pad: RetroPad::new(), }); @@ -146,8 +143,18 @@ fn main() -> Result<()> { let core_path: &String = matches.get_one("core").unwrap(); + rfb::init(); + + let rfb_config = RfbServerConfig { + // default WxH; this is overridden quickly + width: 640, + height: 480, + listen_address: "127.0.0.1".parse::()?, + listen_port: 6930 + }; + // Initalize the app - let mut app = App::new()?; + let mut app = App::new(rfb_config)?; app.load_core(core_path)?; diff --git a/crates/retrovnc/src/rfb.rs b/crates/retrovnc/src/rfb.rs index 4e61e73..3c8b15b 100644 --- a/crates/retrovnc/src/rfb.rs +++ b/crates/retrovnc/src/rfb.rs @@ -8,8 +8,8 @@ use anyhow::{anyhow, Result}; pub struct RfbServerConfig { pub width: u16, pub height: u16, - // TODO: Listen address - // TODO: listen port + pub listen_address: std::net::Ipv4Addr, + pub listen_port: u16, } pub struct RfbServer { @@ -20,16 +20,18 @@ pub struct RfbServer { buttons: [bool; 32], } - impl RfbServer { pub fn new(config: RfbServerConfig) -> Result> { unsafe { // Feed a fake argv in (TODO: Make this better.) + + let ip_string = std::ffi::CString::new(config.listen_address.to_string())?; + let argc = 3; let argv: [*const std::ffi::c_char; 3] = [ b"RfbServer".as_ptr() as *const i8, b"-listen".as_ptr() as *const i8, - b"127.0.0.1".as_ptr() as *const i8, + ip_string.as_ptr(), ]; let screen = rfb::bindings::rfbGetScreen( @@ -42,7 +44,6 @@ impl RfbServer { 4, ); - // result if screen.is_null() { return Err(anyhow!("rfbGetScreen() failed")); } @@ -64,7 +65,7 @@ impl RfbServer { (*screen).kbdAddEvent = Some(Self::on_key_callback); // testing - (*screen).port = 6930; + (*screen).port = config.listen_port as i32; (*screen).ipv6port = 0; ret.resize(config.width, config.height); @@ -76,7 +77,6 @@ impl RfbServer { pub fn resize(&mut self, w: u16, h: u16) { let len = (w as usize) * (h as usize); - //self.framebuffer = Vec::with_capacity(len); self.framebuffer.resize(len, 0); unsafe { @@ -239,3 +239,55 @@ impl RfbServer { } } } + +// Logging support + +use std::ffi; +use tracing::*; + +#[allow(dead_code)] +#[no_mangle] +pub extern "C" fn rfb_log(buf: *const ffi::c_char) { + // Safety: This pointer is never null, and always comes from the stack; + // we really only should get UTF-8 errors here, probably. + unsafe { + match ffi::CStr::from_ptr(buf).to_str() { + Ok(message) => { + info!("{}", message); + } + Err(err) => { + error!("Broken rfb log: {:?}", err); + } + } + } +} + +#[allow(dead_code)] +#[no_mangle] +pub extern "C" fn rfb_log_error(buf: *const ffi::c_char) { + // Safety: This pointer is never null, and always comes from the stack; + // we really only should get UTF-8 errors here, probably. + unsafe { + match ffi::CStr::from_ptr(buf).to_str() { + Ok(message) => { + error!("{}", message); + } + Err(err) => { + error!("Broken rfb log: {:?}", err); + } + } + } +} + +extern "C" { + /// Provided by a C++ helper to do vararg formatting for us. + /// Calls back to the above functions to output messages to Tracing + fn rfb_log_init(); +} + +/// Call at program startup. +pub fn init() { + unsafe { + rfb_log_init(); + } +} diff --git a/crates/retrovnc/src/rfb_log_helper.cpp b/crates/retrovnc/src/rfb_log_helper.cpp new file mode 100644 index 0000000..f1724a3 --- /dev/null +++ b/crates/retrovnc/src/rfb_log_helper.cpp @@ -0,0 +1,43 @@ +#include +#include + +using rfbLogProc = void(*)(const char*, ...); + +extern "C" { + // defined in Rust; these pipe to Tracing + void rfb_log(const char* buf); + void rfb_log_error(const char* buf); + + // libvncserver provides these for customizing where logging goes + extern rfbLogProc rfbLog; + extern rfbLogProc rfbErr; +} + +template +void rfb_log_impl(const char* format, ...) { + char buf[512]{}; + va_list val; + + va_start(val, format); + auto n = std::vsnprintf(&buf[0], sizeof(buf)-1, format, val); + va_end(val); + + // Failed to format for some reason, just give up. + if(n == -1) + return; + + // Remove the last newline and replace it with a null terminator. + if(buf[n-1] == '\n') + buf[n-1] = '\0'; + + // Call the Rust-side reciever. + reciever(&buf[0]); +} + +extern "C" { + void rfb_log_init() { + // Pretty simple. + rfbLog = &rfb_log_impl<&rfb_log>; + rfbErr = &rfb_log_impl<&rfb_log_error>; + } +}