diff options
| -rw-r--r-- | Cargo.lock | 58 | ||||
| -rw-r--r-- | niri-config/Cargo.toml | 1 | ||||
| -rw-r--r-- | niri-config/src/lib.rs | 56 | ||||
| -rw-r--r-- | niri-visual-tests/src/cases/layout.rs | 2 | ||||
| -rw-r--r-- | niri-visual-tests/src/cases/tile.rs | 8 | ||||
| -rw-r--r-- | resources/default-config.kdl | 21 | ||||
| -rw-r--r-- | src/backend/tty.rs | 3 | ||||
| -rw-r--r-- | src/backend/winit.rs | 10 | ||||
| -rw-r--r-- | src/layout/focus_ring.rs | 101 | ||||
| -rw-r--r-- | src/layout/tile.rs | 22 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 2 | ||||
| -rw-r--r-- | src/render_helpers/gradient.rs | 143 | ||||
| -rw-r--r-- | src/render_helpers/mod.rs | 3 | ||||
| -rw-r--r-- | src/render_helpers/primary_gpu_pixel_shader.rs | 97 | ||||
| -rw-r--r-- | src/render_helpers/shaders/gradient_border.frag | 46 | ||||
| -rw-r--r-- | src/render_helpers/shaders/mod.rs | 47 |
16 files changed, 584 insertions, 36 deletions
@@ -763,6 +763,15 @@ dependencies = [ ] [[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "phf", +] + +[[package]] name = "cursor-icon" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2128,6 +2137,7 @@ name = "niri-config" version = "0.1.2" dependencies = [ "bitflags 2.4.2", + "csscolorparser", "knuffel", "miette", "niri-ipc", @@ -2409,6 +2419,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2990,6 +3042,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/niri-config/Cargo.toml b/niri-config/Cargo.toml index 944c8b1e..fed9993e 100644 --- a/niri-config/Cargo.toml +++ b/niri-config/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true [dependencies] bitflags.workspace = true +csscolorparser = "0.6.2" knuffel = "3.2.0" miette = "5.10.0" niri-ipc = { version = "0.1.2", path = "../niri-ipc" } diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index fdf3d700..77642813 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -340,6 +340,10 @@ pub struct FocusRing { pub active_color: Color, #[knuffel(child, default = Self::default().inactive_color)] pub inactive_color: Color, + #[knuffel(child)] + pub active_gradient: Option<Gradient>, + #[knuffel(child)] + pub inactive_gradient: Option<Gradient>, } impl Default for FocusRing { @@ -349,11 +353,32 @@ impl Default for FocusRing { width: 4, active_color: Color::new(127, 200, 255, 255), inactive_color: Color::new(80, 80, 80, 255), + active_gradient: None, + inactive_gradient: None, } } } #[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)] +pub struct Gradient { + #[knuffel(property, str)] + pub from: Color, + #[knuffel(property, str)] + pub to: Color, + #[knuffel(property, default = 180)] + pub angle: i16, + #[knuffel(property, default)] + pub relative_to: GradientRelativeTo, +} + +#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)] +pub enum GradientRelativeTo { + #[default] + Window, + WorkspaceView, +} + +#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)] pub struct Border { #[knuffel(child)] pub off: bool, @@ -363,6 +388,10 @@ pub struct Border { pub active_color: Color, #[knuffel(child, default = Self::default().inactive_color)] pub inactive_color: Color, + #[knuffel(child)] + pub active_gradient: Option<Gradient>, + #[knuffel(child)] + pub inactive_gradient: Option<Gradient>, } impl Default for Border { @@ -372,6 +401,8 @@ impl Default for Border { width: 4, active_color: Color::new(255, 200, 127, 255), inactive_color: Color::new(80, 80, 80, 255), + active_gradient: None, + inactive_gradient: None, } } } @@ -383,6 +414,8 @@ impl From<Border> for FocusRing { width: value.width, active_color: value.active_color, inactive_color: value.inactive_color, + active_gradient: value.active_gradient, + inactive_gradient: value.inactive_gradient, } } } @@ -790,6 +823,15 @@ impl Default for Config { } } +impl FromStr for Color { + type Err = miette::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let [r, g, b, a] = csscolorparser::parse(s).into_diagnostic()?.to_rgba8(); + Ok(Self { r, g, b, a }) + } +} + impl FromStr for Mode { type Err = miette::Error; @@ -909,7 +951,7 @@ mod tests { #[test] fn parse() { check( - r#" + r##" input { keyboard { repeat-delay 600 @@ -961,6 +1003,7 @@ mod tests { width 5 active-color 0 100 200 255 inactive-color 255 200 100 0 + active-gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view" } border { @@ -1034,7 +1077,7 @@ mod tests { debug { render-drm-device "/dev/dri/renderD129" } - "#, + "##, Config { input: Input { keyboard: Keyboard { @@ -1099,6 +1142,13 @@ mod tests { b: 100, a: 0, }, + active_gradient: Some(Gradient { + from: Color::new(10, 20, 30, 255), + to: Color::new(0, 128, 255, 255), + angle: 180, + relative_to: GradientRelativeTo::WorkspaceView, + }), + inactive_gradient: None, }, border: Border { off: false, @@ -1115,6 +1165,8 @@ mod tests { b: 100, a: 0, }, + active_gradient: None, + inactive_gradient: None, }, preset_column_widths: vec![ PresetWidth::Proportion(0.25), diff --git a/niri-visual-tests/src/cases/layout.rs b/niri-visual-tests/src/cases/layout.rs index dd9e29ec..ca8edcec 100644 --- a/niri-visual-tests/src/cases/layout.rs +++ b/niri-visual-tests/src/cases/layout.rs @@ -51,6 +51,8 @@ impl Layout { width: 4, active_color: Color::new(255, 163, 72, 255), inactive_color: Color::new(50, 50, 50, 255), + active_gradient: None, + inactive_gradient: None, }, ..Default::default() }; diff --git a/niri-visual-tests/src/cases/tile.rs b/niri-visual-tests/src/cases/tile.rs index ff0af227..2420314f 100644 --- a/niri-visual-tests/src/cases/tile.rs +++ b/niri-visual-tests/src/cases/tile.rs @@ -104,7 +104,13 @@ impl TestCase for Tile { let location = Point::from(((size.w - tile_size.w) / 2, (size.h - tile_size.h) / 2)); self.tile - .render(renderer, location, Scale::from(1.), true) + .render( + renderer, + location, + Scale::from(1.), + size.to_logical(1), + true, + ) .map(|elem| Box::new(elem) as _) .collect() } diff --git a/resources/default-config.kdl b/resources/default-config.kdl index dd4671fd..9cca2f8c 100644 --- a/resources/default-config.kdl +++ b/resources/default-config.kdl @@ -118,6 +118,24 @@ layout { // Color of the ring on inactive monitors: red, green, blue, alpha. inactive-color 80 80 80 255 + + // You can also use gradients. They take precedence over solid colors. + // Gradients are rendered the same as CSS linear-gradient(angle, from, to). + // Colors can be set in a variety of ways here: + // - CSS named colors: from="red" + // - RGB hex: from="#rgb", from="#rgba", from="#rrggbb", from="#rrggbbaa" + // - CSS-like notation: from="rgb(255, 127, 0)", rgba(), hsl() and a few others. + // The angle is the same as in linear-gradient, and is optional, + // defaulting to 180 (top-to-bottom gradient). + // You can use any CSS linear-gradient tool on the web to set these up. + // + // active-gradient from="#80c8ff" to="#bbddff" angle=45 + + // You can also color the gradient relative to the entire view + // of the workspace, rather than relative to just the window itself. + // To do that, set relative-to="workspace-view". + // + // inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" } // You can also add a border. It's similar to the focus ring, but always visible. @@ -129,6 +147,9 @@ layout { width 4 active-color 255 200 127 255 inactive-color 80 80 80 255 + + // active-gradient from="#ffbb66" to="#ffc880" angle=45 relative-to="workspace-view" + // inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" } // You can customize the widths that "switch-preset-column-width" (Mod+R) toggles between. diff --git a/src/backend/tty.rs b/src/backend/tty.rs index a0a50dd1..1f0cdb16 100644 --- a/src/backend/tty.rs +++ b/src/backend/tty.rs @@ -53,6 +53,7 @@ use super::RenderResult; use crate::frame_clock::FrameClock; use crate::niri::{Niri, RedrawState, State}; use crate::render_helpers::renderer::AsGlesRenderer; +use crate::render_helpers::shaders; use crate::utils::get_monotonic_time; const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888]; @@ -443,6 +444,8 @@ impl Tty { renderer.bind_wl_display(&niri.display_handle)?; + shaders::init(renderer.as_gles_renderer()); + // Create the dmabuf global. let primary_formats = renderer.dmabuf_formats().collect::<HashSet<_>>(); let default_feedback = diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 7f35e153..e9413698 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -19,6 +19,7 @@ use smithay::reexports::winit::window::WindowBuilder; use super::RenderResult; use crate::niri::{Niri, RedrawState, State}; +use crate::render_helpers::shaders; use crate::utils::get_monotonic_time; pub struct Winit { @@ -123,14 +124,13 @@ impl Winit { } pub fn init(&mut self, niri: &mut Niri) { - if let Err(err) = self - .backend - .renderer() - .bind_wl_display(&niri.display_handle) - { + let renderer = self.backend.renderer(); + if let Err(err) = renderer.bind_wl_display(&niri.display_handle) { warn!("error binding renderer wl_display: {err}"); } + shaders::init(renderer); + niri.add_output(self.output.clone(), None); } diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs index 0addd204..24013510 100644 --- a/src/layout/focus_ring.rs +++ b/src/layout/focus_ring.rs @@ -1,26 +1,41 @@ use std::iter::zip; use arrayvec::ArrayVec; -use niri_config; +use niri_config::{self, GradientRelativeTo}; use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; use smithay::backend::renderer::element::Kind; -use smithay::utils::{Logical, Point, Scale, Size}; +use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; + +use crate::niri_render_elements; +use crate::render_helpers::gradient::GradientRenderElement; +use crate::render_helpers::renderer::NiriRenderer; #[derive(Debug)] pub struct FocusRing { buffers: [SolidColorBuffer; 4], locations: [Point<i32, Logical>; 4], + sizes: [Size<i32, Logical>; 4], + full_size: Size<i32, Logical>, + is_active: bool, is_border: bool, config: niri_config::FocusRing, } -pub type FocusRingRenderElement = SolidColorRenderElement; +niri_render_elements! { + FocusRingRenderElement => { + SolidColor = SolidColorRenderElement, + Gradient = GradientRenderElement, + } +} impl FocusRing { pub fn new(config: niri_config::FocusRing) -> Self { Self { buffers: Default::default(), locations: Default::default(), + sizes: Default::default(), + full_size: Default::default(), + is_active: false, is_border: false, config, } @@ -32,20 +47,25 @@ impl FocusRing { pub fn update(&mut self, win_size: Size<i32, Logical>, is_border: bool) { let width = i32::from(self.config.width); + self.full_size = win_size + Size::from((width * 2, width * 2)); if is_border { - self.buffers[0].resize((win_size.w + width * 2, width)); - self.buffers[1].resize((win_size.w + width * 2, width)); - self.buffers[2].resize((width, win_size.h)); - self.buffers[3].resize((width, win_size.h)); + self.sizes[0] = Size::from((win_size.w + width * 2, width)); + self.sizes[1] = Size::from((win_size.w + width * 2, width)); + self.sizes[2] = Size::from((width, win_size.h)); + self.sizes[3] = Size::from((width, win_size.h)); + + for (buf, size) in zip(&mut self.buffers, self.sizes) { + buf.resize(size); + } self.locations[0] = Point::from((-width, -width)); self.locations[1] = Point::from((-width, win_size.h)); self.locations[2] = Point::from((-width, 0)); self.locations[3] = Point::from((win_size.w, 0)); } else { - let size = win_size + Size::from((width * 2, width * 2)); - self.buffers[0].resize(size); + self.sizes[0] = self.full_size; + self.buffers[0].resize(self.sizes[0]); self.locations[0] = Point::from((-width, -width)); } @@ -62,12 +82,16 @@ impl FocusRing { for buf in &mut self.buffers { buf.set_color(color); } + + self.is_active = is_active; } - pub fn render( + pub fn render<R: NiriRenderer>( &self, + renderer: &mut R, location: Point<i32, Logical>, scale: Scale<f64>, + view_size: Size<i32, Logical>, ) -> impl Iterator<Item = FocusRingRenderElement> { let mut rv = ArrayVec::<_, 4>::new(); @@ -75,23 +99,56 @@ impl FocusRing { return rv.into_iter(); } - let mut push = |buffer, location: Point<i32, Logical>| { - let elem = SolidColorRenderElement::from_buffer( - buffer, - location.to_physical_precise_round(scale), - scale, - 1., - Kind::Unspecified, - ); - rv.push(elem.into()); + let gradient = if self.is_active { + self.config.active_gradient + } else { + self.config.inactive_gradient + }; + + let full_rect = Rectangle::from_loc_and_size(location + self.locations[0], self.full_size); + let view_rect = Rectangle::from_loc_and_size((0, 0), view_size); + + let mut push = |buffer, location: Point<i32, Logical>, size: Size<i32, Logical>| { + let elem = gradient.and_then(|gradient| { + let gradient_area = match gradient.relative_to { + GradientRelativeTo::Window => full_rect, + GradientRelativeTo::WorkspaceView => view_rect, + }; + GradientRenderElement::new( + renderer, + scale, + Rectangle::from_loc_and_size(location, size), + gradient_area, + gradient.from.into(), + gradient.to.into(), + ((gradient.angle as f32) - 90.).to_radians(), + ) + .map(Into::into) + }); + + let elem = elem.unwrap_or_else(|| { + SolidColorRenderElement::from_buffer( + buffer, + location.to_physical_precise_round(scale), + scale, + 1., + Kind::Unspecified, + ) + .into() + }); + rv.push(elem); }; if self.is_border { - for (buf, loc) in zip(&self.buffers, self.locations) { - push(buf, location + loc); + for (buf, (loc, size)) in zip(&self.buffers, zip(self.locations, self.sizes)) { + push(buf, location + loc, size); } } else { - push(&self.buffers[0], location + self.locations[0]); + push( + &self.buffers[0], + location + self.locations[0], + self.sizes[0], + ); } rv.into_iter() diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 28bcfb47..ca2779fe 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -7,7 +7,7 @@ use smithay::backend::renderer::element::utils::RescaleRenderElement; use smithay::backend::renderer::element::{Element, Kind}; use smithay::utils::{Logical, Point, Rectangle, Scale, Size}; -use super::focus_ring::FocusRing; +use super::focus_ring::{FocusRing, FocusRingRenderElement}; use super::{LayoutElement, LayoutElementRenderElement, Options}; use crate::animation::Animation; use crate::niri_render_elements; @@ -51,6 +51,7 @@ pub struct Tile<W: LayoutElement> { niri_render_elements! { TileRenderElement<R> => { LayoutElement = LayoutElementRenderElement<R>, + FocusRing = FocusRingRenderElement, SolidColor = SolidColorRenderElement, Offscreen = RescaleRenderElement<OffscreenRenderElement>, } @@ -297,6 +298,7 @@ impl<W: LayoutElement> Tile<W> { renderer: &mut R, location: Point<i32, Logical>, scale: Scale<f64>, + view_size: Size<i32, Logical>, focus_ring: bool, ) -> impl Iterator<Item = TileRenderElement<R>> { let rv = self @@ -307,12 +309,21 @@ impl<W: LayoutElement> Tile<W> { let elem = self.effective_border_width().map(|width| { self.border - .render(location + Point::from((width, width)), scale) + .render( + renderer, + location + Point::from((width, width)), + scale, + view_size, + ) .map(Into::into) }); let rv = rv.chain(elem.into_iter().flatten()); - let elem = focus_ring.then(|| self.focus_ring.render(location, scale).map(Into::into)); + let elem = focus_ring.then(|| { + self.focus_ring + .render(renderer, location, scale, view_size) + .map(Into::into) + }); let rv = rv.chain(elem.into_iter().flatten()); let elem = self.is_fullscreen.then(|| { @@ -333,11 +344,12 @@ impl<W: LayoutElement> Tile<W> { renderer: &mut R, location: Point<i32, Logical>, scale: Scale<f64>, + view_size: Size<i32, Logical>, focus_ring: bool, ) -> impl Iterator<Item = TileRenderElement<R>> { if let Some(anim) = &self.open_animation { let renderer = renderer.as_gles_renderer(); - let elements = self.render_inner(renderer, location, scale, focus_ring); + let elements = self.render_inner(renderer, location, scale, view_size, focus_ring); let elements = elements.collect::<Vec<TileRenderElement<_>>>(); let elem = OffscreenRenderElement::new( @@ -365,7 +377,7 @@ impl<W: LayoutElement> Tile<W> { } else { self.window().set_offscreen_element_id(None); - let elements = self.render_inner(renderer, location, scale, focus_ring); + let elements = self.render_inner(renderer, location, scale, view_size, focus_ring); None.into_iter().chain(Some(elements).into_iter().flatten()) } } diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 5e872340..b9016098 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -1188,7 +1188,7 @@ impl<W: LayoutElement> Workspace<W> { first = false; rv.extend( - tile.render(renderer, tile_pos, output_scale, focus_ring) + tile.render(renderer, tile_pos, output_scale, self.view_size, focus_ring) .map(Into::into), ); } diff --git a/src/render_helpers/gradient.rs b/src/render_helpers/gradient.rs new file mode 100644 index 00000000..1dbed05a --- /dev/null +++ b/src/render_helpers/gradient.rs @@ -0,0 +1,143 @@ +use std::f32::consts::{self, FRAC_PI_2, PI}; + +use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; +use smithay::backend::renderer::gles::element::PixelShaderElement; +use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform}; +use smithay::backend::renderer::utils::CommitCounter; +use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform}; + +use super::primary_gpu_pixel_shader::PrimaryGpuPixelShaderRenderElement; +use super::renderer::NiriRenderer; +use super::shaders::Shaders; +use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; + +/// Renders a sub- or super-rect of an angled linear gradient like CSS linear-gradient(angle, a, b). +#[derive(Debug)] +pub struct GradientRenderElement(PrimaryGpuPixelShaderRenderElement); + +impl GradientRenderElement { + pub fn new( + renderer: &mut impl NiriRenderer, + scale: Scale<f64>, + area: Rectangle<i32, Logical>, + gradient_area: Rectangle<i32, Logical>, + color_from: [f32; 4], + color_to: [f32; 4], + mut angle: f32, + ) -> Option<Self> { + let shader = Shaders::get(renderer).gradient_border.clone()?; + let g_offset = (area.loc - gradient_area.loc).to_f64().to_physical(scale); + + let g_size = gradient_area.size.to_f64().to_physical(scale); + let (w, h) = (g_size.w as f32, g_size.h as f32); + let g_area_angle = f32::atan2(h, w); + let g_area_diag = f32::hypot(h, w); + + // Normalize the angle to [0°; 360°). + while angle < 0. { + angle += consts::TAU; + } + while angle >= consts::TAU { + angle -= consts::TAU; + } + + let angle_diag_to_grad = + if (0. ..=FRAC_PI_2).contains(&angle) || (PI..=PI + FRAC_PI_2).contains(&angle) { + angle - g_area_angle + } else { + (PI - angle) - g_area_angle + }; + let g_total = angle_diag_to_grad.cos().abs() * g_area_diag; + + let elem = PixelShaderElement::new( + shader, + area, + None, + 1., + vec![ + Uniform::new("color_from", color_from), + Uniform::new("color_to", color_to), + Uniform::new("angle", angle), + Uniform::new("gradient_offset", (g_offset.x as f32, g_offset.y as f32)), + Uniform::new("gradient_width", w), + Uniform::new("gradient_total", g_total), + ], + Kind::Unspecified, + ); + Some(Self(PrimaryGpuPixelShaderRenderElement(elem))) + } +} + +impl Element for GradientRenderElement { + fn id(&self) -> &Id { + self.0.id() + } + + fn current_commit(&self) -> CommitCounter { + self.0.current_commit() + } + + fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { + self.0.geometry(scale) + } + + fn transform(&self) -> Transform { + self.0.transform() + } + + fn src(&self) -> Rectangle<f64, Buffer> { + self.0.src() + } + + fn damage_since( + &self, + scale: Scale<f64>, + commit: Option<CommitCounter>, + ) -> Vec<Rectangle<i32, Physical>> { + self.0.damage_since(scale, commit) + } + + fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> { + self.0.opaque_regions(scale) + } + + fn alpha(&self) -> f32 { + self.0.alpha() + } + + fn kind(&self) -> Kind { + self.0.kind() + } +} + +impl RenderElement<GlesRenderer> for GradientRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), GlesError> { + RenderElement::<GlesRenderer>::draw(&self.0, frame, src, dst, damage) + } + + fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> { + self.0.underlying_storage(renderer) + } +} + +impl<'render> RenderElement<TtyRenderer<'render>> for GradientRenderElement { + fn draw( + &self, + frame: &mut TtyFrame<'_, '_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), TtyRendererError<'render>> { + RenderElement::<TtyRenderer<'_>>::draw(&self.0, frame, src, dst, damage) + } + + fn underlying_storage(&self, renderer: &mut TtyRenderer<'render>) -> Option<UnderlyingStorage> { + self.0.underlying_storage(renderer) + } +} diff --git a/src/render_helpers/mod.rs b/src/render_helpers/mod.rs index c05877f9..7d7ea9c1 100644 --- a/src/render_helpers/mod.rs +++ b/src/render_helpers/mod.rs @@ -6,10 +6,13 @@ use smithay::backend::renderer::sync::SyncPoint; use smithay::backend::renderer::{Bind, ExportMem, Frame, Offscreen, Renderer}; use smithay::utils::{Physical, Rectangle, Scale, Size, Transform}; +pub mod gradient; pub mod offscreen; +pub mod primary_gpu_pixel_shader; pub mod primary_gpu_texture; pub mod render_elements; pub mod renderer; +pub mod shaders; pub fn render_to_texture( renderer: &mut GlesRenderer, diff --git a/src/render_helpers/primary_gpu_pixel_shader.rs b/src/render_helpers/primary_gpu_pixel_shader.rs new file mode 100644 index 00000000..b3b75023 --- /dev/null +++ b/src/render_helpers/primary_gpu_pixel_shader.rs @@ -0,0 +1,97 @@ +use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; +use smithay::backend::renderer::gles::element::PixelShaderElement; +use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer}; +use smithay::backend::renderer::utils::CommitCounter; +use smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform}; + +use super::renderer::AsGlesFrame; +use crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError}; + +/// Wrapper for a poxel shader from the primary GPU for rendering with the primary GPU. +#[derive(Debug)] +pub struct PrimaryGpuPixelShaderRenderElement(pub PixelShaderElement); + +impl Element for PrimaryGpuPixelShaderRenderElement { + fn id(&self) -> &Id { + self.0.id() + } + + fn current_commit(&self) -> CommitCounter { + self.0.current_commit() + } + + fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { + self.0.geometry(scale) + } + + fn transform(&self) -> Transform { + self.0.transform() + } + + fn src(&self) -> Rectangle<f64, Buffer> { + self.0.src() + } + + fn damage_since( + &self, + scale: Scale<f64>, + commit: Option<CommitCounter>, + ) -> Vec<Rectangle<i32, Physical>> { + self.0.damage_since(scale, commit) + } + + fn opaque_regions(&self, scale: Scale<f64>) -> Vec<Rectangle<i32, Physical>> { + self.0.opaque_regions(scale) + } + + fn alpha(&self) -> f32 { + self.0.alpha() + } + + fn kind(&self) -> Kind { + self.0.kind() + } +} + +impl RenderElement<GlesRenderer> for PrimaryGpuPixelShaderRenderElement { + fn draw( + &self, + frame: &mut GlesFrame<'_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), GlesError> { + let gles_frame = frame.as_gles_frame(); + RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?; + Ok(()) + } + + fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage> { + // If scanout for things other than Wayland buffers is implemented, this will need to take + // the target GPU into account. + None + } +} + +impl<'render> RenderElement<TtyRenderer<'render>> for PrimaryGpuPixelShaderRenderElement { + fn draw( + &self, + frame: &mut TtyFrame<'_, '_>, + src: Rectangle<f64, Buffer>, + dst: Rectangle<i32, Physical>, + damage: &[Rectangle<i32, Physical>], + ) -> Result<(), TtyRendererError<'render>> { + let gles_frame = frame.as_gles_frame(); + RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage)?; + Ok(()) + } + + fn underlying_storage( + &self, + _renderer: &mut TtyRenderer<'render>, + ) -> Option<UnderlyingStorage> { + // If scanout for things other than Wayland buffers is implemented, this will need to take + // the target GPU into account. + None + } +} diff --git a/src/render_helpers/shaders/gradient_border.frag b/src/render_helpers/shaders/gradient_border.frag new file mode 100644 index 00000000..354968cf --- /dev/null +++ b/src/render_helpers/shaders/gradient_border.frag @@ -0,0 +1,46 @@ +precision mediump float; +uniform float alpha; +#if defined(DEBUG_FLAGS) +uniform float tint; +#endif +uniform vec2 size; +varying vec2 v_coords; + +uniform vec4 color_from; +uniform vec4 color_to; +uniform float angle; +uniform vec2 gradient_offset; +uniform float gradient_width; +uniform float gradient_total; + +#define FRAC_PI_2 1.57079632679 +#define PI 3.14159265359 +# |
