use anyhow::{Result, anyhow}; use log::{info, warn}; use std::process::Command; /// Hyprland-specific window configuration for better integration pub struct HyprlandConfig { pub floating: bool, pub pin: bool, pub no_focus: bool, } impl Default for HyprlandConfig { fn default() -> Self { Self { floating: true, pin: true, no_focus: true, } } } /// Configure a window for optimal Hyprland wallpaper behavior pub fn configure_hyprland_window( _window: &winit::window::Window, _config: &HyprlandConfig, ) -> Result<()> { // On Wayland, we can set window properties for better integration // For now, skip advanced Wayland protocol setup as it's not implemented // For Hyprland, we can use hyprctl to configure the window // This requires the window to be created first, so we'll do this after creation if let Ok(output) = Command::new("which").arg("hyprctl").output() && output.status.success() { info!("Hyprland detected, will configure window properties"); // We'll configure the window after it's mapped in the event loop } Ok(()) } /// Apply Hyprland-specific window rules after window creation pub fn apply_hyprland_rules(window_id: u64, config: &HyprlandConfig) -> Result<()> { #[cfg(target_os = "linux")] { use std::process::Command; use std::thread; use std::time::Duration; // Wait a bit for the window to be mapped thread::sleep(Duration::from_millis(100)); // Use hyprctl to configure the window let mut rules = Vec::new(); if config.floating { rules.push("float"); } if config.pin { rules.push("pin"); } if config.no_focus { rules.push("nofocus"); } if !rules.is_empty() { let rules_str = rules.join(","); let window_selector = format!("address:0x{:x}", window_id); let output = Command::new("hyprctl") .args([ "keyword", "windowrule", &format!("{},{}", rules_str, window_selector), ]) .output() .map_err(|e| anyhow!("Failed to execute hyprctl: {}", e))?; if output.status.success() { info!( "Applied Hyprland rules: {} to window {}", rules_str, window_selector ); } else { let error_msg = String::from_utf8_lossy(&output.stderr); warn!("Failed to apply Hyprland rules: {}", error_msg); } // Also try to move it to the background layer let workspace = format!("special:background,{}", window_selector); let output = Command::new("hyprctl") .args(["dispatch", "movetoworkspace", &workspace]) .output() .map_err(|e| anyhow!("Failed to move window to background: {}", e))?; if output.status.success() { info!("Moved wallpaper window to background workspace"); } } } Ok(()) } /// Get information about available outputs (monitors) pub fn get_output_info() -> Result> { #[cfg(target_os = "linux")] { use std::process::Command; let output = Command::new("hyprctl") .args(["monitors", "-j"]) .output() .map_err(|e| anyhow!("Failed to execute hyprctl: {}", e))?; if output.status.success() { let output_str = String::from_utf8_lossy(&output.stdout); let monitors: Vec = serde_json::from_str(&output_str) .map_err(|e| anyhow!("Failed to parse hyprctl output: {}", e))?; let output_info = monitors .into_iter() .map(|monitor| OutputInfo { name: monitor.name, width: monitor.width, height: monitor.height, x: monitor.x, y: monitor.y, }) .collect(); Ok(output_info) } else { // Fallback to basic monitor detection Ok(vec![OutputInfo::default()]) } } #[cfg(not(target_os = "linux"))] { Ok(vec![OutputInfo::default()]) } } #[derive(Debug, Clone)] pub struct OutputInfo { pub name: String, pub width: i32, pub height: i32, pub x: i32, pub y: i32, } impl Default for OutputInfo { fn default() -> Self { Self { name: "eDP-1".to_string(), width: 1920, height: 1080, x: 0, y: 0, } } } #[cfg(target_os = "linux")] #[derive(serde::Deserialize)] struct HyprlandMonitor { name: String, width: i32, height: i32, x: i32, y: i32, }