This commit is contained in:
Lily Tsuru 2024-12-02 16:30:25 -05:00
parent af21cd3686
commit 90054015a2
14 changed files with 127 additions and 66 deletions

32
Cargo.lock generated
View file

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "anyhow"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@ -41,6 +47,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "dlib" name = "dlib"
version = "0.5.2" version = "0.5.2"
@ -76,8 +88,10 @@ checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
name = "fbcserver" name = "fbcserver"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"cc", "cc",
"minifb", "minifb",
"nix 0.29.0",
] ]
[[package]] [[package]]
@ -288,6 +302,18 @@ dependencies = [
"memoffset", "memoffset",
] ]
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.20.2" version = "1.20.2"
@ -552,7 +578,7 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"downcast-rs", "downcast-rs",
"libc", "libc",
"nix", "nix 0.24.3",
"scoped-tls", "scoped-tls",
"wayland-commons", "wayland-commons",
"wayland-scanner", "wayland-scanner",
@ -565,7 +591,7 @@ version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
dependencies = [ dependencies = [
"nix", "nix 0.24.3",
"once_cell", "once_cell",
"smallvec", "smallvec",
"wayland-sys", "wayland-sys",
@ -577,7 +603,7 @@ version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
dependencies = [ dependencies = [
"nix", "nix 0.24.3",
"wayland-client", "wayland-client",
"xcursor", "xcursor",
] ]

View file

@ -4,7 +4,9 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.93"
minifb = "0.27.0" minifb = "0.27.0"
nix = { version = "0.29.0", features = [ "fs" ] }
[build-dependencies] [build-dependencies]
cc = "1.0.99" cc = "1.0.99"

View file

@ -6,6 +6,7 @@ namespace hazelnut {
enum class DisplayCaptureResult { enum class DisplayCaptureResult {
Ok, Ok,
OkUnchanged, // OK but nothing changed
OkButResized, // OK, but grab the buffer information again. It's different due to a resize OkButResized, // OK, but grab the buffer information again. It's different due to a resize
Fail, // epic fail Fail, // epic fail
}; };

View file

@ -169,7 +169,7 @@ namespace hazelnut {
return true; return true;
} }
void PaintWithTileOptimization(u32* pBufferData) { DisplayCaptureResult PaintWithTileOptimization(u32* pBufferData) {
bool copiedTiles = false; bool copiedTiles = false;
for(u32 dy = 0; dy < diffmapHeight; ++dy) { for(u32 dy = 0; dy < diffmapHeight; ++dy) {
@ -198,6 +198,11 @@ namespace hazelnut {
} }
} }
} }
if(!copiedTiles)
return DisplayCaptureResult::OkUnchanged;
return DisplayCaptureResult::Ok;
} }
void PaintFull(u32* pBufferData) { void PaintFull(u32* pBufferData) {
@ -259,7 +264,7 @@ namespace hazelnut {
} }
if(useTilePainting) [[likely]] { if(useTilePainting) [[likely]] {
PaintWithTileOptimization(&pOut[0]); result = PaintWithTileOptimization(&pOut[0]);
} else { } else {
PaintFull(&pOut[0]); PaintFull(&pOut[0]);
} }

View file

@ -51,22 +51,35 @@ int main(int argc, char** argv) {
while(true) { while(true) {
{ {
auto guard = pHeader->lock.lock(); auto guard = pHeader->lock.lock();
auto result = pCaptureInterface->CaptureFrameTemp(pFrameHeader->bits());
if(result == hazelnut::DisplayCaptureResult::Ok) {
// Do nothing.
} else if(result == hazelnut::DisplayCaptureResult::OkButResized) { auto result = pCaptureInterface->CaptureFrameTemp(pFrameHeader->bits(
// We resized. Notify of that pFrameHeader->serial.load() % hazelnut::kNrFramesQueued,
framebuffer = pCaptureInterface->GetFramebufferInformation(); framebuffer.width,
diff = pCaptureInterface->GetDiffInformation(); framebuffer.height
} else { ));
printf("Failed to capture\n");
break; switch(result) {
case hazelnut::DisplayCaptureResult::Ok:
case hazelnut::DisplayCaptureResult::OkButResized:
if(result == hazelnut::DisplayCaptureResult::OkButResized) {
framebuffer = pCaptureInterface->GetFramebufferInformation();
diff = pCaptureInterface->GetDiffInformation();
}
pFrameHeader->serial.fetch_add(1);
pFrameHeader->width.store(framebuffer.width);
pFrameHeader->height.store(framebuffer.height);
break;
case hazelnut::DisplayCaptureResult::OkUnchanged: continue; break;
default:
printf("Failed to capture");
return 1;
break;
} }
pFrameHeader->serial.fetch_add(1);
pFrameHeader->width.store(framebuffer.width);
pFrameHeader->height.store(framebuffer.height);
} }
} }

View file

@ -3,6 +3,10 @@ use cc;
fn main() { fn main() {
let mut build = cc::Build::new(); let mut build = cc::Build::new();
// HACK: cc sucks
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed=shared/src");
build build
.emit_rerun_if_env_changed(true) .emit_rerun_if_env_changed(true)
.cpp(true) .cpp(true)

View file

@ -21,8 +21,8 @@ namespace hazelnut {
delete pImpl; delete pImpl;
} }
bool IvshmemDevice::Open(const char* devName) { bool IvshmemDevice::Open(int fd) {
return pImpl->Open(devName); return pImpl->Open(fd);
} }
void IvshmemDevice::Close() { void IvshmemDevice::Close() {

View file

@ -14,8 +14,8 @@ namespace hazelnut {
~IvshmemDevice(); ~IvshmemDevice();
/// Opens the device. On Linux [devName] MUST point to a SHMEM file. /// Opens the device. On Linux [fd] MUST be a valid file descriptor for a SHMEM file.
bool Open(const char* devName = nullptr); bool Open(int fd = -1);
void Close(); void Close();

View file

@ -15,17 +15,12 @@ namespace hazelnut {
void* pMap = nullptr; void* pMap = nullptr;
usize size = 0; usize size = 0;
bool OpenDevice(const char* devName) { bool OpenDevice(int fd) {
// devName is not optional on Linux. // fd is not optional on Linux.
if(devName == nullptr) if(fd == -1)
return false; return false;
int devFd = open(devName, O_RDWR, static_cast<mode_t>(0600)); this->fd = fd;
if(devFd == -1)
return false;
fd = devFd;
return true; return true;
} }
@ -41,7 +36,6 @@ namespace hazelnut {
void* map = mmap(0, devSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); void* map = mmap(0, devSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(map == MAP_FAILED) { if(map == MAP_FAILED) {
printf("map failed\n");
return false; return false;
} }
@ -56,8 +50,8 @@ namespace hazelnut {
munmap(pMap, size); munmap(pMap, size);
} }
bool Open(const char* pDevName) { bool Open(int fd) {
if(!OpenDevice(pDevName)) if(!OpenDevice(fd))
return false; return false;
if(!MapMemory()) { if(!MapMemory()) {

View file

@ -138,7 +138,7 @@ namespace hazelnut {
memset(&ivshmemMmapStruct, 0, sizeof(IVSHMEM_MMAP)); memset(&ivshmemMmapStruct, 0, sizeof(IVSHMEM_MMAP));
} }
bool Open(const char* pDevName) { bool Open(int fd) {
if(!OpenDevice()) if(!OpenDevice())
return false; return false;

View file

@ -1,4 +1,8 @@
#include <fcntl.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <thread> #include <thread>
#include "atomic_spinlock.hpp" #include "atomic_spinlock.hpp"
@ -11,7 +15,13 @@ int main(int argc, char** argv) {
hazelnut::IvshmemDevice dev; hazelnut::IvshmemDevice dev;
if(!dev.Open("/dev/shm/lg-win7")) { int fd = open("/dev/shm/lg-win7", O_RDWR, (mode_t)0600);
if(fd == -1) {
printf("Failed to open ivshmem device file\n");
}
if(!dev.Open(fd)) {
printf("Failed to open ivshmem device\n"); printf("Failed to open ivshmem device\n");
return 1; return 1;
} }
@ -21,6 +31,11 @@ int main(int argc, char** argv) {
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024))); printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
memset(ptr, 0, size);
printf("zeroed :)\n");
#if 0
auto* pHeader = (hazelnut::IvshHeader*)&ptr[0];; auto* pHeader = (hazelnut::IvshHeader*)&ptr[0];;
auto* pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000]; auto* pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000];
@ -59,6 +74,6 @@ int main(int argc, char** argv) {
// allow the vm some time to do whatever it is it wants to do // allow the vm some time to do whatever it is it wants to do
std::this_thread::sleep_for(std::chrono::milliseconds(5)); std::this_thread::sleep_for(std::chrono::milliseconds(5));
} }
#endif
return 0; return 0;
} }

View file

@ -1,5 +1,5 @@
//! Hazelnut C++ client bindings. //! Hazelnut C++ client bindings.
use std::ffi; use nix::{fcntl::OFlag, sys::stat::Mode};
pub(crate) mod sys { pub(crate) mod sys {
use std::ffi; use std::ffi;
@ -21,10 +21,7 @@ pub(crate) mod sys {
pub(crate) fn rust_new_hazelnut_client() -> *mut ffi::c_void; pub(crate) fn rust_new_hazelnut_client() -> *mut ffi::c_void;
pub(crate) fn rust_destroy_hazelnut_client(client: *mut ffi::c_void); pub(crate) fn rust_destroy_hazelnut_client(client: *mut ffi::c_void);
pub(crate) fn rust_hazelnut_client_open( pub(crate) fn rust_hazelnut_client_open(client: *mut ffi::c_void, fd: ffi::c_int) -> bool;
client: *mut ffi::c_void,
url: *const ffi::c_char,
) -> bool;
pub(crate) fn rust_hazelnut_client_tick(client: *mut ffi::c_void) -> ResultCode; pub(crate) fn rust_hazelnut_client_tick(client: *mut ffi::c_void) -> ResultCode;
@ -62,14 +59,19 @@ impl HazelnutClient {
} }
/// Opens the given IVSHMEM shmem file. /// Opens the given IVSHMEM shmem file.
pub fn open(&mut self, path: &String) -> bool { pub fn open(&mut self, path: &String) -> anyhow::Result<()> {
let cstr = ffi::CString::new(path.clone()).expect("dumbass"); // NOTE: `fd` is owned by the hazelnut client once it is provided to it, so we do not close it ourselves.
let fd = nix::fcntl::open(path.as_str(), OFlag::O_RDWR, Mode::S_IRUSR | Mode::S_IWUSR)?;
// FIXME: this really should work by FD so we can just return io::Result<> or something, but bleh // FIXME: this really should work by FD so we can just return io::Result<> or something, but bleh
// for now it's "fine", also it's a path in the shared sources currently. // for now it's "fine", also it's a path in the shared sources currently.
unsafe { unsafe {
return sys::rust_hazelnut_client_open(self.0, cstr.as_ptr()); if !sys::rust_hazelnut_client_open(self.0, fd) {
return Err(anyhow::anyhow!("Failed to open Hazelnut client"));
}
} }
Ok(())
} }
/// Ticks this client. /// Ticks this client.

View file

@ -2,7 +2,7 @@ use minifb::{Window, WindowOptions};
mod hzclient; mod hzclient;
fn main() { fn main() -> anyhow::Result<()> {
let mut screen_width: u32 = 320; let mut screen_width: u32 = 320;
let mut screen_height: u32 = 200; let mut screen_height: u32 = 200;
let mut window: Option<Window> = None; let mut window: Option<Window> = None;
@ -11,12 +11,9 @@ fn main() {
let socket_path: String = "/dev/shm/lg-win7".into(); let socket_path: String = "/dev/shm/lg-win7".into();
if !client.open(&socket_path) { client.open(&socket_path)?;
println!("Could not open IVSHMEM.");
return (); println!("Opened IVSHMEM device.");
} else {
println!("Opened IVSHMEM device.");
}
loop { loop {
match window.as_mut() { match window.as_mut() {
@ -62,10 +59,14 @@ fn main() {
} }
hzclient::ResultCode::Unchanged => match window.as_mut() { hzclient::ResultCode::Unchanged => match window.as_mut() {
Some(window) => { Some(window) => {
client.unlock();
window.update(); window.update();
} }
_ => {} _ => {}
}, },
} }
} }
Ok(())
} }

View file

@ -5,8 +5,8 @@
enum class ResultCode : u32 { Unchanged, Changed, Fail }; enum class ResultCode : u32 { Unchanged, Changed, Fail };
struct HazelnutIvshmemClient { struct HazelnutIvshmemClient {
bool Open(const char* path) { bool Open(int fd) {
if(!ivshmemDevice.Open(path)) if(!ivshmemDevice.Open(fd))
return false; return false;
auto* ptr = (u8*)ivshmemDevice.GetPointer(); auto* ptr = (u8*)ivshmemDevice.GetPointer();
@ -14,17 +14,17 @@ struct HazelnutIvshmemClient {
pHeader = (hazelnut::IvshHeader*)&ptr[0]; pHeader = (hazelnut::IvshHeader*)&ptr[0];
pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000]; pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000];
serial = pHeader->serverSessionId.load(); sessionId = pHeader->serverSessionId.load();
lastFrameSerial = 0; lastFrameSerial = 0;
return true; return true;
} }
// THIS LEAVES THE LOCK HELD SO YOU CAN READ ON CHANGE!!!! // NOTE: This function leaves the lock held on successful returns.
ResultCode TickOne() { ResultCode TickOne() {
if(!pHeader) if(!pHeader)
return ResultCode::Fail; return ResultCode::Fail;
if(serial != pHeader->serverSessionId.load()) if(sessionId != pHeader->serverSessionId.load())
return ResultCode::Fail; return ResultCode::Fail;
if(pHeader->lock.try_lock_manually()) { if(pHeader->lock.try_lock_manually()) {
@ -33,15 +33,11 @@ struct HazelnutIvshmemClient {
} }
auto current = pFrameHeader->serial.load(); auto current = pFrameHeader->serial.load();
if(current == lastFrameSerial) { if(current == lastFrameSerial) {
pHeader->lock.unlock();
return ResultCode::Unchanged; return ResultCode::Unchanged;
} }
lastFrameSerial = current; lastFrameSerial = current;
// printf("Frame with serial %u. Width %ux%u\n", lastSerial, pFrameHeader->width.load(), pFrameHeader->height.load());
// pHeader->lock.unlock();
return ResultCode::Changed; return ResultCode::Changed;
} }
@ -51,6 +47,8 @@ struct HazelnutIvshmemClient {
u32* Framebuffer() { u32* Framebuffer() {
if(pFrameHeader) { if(pFrameHeader) {
auto width = pFrameHeader->width.load();
auto height = pFrameHeader->height.load();
return pFrameHeader->bits(); return pFrameHeader->bits();
} }
@ -63,7 +61,7 @@ struct HazelnutIvshmemClient {
hazelnut::IvshHeader* pHeader; hazelnut::IvshHeader* pHeader;
hazelnut::FrameHeader* pFrameHeader; hazelnut::FrameHeader* pFrameHeader;
u32 serial {}; u32 sessionId {};
u32 lastFrameSerial {}; u32 lastFrameSerial {};
}; };
@ -80,9 +78,9 @@ void rust_destroy_hazelnut_client(void* pClient) {
delete(HazelnutIvshmemClient*)pClient; delete(HazelnutIvshmemClient*)pClient;
} }
bool rust_hazelnut_client_open(void* pClient, const char* pszPath) { bool rust_hazelnut_client_open(void* pClient, int fd) {
if(pClient) { if(pClient) {
return ((HazelnutIvshmemClient*)pClient)->Open(pszPath); return ((HazelnutIvshmemClient*)pClient)->Open(fd);
} }
return false; return false;