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
50 match CustomShaderRenderer::new(
51 cell_renderer.device(),
52 cell_renderer.queue(),
53 crate::custom_shader_renderer::CustomShaderRendererConfig {
54 surface_format: cell_renderer.surface_format(),
55 shader_path: &path,
56 width: size_width,
57 height: size_height,
58 animation_enabled: cursor_shader_animation,
59 animation_speed: cursor_shader_animation_speed,
60 window_opacity,
61 full_content_mode: true, channel_paths: &empty_channels,
63 cubemap_path: None, },
65 ) {
66 Ok(mut renderer) => {
67 let cell_w = cell_renderer.cell_width();
68 let cell_h = cell_renderer.cell_height();
69 renderer.update_cell_dimensions(cell_w, cell_h, window_padding);
70 renderer.set_scale_factor(cell_renderer.scale_factor);
71 log::info!(
72 "[SHADER] Cursor shader renderer initialized from: {} (cell={}x{}, padding={})",
73 path.display(),
74 cell_w,
75 cell_h,
76 window_padding
77 );
78 (Some(renderer), Some(shader_path.to_string()))
79 }
80 Err(e) => {
81 log::info!(
82 "[SHADER] ERROR: Failed to load cursor shader '{}': {}",
83 path.display(),
84 e
85 );
86 (None, None)
87 }
88 }
89}
90
91impl Renderer {
96 pub fn cursor_shader_path(&self) -> Option<&str> {
98 self.cursor_shader_path.as_deref()
99 }
100
101 pub fn reload_cursor_shader_from_source(
103 &mut self,
104 source: &str,
105 ) -> Result<(), crate::error::RenderError> {
106 if let Some(ref mut cursor_shader) = self.cursor_shader_renderer {
107 cursor_shader
108 .reload_from_source(self.cell_renderer.device(), source, "cursor_editor")
109 .map_err(|e| crate::error::RenderError::NoActiveShader(format!("{:#}", e)))?;
110 self.dirty = true;
111 Ok(())
112 } else {
113 Err(crate::error::RenderError::NoActiveShader(
114 "No cursor shader renderer active".to_string(),
115 ))
116 }
117 }
118
119 pub fn set_cursor_shader_enabled(
124 &mut self,
125 enabled: bool,
126 path: Option<&str>,
127 window_opacity: f32,
128 animation_enabled: bool,
129 animation_speed: f32,
130 ) -> Result<(), String> {
131 log::debug!(
132 "[cursor-shader] Toggle: enabled={}, path={:?}, animation={}, speed={}, opacity={}",
133 enabled,
134 path,
135 animation_enabled,
136 animation_speed,
137 window_opacity
138 );
139 match (enabled, path) {
140 (true, Some(path)) => {
141 let path_changed = self.cursor_shader_path.as_ref().is_none_or(|p| p != path);
142
143 if let Some(renderer) = &mut self.cursor_shader_renderer
145 && !path_changed
146 {
147 renderer.set_animation_enabled(animation_enabled);
148 renderer.set_animation_speed(animation_speed);
149 renderer.set_opacity(window_opacity);
150 self.dirty = true;
151 log::info!("[cursor-shader] Already loaded; updated animation/opacities");
152 return Ok(());
153 }
154
155 let shader_path_full = par_term_config::Config::shader_path(path);
156 let empty_channels: [Option<std::path::PathBuf>; 4] = [None, None, None, None];
158 match CustomShaderRenderer::new(
159 self.cell_renderer.device(),
160 self.cell_renderer.queue(),
161 crate::custom_shader_renderer::CustomShaderRendererConfig {
162 surface_format: self.cell_renderer.surface_format(),
163 shader_path: &shader_path_full,
164 width: self.size.width,
165 height: self.size.height,
166 animation_enabled,
167 animation_speed,
168 window_opacity,
169 full_content_mode: true, channel_paths: &empty_channels,
171 cubemap_path: None, },
173 ) {
174 Ok(mut renderer) => {
175 renderer.update_cell_dimensions(
177 self.cell_renderer.cell_width(),
178 self.cell_renderer.cell_height(),
179 self.cell_renderer.window_padding(),
180 );
181 renderer.set_scale_factor(self.cell_renderer.scale_factor);
183 renderer.set_keep_text_opaque(self.cell_renderer.keep_text_opaque());
185 let has_background_shader = self.custom_shader_renderer.is_some();
188
189 if has_background_shader {
190 renderer.set_background_color([0.0, 0.0, 0.0], false);
192 renderer.set_background_texture(self.cell_renderer.device(), None);
193 renderer.update_use_background_as_channel0(
194 self.cell_renderer.device(),
195 false,
196 );
197 } else {
198 renderer.set_background_color(
200 self.cell_renderer.solid_background_color(),
201 self.cell_renderer.is_solid_color_background(),
202 );
203 let is_image_mode = self.cell_renderer.has_background_image()
205 && !self.cell_renderer.is_solid_color_background();
206 if is_image_mode {
207 let bg_texture =
208 self.cell_renderer.get_background_as_channel_texture();
209 renderer.set_background_texture(
210 self.cell_renderer.device(),
211 bg_texture,
212 );
213 renderer.update_use_background_as_channel0(
214 self.cell_renderer.device(),
215 true,
216 );
217 }
218 }
219 log::info!(
220 "[cursor-shader] Enabled at runtime: {}",
221 shader_path_full.display()
222 );
223 self.cursor_shader_renderer = Some(renderer);
224 self.cursor_shader_path = Some(path.to_string());
225 self.dirty = true;
226 Ok(())
227 }
228 Err(e) => {
229 let error_msg = format!(
230 "Failed to load cursor shader '{}': {}",
231 shader_path_full.display(),
232 e
233 );
234 log::error!("[cursor-shader] {}", error_msg);
235 Err(error_msg)
236 }
237 }
238 }
239 _ => {
240 if self.cursor_shader_renderer.is_some() {
241 log::info!("[cursor-shader] Disabled at runtime");
242 } else {
243 log::debug!("[cursor-shader] Already disabled");
244 }
245 self.cursor_shader_renderer = None;
246 self.cursor_shader_path = None;
247 self.dirty = true;
248 Ok(())
249 }
250 }
251 }
252
253 pub(super) fn sync_cursor_shader_background_state(&mut self) {
259 let Some(ref mut cursor_shader) = self.cursor_shader_renderer else {
260 return;
261 };
262
263 let has_background_shader = self.custom_shader_renderer.is_some();
264
265 if has_background_shader {
266 cursor_shader.set_background_color([0.0, 0.0, 0.0], false);
267 cursor_shader.set_background_texture(self.cell_renderer.device(), None);
268 cursor_shader.update_use_background_as_channel0(self.cell_renderer.device(), false);
269 } else {
270 cursor_shader.set_background_color(
271 self.cell_renderer.solid_background_color(),
272 self.cell_renderer.is_solid_color_background(),
273 );
274
275 let is_image_mode = self.cell_renderer.has_background_image()
276 && !self.cell_renderer.is_solid_color_background();
277 if is_image_mode {
278 let bg_texture = self.cell_renderer.get_background_as_channel_texture();
279 cursor_shader.set_background_texture(self.cell_renderer.device(), bg_texture);
280 cursor_shader.update_use_background_as_channel0(self.cell_renderer.device(), true);
281 } else {
282 cursor_shader.set_background_texture(self.cell_renderer.device(), None);
283 cursor_shader.update_use_background_as_channel0(self.cell_renderer.device(), false);
284 }
285 }
286 }
287}