diff --git a/Cargo.lock b/Cargo.lock index 7fcf9c27..e52c0905 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1901,9 +1901,9 @@ dependencies = [ [[package]] name = "gbm" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c724107aa10444b1d2709aae4727c18a33c16b3e15ea8a46cc4ae226c084c88a" +checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ "bitflags 2.6.0", "drm 0.14.0", @@ -1914,9 +1914,9 @@ dependencies = [ [[package]] name = "gbm-sys" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9cc2f64de9fa707b5c6b2d2f10d7a7e49e845018a9f5685891eb40d3bab2538" +checksum = "c13a5f2acc785d8fb6bf6b7ab6bfb0ef5dad4f4d97e8e70bb8e470722312f76f" dependencies = [ "libc", ] @@ -3874,9 +3874,9 @@ dependencies = [ [[package]] name = "pixman" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a24da0bec14f4e43a495c1837a3c358b87532e7fe66bd75c348b89f0451b6" +checksum = "459d9be014c5e9725153de54b1cacd6f5bbe258aba7daae10e76ca2cda0ad8c2" dependencies = [ "drm-fourcc", "paste", @@ -4638,7 +4638,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/smithay//smithay?rev=bc1d732#bc1d7320f95cdf17f9e7aa6867cccc5903548032" +source = "git+https://github.com/ids1024/smithay?branch=devices_mut#a3ba93d324f2248eb35f81fb8f980c5c62dae6b5" dependencies = [ "appendlist", "ash", diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 5a656e52..c76b163b 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -2,7 +2,7 @@ use cosmic_config::{cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; pub mod input; pub mod workspace; @@ -31,6 +31,8 @@ pub struct CosmicCompConfig { pub focus_follows_cursor_delay: u64, /// Let X11 applications scale themselves pub descale_xwayland: bool, + /// Path to postprocess shader applied to whole screen + pub postprocess_shader_path: Option, } impl Default for CosmicCompConfig { @@ -60,6 +62,7 @@ impl Default for CosmicCompConfig { cursor_follows_focus: false, focus_follows_cursor_delay: 250, descale_xwayland: false, + postprocess_shader_path: None, } } } diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index be22b7f9..46d079a9 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -226,6 +226,7 @@ impl State { { for (conn, maybe_crtc) in connectors { + let postprocess_shader = self.backend.kms().postprocess_shader.clone(); match device.connector_added( self.backend.kms().primary_node.as_ref(), conn, @@ -234,6 +235,7 @@ impl State { &self.common.event_loop_handle, self.common.shell.clone(), self.common.startup_done.clone(), + postprocess_shader, ) { Ok((output, should_expose)) => { if should_expose { @@ -324,6 +326,7 @@ impl State { } for (conn, maybe_crtc) in changes.added { + let postprocess_shader = backend.postprocess_shader.clone(); match device.connector_added( backend.primary_node.as_ref(), conn, @@ -332,6 +335,7 @@ impl State { &self.common.event_loop_handle, self.common.shell.clone(), self.common.startup_done.clone(), + postprocess_shader, ) { Ok((output, should_expose)) => { if should_expose { @@ -480,6 +484,7 @@ impl Device { evlh: &LoopHandle<'static, State>, shell: Arc>, startup_done: Arc, + postprocess_shader: Option, ) -> Result<(Output, bool)> { let output = self .outputs @@ -543,7 +548,8 @@ impl Device { shell, startup_done, ) { - Ok(data) => { + Ok(mut data) => { + data.set_postprocess_shader(postprocess_shader); self.surfaces.insert(crtc, data); true } diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index b1e54710..78f5fea2 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -64,6 +64,8 @@ pub struct KmsState { pub software_renderer: Option, pub api: GpuManager>, + postprocess_shader: Option, + session: LibSeatSession, libinput: Libinput, } @@ -130,6 +132,8 @@ pub fn init_backend( software_renderer, api: GpuManager::new(GbmGlowBackend::new()).context("Failed to initialize gpu backend")?, + postprocess_shader: None, + session, libinput: libinput_context, }); @@ -623,6 +627,7 @@ impl KmsState { loop_handle, shell.clone(), startup_done.clone(), + self.postprocess_shader.clone(), )?; if output.mirroring().is_none() { w += output.config().transformed_size().w as u32; @@ -759,4 +764,13 @@ impl KmsState { Ok(all_outputs) } + + pub fn update_postprocess_shader(&mut self, shader: Option<&str>) { + self.postprocess_shader = shader.map(|x| x.to_owned()); + for device in self.drm_devices.values_mut() { + for surface in device.surfaces.values_mut() { + surface.set_postprocess_shader(self.postprocess_shader.clone()); + } + } + } } diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index 2fe8ef05..e6a86497 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -3,7 +3,8 @@ use crate::{ backend::render::{ element::{CosmicElement, DamageElement}, - init_shaders, workspace_elements, CursorMode, ElementFilter, GlMultiRenderer, CLEAR_COLOR, + init_shaders, update_postprocess_shader, workspace_elements, CursorMode, ElementFilter, + GlMultiRenderer, PostprocessShader, CLEAR_COLOR, }, config::AdaptiveSync, shell::Shell, @@ -39,9 +40,9 @@ use smithay::{ utils::{constrain_render_elements, ConstrainAlign, ConstrainScaleBehavior}, Element, Kind, RenderElementStates, }, - gles::{GlesRenderbuffer, GlesTexture}, + gles::{element::TextureShaderElement, GlesRenderbuffer, GlesRenderer, GlesTexture}, glow::GlowRenderer, - multigpu::{Error as MultiError, GpuManager}, + multigpu::{ApiDevice, Error as MultiError, GpuManager}, sync::SyncPoint, utils::with_renderer_surface_state, Bind, ImportDma, Offscreen, Renderer, Texture, @@ -76,7 +77,7 @@ use smithay::{ use tracing::{error, trace, warn}; use std::{ - borrow::BorrowMut, + borrow::{Borrow, BorrowMut}, collections::{HashMap, HashSet}, mem, sync::{ @@ -136,7 +137,8 @@ pub struct SurfaceThreadState { output: Output, mirroring: Option, - mirroring_textures: HashMap, + offscreen_textures: HashMap, + postprocess_shader: Option, shell: Arc>, @@ -147,13 +149,14 @@ pub struct SurfaceThreadState { egui: EguiState, } +// Used for mirroring and postprocessing #[derive(Debug)] -struct MirroringState { +struct OffscreenState { texture: TextureRenderBuffer, damage_tracker: OutputDamageTracker, } -impl MirroringState { +impl OffscreenState { fn new_with_renderer( renderer: &mut GlMultiRenderer, format: Fourcc, @@ -179,7 +182,7 @@ impl MirroringState { let damage_tracker = OutputDamageTracker::from_output(output); - Ok(MirroringState { + Ok(OffscreenState { texture: texture_buffer, damage_tracker, }) @@ -238,6 +241,7 @@ pub enum ThreadCommand { node: DrmNode, }, UpdateMirroring(Option), + UpdatePostprocessShader(Option), VBlank(Option), ScheduleRender, SetMode(Mode, SyncSender>), @@ -399,6 +403,12 @@ impl Surface { .send(ThreadCommand::UpdateMirroring(output)); } + pub fn set_postprocess_shader(&mut self, shader: Option) { + let _ = self + .thread_command + .send(ThreadCommand::UpdatePostprocessShader(shader)); + } + pub fn set_mode(&mut self, mode: Mode) -> Result<()> { let (tx, rx) = std::sync::mpsc::sync_channel(1); let _ = self.thread_command.send(ThreadCommand::SetMode(mode, tx)); @@ -542,7 +552,8 @@ fn surface_thread( output, mirroring: None, - mirroring_textures: HashMap::new(), + offscreen_textures: HashMap::new(), + postprocess_shader: None, shell, loop_handle: event_loop.handle(), @@ -585,6 +596,9 @@ fn surface_thread( Event::Msg(ThreadCommand::UpdateMirroring(mirroring_output)) => { state.update_mirroring(mirroring_output); } + Event::Msg(ThreadCommand::UpdatePostprocessShader(shader)) => { + state.update_postprocess_shader(shader); + } Event::Msg(ThreadCommand::SetMode(mode, result)) => { if let Some(compositor) = state.compositor.as_mut() { let _ = result.send(compositor.use_mode(mode).map_err(Into::into)); @@ -741,6 +755,7 @@ impl SurfaceThreadState { let mut renderer = unsafe { GlowRenderer::new(egl) }.context("Failed to create renderer")?; init_shaders(renderer.borrow_mut()).context("Failed to initialize shaders")?; + update_postprocess_shader(renderer.borrow_mut(), self.postprocess_shader.as_deref()); #[cfg(feature = "debug")] { @@ -1081,34 +1096,47 @@ impl SurfaceThreadState { .collect() }).unwrap_or_default(); + let postprocess_shader = Borrow::::borrow(renderer.as_mut()) + .egl_context() + .user_data() + .get::() + .and_then(|x| x.0.lock().unwrap().clone()); + // actual rendering - let res = if let Some(mirrored_output) = self.mirroring.as_ref().filter(|mirrored_output| { - mirrored_output.current_mode().is_some_and(|mirror_mode| { - self.output - .current_mode() - .is_some_and(|mode| mode != mirror_mode) - }) || mirrored_output.current_scale().fractional_scale() - != self.output.current_scale().fractional_scale() - }) { - let mirroring_state = { - let entry = self.mirroring_textures.entry(self.target_node); + // Render offscreen first if postprocessing, or mirroring with different size + let source_output = self + .mirroring + .as_ref() + .filter(|mirrored_output| { + mirrored_output.current_mode().is_some_and(|mirror_mode| { + self.output + .current_mode() + .is_some_and(|mode| mode != mirror_mode) + }) || mirrored_output.current_scale().fractional_scale() + != self.output.current_scale().fractional_scale() + }) + .or(postprocess_shader.as_ref().map(|_| &self.output)); + let mut postproc_states = None; // TODO better way? + let res = if let Some(source_output) = source_output { + let offscreen_state = { + let entry = self.offscreen_textures.entry(self.target_node); let mut new_state = None; if matches!(entry, std::collections::hash_map::Entry::Vacant(_)) { - new_state = Some(MirroringState::new_with_renderer( + new_state = Some(OffscreenState::new_with_renderer( &mut renderer, compositor.format(), - mirrored_output, + source_output, )?); } // I really want a failable initializer... entry.or_insert_with(|| new_state.unwrap()) }; - mirroring_state + offscreen_state .texture .render() .draw::<_, ::Error>(|tex| { - let res = match mirroring_state.damage_tracker.render_output_with( + let res = match offscreen_state.damage_tracker.render_output_with( &mut renderer, tex.clone(), 1, @@ -1120,9 +1148,13 @@ impl SurfaceThreadState { Err(RenderError::OutputNoMode(_)) => unreachable!(), }; + if self.mirroring.is_none() { + postproc_states = Some(res.states); + } + renderer.wait(&res.sync)?; - let transform = mirrored_output.current_transform(); + let transform = source_output.current_transform(); let area = tex.size().to_logical(1, transform); Ok(res @@ -1139,33 +1171,59 @@ impl SurfaceThreadState { let texture_elem = TextureRenderElement::from_texture_render_buffer( (0., 0.), - &mirroring_state.texture, + &offscreen_state.texture, Some(1.0), None, None, Kind::Unspecified, ); + let texture_geometry = texture_elem.geometry(1.0.into()); - elements = constrain_render_elements( - std::iter::once(texture_elem), - (0, 0), - Rectangle::from_loc_and_size( + elements = if let Some(shader) = &postprocess_shader { + let texture_elem = + TextureShaderElement::new(texture_elem, shader.clone(), Vec::new()); + constrain_render_elements( + std::iter::once(texture_elem), (0, 0), - self.output - .geometry() - .size - .as_logical() - .to_f64() - .to_physical(self.output.current_scale().fractional_scale()) - .to_i32_round(), - ), - texture_geometry, - ConstrainScaleBehavior::Fit, - ConstrainAlign::CENTER, - 1.0, - ) - .map(CosmicElement::Mirror) - .collect::>(); + Rectangle::from_loc_and_size( + (0, 0), + self.output + .geometry() + .size + .as_logical() + .to_f64() + .to_physical(self.output.current_scale().fractional_scale()) + .to_i32_round(), + ), + texture_geometry, + ConstrainScaleBehavior::Fit, + ConstrainAlign::CENTER, + 1.0, + ) + .map(CosmicElement::Postprocess) + .collect::>() + } else { + constrain_render_elements( + std::iter::once(texture_elem), + (0, 0), + Rectangle::from_loc_and_size( + (0, 0), + self.output + .geometry() + .size + .as_logical() + .to_f64() + .to_physical(self.output.current_scale().fractional_scale()) + .to_i32_round(), + ), + texture_geometry, + ConstrainScaleBehavior::Fit, + ConstrainAlign::CENTER, + 1.0, + ) + .map(CosmicElement::Mirror) + .collect::>() + }; renderer = self.api.single_renderer(&self.target_node).unwrap(); if let Err(err) = compositor.use_vrr(false) { @@ -1354,7 +1412,8 @@ impl SurfaceThreadState { } if self.mirroring.is_none() { - let states = frame_result.states; + // If postprocessing, use states from first render + let states = postproc_states.unwrap_or(frame_result.states); self.send_dmabuf_feedback(states); } @@ -1446,7 +1505,16 @@ impl SurfaceThreadState { fn update_mirroring(&mut self, mirroring_output: Option) { self.mirroring = mirroring_output; - self.mirroring_textures.clear(); + self.offscreen_textures.clear(); + } + + fn update_postprocess_shader(&mut self, shader: Option) { + // XXX unwrap + for device in self.api.devices_mut().unwrap() { + update_postprocess_shader(device.renderer_mut().borrow_mut(), shader.as_deref()); + } + self.postprocess_shader = shader; + self.offscreen_textures.clear(); } fn send_frame_callbacks(&mut self) { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index c4f3335f..86601c34 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -66,6 +66,24 @@ pub fn init_backend_auto( .seats .add_seat(initial_seat); + // TODO + if let Some(shader_path) = state + .common + .config + .cosmic_conf + .postprocess_shader_path + .as_ref() + { + match std::fs::read_to_string(shader_path) { + Ok(shader) => { + state.backend.update_postprocess_shader(Some(&shader)); + } + Err(_) => { + tracing::error!("failed to read shader at path: {}", shader_path.display()); + } + } + } + { { state diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index b33b7763..1007929b 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -8,7 +8,7 @@ use smithay::{ utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, Element, Id, Kind, RenderElement, UnderlyingStorage, }, - gles::{GlesError, GlesTexture}, + gles::{element::TextureShaderElement, GlesError, GlesTexture}, glow::{GlowFrame, GlowRenderer}, utils::{CommitCounter, DamageSet, OpaqueRegions}, ImportAll, ImportMem, Renderer, @@ -34,6 +34,9 @@ where RelocateRenderElement>>, >, ), + Postprocess( + CropRenderElement>>, + ), #[cfg(feature = "debug")] Egui(TextureRenderElement), } @@ -52,6 +55,7 @@ where CosmicElement::MoveGrab(elem) => elem.id(), CosmicElement::AdditionalDamage(elem) => elem.id(), CosmicElement::Mirror(elem) => elem.id(), + CosmicElement::Postprocess(elem) => elem.id(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.id(), } @@ -65,6 +69,7 @@ where CosmicElement::MoveGrab(elem) => elem.current_commit(), CosmicElement::AdditionalDamage(elem) => elem.current_commit(), CosmicElement::Mirror(elem) => elem.current_commit(), + CosmicElement::Postprocess(elem) => elem.current_commit(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.current_commit(), } @@ -78,6 +83,7 @@ where CosmicElement::MoveGrab(elem) => elem.src(), CosmicElement::AdditionalDamage(elem) => elem.src(), CosmicElement::Mirror(elem) => elem.src(), + CosmicElement::Postprocess(elem) => elem.src(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.src(), } @@ -91,6 +97,7 @@ where CosmicElement::MoveGrab(elem) => elem.geometry(scale), CosmicElement::AdditionalDamage(elem) => elem.geometry(scale), CosmicElement::Mirror(elem) => elem.geometry(scale), + CosmicElement::Postprocess(elem) => elem.geometry(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.geometry(scale), } @@ -104,6 +111,7 @@ where CosmicElement::MoveGrab(elem) => elem.location(scale), CosmicElement::AdditionalDamage(elem) => elem.location(scale), CosmicElement::Mirror(elem) => elem.location(scale), + CosmicElement::Postprocess(elem) => elem.location(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.location(scale), } @@ -117,6 +125,7 @@ where CosmicElement::MoveGrab(elem) => elem.transform(), CosmicElement::AdditionalDamage(elem) => elem.transform(), CosmicElement::Mirror(elem) => elem.transform(), + CosmicElement::Postprocess(elem) => elem.transform(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.transform(), } @@ -134,6 +143,7 @@ where CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit), CosmicElement::AdditionalDamage(elem) => elem.damage_since(scale, commit), CosmicElement::Mirror(elem) => elem.damage_since(scale, commit), + CosmicElement::Postprocess(elem) => elem.damage_since(scale, commit), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.damage_since(scale, commit), } @@ -147,6 +157,7 @@ where CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale), CosmicElement::AdditionalDamage(elem) => elem.opaque_regions(scale), CosmicElement::Mirror(elem) => elem.opaque_regions(scale), + CosmicElement::Postprocess(elem) => elem.opaque_regions(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.opaque_regions(scale), } @@ -160,6 +171,7 @@ where CosmicElement::MoveGrab(elem) => elem.alpha(), CosmicElement::AdditionalDamage(elem) => elem.alpha(), CosmicElement::Mirror(elem) => elem.alpha(), + CosmicElement::Postprocess(elem) => elem.alpha(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.alpha(), } @@ -173,6 +185,7 @@ where CosmicElement::MoveGrab(elem) => elem.kind(), CosmicElement::AdditionalDamage(elem) => elem.kind(), CosmicElement::Mirror(elem) => elem.kind(), + CosmicElement::Postprocess(elem) => elem.kind(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.kind(), } @@ -214,6 +227,18 @@ where ) .map_err(FromGlesError::from_gles_error) } + CosmicElement::Postprocess(elem) => { + let glow_frame = R::glow_frame_mut(frame); + RenderElement::::draw( + elem, + glow_frame, + src, + dst, + damage, + opaque_regions, + ) + .map_err(FromGlesError::from_gles_error) + } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -241,6 +266,10 @@ where let glow_renderer = renderer.glow_renderer_mut(); elem.underlying_storage(glow_renderer) } + CosmicElement::Postprocess(elem) => { + let glow_renderer = renderer.glow_renderer_mut(); + elem.underlying_storage(glow_renderer) + } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_renderer = renderer.glow_renderer_mut(); diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index aa545068..c0f08935 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -5,7 +5,7 @@ use std::{ cell::RefCell, collections::HashMap, ops::ControlFlow, - sync::{Arc, RwLock, Weak}, + sync::{Arc, Mutex, RwLock, Weak}, time::Instant, }; @@ -46,8 +46,8 @@ use smithay::{ AsRenderElements, Element, Id, Kind, RenderElement, }, gles::{ - element::PixelShaderElement, GlesError, GlesPixelProgram, GlesRenderer, Uniform, - UniformName, UniformType, + element::PixelShaderElement, GlesError, GlesPixelProgram, GlesRenderer, + GlesTexProgram, Uniform, UniformName, UniformType, }, glow::GlowRenderer, multigpu::{Error as MultiError, MultiFrame, MultiRenderer}, @@ -65,6 +65,7 @@ use smithay::{ shm::{shm_format_to_fourcc, with_buffer_contents}, }, }; +use tracing::error; #[cfg(feature = "debug")] use smithay_egui::EguiState; @@ -392,6 +393,30 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { Ok(()) } +pub struct PostprocessShader(pub Mutex>); + +pub fn update_postprocess_shader(renderer: &mut GlesRenderer, shader_text: Option<&str>) { + let shader = if let Some(shader_text) = shader_text { + match renderer.compile_custom_texture_shader(shader_text, &[]) { + Ok(shader) => Some(shader), + Err(err) => { + error!("failed to compile postprocess shader: {}", err); + None + } + } + } else { + None + }; + + let egl_context = renderer.egl_context(); + *egl_context + .user_data() + .get_or_insert(|| PostprocessShader(Mutex::new(None))) + .0 + .lock() + .unwrap() = shader; +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CursorMode { None, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index a1a2bdce..8f958232 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -34,7 +34,7 @@ use smithay::{ use std::{borrow::BorrowMut, cell::RefCell, time::Duration}; use tracing::{error, info, warn}; -use super::render::{init_shaders, CursorMode}; +use super::render::{init_shaders, update_postprocess_shader, CursorMode}; #[derive(Debug)] pub struct WinitState { @@ -125,6 +125,10 @@ impl WinitState { Ok(vec![self.output.clone()]) } } + + pub fn update_postprocess_shader(&mut self, shader: Option<&str>) { + update_postprocess_shader(self.backend.renderer().borrow_mut(), shader) + } } pub fn init_backend( diff --git a/src/backend/x11.rs b/src/backend/x11.rs index ba33c14b..22bfda0a 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -41,7 +41,7 @@ use smithay::{ use std::{borrow::BorrowMut, cell::RefCell, os::unix::io::OwnedFd, time::Duration}; use tracing::{debug, error, info, warn}; -use super::render::init_shaders; +use super::render::{init_shaders, update_postprocess_shader}; #[derive(Debug)] enum Allocator { @@ -187,6 +187,10 @@ impl X11State { Ok(vec![surface.output.clone()]) } } + + pub fn update_postprocess_shader(&mut self, shader: Option<&str>) { + update_postprocess_shader(self.renderer.borrow_mut(), shader) + } } #[derive(Debug)] diff --git a/src/config/mod.rs b/src/config/mod.rs index f8bd9a2f..b7464837 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -738,6 +738,22 @@ fn config_changed(config: cosmic_config::Config, keys: Vec, state: &mut state.common.config.cosmic_conf.focus_follows_cursor_delay = new; } } + "postprocess_shader_path" => { + let new = get_config::>(&config, "postprocess_shader_path"); + if new != state.common.config.cosmic_conf.postprocess_shader_path { + let shader = + new.as_ref() + .and_then(|path| match std::fs::read_to_string(path) { + Ok(shader) => Some(shader), + Err(_) => { + error!("failed to read shader at path: {}", path.display()); + None + } + }); + state.backend.update_postprocess_shader(shader.as_deref()); + state.common.config.cosmic_conf.postprocess_shader_path = new; + } + } _ => {} } } diff --git a/src/state.rs b/src/state.rs index fc2ba06e..eb791e18 100644 --- a/src/state.rs +++ b/src/state.rs @@ -435,6 +435,16 @@ impl BackendData { _ => unreachable!("No backend set when getting offscreen renderer"), } } + + /// Set the GLES texture shader used for postprocessing + pub fn update_postprocess_shader(&mut self, shader: Option<&str>) { + match self { + BackendData::Kms(kms) => kms.update_postprocess_shader(shader), + BackendData::Winit(winit) => winit.update_postprocess_shader(shader), + BackendData::X11(x11) => x11.update_postprocess_shader(shader), + _ => unreachable!("No backend was initialized"), + } + } } pub struct KmsNodes {