feat: Implement X11 minimal overlay on save trigger
This commit is contained in:
parent
2d623daa99
commit
b248cb408b
6 changed files with 110 additions and 62 deletions
|
|
@ -1,2 +0,0 @@
|
|||
use pipewire as pw;
|
||||
fn main() {}
|
||||
|
|
@ -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<Frame, Box<dyn std::error::Error + Send + Sync>> {
|
||||
- // 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<dyn std::error::Error + Send + Sync>)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Frame, Box<dyn std::error::Error + Send + Sync>> {
|
||||
- Err("Wayland/Pipewire frame extraction not fully implemented yet".into())
|
||||
+ self.stream.next_frame().map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +53,10 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
61
src/bin/vrec-ui.rs.orig
Normal file
61
src/bin/vrec-ui.rs.orig
Normal file
|
|
@ -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<dyn std::error::Error + Send + Sync>> {
|
||||
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<dyn std::error::Error + Send + Sync>> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<u32> = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue