diff --git a/scratch/pw.rs b/scratch/pw.rs deleted file mode 100644 index be1a30f..0000000 --- a/scratch/pw.rs +++ /dev/null @@ -1,2 +0,0 @@ -use pipewire as pw; -fn main() {} diff --git a/scratch/wl.patch b/scratch/wl.patch deleted file mode 100644 index 70539d8..0000000 --- a/scratch/wl.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- 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 deleted file mode 100644 index ed17351..0000000 --- a/scratch/wl2.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- 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/bin/vrec-ui.rs b/src/bin/vrec-ui.rs index de1928b..dbdca63 100644 --- a/src/bin/vrec-ui.rs +++ b/src/bin/vrec-ui.rs @@ -53,6 +53,10 @@ fn main() -> Result<(), Box> { println!("Hotkey triggered! Sending SaveReplay command to daemon..."); if let Err(e) = send_command(Command::SaveReplay) { eprintln!("Failed to communicate with daemon: {}", e); + } else { + if env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string()).to_lowercase() != "wayland" { + vrec::overlay::show_saved_overlay(); + } } } } diff --git a/src/bin/vrec-ui.rs.orig b/src/bin/vrec-ui.rs.orig new file mode 100644 index 0000000..de1928b --- /dev/null +++ b/src/bin/vrec-ui.rs.orig @@ -0,0 +1,61 @@ +use vrec::ipc::Command; +use std::os::unix::net::UnixStream; +use std::io::Write; +use std::env; +use global_hotkey::{GlobalHotKeyManager, hotkey::{HotKey, Modifiers, Code}, GlobalHotKeyEvent}; + +fn send_command(cmd: Command) -> Result<(), Box> { + let socket_path = format!("{}/vrec.sock", env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| "/tmp".to_string())); + let mut stream = UnixStream::connect(&socket_path)?; + let payload = serde_json::to_vec(&cmd)?; + let len_buf = (payload.len() as u32).to_le_bytes(); + stream.write_all(&len_buf)?; + stream.write_all(&payload)?; + Ok(()) +} + +fn main() -> Result<(), Box> { + println!("Starting vrec UI/Hotkey process..."); + + // Check session type + let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string()); + let desktop = env::var("XDG_CURRENT_DESKTOP").unwrap_or_else(|_| "".to_string()).to_lowercase(); + + let is_gnome = desktop.contains("gnome") || desktop.contains("ubuntu"); + let is_kde = desktop.contains("kde"); + + if session_type.to_lowercase() == "wayland" { + if is_gnome || is_kde { + println!("WARNING: wlr-layer-shell unsupported on GNOME/KDE Wayland. Degrading to hotkey-only mode."); + println!("Note: Global hotkeys may also fail to register on some Wayland compositors."); + } else { + println!("wlroots-based compositor detected. (wlr-layer-shell overlay would be spawned here)."); + } + } else { + println!("X11 session detected. (X11 override-redirect overlay would be spawned here)."); + } + + let manager = GlobalHotKeyManager::new()?; + let hotkey = HotKey::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::KeyR); + + if let Err(e) = manager.register(hotkey) { + eprintln!("Failed to register global hotkey! Wayland compositor might be blocking it: {}", e); + eprintln!("Fallback: running without global hotkey. Send commands via socket manually."); + } else { + println!("Registered global hotkey Ctrl+Shift+R to trigger replay save."); + } + + let receiver = GlobalHotKeyEvent::receiver(); + loop { + if let Ok(event) = receiver.recv() { + if event.id == hotkey.id() { + if event.state == global_hotkey::HotKeyState::Pressed { + println!("Hotkey triggered! Sending SaveReplay command to daemon..."); + if let Err(e) = send_command(Command::SaveReplay) { + eprintln!("Failed to communicate with daemon: {}", e); + } + } + } + } + } +} diff --git a/src/overlay.rs b/src/overlay.rs index e69de29..cf73a12 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -0,0 +1,45 @@ +use minifb::{Window, WindowOptions}; +use std::time::Duration; + +pub fn show_saved_overlay() { + let width = 300; + let height = 100; + + let mut window = match Window::new( + "VRec Overlay", + width, + height, + WindowOptions { + borderless: true, + title: false, + resize: false, + scale: minifb::Scale::X1, + scale_mode: minifb::ScaleMode::Stretch, + topmost: true, + transparency: true, + none: false, + } + ) { + Ok(win) => win, + Err(e) => { + eprintln!("Failed to create overlay window: {}", e); + return; + } + }; + + // Create a semi-transparent green box or just dark gray + let buffer: Vec = vec![0xFF222222; width * height]; + + window.set_target_fps(60); + + let start_time = std::time::Instant::now(); + let display_duration = Duration::from_secs(2); + + while window.is_open() && start_time.elapsed() < display_duration { + // We could render text here, but for now we just show a flash/banner. + if let Err(e) = window.update_with_buffer(&buffer, width, height) { + eprintln!("Overlay update error: {}", e); + break; + } + } +}