retrovnc: cleanup and safety annotations

This commit is contained in:
Lily Tsuru 2024-08-06 07:50:24 -04:00
parent 91dee22da5
commit 4e8ad7616f
3 changed files with 43 additions and 13 deletions

View file

@ -1,14 +1,15 @@
# retrovnc # retrovnc
a headless Libretro frontend that exports a VNC server. A fully headless Libretro frontend that exports a VNC server for display and input.
This is mostly a "fun project", and probably isn't a very great solution for remote gaming. In layman terms, this lets you play games over VNC. Which isn't all that great, but hey.
This is mostly a "fun project" and consists mostly of code I already wrote.
# Dependencies # Dependencies
- A C++ toolchain - A C++ toolchain
- A Rust toolchain. - A Rust toolchain.
- Maybe libvncserver (i'm not sure, it seems like the package can build it).
# Building # Building
@ -16,8 +17,9 @@ This is mostly a "fun project", and probably isn't a very great solution for rem
# Usage # Usage
`$ retrovnc --core <CORE> --rom <ROM>` `$ retrovnc --core <CORE> --rom <ROM>` (see `retrovnc --help` for more options)
For disc-based titles it is probably a good idea to pass the cuesheet file. I will implement stuff later to make this less annoying. For disc-based titles it is probably a good idea to pass the cuesheet file. I will implement stuff later to make this less annoying.

View file

@ -1,3 +1,5 @@
//! EGL bindings and helpers.
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[allow(unused_imports)] #[allow(unused_imports)]
mod egl_impl { mod egl_impl {
@ -15,6 +17,8 @@ mod egl_impl {
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
// TODO: Move these helpers to a new "helpers" module.
pub type GetPlatformDisplayExt = unsafe extern "C" fn( pub type GetPlatformDisplayExt = unsafe extern "C" fn(
platform: types::EGLenum, platform: types::EGLenum,
native_display: *const std::ffi::c_void, native_display: *const std::ffi::c_void,

View file

@ -74,8 +74,12 @@ impl App {
readback_buffer: Vec::new(), readback_buffer: Vec::new(),
}); });
// Very very nasty, but honestly it works. // SAFETY: The boxed allocation will never drop since the main loop always loops forever.
// I'll look into cleaning it up later. // Even if it did, the only way to touch the pointer involves the frontend library calling retro_run,
// and the core calling one of the given callbacks. Therefore this is gnarly, but "fine".
//
// I'm still not really sure how to tell the borrow checker that this is alright,
// short of Box::leak() (which I don't want to do, since ideally I'd like actual cleanup to occur).
let obj = &mut *boxed as &mut dyn FrontendInterface; let obj = &mut *boxed as &mut dyn FrontendInterface;
boxed.frontend = Some(Frontend::new(obj as *mut dyn FrontendInterface)); boxed.frontend = Some(Frontend::new(obj as *mut dyn FrontendInterface));
@ -88,8 +92,13 @@ impl App {
fn init(&mut self) { fn init(&mut self) {
// Currently retrovnc just hardcodes the assumption of a single RetroPad. // Currently retrovnc just hardcodes the assumption of a single RetroPad.
let pad = &mut self.pad as *mut dyn InputDevice;
// SAFETY: This too won't ever be Use-After-Free'd because the only oppoturnity to
// goes away on drop as well. That's a bit flaky reasoning wise, but is true.
//
// In all honesty, I'm not sure this even needs to be a *mut so I could see if
// making it a immutable reference works.
let pad = &mut self.pad as *mut dyn InputDevice;
self.get_frontend().plug_input_device(0, pad); self.get_frontend().plug_input_device(0, pad);
// Initalize the display // Initalize the display
@ -112,8 +121,8 @@ impl App {
} }
fn load_core<P: AsRef<Path>>(&mut self, path: P) -> Result<()> { fn load_core<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
// Unload an existing core.
if self.get_frontend().core_loaded() { if self.get_frontend().core_loaded() {
println!("???");
let _ = self.get_frontend().unload_core(); let _ = self.get_frontend().unload_core();
} }
@ -194,6 +203,7 @@ impl App {
self.egl_context = std::ptr::null(); self.egl_context = std::ptr::null();
} }
/// Deletes all OpenGL FBO resources (the FBO itself, the render texture, and the renderbuffer used for depth)
fn hw_gl_delete_fbo(&mut self) { fn hw_gl_delete_fbo(&mut self) {
unsafe { unsafe {
gl::DeleteFramebuffers(1, std::ptr::addr_of_mut!(self.fbo_id)); gl::DeleteFramebuffers(1, std::ptr::addr_of_mut!(self.fbo_id));
@ -387,20 +397,35 @@ fn main() -> Result<()> {
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber).unwrap();
let matches = command!() let matches = command!()
.arg(arg!(--core <VALUE>).required(true)) .about("Headless VNC libretro frontend (with GPU rendering support)")
.arg(
arg!(--core <VALUE>)
.required(true)
.help("libretro core to load")
.short('c'),
)
// Not that it matters, but this is only really required for cores that require // Not that it matters, but this is only really required for cores that require
// content to be loaded; that's most cores, but libretro does support the difference. // content to be loaded; that's most cores, but libretro does support the difference.
// TODO: A core will tell us if it requires content, if it's not provided we can yell and exit. // TODO: A core will tell us if it requires content, if it's not provided we can yell and exit.
.arg(arg!(--rom <VALUE>).required(false)) .arg(
arg!(--rom <VALUE>)
.required(false)
.help("ROM to load into core")
.short('r'),
)
.arg( .arg(
arg!(--rfb_listen <ADDRESS>) arg!(--rfb_listen <ADDRESS>)
.required(false) .required(false)
.help("VNC listen address")
.short('l')
.default_value("127.0.0.1"), .default_value("127.0.0.1"),
) )
.arg( .arg(
arg!(--rfb_port <PORT>) arg!(--rfb_port <PORT>)
.value_parser(value_parser!(u16)) .value_parser(value_parser!(u16))
.required(true), .required(true)
.help("VNC listen port")
.short('p'),
) )
.get_matches(); .get_matches();
@ -419,7 +444,6 @@ fn main() -> Result<()> {
listen_port: *matches.get_one::<u16>("rfb_port").unwrap(), listen_port: *matches.get_one::<u16>("rfb_port").unwrap(),
}; };
// Initalize the app
let mut app = App::new(rfb_config)?; let mut app = App::new(rfb_config)?;
app.load_core(core_path)?; app.load_core(core_path)?;