diff --git a/Cargo.lock b/Cargo.lock index a73095e..97ae774 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + [[package]] name = "autocfg" version = "1.4.0" @@ -41,6 +47,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "dlib" version = "0.5.2" @@ -76,8 +88,10 @@ checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" name = "fbcserver" version = "0.1.0" dependencies = [ + "anyhow", "cc", "minifb", + "nix 0.29.0", ] [[package]] @@ -288,6 +302,18 @@ dependencies = [ "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]] name = "once_cell" version = "1.20.2" @@ -552,7 +578,7 @@ dependencies = [ "bitflags 1.3.2", "downcast-rs", "libc", - "nix", + "nix 0.24.3", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -565,7 +591,7 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ - "nix", + "nix 0.24.3", "once_cell", "smallvec", "wayland-sys", @@ -577,7 +603,7 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ - "nix", + "nix 0.24.3", "wayland-client", "xcursor", ] diff --git a/Cargo.toml b/Cargo.toml index 3247f31..a0e1f19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] +anyhow = "1.0.93" minifb = "0.27.0" +nix = { version = "0.29.0", features = [ "fs" ] } [build-dependencies] -cc = "1.0.99" \ No newline at end of file +cc = "1.0.99" diff --git a/agent/src/capture.hpp b/agent/src/capture.hpp index e036352..102d37f 100644 --- a/agent/src/capture.hpp +++ b/agent/src/capture.hpp @@ -6,6 +6,7 @@ namespace hazelnut { enum class DisplayCaptureResult { Ok, + OkUnchanged, // OK but nothing changed OkButResized, // OK, but grab the buffer information again. It's different due to a resize Fail, // epic fail }; diff --git a/agent/src/capture_nvfbc.cpp b/agent/src/capture_nvfbc.cpp index b0191ba..21af8ca 100644 --- a/agent/src/capture_nvfbc.cpp +++ b/agent/src/capture_nvfbc.cpp @@ -169,7 +169,7 @@ namespace hazelnut { return true; } - void PaintWithTileOptimization(u32* pBufferData) { + DisplayCaptureResult PaintWithTileOptimization(u32* pBufferData) { bool copiedTiles = false; 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) { @@ -259,7 +264,7 @@ namespace hazelnut { } if(useTilePainting) [[likely]] { - PaintWithTileOptimization(&pOut[0]); + result = PaintWithTileOptimization(&pOut[0]); } else { PaintFull(&pOut[0]); } diff --git a/agent/src/main.cpp b/agent/src/main.cpp index 3be90d9..5091d49 100755 --- a/agent/src/main.cpp +++ b/agent/src/main.cpp @@ -51,22 +51,35 @@ int main(int argc, char** argv) { while(true) { { auto guard = pHeader->lock.lock(); - auto result = pCaptureInterface->CaptureFrameTemp(pFrameHeader->bits()); - if(result == hazelnut::DisplayCaptureResult::Ok) { - // Do nothing. - } else if(result == hazelnut::DisplayCaptureResult::OkButResized) { - // We resized. Notify of that - framebuffer = pCaptureInterface->GetFramebufferInformation(); - diff = pCaptureInterface->GetDiffInformation(); - } else { - printf("Failed to capture\n"); - break; + + + auto result = pCaptureInterface->CaptureFrameTemp(pFrameHeader->bits( + pFrameHeader->serial.load() % hazelnut::kNrFramesQueued, + framebuffer.width, + framebuffer.height + )); + + 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); } } diff --git a/build.rs b/build.rs index 2cb59fd..bda2fd2 100644 --- a/build.rs +++ b/build.rs @@ -3,6 +3,10 @@ use cc; fn main() { let mut build = cc::Build::new(); + // HACK: cc sucks + println!("cargo:rerun-if-changed=src"); + println!("cargo:rerun-if-changed=shared/src"); + build .emit_rerun_if_env_changed(true) .cpp(true) diff --git a/shared/src/ivshmem.cpp b/shared/src/ivshmem.cpp index a77b45c..32882d1 100644 --- a/shared/src/ivshmem.cpp +++ b/shared/src/ivshmem.cpp @@ -21,8 +21,8 @@ namespace hazelnut { delete pImpl; } - bool IvshmemDevice::Open(const char* devName) { - return pImpl->Open(devName); + bool IvshmemDevice::Open(int fd) { + return pImpl->Open(fd); } void IvshmemDevice::Close() { diff --git a/shared/src/ivshmem.hpp b/shared/src/ivshmem.hpp index 197be0d..65dbcc0 100644 --- a/shared/src/ivshmem.hpp +++ b/shared/src/ivshmem.hpp @@ -14,8 +14,8 @@ namespace hazelnut { ~IvshmemDevice(); - /// Opens the device. On Linux [devName] MUST point to a SHMEM file. - bool Open(const char* devName = nullptr); + /// Opens the device. On Linux [fd] MUST be a valid file descriptor for a SHMEM file. + bool Open(int fd = -1); void Close(); diff --git a/shared/src/ivshmem_linux.cpp b/shared/src/ivshmem_linux.cpp index 96c02bb..e9ce787 100644 --- a/shared/src/ivshmem_linux.cpp +++ b/shared/src/ivshmem_linux.cpp @@ -15,17 +15,12 @@ namespace hazelnut { void* pMap = nullptr; usize size = 0; - bool OpenDevice(const char* devName) { - // devName is not optional on Linux. - if(devName == nullptr) + bool OpenDevice(int fd) { + // fd is not optional on Linux. + if(fd == -1) return false; - int devFd = open(devName, O_RDWR, static_cast(0600)); - - if(devFd == -1) - return false; - - fd = devFd; + this->fd = fd; return true; } @@ -41,7 +36,6 @@ namespace hazelnut { void* map = mmap(0, devSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(map == MAP_FAILED) { - printf("map failed\n"); return false; } @@ -56,8 +50,8 @@ namespace hazelnut { munmap(pMap, size); } - bool Open(const char* pDevName) { - if(!OpenDevice(pDevName)) + bool Open(int fd) { + if(!OpenDevice(fd)) return false; if(!MapMemory()) { diff --git a/shared/src/ivshmem_windows.cpp b/shared/src/ivshmem_windows.cpp index e968bdf..0434b25 100644 --- a/shared/src/ivshmem_windows.cpp +++ b/shared/src/ivshmem_windows.cpp @@ -138,7 +138,7 @@ namespace hazelnut { memset(&ivshmemMmapStruct, 0, sizeof(IVSHMEM_MMAP)); } - bool Open(const char* pDevName) { + bool Open(int fd) { if(!OpenDevice()) return false; diff --git a/shared/src/test.cpp b/shared/src/test.cpp index 1dfd519..14beafe 100644 --- a/shared/src/test.cpp +++ b/shared/src/test.cpp @@ -1,4 +1,8 @@ +#include +#include +#include +#include #include #include "atomic_spinlock.hpp" @@ -11,7 +15,13 @@ int main(int argc, char** argv) { 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"); return 1; } @@ -21,6 +31,11 @@ int main(int argc, char** argv) { 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* 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 std::this_thread::sleep_for(std::chrono::milliseconds(5)); } - +#endif return 0; } \ No newline at end of file diff --git a/src/hzclient.rs b/src/hzclient.rs index 81526f3..dfc7607 100644 --- a/src/hzclient.rs +++ b/src/hzclient.rs @@ -1,5 +1,5 @@ //! Hazelnut C++ client bindings. -use std::ffi; +use nix::{fcntl::OFlag, sys::stat::Mode}; pub(crate) mod sys { 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_destroy_hazelnut_client(client: *mut ffi::c_void); - pub(crate) fn rust_hazelnut_client_open( - client: *mut ffi::c_void, - url: *const ffi::c_char, - ) -> bool; + pub(crate) fn rust_hazelnut_client_open(client: *mut ffi::c_void, fd: ffi::c_int) -> bool; 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. - pub fn open(&mut self, path: &String) -> bool { - let cstr = ffi::CString::new(path.clone()).expect("dumbass"); + pub fn open(&mut self, path: &String) -> anyhow::Result<()> { + // 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 // for now it's "fine", also it's a path in the shared sources currently. 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. diff --git a/src/main.rs b/src/main.rs index beee0c9..690fbe1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use minifb::{Window, WindowOptions}; mod hzclient; -fn main() { +fn main() -> anyhow::Result<()> { let mut screen_width: u32 = 320; let mut screen_height: u32 = 200; let mut window: Option = None; @@ -11,12 +11,9 @@ fn main() { let socket_path: String = "/dev/shm/lg-win7".into(); - if !client.open(&socket_path) { - println!("Could not open IVSHMEM."); - return (); - } else { - println!("Opened IVSHMEM device."); - } + client.open(&socket_path)?; + + println!("Opened IVSHMEM device."); loop { match window.as_mut() { @@ -62,10 +59,14 @@ fn main() { } hzclient::ResultCode::Unchanged => match window.as_mut() { Some(window) => { + client.unlock(); + window.update(); } _ => {} }, } } + + Ok(()) } diff --git a/src/rust_wrapper.cpp b/src/rust_wrapper.cpp index 2e53780..5b14164 100644 --- a/src/rust_wrapper.cpp +++ b/src/rust_wrapper.cpp @@ -5,8 +5,8 @@ enum class ResultCode : u32 { Unchanged, Changed, Fail }; struct HazelnutIvshmemClient { - bool Open(const char* path) { - if(!ivshmemDevice.Open(path)) + bool Open(int fd) { + if(!ivshmemDevice.Open(fd)) return false; auto* ptr = (u8*)ivshmemDevice.GetPointer(); @@ -14,17 +14,17 @@ struct HazelnutIvshmemClient { pHeader = (hazelnut::IvshHeader*)&ptr[0]; pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000]; - serial = pHeader->serverSessionId.load(); + sessionId = pHeader->serverSessionId.load(); lastFrameSerial = 0; 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() { if(!pHeader) return ResultCode::Fail; - if(serial != pHeader->serverSessionId.load()) + if(sessionId != pHeader->serverSessionId.load()) return ResultCode::Fail; if(pHeader->lock.try_lock_manually()) { @@ -33,15 +33,11 @@ struct HazelnutIvshmemClient { } auto current = pFrameHeader->serial.load(); - if(current == lastFrameSerial) { - pHeader->lock.unlock(); return ResultCode::Unchanged; } lastFrameSerial = current; - // printf("Frame with serial %u. Width %ux%u\n", lastSerial, pFrameHeader->width.load(), pFrameHeader->height.load()); - // pHeader->lock.unlock(); return ResultCode::Changed; } @@ -51,6 +47,8 @@ struct HazelnutIvshmemClient { u32* Framebuffer() { if(pFrameHeader) { + auto width = pFrameHeader->width.load(); + auto height = pFrameHeader->height.load(); return pFrameHeader->bits(); } @@ -63,7 +61,7 @@ struct HazelnutIvshmemClient { hazelnut::IvshHeader* pHeader; hazelnut::FrameHeader* pFrameHeader; - u32 serial {}; + u32 sessionId {}; u32 lastFrameSerial {}; }; @@ -80,9 +78,9 @@ void rust_destroy_hazelnut_client(void* 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) { - return ((HazelnutIvshmemClient*)pClient)->Open(pszPath); + return ((HazelnutIvshmemClient*)pClient)->Open(fd); } return false;