aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--flake.nix21
-rw-r--r--src/main.rs63
-rw-r--r--src/wayland.rs11
3 files changed, 40 insertions, 55 deletions
diff --git a/flake.nix b/flake.nix
index 9f01226..caa775f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -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))?;