//! Game capture source configuration using libobs-simple. //! //! This module provides capture sources for recording game footage, //! including game capture, window capture, and monitor capture. use serde::{Deserialize, Serialize}; /// Game capture source for recording game footage. /// /// Uses OBS game capture for efficient GPU-based capture. #[derive(Debug, Clone)] pub struct GameCapture { /// Source name. pub name: String, /// Window title to capture (if specific window). pub window: Option, /// Process name to capture. pub process_name: Option, /// Capture mode. pub mode: CaptureMode, /// Whether to capture cursor. pub capture_cursor: bool, /// Window class (Windows only). pub window_class: Option, } impl Default for GameCapture { fn default() -> Self { Self { name: "Game Capture".to_string(), window: None, process_name: Some("League of Legends.exe".to_string()), mode: CaptureMode::Process, capture_cursor: false, window_class: Some("RiotWindowClass".to_string()), } } } impl GameCapture { /// Create a new game capture source. pub fn new(name: &str) -> Self { Self { name: name.to_string(), ..Default::default() } } /// Create a game capture configured for League of Legends. pub fn for_league_of_legends() -> Self { Self { name: "League of Legends Capture".to_string(), window: Some("League of Legends (TM) Client".to_string()), process_name: Some("League of Legends.exe".to_string()), mode: CaptureMode::Window, capture_cursor: false, window_class: Some("RiotWindowClass".to_string()), } } /// Set the window to capture. pub fn with_window(mut self, window: &str) -> Self { self.window = Some(window.to_string()); self.mode = CaptureMode::Window; self } /// Set the process name to capture. pub fn with_process(mut self, process: &str) -> Self { self.process_name = Some(process.to_string()); self.mode = CaptureMode::Process; self } /// Set the window class (Windows only). pub fn with_window_class(mut self, class: &str) -> Self { self.window_class = Some(class.to_string()); self } /// Set whether to capture the cursor. pub fn with_cursor(mut self, capture: bool) -> Self { self.capture_cursor = capture; self } /// Get the window string for OBS in the format "Title:Class:Executable". pub fn window_string(&self) -> Option { match (&self.window, &self.window_class, &self.process_name) { (Some(window), Some(class), Some(process)) => { Some(format!("{}:{}:{}", window, class, process)) } (Some(window), None, Some(process)) => Some(format!("{}::{}", window, process)), (Some(window), Some(class), None) => Some(format!("{}:{}:", window, class)), _ => None, } } } /// Capture mode. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[derive(Default)] pub enum CaptureMode { /// Capture any fullscreen application. Any, /// Capture a specific window. Window, /// Capture a specific process. #[default] Process, } /// Window capture source (alternative to game capture). /// /// Uses OBS window capture which works with more applications /// but may have slightly higher overhead than game capture. #[derive(Debug, Clone)] pub struct WindowCapture { /// Source name. pub name: String, /// Window title. pub window_title: String, /// Window class (X11/Windows). pub window_class: Option, /// Whether to capture cursor. pub capture_cursor: bool, } impl WindowCapture { /// Create a new window capture source. pub fn new(name: &str, window_title: &str) -> Self { Self { name: name.to_string(), window_title: window_title.to_string(), window_class: None, capture_cursor: false, } } /// Set the window class (for X11/Windows). pub fn with_class(mut self, class: &str) -> Self { self.window_class = Some(class.to_string()); self } /// Set whether to capture the cursor. pub fn with_cursor(mut self, capture: bool) -> Self { self.capture_cursor = capture; self } } /// Monitor capture source (fallback). /// /// Captures an entire monitor. Useful as a fallback when /// game capture or window capture don't work. #[derive(Debug, Clone)] pub struct MonitorCapture { /// Source name. pub name: String, /// Monitor index (0-based). pub monitor: u32, /// Whether to capture cursor. pub capture_cursor: bool, } impl MonitorCapture { /// Create a new monitor capture source. pub fn new(name: &str, monitor: u32) -> Self { Self { name: name.to_string(), monitor, capture_cursor: false, } } /// Set whether to capture the cursor. pub fn with_cursor(mut self, capture: bool) -> Self { self.capture_cursor = capture; self } } /// Capture source type. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SourceType { /// Game capture source. GameCapture, /// Window capture source. WindowCapture, /// Monitor capture source. MonitorCapture, } #[cfg(test)] mod tests { use super::*; #[test] fn test_game_capture_creation() { let capture = GameCapture::new("Test Capture"); assert_eq!(capture.name, "Test Capture"); assert_eq!(capture.mode, CaptureMode::Process); } #[test] fn test_game_capture_with_process() { let capture = GameCapture::new("Test").with_process("League of Legends.exe"); assert_eq!( capture.process_name, Some("League of Legends.exe".to_string()) ); assert_eq!(capture.mode, CaptureMode::Process); } #[test] fn test_league_capture_config() { let capture = GameCapture::for_league_of_legends(); assert_eq!(capture.name, "League of Legends Capture"); assert_eq!( capture.process_name, Some("League of Legends.exe".to_string()) ); assert_eq!(capture.window_class, Some("RiotWindowClass".to_string())); } #[test] fn test_window_string() { let capture = GameCapture::for_league_of_legends(); let window_str = capture.window_string(); assert!(window_str.is_some()); let s = window_str.unwrap(); assert!(s.contains("League of Legends")); assert!(s.contains("RiotWindowClass")); } }