par_term_render/renderer/shaders/
cursor.rs1use super::super::Renderer;
8use super::CursorShaderInitParams;
9use crate::cell_renderer::CellRenderer;
10use crate::custom_shader_renderer::CustomShaderRenderer;
11
12pub(super) fn init_cursor_shader(
16 cell_renderer: &CellRenderer,
17 params: CursorShaderInitParams<'_>,
18) -> (Option<CustomShaderRenderer>, Option<String>) {
19 let CursorShaderInitParams {
20 size_width,
21 size_height,
22 window_padding,
23 path: cursor_shader_path,
24 enabled: cursor_shader_enabled,
25 animation: cursor_shader_animation,
26 animation_speed: cursor_shader_animation_speed,
27 window_opacity,
28 } = params;
29 log::debug!(
30 "[cursor-shader] Init: enabled={}, path={:?}, animation={}, speed={}",
31 cursor_shader_enabled,
32 cursor_shader_path,
33 cursor_shader_animation,
34 cursor_shader_animation_speed
35 );
36
37 if !cursor_shader_enabled {
38 log::info!("[cursor-shader] Disabled by config");
39 return (None, None);
40 }
41
42 let Some(shader_path) = cursor_shader_path else {
43 log::info!("[cursor-shader] Enabled but no path provided");
44 return (None, None);
45 };
46
47 let path = par_term_config::Config::shader_path(shader_path);
48 let empty_channels: [Option<std::path::PathBuf>; 4] = [None, None, None, None];
49 let empty_custom_uniforms = std::collections::BTreeMap::new();
50
51 match CustomShaderRenderer::new(
52 cell_renderer.device(),
53 cell_renderer.queue(),
54 crate::custom_shader_renderer::CustomShaderRendererConfig {
55 surface_format: cell_renderer.surface_format(),
56 shader_path: &path,
57 width: size_width,
58 height: size_height,
59 animation_enabled: cursor_shader_animation,
60 animation_speed: cursor_shader_animation_speed,
61 window_opacity,
62 full_content_mode: true, channel_paths: &empty_channels,
64 cubemap_path: None, custom_uniforms: &empty_custom_uniforms,
66 background_channel0_blend_mode: par_term_config::ShaderBackgroundBlendMode::Replace,
67 },
68 ) {
69 Ok(mut renderer) => {
70 let cell_w = cell_renderer.cell_width();
71 let cell_h = cell_renderer.cell_height();
72 renderer.update_cell_dimensions(cell_w, cell_h, window_padding);
73 renderer.set_scale_factor(cell_renderer.scale_factor);
74 log::info!(
75 "[SHADER] Cursor shader renderer initialized from: {} (cell={}x{}, padding={})",
76 path.display(),
77 cell_w,
78 cell_h,
79 window_padding
80 );
81 (Some(renderer), Some(shader_path.to_string()))
82 }
83 Err(e) => {
84 log::info!(
85 "[SHADER] ERROR: Failed to load cursor shader '{}': {}",
86 path.display(),
87 e
88 );
89 (None, None)
90 }
91 }
92}
93
94impl Renderer {
99 pub fn cursor_shader_path(&self) -> Option<&str> {
101 self.cursor_shader_path.as_deref()
102 }
103
104 pub fn reload_cursor_shader_from_source(
106 &mut self,
107 source: &str,
108 ) -> Result<(), crate::error::RenderError> {
109 if let Some(ref mut cursor_shader) = self.cursor_shader_renderer {
110 cursor_shader
111 .reload_from_source(self.cell_renderer.device(), source, "cursor_editor")
112 .map_err(|e| crate::error::RenderError::NoActiveShader(format!("{:#}", e)))?;
113 self.dirty = true;
114 Ok(())
115 } else {
116 Err(crate::error::RenderError::NoActiveShader(
117 "No cursor shader renderer active".to_string(),
118 ))
119 }
120 }
121
122 pub fn set_cursor_shader_enabled(
127 &mut self,
128 enabled: bool,
129 path: Option<&str>,
130 window_opacity: f32,
131 animation_enabled: bool,
132 animation_speed: f32,
133 ) -> Result<(), String> {
134 log::debug!(
135 "[cursor-shader] Toggle: enabled={}, path={:?}, animation={}, speed={}, opacity={}",
136 enabled,
137 path,
138 animation_enabled,
139 animation_speed,
140 window_opacity
141 );
142 match (enabled, path) {
143 (true, Some(path)) => {
144 let path_changed = self.cursor_shader_path.as_ref().is_none_or(|p| p != path);
145
146 if let Some(renderer) = &mut self.cursor_shader_renderer
148 && !path_changed
149 {
150 renderer.set_animation_enabled(animation_enabled);
151 renderer.set_animation_speed(animation_speed);
152 renderer.set_opacity(window_opacity);
153 self.dirty = true;
154 log::info!("[cursor-shader] Already loaded; updated animation/opacities");
155 return Ok(());
156 }
157
158 let shader_path_full = par_term_config::Config::shader_path(path);
159 let empty_channels: [Option<std::path::PathBuf>; 4] = [None, None, None, None];
161 let empty_custom_uniforms = std::collections::BTreeMap::new();
162 match CustomShaderRenderer::new(
163 self.cell_renderer.device(),
164 self.cell_renderer.queue(),
165 crate::custom_shader_renderer::CustomShaderRendererConfig {
166 surface_format: self.cell_renderer.surface_format(),
167 shader_path: &shader_path_full,
168 width: self.size.width,
169 height: self.size.height,
170 animation_enabled,
171 animation_speed,
172 window_opacity,
173 full_content_mode: true, channel_paths: &empty_channels,
175 cubemap_path: None, custom_uniforms: &empty_custom_uniforms,
177 background_channel0_blend_mode:
178 par_term_config::ShaderBackgroundBlendMode::Replace,
179 },
180 ) {
181 Ok(mut renderer) => {
182 renderer.update_cell_dimensions(
184 self.cell_renderer.cell_width(),
185 self.cell_renderer.cell_height(),
186 self.cell_renderer.window_padding(),
187 );
188 renderer.set_scale_factor(self.cell_renderer.scale_factor);
190 renderer.set_keep_text_opaque(self.cell_renderer.keep_text_opaque());
192 let has_background_shader = self.custom_shader_renderer.is_some();
195
196 if has_background_shader {
197 renderer.set_background_color([0.0, 0.0, 0.0], false);
199 renderer.set_background_texture(self.cell_renderer.device(), None);
200 renderer.update_use_background_as_channel0(
201 self.cell_renderer.device(),
202 false,
203 );
204 } else {
205 renderer.set_background_color(
207 self.cell_renderer.solid_background_color(),
208 self.cell_renderer.is_solid_color_background(),
209 );
210 let is_image_mode = self.cell_renderer.has_background_image()
212 && !self.cell_renderer.is_solid_color_background();
213 if is_image_mode {
214 let bg_texture =
215 self.cell_renderer.get_background_as_channel_texture();
216 renderer.set_background_texture(
217 self.cell_renderer.device(),
218 bg_texture,
219 );
220 renderer.update_use_background_as_channel0(
221 self.cell_renderer.device(),
222 true,
223 );
224 }
225 }
226 log::info!(
227 "[cursor-shader] Enabled at runtime: {}",
228 shader_path_full.display()
229 );
230 self.cursor_shader_renderer = Some(renderer);
231 self.cursor_shader_path = Some(path.to_string());
232 self.dirty = true;
233 Ok(())
234 }
235 Err(e) => {
236 let error_msg = format!(
237 "Failed to load cursor shader '{}': {}",
238 shader_path_full.display(),
239 e
240 );
241 log::error!("[cursor-shader] {}", error_msg);
242 Err(error_msg)
243 }
244 }
245 }
246 _ => {
247 if self.cursor_shader_renderer.is_some() {
248 log::info!("[cursor-shader] Disabled at runtime");
249 } else {
250 log::debug!("[cursor-shader] Already disabled");
251 }
252 self.cursor_shader_renderer = None;
253 self.cursor_shader_path = None;
254 self.dirty = true;
255 Ok(())
256 }
257 }
258 }
259
260 pub(super) fn sync_cursor_shader_background_state(&mut self) {
266 let Some(ref mut cursor_shader) = self.cursor_shader_renderer else {
267 return;
268 };
269
270 let has_background_shader = self.custom_shader_renderer.is_some();
271
272 if has_background_shader {
273 cursor_shader.set_background_color([0.0, 0.0, 0.0], false);
274 cursor_shader.set_background_texture(self.cell_renderer.device(), None);
275 cursor_shader.update_use_background_as_channel0(self.cell_renderer.device(), false);
276 } else {
277 cursor_shader.set_background_color(
278 self.cell_renderer.solid_background_color(),
279 self.cell_renderer.is_solid_color_background(),
280 );
281
282 let is_image_mode = self.cell_renderer.has_background_image()
283 && !self.cell_renderer.is_solid_color_background();
284 if is_image_mode {
285 let bg_texture = self.cell_renderer.get_background_as_channel_texture();
286 cursor_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
287 cursor_shader.update_use_background_as_channel0(self.cell_renderer.device(), true);
288 } else {
289 cursor_shader.set_background_texture(self.cell_renderer.device(), None);
290 cursor_shader.update_use_background_as_channel0(self.cell_renderer.device(), false);
291 }
292 }
293 }
294}