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