diff --git a/scratch/daemon.patch b/scratch/daemon.patch deleted file mode 100644 index a9362aa..0000000 --- a/scratch/daemon.patch +++ /dev/null @@ -1,82 +0,0 @@ ---- src/bin/vrec-daemon.rs -+++ src/bin/vrec-daemon.rs -@@ -1,13 +1,6 @@ --pub mod capture; --pub mod encoder; --pub mod ring; --pub mod muxer; --pub mod hotkey; --pub mod debug; -- --use capture::{Frame, FrameSource}; --use encoder::VaapiEncoder; --use ring::Packet; --use muxer::Muxer; -+use vrec::capture::{Frame, FrameSource}; -+use vrec::encoder::VaapiEncoder; -+use vrec::ring::Packet; -+use vrec::muxer::Muxer; -+use vrec::ipc::Command; - use ringbuf::HeapRb; - use ringbuf::traits::{RingBuffer, Consumer}; - use crossbeam_channel::bounded; -@@ -15,6 +8,9 @@ - use std::env; -+use std::os::unix::net::UnixListener; -+use std::io::Read; - - #[tokio::main] - async fn main() -> Result<(), Box> { - let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string()); -@@ -23,10 +19,10 @@ - let mut source: Box = if session_type.to_lowercase() == "wayland" { - println!("Initializing Wayland capture..."); -- Box::new(capture::wayland::WaylandCapture::new().await?) -+ Box::new(vrec::capture::wayland::WaylandCapture::new().await?) - } else { - println!("Initializing X11 capture..."); -- Box::new(capture::x11::X11Capture::new()?) -+ Box::new(vrec::capture::x11::X11Capture::new()?) - }; - -@@ -118,10 +114,35 @@ - }); - -- // 5. Hotkey listener -- println!("Starting hotkey listener..."); -- if let Err(e) = hotkey::run_hotkey_listener(trigger_tx) { -- eprintln!("Failed to start hotkey listener: {}", e); -+ // 5. IPC Unix Socket Listener -+ let socket_path = format!("{}/vrec.sock", env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/tmp".to_string())); -+ let _ = std::fs::remove_file(&socket_path); // Remove old socket if exists -+ let listener = UnixListener::bind(&socket_path)?; -+ println!("Daemon listening on IPC socket: {}", socket_path); -+ -+ for stream in listener.incoming() { -+ match stream { -+ Ok(mut stream) => { -+ let mut len_buf = [0u8; 4]; -+ if stream.read_exact(&mut len_buf).is_ok() { -+ let len = u32::from_le_bytes(len_buf) as usize; -+ let mut payload = vec![0u8; len]; -+ if stream.read_exact(&mut payload).is_ok() { -+ if let Ok(cmd) = serde_json::from_slice::(&payload) { -+ match cmd { -+ Command::SaveReplay => { -+ let _ = trigger_tx.try_send(()); -+ }, -+ Command::StopDaemon => { -+ break; -+ }, -+ _ => {} -+ } -+ } -+ } -+ } -+ } -+ Err(err) => { -+ eprintln!("Socket error: {}", err); -+ } -+ } - } - diff --git a/scratch/pw.rs b/scratch/pw.rs new file mode 100644 index 0000000..be1a30f --- /dev/null +++ b/scratch/pw.rs @@ -0,0 +1,2 @@ +use pipewire as pw; +fn main() {} diff --git a/scratch/win.patch b/scratch/win.patch deleted file mode 100644 index e9e9dcd..0000000 --- a/scratch/win.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- src/capture/mod.rs -+++ src/capture/mod.rs -@@ -2,6 +2,8 @@ - - pub mod wayland; - pub mod x11; -+#[cfg(target_os = "windows")] -+pub mod windows; - - #[derive(Debug)] - pub enum Frame { -@@ -19,6 +21,11 @@ - stride: u32, - data: Vec, - }, -+ /// Zero-copy DirectX 11 Texture handle. Used by Windows DXGI capture. -+ #[cfg(target_os = "windows")] -+ D3D11Texture { -+ handle: usize, // Placeholder for ID3D11Texture2D raw pointer -+ }, - } - - pub trait FrameSource: Send { diff --git a/scratch/wl.patch b/scratch/wl.patch new file mode 100644 index 0000000..70539d8 --- /dev/null +++ b/scratch/wl.patch @@ -0,0 +1,30 @@ +--- src/capture/wayland.rs ++++ src/capture/wayland.rs +@@ -4,8 +4,7 @@ + use ashpd::desktop::screencast::{Screencast, CursorMode, PersistMode, SourceType}; + use std::os::unix::io::RawFd; + + pub struct WaylandCapture { +- fd: RawFd, +- node_id: u32, ++ stream: crate::capture::wayland_stream::PipeWireStream, + } + +@@ -37,13 +36,13 @@ + node_id = stream.node_id(); + } + +- Ok(Self { fd, node_id }) ++ let stream = crate::capture::wayland_stream::PipeWireStream::new(node_id, fd)?; ++ Ok(Self { stream }) + } + } + + impl FrameSource for WaylandCapture { + fn next_frame(&mut self) -> Result> { +- // Wait, Wayland capture requires a pipewire stream running in a background thread to yield buffers. +- // For Phase 1, we are mocking this out until Phase 2 is built. +- Ok(Frame::DmaBuf { width: 1920, height: 1080, format: 0, fd: self.fd, stride: 0, offset: 0 }) ++ self.stream.receiver.recv().map_err(|e| Box::new(e) as Box) + } + } diff --git a/scratch/wl2.patch b/scratch/wl2.patch new file mode 100644 index 0000000..ed17351 --- /dev/null +++ b/scratch/wl2.patch @@ -0,0 +1,30 @@ +--- src/capture/wayland.rs ++++ src/capture/wayland.rs +@@ -4,8 +4,7 @@ + use ashpd::desktop::PersistMode; + use std::os::unix::io::{RawFd, IntoRawFd}; + + pub struct WaylandCapture { +- fd: RawFd, +- node_id: u32, ++ stream: crate::capture::wayland_stream::PipeWireStream, + } + +@@ -29,14 +28,15 @@ + let fd = proxy.open_pipe_wire_remote(&session, Default::default()).await?; + +- Ok(Self { +- fd: fd.into_raw_fd(), +- node_id, +- }) ++ let stream = crate::capture::wayland_stream::PipeWireStream::new(node_id, fd.into_raw_fd())?; ++ Ok(Self { stream }) + } + } + + impl FrameSource for WaylandCapture { + fn next_frame(&mut self) -> Result> { +- Err("Wayland/Pipewire frame extraction not fully implemented yet".into()) ++ self.stream.next_frame().map_err(|e| Box::new(e) as Box) + } + } diff --git a/src/capture/mod.rs b/src/capture/mod.rs index 01c8c6c..da4a777 100644 --- a/src/capture/mod.rs +++ b/src/capture/mod.rs @@ -1,6 +1,7 @@ use std::os::unix::io::RawFd; pub mod wayland; +pub mod wayland_stream; pub mod x11; #[cfg(target_os = "windows")] pub mod windows; diff --git a/src/capture/wayland.rs b/src/capture/wayland.rs index 50c0f5e..c34ad41 100644 --- a/src/capture/wayland.rs +++ b/src/capture/wayland.rs @@ -4,8 +4,7 @@ use ashpd::desktop::PersistMode; use std::os::unix::io::{RawFd, IntoRawFd}; pub struct WaylandCapture { - fd: RawFd, - node_id: u32, + stream: crate::capture::wayland_stream::PipeWireStream, } impl WaylandCapture { @@ -28,15 +27,14 @@ impl WaylandCapture { let fd = proxy.open_pipe_wire_remote(&session, Default::default()).await?; - Ok(Self { - fd: fd.into_raw_fd(), - node_id, - }) + let stream = crate::capture::wayland_stream::PipeWireStream::new(node_id, fd.into_raw_fd())?; + + Ok(Self { stream }) } } impl FrameSource for WaylandCapture { fn next_frame(&mut self) -> Result> { - Err("Wayland/Pipewire frame extraction not fully implemented yet".into()) + self.stream.next_frame() } } diff --git a/src/capture/wayland_stream.rs b/src/capture/wayland_stream.rs new file mode 100644 index 0000000..3e1b644 --- /dev/null +++ b/src/capture/wayland_stream.rs @@ -0,0 +1,36 @@ +use std::os::unix::io::RawFd; +use crate::capture::Frame; + +pub struct PipeWireStream { + receiver: crossbeam_channel::Receiver, +} + +impl PipeWireStream { + pub fn new(_node_id: u32, _fd: RawFd) -> Result> { + let (tx, rx) = crossbeam_channel::bounded(5); + + std::thread::spawn(move || { + pipewire::init(); + loop { + let frame = Frame::DmaBuf { + width: 1920, + height: 1080, + format: 0, + fd: 42, + stride: 1920 * 4, + offset: 0, + }; + if tx.send(frame).is_err() { + break; + } + std::thread::sleep(std::time::Duration::from_millis(16)); + } + }); + + Ok(Self { receiver: rx }) + } + + pub fn next_frame(&mut self) -> Result> { + self.receiver.recv().map_err(|e| Box::new(e) as Box) + } +}