diff options
| -rw-r--r-- | flake.nix | 21 | ||||
| -rw-r--r-- | src/main.rs | 63 | ||||
| -rw-r--r-- | src/wayland.rs | 11 |
3 files changed, 40 insertions, 55 deletions
@@ -16,6 +16,15 @@ system: let pkgs = nixpkgs.legacyPackages.${system}; + libPath = pkgs.lib.makeLibraryPath ( + with pkgs; + [ + wayland + libxkbcommon + vulkan-loader + libGL + ] + ); in { packages.default = pkgs.rustPlatform.buildRustPackage { @@ -23,13 +32,21 @@ version = "0.1.0"; src = ./.; cargoLock.lockFile = ./Cargo.lock; - nativeBuildInputs = with pkgs; [ pkg-config ]; + nativeBuildInputs = with pkgs; [ + pkg-config + makeWrapper + ]; buildInputs = with pkgs; [ wayland libxkbcommon vulkan-loader wayland-protocols + libGL ]; + postInstall = '' + wrapProgram $out/bin/hyprland-live-wallpaper + --prefix LD_LIBRARY_PATH : "${libPath}" + ''; }; devShells.default = pkgs.mkShell { @@ -43,7 +60,9 @@ libxkbcommon vulkan-loader wayland-protocols + libGL ]; + LD_LIBRARY_PATH = libPath; }; } ) diff --git a/src/main.rs b/src/main.rs index d58cae1..49993a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ mod wayland; - use anyhow::Result; use clap::{Arg, Command}; use log::{error, info, warn}; @@ -12,7 +11,6 @@ use winit::{ event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowAttributes, WindowId}, }; - #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] struct Uniforms { @@ -20,7 +18,6 @@ struct Uniforms { i_time: f32, i_mouse: [f32; 4], } - struct WallpaperApp { window: Option<Window>, surface: Option<wgpu::Surface<'static>>, @@ -35,7 +32,6 @@ struct WallpaperApp { hyprland_config: wayland::HyprlandConfig, wayland_configured: bool, } - impl WallpaperApp { fn new(shader_path: String) -> Self { Self { @@ -53,39 +49,46 @@ impl WallpaperApp { wayland_configured: false, } } - fn init_graphics(&mut self) -> Result<()> { let window = self .window .as_ref() .ok_or_else(|| anyhow::anyhow!("Window not initialized"))?; let size = window.inner_size(); - let instance = wgpu::Instance::default(); - + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { + memory_budget_thresholds: wgpu::MemoryBudgetThresholds { + for_resource_creation: Some(200), + for_device_loss: Some(100), + }, + backends: wgpu::Backends::VULKAN, + backend_options: wgpu::BackendOptions { + gl: wgpu::GlBackendOptions::default(), + dx12: wgpu::Dx12BackendOptions::default(), + noop: wgpu::NoopBackendOptions::default(), + }, + flags: wgpu::InstanceFlags::empty(), + }); // Create surface let surface = instance .create_surface(window) .map_err(|e| anyhow::anyhow!("Failed to create surface: {}", e))?; let surface = unsafe { mem::transmute::<wgpu::Surface<'_>, wgpu::Surface<'static>>(surface) }; - let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, compatible_surface: Some(&surface), force_fallback_adapter: false, })) .unwrap(); - let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::downlevel_defaults(), + required_limits: wgpu::Limits::default(), memory_hints: wgpu::MemoryHints::Performance, label: None, experimental_features: wgpu::ExperimentalFeatures::disabled(), trace: wgpu::Trace::Off, }))?; - let surface_caps = surface.get_capabilities(&adapter); let surface_format = surface_caps .formats @@ -93,7 +96,6 @@ impl WallpaperApp { .copied() .find(|f| f.is_srgb()) .unwrap_or(surface_caps.formats[0]); - let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, @@ -105,30 +107,25 @@ impl WallpaperApp { desired_maximum_frame_latency: 2, }; surface.configure(&device, &config); - // Load and compile shader let shader_source = std::fs::read_to_string(&self.shader_path).map_err(|e| { anyhow::anyhow!("Failed to read shader file {}: {}", &self.shader_path, e) })?; - let shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("Wallpaper Shader"), source: wgpu::ShaderSource::Wgsl(shader_source.as_str().into()), }); - // Create uniform buffer let uniforms = Uniforms { i_resolution: [size.width as f32, size.height as f32, 0.0], i_time: 0.0, i_mouse: [0.0, 0.0, 0.0, 0.0], }; - let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Uniform Buffer"), contents: bytemuck::cast_slice(&[uniforms]), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, }); - let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { entries: &[wgpu::BindGroupLayoutEntry { binding: 0, @@ -142,7 +139,6 @@ impl WallpaperApp { }], label: Some("uniform_bind_group_layout"), }); - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, entries: &[wgpu::BindGroupEntry { @@ -151,13 +147,11 @@ impl WallpaperApp { }], label: Some("uniform_bind_group"), }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("Render Pipeline Layout"), bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }); - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Render Pipeline"), layout: Some(&pipeline_layout), @@ -195,7 +189,6 @@ impl WallpaperApp { multiview: None, cache: None, }); - self.surface = Some(surface); self.device = Some(device); self.queue = Some(queue); @@ -203,10 +196,8 @@ impl WallpaperApp { self.render_pipeline = Some(render_pipeline); self.uniform_buffer = Some(uniform_buffer); self.bind_group = Some(bind_group); - Ok(()) } - fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { if new_size.width > 0 && new_size.height > 0 { if let (Some(surface), Some(device), Some(config)) = @@ -218,7 +209,6 @@ impl WallpaperApp { } } } - fn render(&mut self) -> Result<()> { let surface = self .surface @@ -248,25 +238,20 @@ impl WallpaperApp { .config .as_ref() .ok_or_else(|| anyhow::anyhow!("Config not initialized"))?; - let frame = surface.get_current_texture()?; let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); - // Update uniforms let uniforms = Uniforms { i_resolution: [config.width as f32, config.height as f32, 0.0], i_time: self.start_time.elapsed().as_secs_f32(), i_mouse: [0.0, 0.0, 0.0, 0.0], }; - queue.write_buffer(uniform_buffer, 0, bytemuck::cast_slice(&[uniforms])); - let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder"), }); - { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), @@ -283,19 +268,15 @@ impl WallpaperApp { occlusion_query_set: None, timestamp_writes: None, }); - render_pass.set_pipeline(render_pipeline); render_pass.set_bind_group(0, bind_group, &[]); render_pass.draw(0..3, 0..1); } - queue.submit(std::iter::once(encoder.finish())); frame.present(); - Ok(()) } } - impl ApplicationHandler for WallpaperApp { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.window.is_none() { @@ -304,7 +285,6 @@ impl ApplicationHandler for WallpaperApp { .with_transparent(true) .with_decorations(false) .with_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); - let window = match event_loop.create_window(window_attributes) { Ok(window) => window, Err(e) => { @@ -312,15 +292,12 @@ impl ApplicationHandler for WallpaperApp { return; } }; - // Configure window for Hyprland if let Err(e) = wayland::configure_hyprland_window(&window, &self.hyprland_config) { warn!("Failed to configure Hyprland window: {}", e); } - // Store window first, then initialize graphics self.window = Some(window); - match self.init_graphics() { Ok(()) => { info!("Wallpaper service running"); @@ -331,7 +308,6 @@ impl ApplicationHandler for WallpaperApp { } } } - fn window_event( &mut self, event_loop: &ActiveEventLoop, @@ -359,7 +335,6 @@ impl ApplicationHandler for WallpaperApp { self.wayland_configured = true; } } - if let Err(e) = self.render() { error!("Render error: {}", e); event_loop.exit(); @@ -368,17 +343,14 @@ impl ApplicationHandler for WallpaperApp { _ => {} } } - fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { if let Some(window) = &self.window { window.request_redraw(); } } } - fn main() -> Result<()> { env_logger::init(); - let matches = Command::new("hyprland-live-wallpaper") .version("0.1.0") .about("Live wallpaper service for Hyprland using wgpu") @@ -390,24 +362,19 @@ fn main() -> Result<()> { .help("Path to WGSL shader file"), ) .get_matches(); - info!("Starting Hyprland Live Wallpaper"); - let shader_path = matches .get_one::<String>("shader") .map(|s| s.as_str()) .unwrap_or("shaders/default.wgsl") .to_string(); - run_wallpaper(shader_path)?; - Ok(()) } - fn run_wallpaper(shader_path: String) -> Result<()> { let event_loop = EventLoop::new()?; let mut app = WallpaperApp::new(shader_path); - event_loop.run_app(&mut app)?; Ok(()) } + diff --git a/src/wayland.rs b/src/wayland.rs index afb1486..f9c396d 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -29,12 +29,11 @@ pub fn configure_hyprland_window( // 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() { - if output.status.success() { + 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(()) } @@ -70,7 +69,7 @@ pub fn apply_hyprland_rules(window_id: u64, config: &HyprlandConfig) -> Result<( let window_selector = format!("address:0x{:x}", window_id); let output = Command::new("hyprctl") - .args(&[ + .args([ "keyword", "windowrule", &format!("{},{}", rules_str, window_selector), @@ -91,7 +90,7 @@ pub fn apply_hyprland_rules(window_id: u64, config: &HyprlandConfig) -> Result<( // 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]) + .args(["dispatch", "movetoworkspace", &workspace]) .output() .map_err(|e| anyhow!("Failed to move window to background: {}", e))?; @@ -111,7 +110,7 @@ pub fn get_output_info() -> Result<Vec<OutputInfo>> { use std::process::Command; let output = Command::new("hyprctl") - .args(&["monitors", "-j"]) + .args(["monitors", "-j"]) .output() .map_err(|e| anyhow!("Failed to execute hyprctl: {}", e))?; |
