mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Implement gradient borders
This commit is contained in:
Generated
+58
@@ -762,6 +762,15 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -2128,6 +2137,7 @@ name = "niri-config"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"csscolorparser",
|
||||
"knuffel",
|
||||
"miette",
|
||||
"niri-ipc",
|
||||
@@ -2408,6 +2418,48 @@ version = "2.3.1"
|
||||
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"
|
||||
@@ -2989,6 +3041,12 @@ version = "0.3.7"
|
||||
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"
|
||||
|
||||
@@ -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" }
|
||||
|
||||
+54
-2
@@ -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,10 +353,31 @@ 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)]
|
||||
@@ -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),
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+79
-22
@@ -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()
|
||||
|
||||
+17
-5
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
#define FRAC_3_PI_2 4.71238898038
|
||||
#define TAU 6.28318530718
|
||||
|
||||
void main() {
|
||||
vec2 coords = v_coords * size + gradient_offset;
|
||||
|
||||
if ((FRAC_PI_2 <= angle && angle < PI) || (FRAC_3_PI_2 <= angle && angle < TAU))
|
||||
coords.x -= gradient_width;
|
||||
|
||||
float frag_angle = FRAC_PI_2;
|
||||
if (coords.x != 0.0)
|
||||
frag_angle = atan(coords.y, coords.x);
|
||||
|
||||
float angle_frag_to_grad = frag_angle - angle;
|
||||
|
||||
float frac = cos(angle_frag_to_grad) * length(coords) / gradient_total;
|
||||
if (PI <= angle)
|
||||
frac += 1.0;
|
||||
frac = clamp(frac, 0.0, 1.0);
|
||||
|
||||
vec4 out_color = mix(color_from, color_to, frac);
|
||||
|
||||
#if defined(DEBUG_FLAGS)
|
||||
if (tint == 1.0)
|
||||
out_color = vec4(0.0, 0.3, 0.0, 0.2) + out_color * 0.8;
|
||||
#endif
|
||||
|
||||
gl_FragColor = out_color;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use smithay::backend::renderer::gles::{GlesPixelProgram, GlesRenderer, UniformName, UniformType};
|
||||
|
||||
use super::renderer::NiriRenderer;
|
||||
|
||||
pub struct Shaders {
|
||||
pub gradient_border: Option<GlesPixelProgram>,
|
||||
}
|
||||
|
||||
impl Shaders {
|
||||
fn compile(renderer: &mut GlesRenderer) -> Self {
|
||||
let _span = tracy_client::span!("Shaders::compile");
|
||||
|
||||
let gradient_border = renderer
|
||||
.compile_custom_pixel_shader(
|
||||
include_str!("gradient_border.frag"),
|
||||
&[
|
||||
UniformName::new("color_from", UniformType::_4f),
|
||||
UniformName::new("color_to", UniformType::_4f),
|
||||
UniformName::new("angle", UniformType::_1f),
|
||||
UniformName::new("gradient_offset", UniformType::_2f),
|
||||
UniformName::new("gradient_width", UniformType::_1f),
|
||||
UniformName::new("gradient_total", UniformType::_1f),
|
||||
],
|
||||
)
|
||||
.map_err(|err| {
|
||||
warn!("error compiling gradient border shader: {err:?}");
|
||||
})
|
||||
.ok();
|
||||
|
||||
Self { gradient_border }
|
||||
}
|
||||
|
||||
pub fn get(renderer: &mut impl NiriRenderer) -> &Self {
|
||||
let renderer = renderer.as_gles_renderer();
|
||||
let data = renderer.egl_context().user_data();
|
||||
data.get()
|
||||
.expect("shaders::init() must be called when creating the renderer")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(renderer: &mut GlesRenderer) {
|
||||
let shaders = Shaders::compile(renderer);
|
||||
let data = renderer.egl_context().user_data();
|
||||
if !data.insert_if_missing(|| shaders) {
|
||||
error!("shaders were already compiled");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user