use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; use smithay::backend::renderer::gles::{ GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform, }; use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions}; use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform}; use super::texture::TextureRenderElement; use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; use crate::render_helpers::renderer::AsGlesFrame as _; use crate::render_helpers::shaders::Shaders; #[derive(Debug, Clone)] pub struct GradientFadeTextureRenderElement { inner: TextureRenderElement, program: GradientFadeShader, uniforms: Vec>, } #[derive(Debug, Clone)] pub struct GradientFadeShader(GlesTexProgram); impl GradientFadeTextureRenderElement { pub fn new(texture: TextureRenderElement, program: GradientFadeShader) -> Self { let logical_w = texture.buffer().logical_size().w; let logical_src_w = texture.logical_src().size.w; let cutoff = if logical_src_w < logical_w { // Texture is clipped, add a fade. let cutoff = 1. - f64::min(18. / logical_src_w, 1.); let full = logical_src_w / logical_w; ((cutoff * full) as f32, full as f32) } else { // Texture is displayed full-size, no cutoff necessary. (1., 1.) }; let uniforms = vec![Uniform::new("cutoff", cutoff)]; Self { inner: texture, program, uniforms, } } pub fn shader(renderer: &mut GlesRenderer) -> Option { let program = Shaders::get(renderer).gradient_fade.clone(); program.map(GradientFadeShader) } } impl Element for GradientFadeTextureRenderElement { fn id(&self) -> &Id { self.inner.id() } fn current_commit(&self) -> CommitCounter { self.inner.current_commit() } fn geometry(&self, scale: Scale) -> Rectangle { self.inner.geometry(scale) } fn transform(&self) -> Transform { self.inner.transform() } fn src(&self) -> Rectangle { self.inner.src() } fn damage_since( &self, scale: Scale, commit: Option, ) -> DamageSet { self.inner.damage_since(scale, commit) } fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { self.inner.opaque_regions(scale) } fn alpha(&self) -> f32 { self.inner.alpha() } fn kind(&self) -> Kind { self.inner.kind() } } impl RenderElement for GradientFadeTextureRenderElement { fn draw( &self, frame: &mut GlesFrame<'_, '_>, src: Rectangle, dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], ) -> Result<(), GlesError> { frame.override_default_tex_program(self.program.0.clone(), self.uniforms.clone()); RenderElement::::draw(&self.inner, frame, src, dst, damage, opaque_regions)?; frame.clear_tex_program_override(); Ok(()) } fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option> { // If scanout for things other than Wayland buffers is implemented, this will need to take // the target GPU into account. None } } impl<'render> RenderElement> for GradientFadeTextureRenderElement { fn draw( &self, frame: &mut TtyFrame<'render, '_, '_>, src: Rectangle, dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], ) -> Result<(), TtyRendererError<'render>> { let gles_frame = frame.as_gles_frame(); RenderElement::::draw(&self, gles_frame, src, dst, damage, opaque_regions)?; Ok(()) } fn underlying_storage( &self, _renderer: &mut TtyRenderer<'render>, ) -> Option> { // If scanout for things other than Wayland buffers is implemented, this will need to take // the target GPU into account. None } }