working ivshmem setup
This commit is contained in:
parent
7a6ef52144
commit
758df315a1
13 changed files with 387 additions and 279 deletions
34
Cargo.lock
generated
34
Cargo.lock
generated
|
@ -26,12 +26,6 @@ version = "3.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.1"
|
||||
|
@ -47,12 +41,6 @@ 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"
|
||||
|
@ -88,10 +76,8 @@ checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
|||
name = "fbcserver"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"libc",
|
||||
"cc",
|
||||
"minifb",
|
||||
"nix 0.29.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -302,18 +288,6 @@ 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"
|
||||
|
@ -578,7 +552,7 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
"downcast-rs",
|
||||
"libc",
|
||||
"nix 0.24.3",
|
||||
"nix",
|
||||
"scoped-tls",
|
||||
"wayland-commons",
|
||||
"wayland-scanner",
|
||||
|
@ -591,7 +565,7 @@ version = "0.29.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
||||
dependencies = [
|
||||
"nix 0.24.3",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
|
@ -603,7 +577,7 @@ version = "0.29.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
||||
dependencies = [
|
||||
"nix 0.24.3",
|
||||
"nix",
|
||||
"wayland-client",
|
||||
"xcursor",
|
||||
]
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.5.0"
|
||||
libc = "0.2.167"
|
||||
minifb = "0.27.0"
|
||||
nix = { version = "0.29.0", features = [ "stat", "mman" ] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.99"
|
|
@ -33,7 +33,6 @@ LINK_LIBS := $(VS2022_PATH)/ucrt/lib/$(ARCH)/libucrt$(D).lib \
|
|||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/user32.lib \
|
||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/comctl32.lib \
|
||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/setupapi.lib \
|
||||
$(VS2022_PATH)/winsdk/lib/$(ARCH)/ws2_32.lib
|
||||
|
||||
.PHONY: all dumpinfo clean matrix
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ namespace hazelnut {
|
|||
blockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_32X32;
|
||||
|
||||
// set up a session
|
||||
NVFBC_TOSYS_SETUP_PARAMS fbcSysSetupParams{};
|
||||
NVFBC_TOSYS_SETUP_PARAMS fbcSysSetupParams {};
|
||||
fbcSysSetupParams.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER;
|
||||
fbcSysSetupParams.eMode = NVFBC_TOSYS_ARGB;
|
||||
fbcSysSetupParams.bWithHWCursor = true;
|
||||
|
@ -197,8 +197,6 @@ namespace hazelnut {
|
|||
auto* pSrcData = (u8*)&pRawFramebuffer[0];
|
||||
|
||||
for(u32 y = 0; y < grabInfo.dwHeight; ++y) {
|
||||
|
||||
|
||||
// Convert to BGRA
|
||||
// FIXME: Make this SIMD. I can't into this very well
|
||||
#if 0
|
||||
|
@ -224,8 +222,8 @@ namespace hazelnut {
|
|||
FramebufferInformation GetFramebufferInformation() override { return FramebufferInformation { convertedFramebuffer.data(), width, height }; }
|
||||
|
||||
DiffInformation GetDiffInformation() override {
|
||||
//diffmapWidth = (u32)ceil((f32)width / 32);
|
||||
//diffmapHeight = (u32)ceil((f32)height / 32);
|
||||
// diffmapWidth = (u32)ceil((f32)width / 32);
|
||||
// diffmapHeight = (u32)ceil((f32)height / 32);
|
||||
|
||||
diffmapWidth = DiffMapDimension(width, blockSize);
|
||||
diffmapHeight = DiffMapDimension(height, blockSize);
|
||||
|
|
|
@ -12,17 +12,17 @@
|
|||
#include "Utils.hpp"
|
||||
// clang-format on
|
||||
|
||||
|
||||
#include "atomic_spinlock.hpp"
|
||||
#include "ivshmem_protocol.hpp"
|
||||
|
||||
struct tileRect {
|
||||
u32 x, y, width, height;
|
||||
};
|
||||
|
||||
struct Test {
|
||||
hazelnut::AtomicSpinlock lk{};
|
||||
hazelnut::AtomicSpinlock lk {};
|
||||
std::atomic<u32> sessionId {};
|
||||
std::atomic<u32> pingPong{};
|
||||
std::atomic<u32> pingPong {};
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -34,43 +34,108 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
auto size = dev.GetSize();
|
||||
auto ptr = (u32*)dev.GetPointer();
|
||||
auto ptr = (u8*)dev.GetPointer();
|
||||
|
||||
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
||||
|
||||
// wipe the first 1mb
|
||||
memset(&ptr[0], 0, 1*(1024*1024));
|
||||
memset(&ptr[0], 0, 1 * (1024 * 1024));
|
||||
|
||||
printf("wiped memory\n");
|
||||
|
||||
// Setup a test struct at the start of ivshmem
|
||||
auto* pTest = new(&ptr[0]) Test;
|
||||
// sex
|
||||
auto* pHeader = new(&ptr[0]) hazelnut::IvshHeader {};
|
||||
auto* pFrameHeader = new(&ptr[0x1000]) hazelnut::FrameHeader {};
|
||||
|
||||
// reset pingpong counter
|
||||
pTest->pingPong.store(0);
|
||||
|
||||
u32 tries = 0;
|
||||
u32 curTries = 10;
|
||||
pHeader->serverSessionId.store(rand());
|
||||
|
||||
pTest->sessionId.store(rand());
|
||||
// Create a capture interface
|
||||
auto capture = hazelnut::CreateDisplayCapture(hazelnut::GuessBestCaptureInterface());
|
||||
if(!capture) {
|
||||
printf("Failed to create a capture interface\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Successfully created a framebuffer capture interface\n");
|
||||
|
||||
bool firstFrame = true;
|
||||
std::vector<tileRect> tiles {};
|
||||
hazelnut::FramebufferInformation framebuffer {};
|
||||
hazelnut::DiffInformation diff {};
|
||||
|
||||
while(true) {
|
||||
auto result = capture->CaptureFrame();
|
||||
if(result == hazelnut::DisplayCaptureResult::Ok) {
|
||||
tiles.clear();
|
||||
#if 0
|
||||
if(firstFrame == false) {
|
||||
for(u32 y = 0; y < diff.diffMapHeight; ++y) {
|
||||
for(u32 x = 0; x < diff.diffmapWidth; ++x) {
|
||||
auto& bl = diff.pDiffMap[y * diff.diffmapWidth + x];
|
||||
if(bl != 0) {
|
||||
tiles.push_back(tileRect {
|
||||
x * (framebuffer.width / diff.diffmapWidth), // x
|
||||
y * (framebuffer.height / diff.diffMapHeight), // y
|
||||
framebuffer.width / diff.diffmapWidth, // width
|
||||
framebuffer.height / diff.diffMapHeight // height
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tiles.empty())
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
auto guard = pHeader->lock.lock();
|
||||
|
||||
pFrameHeader->serial.fetch_add(1);
|
||||
pFrameHeader->width.store(framebuffer.width);
|
||||
pFrameHeader->height.store(framebuffer.height);
|
||||
|
||||
if(framebuffer.pFramebuffer == nullptr)
|
||||
continue;
|
||||
|
||||
memcpy(pFrameHeader->bits(), &framebuffer.pFramebuffer[0], (framebuffer.width * framebuffer.height) * 4);
|
||||
|
||||
//printf("FRAME SERIAL %u loaded\n", pFrameHeader->serial.load());
|
||||
}
|
||||
|
||||
if(firstFrame)
|
||||
firstFrame = false;
|
||||
} else if(result == hazelnut::DisplayCaptureResult::OkButResized) {
|
||||
// We resized. Notify of that
|
||||
framebuffer = capture->GetFramebufferInformation();
|
||||
diff = capture->GetDiffInformation();
|
||||
firstFrame = true;
|
||||
|
||||
} else {
|
||||
printf("Failed to capture\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
while(true) {
|
||||
// lock
|
||||
{
|
||||
auto guard = pTest->lk.lock();
|
||||
auto guard = pHeader->lk.lock();
|
||||
Sleep(5);
|
||||
}
|
||||
|
||||
|
||||
printf("pingpong %u\n", pTest->pingPong.load());
|
||||
printf("pingpong %u\n", pHeader->pingPong.load());
|
||||
|
||||
if(tries++ == curTries) {
|
||||
tries = 0;
|
||||
pTest->pingPong.fetch_add(1);
|
||||
pHeader->pingPong.fetch_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
14
build.rs
Normal file
14
build.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use cc;
|
||||
|
||||
fn main() {
|
||||
let mut build = cc::Build::new();
|
||||
|
||||
build
|
||||
.emit_rerun_if_env_changed(true)
|
||||
.cpp(true)
|
||||
.std("c++20")
|
||||
.include("shared/src")
|
||||
.file("shared/src/ivshmem.cpp")
|
||||
.file("src/rust_wrapper.cpp")
|
||||
.compile("rust_ivshmem_bare");
|
||||
}
|
|
@ -4,33 +4,35 @@
|
|||
|
||||
namespace hazelnut {
|
||||
|
||||
#pragma pack(push, 4)
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
||||
/// Header for Hazelnut ivshmem. At 0x0 in the ivshmem memory space.
|
||||
struct alignas(4096) IvshHeader {
|
||||
struct IvshHeader {
|
||||
std::atomic<u32> magic;
|
||||
|
||||
std::atomic<u32> serverSessionId{};
|
||||
|
||||
// When this lock is held by the host, the host can read the rest of memory freely.
|
||||
// We wait for the host to release before updating memory ourselves.
|
||||
AtomicSpinlock lock;
|
||||
|
||||
// See the next page boundary for FrameHeader
|
||||
// Immediately after is FrameHeader
|
||||
};
|
||||
|
||||
/// Stored at a page boundary
|
||||
struct alignas(4096) FrameHeader {
|
||||
struct FrameHeader {
|
||||
/// The serial of the frame. If this is unchanged between a
|
||||
/// lock-grab-unlock cycle, the frame has not changed.
|
||||
std::atomic<u32> serial;
|
||||
std::atomic<u32> serial{};
|
||||
|
||||
// size
|
||||
std::atomic<u32> width;
|
||||
std::atomic<u32> height;
|
||||
std::atomic<u32> width{};
|
||||
std::atomic<u32> height{};
|
||||
|
||||
// obtain bits at the next page!
|
||||
// obtain bits at the next page boundary
|
||||
u32* bits() {
|
||||
return reinterpret_cast<u32*>(this + 1);
|
||||
return reinterpret_cast<u32*>(reinterpret_cast<u8*>(this) + 0x1000);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -4,12 +4,10 @@
|
|||
#include "atomic_spinlock.hpp"
|
||||
#include "ivshmem.hpp"
|
||||
|
||||
#include "ivshmem_protocol.hpp"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
struct Test {
|
||||
hazelnut::AtomicSpinlock lk {};
|
||||
std::atomic<u32> sessionId {};
|
||||
std::atomic<u32> pingPong {};
|
||||
};
|
||||
|
||||
|
||||
hazelnut::IvshmemDevice dev;
|
||||
|
||||
|
@ -19,55 +17,43 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
auto size = dev.GetSize();
|
||||
auto ptr = (u32*)dev.GetPointer();
|
||||
auto ptr = (u8*)dev.GetPointer();
|
||||
|
||||
printf("opened a %zu MB large ivshmem\n", (size / (1024 * 1024)));
|
||||
|
||||
auto* pTest = (Test*)ptr;
|
||||
u32 last = 0;
|
||||
u32 nrSame = 0;
|
||||
auto* pHeader = (hazelnut::IvshHeader*)&ptr[0];;
|
||||
auto* pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000];
|
||||
|
||||
u64 initalizedSessionId = pTest->sessionId.load();
|
||||
u64 initalizedSessionId = pHeader->serverSessionId.load();
|
||||
u32 lastSerial = 0;
|
||||
|
||||
while(true) {
|
||||
//printf("%d\n", pTest->timestamp.load());
|
||||
|
||||
if(initalizedSessionId != pTest->sessionId.load()) {
|
||||
if(initalizedSessionId != pHeader->serverSessionId.load()) {
|
||||
printf("Agent restarted. Closing\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// need monotonic way
|
||||
#if 0
|
||||
if(auto curTimestamp = pTest->timestamp.load(); curTimestamp < lastTimestamp) {
|
||||
//printf("Not connected or agent crashed %d, %d\n", pTest->timestamp.load() , time(nullptr));
|
||||
printf("Not connected or agent crashed. %zu\n", curTimestamp);
|
||||
break;
|
||||
} else {
|
||||
lastTimestamp = curTimestamp;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// lock for a bit
|
||||
{
|
||||
if(pTest->lk.try_lock_manually()) {
|
||||
if(pHeader->lock.try_lock_manually()) {
|
||||
// failed to lock
|
||||
continue;
|
||||
}
|
||||
|
||||
auto current = pTest->pingPong.load();
|
||||
auto current = pFrameHeader->serial.load();
|
||||
|
||||
if(current == last) {
|
||||
pTest->lk.unlock();
|
||||
if(current == lastSerial) {
|
||||
pHeader->lock.unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
nrSame = 0;
|
||||
|
||||
last = current;
|
||||
printf("pingpong %u\n", current);
|
||||
lastSerial = current;
|
||||
printf("Frame with serial %u. Width %ux%u\n", lastSerial, pFrameHeader->width.load(), pFrameHeader->height.load());
|
||||
|
||||
pTest->lk.unlock();
|
||||
pHeader->lock.unlock();
|
||||
}
|
||||
|
||||
// allow the vm some time to do whatever it is it wants to do
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[repr(C, packed(4))]
|
||||
pub struct AtomicSpinlock {
|
||||
lock: AtomicU32
|
||||
}
|
||||
|
||||
pub struct AtomicSpinlockGuard<'a> {
|
||||
lock: &'a mut AtomicSpinlock,
|
||||
}
|
||||
|
||||
impl Drop for AtomicSpinlockGuard {
|
||||
fn drop(&mut self) {
|
||||
self.lock.store(0, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicSpinlock {
|
||||
fn init(&mut self) {
|
||||
self.lock.store(0, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
|
||||
fn lock(&mut self) -> AtomicSpinlockGuard<'_> {
|
||||
loop {
|
||||
match self
|
||||
.sync
|
||||
.compare_exchange(cur, 1, Ordering::SeqCst, Ordering::SeqCst)
|
||||
{
|
||||
Ok(last) => return HzLockGuard { lock: self },
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
89
src/hzclient.rs
Normal file
89
src/hzclient.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use std::{ffi, path::Path};
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum ResultCode {
|
||||
Unchanged,
|
||||
Changed,
|
||||
Fail,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn rust_new_hazelnut_client() -> *mut ffi::c_void;
|
||||
fn rust_destroy_hazelnut_client(client: *mut ffi::c_void);
|
||||
|
||||
fn rust_hazelnut_client_open(client: *mut ffi::c_void, url: *const ffi::c_char) -> bool;
|
||||
|
||||
fn rust_hazelnut_client_tick(client: *mut ffi::c_void) -> ResultCode;
|
||||
|
||||
fn rust_hazelnut_client_lock(client: *mut ffi::c_void);
|
||||
fn rust_hazelnut_client_unlock(client: *mut ffi::c_void);
|
||||
|
||||
fn rust_hazelnut_client_get_framebuffer(client: *mut ffi::c_void) -> *mut u32;
|
||||
fn rust_hazelnut_client_get_width(client: *mut ffi::c_void) -> u32;
|
||||
fn rust_hazelnut_client_get_height(client: *mut ffi::c_void) -> u32;
|
||||
}
|
||||
|
||||
pub struct HazelnutClient(*mut std::ffi::c_void);
|
||||
|
||||
impl HazelnutClient {
|
||||
pub fn new() -> HazelnutClient {
|
||||
unsafe { Self(rust_new_hazelnut_client()) }
|
||||
}
|
||||
|
||||
pub fn open(&mut self, path: String) -> bool {
|
||||
let cstr = ffi::CString::new(path).expect("dumbass");
|
||||
|
||||
unsafe {
|
||||
return rust_hazelnut_client_open(self.0, cstr.as_ptr());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn tick_one(&mut self) -> ResultCode {
|
||||
unsafe {
|
||||
return rust_hazelnut_client_tick(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&mut self) {
|
||||
unsafe {
|
||||
return rust_hazelnut_client_lock(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlock(&mut self) {
|
||||
unsafe {
|
||||
return rust_hazelnut_client_unlock(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// only use while lock is held!
|
||||
pub fn dimensions(&mut self) -> (u32, u32) {
|
||||
let tup = unsafe {
|
||||
let width = rust_hazelnut_client_get_width(self.0);
|
||||
let height = rust_hazelnut_client_get_height(self.0);
|
||||
(width, height)
|
||||
};
|
||||
tup
|
||||
}
|
||||
|
||||
/// only use while lock is held!
|
||||
pub fn framebuffer(&mut self) -> &mut [u32] {
|
||||
let sl = unsafe {
|
||||
let fb_ptr = rust_hazelnut_client_get_framebuffer(self.0);
|
||||
let dim = self.dimensions();
|
||||
|
||||
std::slice::from_raw_parts_mut(fb_ptr, ((dim.0 * dim.1) as usize * 4))
|
||||
};
|
||||
sl
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HazelnutClient {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
rust_destroy_hazelnut_client(self.0);
|
||||
}
|
||||
self.0 = std::ptr::null_mut();
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
//! Utilities for ivshmem
|
||||
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::atomic_spinlock::AtomicSpinlock;
|
||||
|
||||
// at 0x0 in ivshmem map
|
||||
#[repr(C, align(4096))]
|
||||
pub struct HzHeader {
|
||||
magic: AtomicU32,
|
||||
sync: AtomicSpinlock,
|
||||
|
||||
// frame serial. if this is unchanged from prev. load it's not the same
|
||||
frame_serial: AtomicU32,
|
||||
}
|
||||
|
||||
impl HzHeader {
|
||||
fn valid(&self) -> bool {
|
||||
self.magic.load(std::sync::atomic::Ordering::Acquire) == 0xabcd1234
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct HzFrameHeader {
|
||||
width: AtomicU32,
|
||||
height: AtomicU32,
|
||||
}
|
||||
|
||||
pub struct Ivshmem {}
|
152
src/main.rs
152
src/main.rs
|
@ -1,139 +1,57 @@
|
|||
use std::{
|
||||
io::Read,
|
||||
net::{TcpListener, TcpStream},
|
||||
};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use minifb::{Window, WindowOptions};
|
||||
|
||||
mod atomic_spinlock;
|
||||
mod ivshmem;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Message {
|
||||
Resize { width: u32, height: u32 },
|
||||
|
||||
Data {},
|
||||
}
|
||||
|
||||
pub const MESSAGETYPE_RESIZE: u32 = 0;
|
||||
pub const MESSAGETYPE_DATA: u32 = 1;
|
||||
|
||||
fn read_message(
|
||||
stream: &mut TcpStream,
|
||||
argb_buffer: &mut Vec<u32>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Option<Message> {
|
||||
let message_type = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
let message_len = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
|
||||
match message_type {
|
||||
MESSAGETYPE_RESIZE => {
|
||||
if message_len != 8 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let width = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
let height = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
Some(Message::Resize {
|
||||
width: width,
|
||||
height: height,
|
||||
})
|
||||
}
|
||||
|
||||
MESSAGETYPE_DATA => {
|
||||
let tile_count = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
|
||||
//println!("{tile_count} tiles");
|
||||
|
||||
// tile data, painted directly onto the argb buffer. It's stupid
|
||||
let argb_slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
argb_buffer.as_mut_ptr() as *mut u8,
|
||||
argb_buffer.len() * core::mem::size_of::<u32>(),
|
||||
)
|
||||
};
|
||||
|
||||
for i in 0..tile_count {
|
||||
// tile rect
|
||||
let tile_x = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
let tile_y = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
let tile_width = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
let tile_height = stream.read_u32::<LittleEndian>().expect("fuck");
|
||||
|
||||
//println!("tile{i}: {tile_x} {tile_y} {tile_width}x{tile_height}");
|
||||
}
|
||||
|
||||
for y in 0..height {
|
||||
//println!("{y} {tile_y} {tile_height}");
|
||||
//for x in tile_
|
||||
let dest_slice = &mut argb_slice
|
||||
[((y * width) * 4) as usize..((y * width + (width)) * 4) as usize];
|
||||
|
||||
stream.read_exact(&mut dest_slice[..]).expect("FUCK");
|
||||
}
|
||||
|
||||
Some(Message::Data {})
|
||||
}
|
||||
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
mod hzclient;
|
||||
|
||||
fn main() {
|
||||
let listener = TcpListener::bind("192.168.1.149:9438").expect("fuck");
|
||||
let (mut socket, client_addr) = listener.accept().expect("FUCK!");
|
||||
|
||||
// disable nagles garbage bullshit
|
||||
socket.set_nodelay(true).expect("fuck tcp");
|
||||
let mut screen_width: u32 = 320;
|
||||
let mut screen_height: u32 = 200;
|
||||
|
||||
let mut window = Window::new("FbcServer", 320, 200, WindowOptions::default())
|
||||
.expect("you banned forever: rules do not");
|
||||
|
||||
let mut argb_buffer: Vec<u32> = Vec::new();
|
||||
let mut screen_width: u32 = 320;
|
||||
let mut screen_height: u32 = 200;
|
||||
let mut client = hzclient::HazelnutClient::new();
|
||||
|
||||
if !client.open("/dev/shm/lg-win7".into()) {
|
||||
println!("FUCK");
|
||||
} else {
|
||||
println!("Opened ivshmem!!!");
|
||||
}
|
||||
|
||||
//let mut argb_buffer: Vec<u32> = Vec::new();
|
||||
|
||||
loop {
|
||||
if let Some(message) =
|
||||
read_message(&mut socket, &mut argb_buffer, screen_width, screen_height)
|
||||
{
|
||||
match message {
|
||||
Message::Resize { width, height } => {
|
||||
println!("read message {:?}", message);
|
||||
screen_width = width;
|
||||
screen_height = height;
|
||||
match client.tick_one() {
|
||||
hzclient::ResultCode::Fail => break,
|
||||
hzclient::ResultCode::Changed => {
|
||||
let dims = client.dimensions();
|
||||
|
||||
if screen_width != dims.0 && screen_height != dims.1 {
|
||||
screen_width = dims.0;
|
||||
screen_height = dims.1;
|
||||
|
||||
window = Window::new(
|
||||
"FbcServer",
|
||||
width as usize,
|
||||
height as usize,
|
||||
screen_width as usize,
|
||||
screen_height as usize,
|
||||
WindowOptions::default(),
|
||||
)
|
||||
.expect("you banned forever: rules do not");
|
||||
}
|
||||
|
||||
argb_buffer.resize((screen_width * screen_height) as usize, 0);
|
||||
}
|
||||
Message::Data {} => {
|
||||
//println!("read DATA");
|
||||
window
|
||||
.update_with_buffer(
|
||||
&argb_buffer[..],
|
||||
screen_width as usize,
|
||||
screen_height as usize,
|
||||
)
|
||||
.expect("Failed to update screen");
|
||||
}
|
||||
window.update_with_buffer(
|
||||
&client.framebuffer(),
|
||||
screen_width as usize,
|
||||
screen_height as usize,
|
||||
).expect("well its done anyways");
|
||||
|
||||
client.unlock();
|
||||
}
|
||||
hzclient::ResultCode::Unchanged => {
|
||||
window.update();
|
||||
// Not needed, C++ unlocks us
|
||||
//client.unlock();
|
||||
}
|
||||
} else {
|
||||
println!("invalid message, termination.");
|
||||
break;
|
||||
}
|
||||
|
||||
//window.upd
|
||||
}
|
||||
|
||||
println!("Hello, world!");
|
||||
|
|
130
src/rust_wrapper.cpp
Normal file
130
src/rust_wrapper.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include "ivshmem.hpp"
|
||||
#include "ivshmem_protocol.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
enum class ResultCode : u32 { Unchanged, Changed, Fail };
|
||||
|
||||
struct HazelnutIvshmemClient {
|
||||
bool Open(const char* path) {
|
||||
if(!ivshmemDevice.Open(path))
|
||||
return false;
|
||||
|
||||
auto* ptr = (u8*)ivshmemDevice.GetPointer();
|
||||
|
||||
pHeader = (hazelnut::IvshHeader*)&ptr[0];
|
||||
pFrameHeader = (hazelnut::FrameHeader*)&ptr[0x1000];
|
||||
|
||||
serial = pHeader->serverSessionId.load();
|
||||
lastFrameSerial = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// THIS LEAVES THE LOCK HELD SO YOU CAN READ ON CHANGE!!!!
|
||||
ResultCode TickOne() {
|
||||
if(!pHeader)
|
||||
return ResultCode::Fail;
|
||||
|
||||
if(serial != pHeader->serverSessionId.load())
|
||||
return ResultCode::Fail;
|
||||
|
||||
if(pHeader->lock.try_lock_manually()) {
|
||||
// failed to lock
|
||||
return ResultCode::Unchanged;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void Lock() { pHeader->lock.lock_manually(); }
|
||||
|
||||
void Unlock() { pHeader->lock.unlock(); }
|
||||
|
||||
u32* Framebuffer() {
|
||||
if(pFrameHeader) {
|
||||
return pFrameHeader->bits();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hazelnut::IvshmemDevice ivshmemDevice;
|
||||
|
||||
// Protocol/memory stuff
|
||||
hazelnut::IvshHeader* pHeader;
|
||||
hazelnut::FrameHeader* pFrameHeader;
|
||||
|
||||
u32 serial {};
|
||||
|
||||
u32 lastFrameSerial {};
|
||||
};
|
||||
|
||||
// Rust bindings
|
||||
|
||||
extern "C" {
|
||||
void* rust_new_hazelnut_client() {
|
||||
return (void*)new HazelnutIvshmemClient;
|
||||
}
|
||||
|
||||
void rust_destroy_hazelnut_client(void* pClient) {
|
||||
if(pClient)
|
||||
delete(HazelnutIvshmemClient*)pClient;
|
||||
}
|
||||
|
||||
bool rust_hazelnut_client_open(void* pClient, const char* pszPath) {
|
||||
if(pClient) {
|
||||
return ((HazelnutIvshmemClient*)pClient)->Open(pszPath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int rust_hazelnut_client_tick(void* pClient) {
|
||||
if(pClient) {
|
||||
return (int)((HazelnutIvshmemClient*)pClient)->TickOne();
|
||||
}
|
||||
return (int)ResultCode::Fail;
|
||||
}
|
||||
|
||||
void rust_hazelnut_client_lock(void* pClient) {
|
||||
if(pClient) {
|
||||
return ((HazelnutIvshmemClient*)pClient)->Lock();
|
||||
}
|
||||
}
|
||||
|
||||
void rust_hazelnut_client_unlock(void* pClient) {
|
||||
if(pClient) {
|
||||
return ((HazelnutIvshmemClient*)pClient)->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
u32* rust_hazelnut_client_get_framebuffer(void* pClient) {
|
||||
if(pClient) {
|
||||
return ((HazelnutIvshmemClient*)pClient)->Framebuffer();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 rust_hazelnut_client_get_width(void* pClient) {
|
||||
if(pClient) {
|
||||
return ((HazelnutIvshmemClient*)pClient)->pFrameHeader->width.load();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 rust_hazelnut_client_get_height(void* pClient) {
|
||||
if(pClient) {
|
||||
return ((HazelnutIvshmemClient*)pClient)->pFrameHeader->height.load();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue