Compare commits
2 commits
d4329be132
...
18b30f2148
Author | SHA1 | Date | |
---|---|---|---|
18b30f2148 | |||
596839e4ed |
7 changed files with 184 additions and 25 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -407,6 +407,7 @@ name = "retrovnc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"cc",
|
||||||
"clap",
|
"clap",
|
||||||
"libvnc-sys",
|
"libvnc-sys",
|
||||||
"retro_frontend",
|
"retro_frontend",
|
||||||
|
|
14
crates/retro_frontend/src/libretro_vfs.rs
Normal file
14
crates/retro_frontend/src/libretro_vfs.rs
Normal file
|
@ -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 )
|
||||||
|
|
|
@ -11,3 +11,6 @@ libvnc-sys = "0.1.4"
|
||||||
retro_frontend = { path = "../retro_frontend" }
|
retro_frontend = { path = "../retro_frontend" }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = "0.3.18"
|
tracing-subscriber = "0.3.18"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1.0.99"
|
||||||
|
|
12
crates/retrovnc/build.rs
Normal file
12
crates/retrovnc/build.rs
Normal file
|
@ -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");
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use std::path::Path;
|
use std::{net::Ipv4Addr, path::Path};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use retro_frontend::{
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use tracing_subscriber::FmtSubscriber;
|
use tracing_subscriber::FmtSubscriber;
|
||||||
|
|
||||||
use clap::{arg, command};
|
use clap::{arg, command, value_parser};
|
||||||
|
|
||||||
mod rfb;
|
mod rfb;
|
||||||
use rfb::*;
|
use rfb::*;
|
||||||
|
@ -21,13 +21,10 @@ struct App {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn new() -> Result<Box<Self>> {
|
fn new(rfb_config: RfbServerConfig) -> Result<Box<Self>> {
|
||||||
let mut boxed = Box::new(Self {
|
let mut boxed = Box::new(Self {
|
||||||
frontend: None,
|
frontend: None,
|
||||||
rfb_server: RfbServer::new(RfbServerConfig {
|
rfb_server: RfbServer::new(rfb_config)?,
|
||||||
width: 640,
|
|
||||||
height: 480,
|
|
||||||
})?,
|
|
||||||
// nasty, but idk a better way
|
// nasty, but idk a better way
|
||||||
pad: RetroPad::new(),
|
pad: RetroPad::new(),
|
||||||
});
|
});
|
||||||
|
@ -142,12 +139,35 @@ fn main() -> Result<()> {
|
||||||
// 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.
|
||||||
.arg(arg!(--rom <VALUE>).required(false))
|
.arg(arg!(--rom <VALUE>).required(false))
|
||||||
|
.arg(
|
||||||
|
arg!(--rfb_listen <ADDRESS>)
|
||||||
|
.required(false)
|
||||||
|
.default_value("127.0.0.1"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
arg!(--rfb_port <PORT>)
|
||||||
|
.value_parser(value_parser!(u16))
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let core_path: &String = matches.get_one("core").unwrap();
|
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: matches
|
||||||
|
.get_one::<String>("rfb_listen")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<Ipv4Addr>()?,
|
||||||
|
listen_port: *matches.get_one::<u16>("rfb_port").unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
// Initalize the app
|
// Initalize the app
|
||||||
let mut app = App::new()?;
|
let mut app = App::new(rfb_config)?;
|
||||||
|
|
||||||
app.load_core(core_path)?;
|
app.load_core(core_path)?;
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ use anyhow::{anyhow, Result};
|
||||||
pub struct RfbServerConfig {
|
pub struct RfbServerConfig {
|
||||||
pub width: u16,
|
pub width: u16,
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
// TODO: Listen address
|
pub listen_address: std::net::Ipv4Addr,
|
||||||
// TODO: listen port
|
pub listen_port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RfbServer {
|
pub struct RfbServer {
|
||||||
|
@ -20,16 +20,20 @@ pub struct RfbServer {
|
||||||
buttons: [bool; 32],
|
buttons: [bool; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl RfbServer {
|
impl RfbServer {
|
||||||
pub fn new(config: RfbServerConfig) -> Result<Box<Self>> {
|
pub fn new(config: RfbServerConfig) -> Result<Box<Self>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Feed a fake argv in (TODO: Make this better.)
|
// Feed a fake argv in (TODO: Make this better.)
|
||||||
|
|
||||||
|
let ip_string = std::ffi::CString::new(config.listen_address.to_string())?;
|
||||||
|
|
||||||
|
info!("?? {:?}", ip_string);
|
||||||
|
|
||||||
let argc = 3;
|
let argc = 3;
|
||||||
let argv: [*const std::ffi::c_char; 3] = [
|
let argv: [*const std::ffi::c_char; 3] = [
|
||||||
b"RfbServer".as_ptr() as *const i8,
|
b"RfbServer\0".as_ptr() as *const i8,
|
||||||
b"-listen".as_ptr() as *const i8,
|
b"-listen\0".as_ptr() as *const i8,
|
||||||
b"127.0.0.1".as_ptr() as *const i8,
|
ip_string.as_ptr(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let screen = rfb::bindings::rfbGetScreen(
|
let screen = rfb::bindings::rfbGetScreen(
|
||||||
|
@ -42,7 +46,6 @@ impl RfbServer {
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
|
|
||||||
// result
|
|
||||||
if screen.is_null() {
|
if screen.is_null() {
|
||||||
return Err(anyhow!("rfbGetScreen() failed"));
|
return Err(anyhow!("rfbGetScreen() failed"));
|
||||||
}
|
}
|
||||||
|
@ -63,9 +66,11 @@ impl RfbServer {
|
||||||
(*screen).newClientHook = Some(Self::connection_callback);
|
(*screen).newClientHook = Some(Self::connection_callback);
|
||||||
(*screen).kbdAddEvent = Some(Self::on_key_callback);
|
(*screen).kbdAddEvent = Some(Self::on_key_callback);
|
||||||
|
|
||||||
|
|
||||||
// testing
|
// testing
|
||||||
(*screen).port = 6930;
|
(*screen).port = config.listen_port as i32;
|
||||||
(*screen).ipv6port = 0;
|
(*screen).ipv6port = 0;
|
||||||
|
(*screen).deferUpdateTime = 16;
|
||||||
|
|
||||||
ret.resize(config.width, config.height);
|
ret.resize(config.width, config.height);
|
||||||
|
|
||||||
|
@ -76,7 +81,6 @@ impl RfbServer {
|
||||||
pub fn resize(&mut self, w: u16, h: u16) {
|
pub fn resize(&mut self, w: u16, h: u16) {
|
||||||
let len = (w as usize) * (h as usize);
|
let len = (w as usize) * (h as usize);
|
||||||
|
|
||||||
//self.framebuffer = Vec::with_capacity(len);
|
|
||||||
self.framebuffer.resize(len, 0);
|
self.framebuffer.resize(len, 0);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -93,14 +97,6 @@ impl RfbServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_buffer(&mut self, slice: &[u32], width: u16, height: u16) {
|
pub fn update_buffer(&mut self, slice: &[u32], width: u16, height: u16) {
|
||||||
// lame slow loop (dont use this sucks)
|
|
||||||
/*
|
|
||||||
for x in 0..width {
|
|
||||||
for y in 0..height {
|
|
||||||
self.framebuffer[(y * width + x) as usize] = slice[(y * width + x) as usize];
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
self.framebuffer.copy_from_slice(&slice);
|
self.framebuffer.copy_from_slice(&slice);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -114,6 +110,7 @@ impl RfbServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start the server.
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
rfb::bindings::rfbInitServerWithPthreadsAndZRLE(self.ptr.as_ptr());
|
rfb::bindings::rfbInitServerWithPthreadsAndZRLE(self.ptr.as_ptr());
|
||||||
|
@ -123,6 +120,14 @@ impl RfbServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shutdown the server. This does not destroy the RFB screen, therefore [RfbServer::start] can be
|
||||||
|
/// used to start it back up. (Only dropping the RfbServer will destroy the screen currently.)
|
||||||
|
pub fn shutdown(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
rfb::bindings::rfbShutdownServer(self.ptr.as_ptr(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_buttons(&self) -> [bool; 32] {
|
pub fn get_buttons(&self) -> [bool; 32] {
|
||||||
self.buttons
|
self.buttons
|
||||||
}
|
}
|
||||||
|
@ -239,3 +244,64 @@ impl RfbServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for RfbServer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Shut down the server and release resources.
|
||||||
|
rfb::bindings::rfbScreenCleanup(self.ptr.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
43
crates/retrovnc/src/rfb_log_helper.cpp
Normal file
43
crates/retrovnc/src/rfb_log_helper.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
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<auto* reciever>
|
||||||
|
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>;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue