diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 5a51f37e..e53e3cf9 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -48,7 +48,7 @@ use smithay::{ damage::{Error as RenderError, OutputDamageTracker, OutputNoMode}, element::{ utils::{Relocate, RelocateRenderElement}, - AsRenderElements, Element, RenderElement, RenderElementStates, + AsRenderElements, Element, Id, RenderElement, RenderElementStates, }, gles::{ element::PixelShaderElement, GlesError, GlesPixelProgram, GlesRenderer, Uniform, @@ -61,7 +61,7 @@ use smithay::{ }, desktop::layer_map_for_output, output::Output, - utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Size}, + utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale}, wayland::{ dmabuf::get_dmabuf, shell::wlr_layer::Layer, @@ -85,41 +85,43 @@ pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; pub static ACTIVE_GROUP_COLOR: [f32; 3] = [0.678, 0.635, 0.619]; pub static GROUP_COLOR: [f32; 3] = [0.431, 0.404, 0.396]; pub static FOCUS_INDICATOR_COLOR: [f32; 3] = [0.580, 0.921, 0.921]; -pub static FOCUS_INDICATOR_SHADER: &str = include_str!("./shaders/focus_indicator.frag"); + +pub static OUTLINE_SHADER: &str = include_str!("./shaders/rounded_outline.frag"); +pub static RECTANGLE_SHADER: &str = include_str!("./shaders/rounded_rectangle.frag"); pub struct IndicatorShader(pub GlesPixelProgram); #[derive(Clone)] -pub enum Key { +pub enum IndicatorKey { Group(Weak<()>), Window(CosmicMapped), } -impl std::hash::Hash for Key { +impl std::hash::Hash for IndicatorKey { fn hash(&self, state: &mut H) { match self { - Key::Group(arc) => (arc.as_ptr() as usize).hash(state), - Key::Window(window) => window.hash(state), + IndicatorKey::Group(arc) => (arc.as_ptr() as usize).hash(state), + IndicatorKey::Window(window) => window.hash(state), } } } -impl PartialEq for Key { +impl PartialEq for IndicatorKey { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Key::Group(g1), Key::Group(g2)) => Weak::ptr_eq(g1, g2), - (Key::Window(w1), Key::Window(w2)) => w1 == w2, + (IndicatorKey::Group(g1), IndicatorKey::Group(g2)) => Weak::ptr_eq(g1, g2), + (IndicatorKey::Window(w1), IndicatorKey::Window(w2)) => w1 == w2, _ => false, } } } -impl Eq for Key {} -impl From for Key { +impl Eq for IndicatorKey {} +impl From for IndicatorKey { fn from(window: CosmicMapped) -> Self { - Key::Window(window) + IndicatorKey::Window(window) } } -impl From for Key { +impl From for IndicatorKey { fn from(group: WindowGroup) -> Self { - Key::Group(group.alive.clone()) + IndicatorKey::Group(group.alive.clone()) } } @@ -129,7 +131,7 @@ struct IndicatorSettings { alpha: f32, color: [f32; 3], } -type IndicatorCache = RefCell>; +type IndicatorCache = RefCell>; impl IndicatorShader { pub fn get(renderer: &R) -> GlesPixelProgram { @@ -144,7 +146,7 @@ impl IndicatorShader { pub fn focus_element( renderer: &R, - key: impl Into, + key: impl Into, mut element_geo: Rectangle, thickness: u8, alpha: f32, @@ -159,7 +161,7 @@ impl IndicatorShader { pub fn element( renderer: &R, - key: impl Into, + key: impl Into, geo: Rectangle, thickness: u8, alpha: f32, @@ -178,8 +180,8 @@ impl IndicatorShader { user_data.insert_if_missing(|| IndicatorCache::new(HashMap::new())); let mut cache = user_data.get::().unwrap().borrow_mut(); cache.retain(|k, _| match k { - Key::Group(w) => w.upgrade().is_some(), - Key::Window(w) => w.alive(), + IndicatorKey::Group(w) => w.upgrade().is_some(), + IndicatorKey::Window(w) => w.alive(), }); let key = key.into(); @@ -213,23 +215,139 @@ impl IndicatorShader { } } +pub struct BackdropShader(pub GlesPixelProgram); + +#[derive(Clone)] +pub enum BackdropKey { + Static(Id), + Window(CosmicMapped), +} +impl std::hash::Hash for BackdropKey { + fn hash(&self, state: &mut H) { + match self { + BackdropKey::Static(id) => id.hash(state), + BackdropKey::Window(window) => window.hash(state), + } + } +} +impl PartialEq for BackdropKey { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (BackdropKey::Static(s1), BackdropKey::Static(s2)) => s1 == s2, + (BackdropKey::Window(w1), BackdropKey::Window(w2)) => w1 == w2, + _ => false, + } + } +} +impl Eq for BackdropKey {} +impl From for BackdropKey { + fn from(window: CosmicMapped) -> Self { + BackdropKey::Window(window) + } +} +impl From for BackdropKey { + fn from(id: Id) -> Self { + BackdropKey::Static(id) + } +} + +#[derive(PartialEq)] +struct BackdropSettings { + radius: f32, + alpha: f32, + color: [f32; 3], +} +type BackdropCache = RefCell>; + +impl BackdropShader { + pub fn get(renderer: &R) -> GlesPixelProgram { + Borrow::::borrow(renderer.glow_renderer()) + .egl_context() + .user_data() + .get::() + .expect("Custom Shaders not initialized") + .0 + .clone() + } + + pub fn element( + renderer: &R, + key: impl Into, + geo: Rectangle, + radius: f32, + alpha: f32, + color: [f32; 3], + ) -> PixelShaderElement { + let settings = BackdropSettings { + radius, + alpha, + color, + }; + + let user_data = Borrow::::borrow(renderer.glow_renderer()) + .egl_context() + .user_data(); + + user_data.insert_if_missing(|| BackdropCache::new(HashMap::new())); + let mut cache = user_data.get::().unwrap().borrow_mut(); + cache.retain(|k, _| match k { + BackdropKey::Static(_) => true, + BackdropKey::Window(w) => w.alive(), + }); + + let key = key.into(); + if cache + .get(&key) + .filter(|(old_settings, _)| &settings == old_settings) + .is_none() + { + let shader = Self::get(renderer); + + let elem = PixelShaderElement::new( + shader, + geo, + None, // TODO + alpha, + vec![Uniform::new("color", color), Uniform::new("radius", radius)], + ); + cache.insert(key.clone(), (settings, elem)); + } + + let elem = &mut cache.get_mut(&key).unwrap().1; + if elem.geometry(1.0.into()).to_logical(1) != geo { + elem.resize(geo, None); + } + elem.clone() + } +} + pub fn init_shaders(renderer: &mut R) -> Result<(), GlesError> { let glow_renderer = renderer.glow_renderer_mut(); let gles_renderer: &mut GlesRenderer = glow_renderer.borrow_mut(); - let indicator_shader = gles_renderer.compile_custom_pixel_shader( - FOCUS_INDICATOR_SHADER, + let outline_shader = gles_renderer.compile_custom_pixel_shader( + OUTLINE_SHADER, &[ UniformName::new("color", UniformType::_3f), UniformName::new("thickness", UniformType::_1f), UniformName::new("radius", UniformType::_1f), ], )?; + let rectangle_shader = gles_renderer.compile_custom_pixel_shader( + RECTANGLE_SHADER, + &[ + UniformName::new("color", UniformType::_3f), + UniformName::new("radius", UniformType::_1f), + ], + )?; let egl_context = gles_renderer.egl_context(); egl_context .user_data() - .insert_if_missing(|| IndicatorShader(indicator_shader)); + .insert_if_missing(|| IndicatorShader(outline_shader)); + egl_context + .user_data() + .insert_if_missing(|| BackdropShader(rectangle_shader)); Ok(()) } diff --git a/src/backend/render/shaders/focus_indicator.frag b/src/backend/render/shaders/rounded_outline.frag similarity index 88% rename from src/backend/render/shaders/focus_indicator.frag rename to src/backend/render/shaders/rounded_outline.frag index f94aa116..749748f2 100644 --- a/src/backend/render/shaders/focus_indicator.frag +++ b/src/backend/render/shaders/rounded_outline.frag @@ -19,7 +19,7 @@ void main() { vec2 location = v_coords * size; vec4 mix_color; - float distance = rounded_box(location - center, size / 2.0 - vec2(thickness / 2.0), radius); + float distance = rounded_box(location - center, (size / 2.0) - (thickness / 2.0), radius); float smoothedAlpha = 1.0 - smoothstep(0.0, 1.0, abs(distance) - (thickness / 2.0)); mix_color = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(color, alpha), smoothedAlpha); diff --git a/src/backend/render/shaders/rounded_rectangle.frag b/src/backend/render/shaders/rounded_rectangle.frag new file mode 100644 index 00000000..30644f51 --- /dev/null +++ b/src/backend/render/shaders/rounded_rectangle.frag @@ -0,0 +1,32 @@ +precision mediump float; +uniform float alpha; +#if defined(DEBUG_FLAGS) +uniform float tint; +#endif +uniform vec2 size; +varying vec2 v_coords; + +uniform vec3 color; +uniform float radius; + +float rounded_box(vec2 center, vec2 size, float radius) { + return length(max(abs(center) - size + radius, 0.0)) - radius; +} + +void main() { + vec2 center = size / 2.0; + vec2 location = v_coords * size; + vec4 mix_color; + + float distance = rounded_box(location - center, size / 2.0, radius); + float smoothedAlpha = 1.0 - smoothstep(0.0, 1.0, distance); + + mix_color = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(color, alpha), smoothedAlpha); + +#if defined(DEBUG_FLAGS) + if (tint == 1.0) + mix_color = vec4(0.0, 0.3, 0.0, 0.2) + mix_color * 0.8; +#endif + + gl_FragColor = mix_color; +} \ No newline at end of file diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 2ee47c93..4bf7f02f 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ element::{AsGlowFrame, AsGlowRenderer}, - GlMultiError, GlMultiFrame, GlMultiRenderer, + BackdropShader, GlMultiError, GlMultiFrame, GlMultiRenderer, }, shell::{ layout::{ @@ -26,17 +26,14 @@ use crate::{ use calloop::LoopHandle; use indexmap::IndexSet; use smithay::{ - backend::{ - allocator::Fourcc, - renderer::{ - element::{ - surface::WaylandSurfaceRenderElement, texture::TextureRenderElement, - AsRenderElements, Element, Id, RenderElement, - }, - gles::{GlesError, GlesTexture}, - glow::{GlowFrame, GlowRenderer}, - ImportAll, ImportMem, Renderer, + backend::renderer::{ + element::{ + surface::WaylandSurfaceRenderElement, texture::TextureRenderElement, AsRenderElements, + Element, Id, RenderElement, }, + gles::{GlesError, GlesTexture}, + glow::{GlowFrame, GlowRenderer}, + ImportAll, ImportMem, Renderer, }, desktop::{layer_map_for_output, space::SpaceElement}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, @@ -66,6 +63,7 @@ pub struct Workspace { pub focus_stack: FocusStacks, pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, pub screencopy_sessions: Vec, + pub(super) backdrop_id: Id, } #[derive(Debug, Default)] @@ -88,6 +86,7 @@ impl Workspace { focus_stack: FocusStacks::default(), pending_buffers: Vec::new(), screencopy_sessions: Vec::new(), + backdrop_id: Id::new(), } } @@ -487,6 +486,7 @@ impl Workspace { let output_scale = output.current_scale().fractional_scale(); let layer_map = layer_map_for_output(output); + let zone = layer_map.non_exclusive_zone(); if let Some(fullscreen) = self.fullscreen.get(output) { render_elements.extend( @@ -609,53 +609,28 @@ impl Workspace { } } - if let OverviewMode::Started(_, start) = overview { - let alpha = Instant::now().duration_since(start).as_millis() as f64 / 100.0; - - #[derive(Clone)] - struct BackdropTexture(Id, GlesTexture); - - if renderer - .glow_renderer() - .egl_context() - .user_data() - .get::() - .is_none() - { - let tex = BackdropTexture( - Id::new(), - renderer - .glow_renderer_mut() - .import_memory(&[0, 0, 0, 255], Fourcc::Abgr8888, (1, 1).into(), false) - .unwrap(), - ); - renderer - .glow_renderer() - .egl_context() - .user_data() - .insert_if_missing(|| tex); - }; - let BackdropTexture(id, tex) = renderer - .glow_renderer() - .egl_context() - .user_data() - .get::() - .unwrap() - .clone(); + let alpha = match overview { + OverviewMode::Started(_, start) => Some( + (Instant::now().duration_since(start).as_millis() as f64 / 100.0).min(1.0) + as f32, + ), + OverviewMode::Ended(ended) => Some( + 1.0 - (Instant::now().duration_since(ended).as_millis() as f64 / 100.0).min(1.0) + as f32, + ), + _ => None, + }; + if let Some(alpha) = alpha { render_elements.push( - TextureRenderElement::from_static_texture( - id, - renderer.id(), - (0.0, 0.0), - tex, - 1, - smithay::utils::Transform::Normal, - Some(alpha.min(0.8) as f32), - Some(Rectangle::from_loc_and_size((0., 0.), (1., 1.))), - Some(output.geometry().size), - None, - ) + Into::>::into(BackdropShader::element( + renderer, + self.backdrop_id.clone(), + zone, + 0., + alpha * 0.65, + [0.0, 0.0, 0.0], + )) .into(), ) }