Format Rust sources at 100 columns
This commit is contained in:
1 parent
4661f595f8
commit
610478f8c7
336 files changed
+23497
-7892
No files matched your search
@@ -39,7 +39,10 @@ impl Backdrop {
|
||||
continue;
|
||||
}
|
||||
|
||||
let previous = self.sources.iter().find(|source| &source.id == element.id());
|
||||
let previous = self
|
||||
.sources
|
||||
.iter()
|
||||
.find(|source| &source.id == element.id());
|
||||
changed |= previous.is_none_or(|source| {
|
||||
source.geometry != geometry
|
||||
|| source.alpha != element.alpha()
|
||||
@@ -48,10 +51,13 @@ impl Backdrop {
|
||||
|| source.visible != visible
|
||||
});
|
||||
if let Some(previous) = previous {
|
||||
changed |= element.damage_since(scale, Some(previous.commit)).iter().any(|damage| {
|
||||
let damage = Rectangle::new(damage.loc + geometry.loc, damage.size);
|
||||
visible.iter().any(|rect| rect.overlaps(damage))
|
||||
});
|
||||
changed |= element
|
||||
.damage_since(scale, Some(previous.commit))
|
||||
.iter()
|
||||
.any(|damage| {
|
||||
let damage = Rectangle::new(damage.loc + geometry.loc, damage.size);
|
||||
visible.iter().any(|rect| rect.overlaps(damage))
|
||||
});
|
||||
}
|
||||
|
||||
sources.push(Source {
|
||||
@@ -163,7 +169,9 @@ mod tests {
|
||||
}
|
||||
fn current_commit(&self) -> CommitCounter {
|
||||
if let Some(backdrop) = &self.probe {
|
||||
let _guard = backdrop.try_lock().expect("backdrop lock held during an element query");
|
||||
let _guard = backdrop
|
||||
.try_lock()
|
||||
.expect("backdrop lock held during an element query");
|
||||
}
|
||||
self.commit
|
||||
}
|
||||
@@ -173,7 +181,11 @@ mod tests {
|
||||
fn geometry(&self, _: RenderScale<f64>) -> Rectangle<i32, Physical> {
|
||||
self.rect
|
||||
}
|
||||
fn damage_since(&self, _: RenderScale<f64>, commit: Option<CommitCounter>) -> DamageSet<i32, Physical> {
|
||||
fn damage_since(
|
||||
&self,
|
||||
_: RenderScale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> DamageSet<i32, Physical> {
|
||||
if commit == Some(self.commit) {
|
||||
DamageSet::default()
|
||||
} else {
|
||||
@@ -209,13 +221,31 @@ mod tests {
|
||||
|
||||
// Open: ignore a repeated instance of the cached popup material, and
|
||||
// allow dependencies to query backdrop state without a nested lock.
|
||||
assert!(update_shared(&backdrop, &elements, &owner, region, 1.0.into()));
|
||||
assert!(update_shared(
|
||||
&backdrop,
|
||||
&elements,
|
||||
&owner,
|
||||
region,
|
||||
1.0.into()
|
||||
));
|
||||
assert_eq!(backdrop.lock().unwrap().sources.len(), 1);
|
||||
assert!(!update_shared(&backdrop, &elements, &owner, region, 1.0.into()));
|
||||
assert!(!update_shared(
|
||||
&backdrop,
|
||||
&elements,
|
||||
&owner,
|
||||
region,
|
||||
1.0.into()
|
||||
));
|
||||
|
||||
// Update both content and bounds while the popup remains visible.
|
||||
elements[1].repaint(vec![rect(10, 10, 5, 5)]);
|
||||
assert!(update_shared(&backdrop, &elements, &owner, region, 1.0.into()));
|
||||
assert!(update_shared(
|
||||
&backdrop,
|
||||
&elements,
|
||||
&owner,
|
||||
region,
|
||||
1.0.into()
|
||||
));
|
||||
assert!(update_shared(
|
||||
&backdrop,
|
||||
&elements,
|
||||
@@ -225,10 +255,28 @@ mod tests {
|
||||
));
|
||||
|
||||
// Close and reopen: dependencies disappear, then are tracked again.
|
||||
assert!(update_shared::<TestElement>(&backdrop, &[], &owner, region, 1.0.into()));
|
||||
assert!(update_shared::<TestElement>(
|
||||
&backdrop,
|
||||
&[],
|
||||
&owner,
|
||||
region,
|
||||
1.0.into()
|
||||
));
|
||||
assert!(backdrop.lock().unwrap().sources.is_empty());
|
||||
assert!(update_shared(&backdrop, &elements, &owner, region, 1.0.into()));
|
||||
assert!(!update_shared(&backdrop, &elements, &owner, region, 1.0.into()));
|
||||
assert!(update_shared(
|
||||
&backdrop,
|
||||
&elements,
|
||||
&owner,
|
||||
region,
|
||||
1.0.into()
|
||||
));
|
||||
assert!(!update_shared(
|
||||
&backdrop,
|
||||
&elements,
|
||||
&owner,
|
||||
region,
|
||||
1.0.into()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -33,10 +33,14 @@ fn render_window_texture(
|
||||
if bytes == 0 || bytes > remaining {
|
||||
return Ok(None);
|
||||
}
|
||||
let content = render_elements_from_surface_tree::<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>(
|
||||
let content = render_elements_from_surface_tree::<
|
||||
GlesRenderer,
|
||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||
>(
|
||||
renderer,
|
||||
toplevel.wl_surface(),
|
||||
Point::<i32, Logical>::from((-geometry.loc.x, -geometry.loc.y)).to_physical_precise_round(scale),
|
||||
Point::<i32, Logical>::from((-geometry.loc.x, -geometry.loc.y))
|
||||
.to_physical_precise_round(scale),
|
||||
scale,
|
||||
1.0,
|
||||
RenderElementKind::Unspecified,
|
||||
@@ -46,7 +50,11 @@ fn render_window_texture(
|
||||
}
|
||||
let mut texture = match reusable.filter(|texture| texture.size() == (size.w, size.h).into()) {
|
||||
Some(texture) => texture,
|
||||
None => Offscreen::<GlesTexture>::create_buffer(renderer, Fourcc::Abgr8888, (size.w, size.h).into())?,
|
||||
None => Offscreen::<GlesTexture>::create_buffer(
|
||||
renderer,
|
||||
Fourcc::Abgr8888,
|
||||
(size.w, size.h).into(),
|
||||
)?,
|
||||
};
|
||||
{
|
||||
let mut target = renderer.bind(&mut texture)?;
|
||||
@@ -93,13 +101,22 @@ pub(crate) fn capture_window_frame(
|
||||
.get_or_insert_threadsafe(|| Arc::new(CaptureTarget::default()))
|
||||
.clone();
|
||||
let reusable = cache.0.lock().unwrap().take();
|
||||
let mut snapshot = render_window_texture(renderer, window, geometry, scale, 128 * 1024 * 1024, reusable)
|
||||
.map_err(|error| error.to_string())?
|
||||
.ok_or("Window has no capturable content")?;
|
||||
let mut snapshot = render_window_texture(
|
||||
renderer,
|
||||
window,
|
||||
geometry,
|
||||
scale,
|
||||
128 * 1024 * 1024,
|
||||
reusable,
|
||||
)
|
||||
.map_err(|error| error.to_string())?
|
||||
.ok_or("Window has no capturable content")?;
|
||||
let size = snapshot.texture.size();
|
||||
if let Some((state, cursor_geometry)) = cursor {
|
||||
let elements = cursor_elements(state, renderer, cursor_geometry, scale);
|
||||
let mut target = renderer.bind(&mut snapshot.texture).map_err(|e| e.to_string())?;
|
||||
let mut target = renderer
|
||||
.bind(&mut snapshot.texture)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut frame = renderer
|
||||
.render(&mut target, (size.w, size.h).into(), Transform::Normal)
|
||||
.map_err(|e| e.to_string())?;
|
||||
@@ -113,7 +130,11 @@ pub(crate) fn capture_window_frame(
|
||||
let _ = frame.finish().map_err(|e| e.to_string())?;
|
||||
}
|
||||
let mapping = renderer
|
||||
.copy_texture(&snapshot.texture, Rectangle::from_size(size), Fourcc::Abgr8888)
|
||||
.copy_texture(
|
||||
&snapshot.texture,
|
||||
Rectangle::from_size(size),
|
||||
Fourcc::Abgr8888,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
if mapping.format() != Some(Fourcc::Abgr8888) {
|
||||
return Err("Unsupported window readback format".into());
|
||||
@@ -125,7 +146,9 @@ pub(crate) fn capture_window_frame(
|
||||
// Smithay maps its PBO here and can wait for the GPU. Nonblocking readback
|
||||
// needs a retained mapping plus an EGL-context-owned fence, polled before
|
||||
// mapping; moving this call to the conversion worker is not context-safe.
|
||||
let source = renderer.map_texture(&mapping).map_err(|error| error.to_string())?;
|
||||
let source = renderer
|
||||
.map_texture(&mapping)
|
||||
.map_err(|error| error.to_string())?;
|
||||
if source.len() < bytes {
|
||||
return Err("Incomplete window readback".into());
|
||||
}
|
||||
@@ -164,7 +187,9 @@ mod tests {
|
||||
convert_window_pixels(&mut pixels);
|
||||
assert_eq!(
|
||||
pixels,
|
||||
[30, 20, 10, 255, 255, 128, 64, 128, 0, 0, 0, 0, 255, 255, 255, 1]
|
||||
[
|
||||
30, 20, 10, 255, 255, 128, 64, 128, 0, 0, 0, 0, 255, 255, 255, 1
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,11 @@ pub(crate) struct ClosedWindow {
|
||||
|
||||
impl ClosedWindow {
|
||||
pub fn bytes(&self) -> usize {
|
||||
self.snapshot.bytes() + self.handoff.as_ref().map_or(0, |(snapshot, _)| snapshot.bytes())
|
||||
self.snapshot.bytes()
|
||||
+ self
|
||||
.handoff
|
||||
.as_ref()
|
||||
.map_or(0, |(snapshot, _)| snapshot.bytes())
|
||||
}
|
||||
|
||||
pub fn advance(&mut self, delta: Duration, spring: SpringConfig) -> bool {
|
||||
@@ -82,7 +86,9 @@ pub(super) fn grouped_elements(
|
||||
.render
|
||||
.closing
|
||||
.iter()
|
||||
.filter(|window| window.output == output_id && window.snapshot.context == renderer.context_id().erased())
|
||||
.filter(|window| {
|
||||
window.output == output_id && window.snapshot.context == renderer.context_id().erased()
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
if windows.is_empty() {
|
||||
@@ -97,7 +103,8 @@ pub(super) fn grouped_elements(
|
||||
window.advance(delta, state.spring_config);
|
||||
}
|
||||
let visual = window.presentation.bounds.current;
|
||||
let corners = RoundedRect::new(visual, output_geometry.loc, scale, window.radius).with_shape(window.shape);
|
||||
let corners = RoundedRect::new(visual, output_geometry.loc, scale, window.radius)
|
||||
.with_shape(window.shape);
|
||||
let constrain = rounded_visual_rect(visual, output_geometry.loc);
|
||||
let Some(programs) = corner_program(&mut state.render, renderer, window.shape) else {
|
||||
continue;
|
||||
@@ -124,7 +131,8 @@ pub(super) fn grouped_elements(
|
||||
constrain,
|
||||
corners,
|
||||
scale,
|
||||
theme.border_width + (theme.focus_ring_width - theme.border_width) * window.presentation.focus(),
|
||||
theme.border_width
|
||||
+ (theme.focus_ring_width - theme.border_width) * window.presentation.focus(),
|
||||
theme.border_color.0,
|
||||
theme.border_gradient,
|
||||
window.presentation.focus() as f32,
|
||||
@@ -196,8 +204,13 @@ pub(super) fn grouped_elements(
|
||||
elements.push(material);
|
||||
}
|
||||
if window.material.is_none()
|
||||
&& let Some(blur) =
|
||||
super::window_blur::window_blur_element(state, renderer, output, window.presentation, corners)
|
||||
&& let Some(blur) = super::window_blur::window_blur_element(
|
||||
state,
|
||||
renderer,
|
||||
output,
|
||||
window.presentation,
|
||||
corners,
|
||||
)
|
||||
{
|
||||
elements.push(blur);
|
||||
}
|
||||
|
||||
@@ -219,7 +219,9 @@ fn window_corner_shader_timing_sample() {
|
||||
Uniform::new("clip_rect", [0.0f32, 0.0, 1024.0, 768.0]),
|
||||
Uniform::new("radius", 16.0f32),
|
||||
];
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, (1024, 768).into()).unwrap();
|
||||
let mut texture: GlesTexture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, (1024, 768).into())
|
||||
.unwrap();
|
||||
|
||||
for shape in [CornerShape::Circular, CornerShape::Continuous] {
|
||||
let programs = corner_program(&mut resources, &mut renderer, shape).unwrap();
|
||||
@@ -228,7 +230,9 @@ fn window_corner_shader_timing_sample() {
|
||||
for iteration in 0..110 {
|
||||
let started = std::time::Instant::now();
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
let mut frame = renderer.render(&mut target, size, Transform::Normal).unwrap();
|
||||
let mut frame = renderer
|
||||
.render(&mut target, size, Transform::Normal)
|
||||
.unwrap();
|
||||
frame.clear(Color32F::TRANSPARENT, &[rect]).unwrap();
|
||||
frame
|
||||
.render_pixel_shader_to(
|
||||
@@ -256,7 +260,11 @@ fn window_corner_shader_timing_sample() {
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize(renderer: &mut GlesRenderer, program: &GlesPixelProgram, uniforms: &[Uniform<'_>]) -> Vec<u8> {
|
||||
fn rasterize(
|
||||
renderer: &mut GlesRenderer,
|
||||
program: &GlesPixelProgram,
|
||||
uniforms: &[Uniform<'_>],
|
||||
) -> Vec<u8> {
|
||||
let size = (64, 64).into();
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, size).unwrap();
|
||||
let rect = Rectangle::<i32, Physical>::from_size((64, 64).into());
|
||||
@@ -294,7 +302,8 @@ fn panel_shader_matches_core_outlines_and_resets_for_popovers() {
|
||||
use smithay::backend::renderer::gles::UniformValue;
|
||||
let mut renderer = renderer();
|
||||
let mut resources = RenderResources::default();
|
||||
let program = material_program_for_corners(&mut resources, &mut renderer, CornerShape::Circular).unwrap();
|
||||
let program =
|
||||
material_program_for_corners(&mut resources, &mut renderer, CornerShape::Circular).unwrap();
|
||||
let bounds = ferese_layout::Rect::new(0., 0., 64., 64.);
|
||||
let groups = [
|
||||
GroupBounds {
|
||||
@@ -346,7 +355,19 @@ fn panel_shader_matches_core_outlines_and_resets_for_popovers() {
|
||||
let matrices = scene
|
||||
.packets()
|
||||
.into_iter()
|
||||
.map(|p| [p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8] * 4. + p[9]])
|
||||
.map(|p| {
|
||||
[
|
||||
p[0],
|
||||
p[1],
|
||||
p[2],
|
||||
p[3],
|
||||
p[4],
|
||||
p[5],
|
||||
p[6],
|
||||
p[7],
|
||||
p[8] * 4. + p[9],
|
||||
]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut uniforms = vec![
|
||||
Uniform::new("visible_rect", [0.0f32, 0., 64., 64.]),
|
||||
@@ -441,7 +462,10 @@ fn apple_window_fill_and_inset_border_match_path_reference_at_fractional_scales(
|
||||
let inner = coverage(distance + f64::from(width));
|
||||
let index = (y * 64 + x) * 4;
|
||||
|
||||
for (actual, expected) in [(fill[index + 3], outer), (edge[index + 3], (outer - inner).max(0.0))] {
|
||||
for (actual, expected) in [
|
||||
(fill[index + 3], outer),
|
||||
(edge[index + 3], (outer - inner).max(0.0)),
|
||||
] {
|
||||
let expected = (expected * 255.0).round() as u8;
|
||||
assert!(
|
||||
actual.abs_diff(expected) <= 2,
|
||||
@@ -510,9 +534,12 @@ fn apple_shoulder_extent_never_claims_transparent_pixels_as_opaque() {
|
||||
fn docked_panel_fill_covers_the_entire_attached_edge() {
|
||||
let mut renderer = renderer();
|
||||
let mut resources = RenderResources::default();
|
||||
let material = material_program_for_corners(&mut resources, &mut renderer, CornerShape::Circular).unwrap();
|
||||
let material =
|
||||
material_program_for_corners(&mut resources, &mut renderer, CornerShape::Circular).unwrap();
|
||||
for edge in [ferese_panel::Edge::Top, ferese_panel::Edge::Bottom] {
|
||||
let radii = ferese_panel::CornerRadii([14., 8., 12., 20.]).at_edge(edge, true).0;
|
||||
let radii = ferese_panel::CornerRadii([14., 8., 12., 20.])
|
||||
.at_edge(edge, true)
|
||||
.0;
|
||||
let pixels = rasterize(
|
||||
&mut renderer,
|
||||
&material.0,
|
||||
@@ -537,7 +564,11 @@ fn docked_panel_fill_covers_the_entire_attached_edge() {
|
||||
"wallpaper exposed at {edge:?} pixel {x}"
|
||||
);
|
||||
}
|
||||
assert_eq!(pixels[free_row * 64 * 4 + 3], 0, "opposite corners remain rounded");
|
||||
assert_eq!(
|
||||
pixels[free_row * 64 * 4 + 3],
|
||||
0,
|
||||
"opposite corners remain rounded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +577,8 @@ fn docked_panel_fill_covers_the_entire_attached_edge() {
|
||||
fn asymmetric_panel_fill_and_shadow_match_each_corner() {
|
||||
let mut renderer = renderer();
|
||||
let mut resources = RenderResources::default();
|
||||
let material = material_program_for_corners(&mut resources, &mut renderer, CornerShape::Circular).unwrap();
|
||||
let material =
|
||||
material_program_for_corners(&mut resources, &mut renderer, CornerShape::Circular).unwrap();
|
||||
let rect = [2.0f32, 2.0, 60.0, 60.0];
|
||||
let radii = [0.0f32, 4.0, 12.0, 20.0];
|
||||
for paint_mode in [0.0f32, 2.0] {
|
||||
@@ -596,7 +628,9 @@ fn apple_shadow_and_window_tint_match_the_same_outline() {
|
||||
let mut renderer = renderer();
|
||||
let mut resources = RenderResources::default();
|
||||
let programs = corner_program(&mut resources, &mut renderer, CornerShape::Continuous).unwrap();
|
||||
let material = material_program_for_corners(&mut resources, &mut renderer, CornerShape::Continuous).unwrap();
|
||||
let material =
|
||||
material_program_for_corners(&mut resources, &mut renderer, CornerShape::Continuous)
|
||||
.unwrap();
|
||||
let rect = [1.25f32, 2.5, 61.5, 59.0];
|
||||
let radius = 12.375f32;
|
||||
let shadow = rasterize(
|
||||
@@ -634,7 +668,10 @@ fn apple_shadow_and_window_tint_match_the_same_outline() {
|
||||
let shadow_alpha = (-0.5 * (distance.max(0.0) / 4.0).powi(2)).exp();
|
||||
let index = (y * 64 + x) * 4;
|
||||
|
||||
for (actual, expected) in [(tint[index + 3], coverage(distance)), (shadow[index + 3], shadow_alpha)] {
|
||||
for (actual, expected) in [
|
||||
(tint[index + 3], coverage(distance)),
|
||||
(shadow[index + 3], shadow_alpha),
|
||||
] {
|
||||
assert!(
|
||||
actual.abs_diff((expected * 255.0).round() as u8) <= 2,
|
||||
"outline mismatch at ({x}, {y})"
|
||||
@@ -680,8 +717,16 @@ fn window_role_change_invalidates_decorations_and_preserves_the_resize_snapshot(
|
||||
.unwrap()
|
||||
.borders
|
||||
.contexts
|
||||
.insert(context.clone(), CachedBorder { element, parameters });
|
||||
let texture = renderer.create_buffer(Fourcc::Abgr8888, (64, 64).into()).unwrap();
|
||||
.insert(
|
||||
context.clone(),
|
||||
CachedBorder {
|
||||
element,
|
||||
parameters,
|
||||
},
|
||||
);
|
||||
let texture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, (64, 64).into())
|
||||
.unwrap();
|
||||
resources.set_snapshot(
|
||||
id,
|
||||
ResizeSnapshot {
|
||||
@@ -711,7 +756,10 @@ fn window_role_change_invalidates_decorations_and_preserves_the_resize_snapshot(
|
||||
fn endpoint_derivatives(points: Cubic, end: bool) -> (Point2, Point2) {
|
||||
if end {
|
||||
(
|
||||
[3.0 * (points[3][0] - points[2][0]), 3.0 * (points[3][1] - points[2][1])],
|
||||
[
|
||||
3.0 * (points[3][0] - points[2][0]),
|
||||
3.0 * (points[3][1] - points[2][1]),
|
||||
],
|
||||
[
|
||||
6.0 * (points[3][0] - 2.0 * points[2][0] + points[1][0]),
|
||||
6.0 * (points[3][1] - 2.0 * points[2][1] + points[1][1]),
|
||||
@@ -719,7 +767,10 @@ fn endpoint_derivatives(points: Cubic, end: bool) -> (Point2, Point2) {
|
||||
)
|
||||
} else {
|
||||
(
|
||||
[3.0 * (points[1][0] - points[0][0]), 3.0 * (points[1][1] - points[0][1])],
|
||||
[
|
||||
3.0 * (points[1][0] - points[0][0]),
|
||||
3.0 * (points[1][1] - points[0][1]),
|
||||
],
|
||||
[
|
||||
6.0 * (points[2][0] - 2.0 * points[1][0] + points[0][0]),
|
||||
6.0 * (points[2][1] - 2.0 * points[1][1] + points[0][1]),
|
||||
@@ -729,7 +780,8 @@ fn endpoint_derivatives(points: Cubic, end: bool) -> (Point2, Point2) {
|
||||
}
|
||||
|
||||
fn curvature(tangent: Point2, acceleration: Point2) -> f64 {
|
||||
(tangent[0] * acceleration[1] - tangent[1] * acceleration[0]).abs() / tangent[0].hypot(tangent[1]).powi(3)
|
||||
(tangent[0] * acceleration[1] - tangent[1] * acceleration[0]).abs()
|
||||
/ tangent[0].hypot(tangent[1]).powi(3)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -745,10 +797,12 @@ fn measured_uikit_profile_has_flat_shoulders_but_small_internal_join_discontinui
|
||||
assert_eq!(pair[0][3], pair[1][0], "join must be position-continuous");
|
||||
let (before, before_acceleration) = endpoint_derivatives(pair[0], true);
|
||||
let (after, after_acceleration) = endpoint_derivatives(pair[1], false);
|
||||
let cosine =
|
||||
(before[0] * after[0] + before[1] * after[1]) / before[0].hypot(before[1]) / after[0].hypot(after[1]);
|
||||
let cosine = (before[0] * after[0] + before[1] * after[1])
|
||||
/ before[0].hypot(before[1])
|
||||
/ after[0].hypot(after[1]);
|
||||
let angle = cosine.clamp(-1.0, 1.0).acos().to_degrees();
|
||||
let curvature_jump = (curvature(before, before_acceleration) - curvature(after, after_acceleration)).abs();
|
||||
let curvature_jump =
|
||||
(curvature(before, before_acceleration) - curvature(after, after_acceleration)).abs();
|
||||
// This is a measured Apple-style approximation, not a globally G1/G2
|
||||
// curve. Preserve and document its limitation instead of falsely
|
||||
// passing a continuity test, or silently changing the source profile.
|
||||
@@ -767,7 +821,9 @@ fn corner_heavy_shader_timing_sample() {
|
||||
let current = include_str!("shaders/window_corners.glsl");
|
||||
let size = (1024, 768).into();
|
||||
let rect = Rectangle::<i32, Physical>::from_size(size);
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, (1024, 768).into()).unwrap();
|
||||
let mut texture: GlesTexture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, (1024, 768).into())
|
||||
.unwrap();
|
||||
let body = "\nvoid main() { float d = rounded_rect_distance(mod(gl_FragCoord.xy, vec2(64.0)), vec4(0.0, 0.0, 64.0, 64.0), radius); gl_FragColor = vec4(edge_coverage(d)); }";
|
||||
for (name, source) in reference
|
||||
.as_deref()
|
||||
@@ -784,7 +840,9 @@ fn corner_heavy_shader_timing_sample() {
|
||||
for i in 0..110 {
|
||||
let start = std::time::Instant::now();
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
let mut frame = renderer.render(&mut target, size, Transform::Normal).unwrap();
|
||||
let mut frame = renderer
|
||||
.render(&mut target, size, Transform::Normal)
|
||||
.unwrap();
|
||||
frame.clear(Color32F::TRANSPARENT, &[rect]).unwrap();
|
||||
frame
|
||||
.render_pixel_shader_to(
|
||||
@@ -817,7 +875,9 @@ fn window_tint_does_not_inherit_panel_geometry() {
|
||||
use smithay::backend::renderer::gles::UniformValue;
|
||||
use smithay::output::{Mode, PhysicalProperties, Subpixel};
|
||||
fn paint(renderer: &mut GlesRenderer, element: &impl RenderElement<GlesRenderer>) -> Vec<u8> {
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, (96, 96).into()).unwrap();
|
||||
let mut texture: GlesTexture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, (96, 96).into())
|
||||
.unwrap();
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
let rect = Rectangle::from_size((96, 96).into());
|
||||
let mut frame = renderer
|
||||
@@ -837,7 +897,11 @@ fn window_tint_does_not_inherit_panel_geometry() {
|
||||
.unwrap();
|
||||
let _ = frame.finish().unwrap();
|
||||
let mapping = renderer
|
||||
.copy_framebuffer(&target, Rectangle::from_size((96, 96).into()), Fourcc::Abgr8888)
|
||||
.copy_framebuffer(
|
||||
&target,
|
||||
Rectangle::from_size((96, 96).into()),
|
||||
Fourcc::Abgr8888,
|
||||
)
|
||||
.unwrap();
|
||||
renderer.map_texture(&mapping).unwrap().to_vec()
|
||||
}
|
||||
@@ -863,7 +927,8 @@ fn window_tint_does_not_inherit_panel_geometry() {
|
||||
Some((0, 0).into()),
|
||||
);
|
||||
let geometry = Rectangle::new((6, 6).into(), (80, 80).into());
|
||||
let corners = RoundedRect::from_logical(geometry, 1.0, 12.0).with_shape(CornerShape::Continuous);
|
||||
let corners =
|
||||
RoundedRect::from_logical(geometry, 1.0, 12.0).with_shape(CornerShape::Continuous);
|
||||
for resize_fill in [false, true] {
|
||||
let tint = window_tint_element(
|
||||
&mut resources,
|
||||
@@ -949,14 +1014,23 @@ fn floating_blur_sampling_halo_does_not_paint_neighboring_islands() {
|
||||
];
|
||||
let rect = Rectangle::<i32, Physical>::new(
|
||||
(capture[0] as i32, capture[1] as i32).into(),
|
||||
((capture[2] - capture[0]) as i32, (capture[3] - capture[1]) as i32).into(),
|
||||
(
|
||||
(capture[2] - capture[0]) as i32,
|
||||
(capture[3] - capture[1]) as i32,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
let mut pixels = vec![128_u8; (rect.size.w * rect.size.h * 4) as usize];
|
||||
for p in pixels.chunks_exact_mut(4) {
|
||||
p[3] = 255;
|
||||
}
|
||||
let source = renderer
|
||||
.import_memory(&pixels, Fourcc::Abgr8888, (rect.size.w, rect.size.h).into(), false)
|
||||
.import_memory(
|
||||
&pixels,
|
||||
Fourcc::Abgr8888,
|
||||
(rect.size.w, rect.size.h).into(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let uniforms = vec![
|
||||
Uniform::new("visible_rect", visible),
|
||||
@@ -981,10 +1055,15 @@ fn floating_blur_sampling_halo_does_not_paint_neighboring_islands() {
|
||||
.create_buffer(Fourcc::Abgr8888, (size.w, size.h).into())
|
||||
.unwrap();
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
let mut frame = renderer.render(&mut target, size, Transform::Normal).unwrap();
|
||||
let mut frame = renderer
|
||||
.render(&mut target, size, Transform::Normal)
|
||||
.unwrap();
|
||||
frame.clear([0.; 4].into(), &[full]).unwrap();
|
||||
// OpenGL coordinates have the opposite Y direction from the frame.
|
||||
let dst = Rectangle::new((rect.loc.x, size.h - rect.loc.y - rect.size.h).into(), rect.size);
|
||||
let dst = Rectangle::new(
|
||||
(rect.loc.x, size.h - rect.loc.y - rect.size.h).into(),
|
||||
rect.size,
|
||||
);
|
||||
frame
|
||||
.render_texture_from_to(
|
||||
&source,
|
||||
@@ -1000,7 +1079,11 @@ fn floating_blur_sampling_halo_does_not_paint_neighboring_islands() {
|
||||
.unwrap();
|
||||
let _ = frame.finish().unwrap();
|
||||
let mapping = renderer
|
||||
.copy_framebuffer(&target, Rectangle::from_size((size.w, size.h).into()), Fourcc::Abgr8888)
|
||||
.copy_framebuffer(
|
||||
&target,
|
||||
Rectangle::from_size((size.w, size.h).into()),
|
||||
Fourcc::Abgr8888,
|
||||
)
|
||||
.unwrap();
|
||||
let rendered = renderer.map_texture(&mapping).unwrap().to_vec();
|
||||
let mut painted = 0;
|
||||
|
||||
@@ -13,10 +13,14 @@ pub(super) fn corner_program(
|
||||
shape: CornerShape,
|
||||
) -> Option<RoundedClipPrograms> {
|
||||
let context = renderer.context_id().erased();
|
||||
if let Some(program) = resources.contexts.get(&context).and_then(|programs| match shape {
|
||||
CornerShape::Circular => programs.rounded.as_ref(),
|
||||
CornerShape::Continuous => programs.continuous.as_ref(),
|
||||
}) {
|
||||
if let Some(program) = resources
|
||||
.contexts
|
||||
.get(&context)
|
||||
.and_then(|programs| match shape {
|
||||
CornerShape::Circular => programs.rounded.as_ref(),
|
||||
CornerShape::Continuous => programs.continuous.as_ref(),
|
||||
})
|
||||
{
|
||||
return Some(program.clone());
|
||||
}
|
||||
|
||||
@@ -43,11 +47,18 @@ pub(super) fn corner_program(
|
||||
UniformName::new("opacity", UniformType::_1f),
|
||||
UniformName::new("shadow_color", UniformType::_4f),
|
||||
];
|
||||
let texture =
|
||||
renderer.compile_custom_texture_shader(corner_shader_for(ROUNDED_TEXTURE_SHADER, shape), &texture_uniforms);
|
||||
let border =
|
||||
renderer.compile_custom_pixel_shader(corner_shader_for(ROUNDED_BORDER_SHADER, shape), &border_uniforms);
|
||||
let shadow = renderer.compile_custom_pixel_shader(corner_shader_for(WINDOW_SHADOW_SHADER, shape), &shadow_uniforms);
|
||||
let texture = renderer.compile_custom_texture_shader(
|
||||
corner_shader_for(ROUNDED_TEXTURE_SHADER, shape),
|
||||
&texture_uniforms,
|
||||
);
|
||||
let border = renderer.compile_custom_pixel_shader(
|
||||
corner_shader_for(ROUNDED_BORDER_SHADER, shape),
|
||||
&border_uniforms,
|
||||
);
|
||||
let shadow = renderer.compile_custom_pixel_shader(
|
||||
corner_shader_for(WINDOW_SHADOW_SHADER, shape),
|
||||
&shadow_uniforms,
|
||||
);
|
||||
let solid = renderer.compile_custom_pixel_shader(
|
||||
corner_shader_for(ROUNDED_SOLID_SHADER, shape),
|
||||
&[
|
||||
@@ -57,7 +68,9 @@ pub(super) fn corner_program(
|
||||
],
|
||||
);
|
||||
let compiled = texture.and_then(|texture| {
|
||||
border.and_then(|border| shadow.and_then(|shadow| solid.map(|solid| (texture, border, shadow, solid))))
|
||||
border.and_then(|border| {
|
||||
shadow.and_then(|shadow| solid.map(|solid| (texture, border, shadow, solid)))
|
||||
})
|
||||
});
|
||||
match compiled {
|
||||
Ok((texture, border, shadow, solid)) => {
|
||||
@@ -130,13 +143,22 @@ pub(super) fn window_border_element(
|
||||
Some(g) => (
|
||||
g.from.0,
|
||||
g.to.0,
|
||||
border_gradient_line(corners.rect, mode.size, output.current_transform().invert(), g.angle),
|
||||
border_gradient_line(
|
||||
corners.rect,
|
||||
mode.size,
|
||||
output.current_transform().invert(),
|
||||
g.angle,
|
||||
),
|
||||
),
|
||||
None => (theme.accent_color.0, theme.accent_color.0, [0.0; 4]),
|
||||
};
|
||||
let parameters = BorderParameters {
|
||||
geometry,
|
||||
clip_rect: framebuffer_clip_rect(corners.rect, mode.size, output.current_transform().invert()),
|
||||
clip_rect: framebuffer_clip_rect(
|
||||
corners.rect,
|
||||
mode.size,
|
||||
output.current_transform().invert(),
|
||||
),
|
||||
radius: corners.radius,
|
||||
width,
|
||||
color: color_with_alpha(from, opacity),
|
||||
@@ -235,7 +257,11 @@ pub(super) fn window_tint_element(
|
||||
let program = material_program_for_corners(resources, renderer, corners.shape)?;
|
||||
let parameters = BorderParameters {
|
||||
geometry,
|
||||
clip_rect: framebuffer_clip_rect(corners.rect, mode.size, output.current_transform().invert()),
|
||||
clip_rect: framebuffer_clip_rect(
|
||||
corners.rect,
|
||||
mode.size,
|
||||
output.current_transform().invert(),
|
||||
),
|
||||
radius: corners.radius,
|
||||
width: 0.0,
|
||||
color,
|
||||
@@ -253,17 +279,20 @@ pub(super) fn window_tint_element(
|
||||
} else {
|
||||
&mut resources.windows.entry(id).or_default().dim
|
||||
};
|
||||
let cached = buffers.contexts.entry(context).or_insert_with(|| CachedBorder {
|
||||
element: SharedPixelShaderElement::new(
|
||||
program.0,
|
||||
geometry,
|
||||
None,
|
||||
1.0,
|
||||
uniforms(¶meters),
|
||||
RenderElementKind::Unspecified,
|
||||
),
|
||||
parameters: parameters.clone(),
|
||||
});
|
||||
let cached = buffers
|
||||
.contexts
|
||||
.entry(context)
|
||||
.or_insert_with(|| CachedBorder {
|
||||
element: SharedPixelShaderElement::new(
|
||||
program.0,
|
||||
geometry,
|
||||
None,
|
||||
1.0,
|
||||
uniforms(¶meters),
|
||||
RenderElementKind::Unspecified,
|
||||
),
|
||||
parameters: parameters.clone(),
|
||||
});
|
||||
if cached.parameters != parameters {
|
||||
if cached.parameters.geometry != geometry {
|
||||
cached.element.resize(geometry, None);
|
||||
@@ -311,7 +340,11 @@ pub(super) fn window_shadow_element(
|
||||
let parameters = ShadowParameters {
|
||||
blur: (blur * scale) as f32,
|
||||
bounds,
|
||||
shadow_rect: framebuffer_clip_rect(shadow_geometry, mode.size, output.current_transform().invert()),
|
||||
shadow_rect: framebuffer_clip_rect(
|
||||
shadow_geometry,
|
||||
mode.size,
|
||||
output.current_transform().invert(),
|
||||
),
|
||||
radius: corners.radius,
|
||||
opacity: opacity as f32,
|
||||
color,
|
||||
@@ -349,8 +382,16 @@ pub(super) fn window_shadow_element(
|
||||
Some(PhysicalShaderElement {
|
||||
inner: cached.element.clone(),
|
||||
geometry: Rectangle::new(
|
||||
(shadow_geometry.loc.x - extent, shadow_geometry.loc.y - extent).into(),
|
||||
(shadow_geometry.size.w + 2 * extent, shadow_geometry.size.h + 2 * extent).into(),
|
||||
(
|
||||
shadow_geometry.loc.x - extent,
|
||||
shadow_geometry.loc.y - extent,
|
||||
)
|
||||
.into(),
|
||||
(
|
||||
shadow_geometry.size.w + 2 * extent,
|
||||
shadow_geometry.size.h + 2 * extent,
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
})
|
||||
}
|
||||
@@ -365,7 +406,11 @@ pub(super) fn shadow_uniforms(parameters: &ShadowParameters) -> Vec<Uniform<'sta
|
||||
]
|
||||
}
|
||||
|
||||
pub(super) fn shadow_bounds(geometry: Rectangle<i32, Logical>, offset_y: f64, blur: f64) -> Rectangle<i32, Logical> {
|
||||
pub(super) fn shadow_bounds(
|
||||
geometry: Rectangle<i32, Logical>,
|
||||
offset_y: f64,
|
||||
blur: f64,
|
||||
) -> Rectangle<i32, Logical> {
|
||||
let extent = (blur * 2.0).ceil() as i32;
|
||||
let offset_y = offset_y.round() as i32;
|
||||
|
||||
@@ -399,21 +444,30 @@ pub(super) fn rounded_window_elements(
|
||||
let geometry = window.geometry();
|
||||
let mut reference = geometry.to_physical_precise_round(scale);
|
||||
let behavior = if let Some(size) = native_size {
|
||||
reference.size = Size::<i32, Logical>::from((size.width, size.height)).to_physical_precise_round(scale);
|
||||
reference.size =
|
||||
Size::<i32, Logical>::from((size.width, size.height)).to_physical_precise_round(scale);
|
||||
ConstrainScaleBehavior::Stretch
|
||||
} else {
|
||||
behavior
|
||||
};
|
||||
let location = physical_constrain.loc - geometry.loc.to_physical_precise_round(scale);
|
||||
let clip = framebuffer_clip_rect(physical_constrain, mode.size, output.current_transform().invert());
|
||||
let clip = framebuffer_clip_rect(
|
||||
physical_constrain,
|
||||
mode.size,
|
||||
output.current_transform().invert(),
|
||||
);
|
||||
let radius = corners.radius;
|
||||
let surface = toplevel.wl_surface();
|
||||
|
||||
let mut content = PopupManager::popups_for_surface(surface)
|
||||
.flat_map(|(popup, popup_offset)| {
|
||||
let offset = (geometry.loc + popup_offset - popup.geometry().loc).to_physical_precise_round(scale);
|
||||
let offset = (geometry.loc + popup_offset - popup.geometry().loc)
|
||||
.to_physical_precise_round(scale);
|
||||
|
||||
render_elements_from_surface_tree::<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>(
|
||||
render_elements_from_surface_tree::<
|
||||
GlesRenderer,
|
||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||
>(
|
||||
renderer,
|
||||
popup.wl_surface(),
|
||||
location + offset,
|
||||
|
||||
@@ -12,27 +12,34 @@ pub(super) fn append_material_surface(
|
||||
output_crop: Rectangle<i32, Physical>,
|
||||
elements: &mut Vec<AnimatedWindowRenderElement>,
|
||||
) {
|
||||
let radius =
|
||||
if crate::effects::surface_role(surface).is_some_and(|(role, _)| role == crate::effects::SemanticRole::Panel) {
|
||||
state.theme_settings.panel_radius
|
||||
} else {
|
||||
state.theme_settings.material_radius
|
||||
};
|
||||
let radius = if crate::effects::surface_role(surface)
|
||||
.is_some_and(|(role, _)| role == crate::effects::SemanticRole::Panel)
|
||||
{
|
||||
state.theme_settings.panel_radius
|
||||
} else {
|
||||
state.theme_settings.material_radius
|
||||
};
|
||||
let regions = crate::effects::surface_regions(surface);
|
||||
let opacities = crate::effects::surface_region_opacities(surface);
|
||||
let targets: Vec<_> = match ®ions {
|
||||
None => vec![(geometry, RoundedRect::from_logical(geometry, scale, radius), 1.0)],
|
||||
None => vec![(
|
||||
geometry,
|
||||
RoundedRect::from_logical(geometry, scale, radius),
|
||||
1.0,
|
||||
)],
|
||||
Some(regions) => regions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, r)| {
|
||||
material_region_geometry(*r, geometry, content_origin, scale).map(|(rect, corners)| {
|
||||
(
|
||||
rect,
|
||||
corners,
|
||||
opacities.get(index).copied().unwrap_or(1000) as f32 / 1000.0,
|
||||
)
|
||||
})
|
||||
material_region_geometry(*r, geometry, content_origin, scale).map(
|
||||
|(rect, corners)| {
|
||||
(
|
||||
rect,
|
||||
corners,
|
||||
opacities.get(index).copied().unwrap_or(1000) as f32 / 1000.0,
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
@@ -63,8 +70,12 @@ pub(super) fn append_material_surface(
|
||||
})
|
||||
.collect();
|
||||
if let Some(buffers) = state.render.surfaces.get_mut(surface) {
|
||||
buffers.contexts.retain(|(_, _, index), _| *index < targets.len());
|
||||
buffers.captures.retain(|(_, _, index), _| *index < targets.len());
|
||||
buffers
|
||||
.contexts
|
||||
.retain(|(_, _, index), _| *index < targets.len());
|
||||
buffers
|
||||
.captures
|
||||
.retain(|(_, _, index), _| *index < targets.len());
|
||||
}
|
||||
let material = (!materials.is_empty() && regions.is_none()).then_some(());
|
||||
let clip = material.as_ref().and_then(|_| {
|
||||
@@ -78,7 +89,10 @@ pub(super) fn append_material_surface(
|
||||
))
|
||||
});
|
||||
// Smithay element lists are front to back: content, material, shadow.
|
||||
let content = render_elements_from_surface_tree::<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>(
|
||||
let content = render_elements_from_surface_tree::<
|
||||
GlesRenderer,
|
||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||
>(
|
||||
renderer,
|
||||
surface,
|
||||
content_origin.to_physical_precise_round(scale),
|
||||
@@ -118,7 +132,11 @@ fn material_region_geometry(
|
||||
scale: f64,
|
||||
) -> Option<(Rectangle<i32, Logical>, RoundedRect)> {
|
||||
let rect = Rectangle::<f64, Logical>::new(
|
||||
(f64::from(origin.x) + region[0], f64::from(origin.y) + region[1]).into(),
|
||||
(
|
||||
f64::from(origin.x) + region[0],
|
||||
f64::from(origin.y) + region[1],
|
||||
)
|
||||
.into(),
|
||||
(region[2], region[3]).into(),
|
||||
)
|
||||
.intersection(geometry.to_f64())?;
|
||||
@@ -135,7 +153,10 @@ fn material_region_geometry(
|
||||
fn framebuffer_corner_radii(radii: [f32; 4], transform: Transform) -> [f32; 4] {
|
||||
let size = Size::<f64, Physical>::from((1., 1.));
|
||||
let mut transformed = [0.; 4];
|
||||
for (radius, point) in radii.into_iter().zip([(0., 0.), (1., 0.), (1., 1.), (0., 1.)]) {
|
||||
for (radius, point) in radii
|
||||
.into_iter()
|
||||
.zip([(0., 0.), (1., 0.), (1., 1.), (0., 1.)])
|
||||
{
|
||||
let point = transform.transform_point_in(Point::<f64, Physical>::from(point), &size);
|
||||
let index = match (point.x > 0.5, point.y > 0.5) {
|
||||
(false, false) => 0,
|
||||
@@ -181,7 +202,11 @@ fn resolve_surface_material(
|
||||
panel_opacity: Option<f32>,
|
||||
panel_style: ferese_panel::PanelStyle,
|
||||
) -> crate::effects::ResolvedMaterial {
|
||||
let mut material = crate::effects::resolve_material(role, settings.material_style, settings.shell_opacity as f32);
|
||||
let mut material = crate::effects::resolve_material(
|
||||
role,
|
||||
settings.material_style,
|
||||
settings.shell_opacity as f32,
|
||||
);
|
||||
if role == crate::effects::SemanticRole::Panel {
|
||||
material.opacity = ferese_config::theme::effective_panel_opacity(
|
||||
material.opacity,
|
||||
@@ -226,7 +251,8 @@ pub(super) fn material_element_with_role(
|
||||
} else {
|
||||
state.theme_settings.surface_base_color
|
||||
};
|
||||
let morph = role == crate::effects::SemanticRole::Panel && state.panel_style == ferese_panel::PanelStyle::Morph;
|
||||
let morph = role == crate::effects::SemanticRole::Panel
|
||||
&& state.panel_style == ferese_panel::PanelStyle::Morph;
|
||||
let background = if morph {
|
||||
state.theme_settings.surface_raised_color
|
||||
} else {
|
||||
@@ -250,8 +276,8 @@ pub(super) fn material_element_with_role(
|
||||
.to_logical(scale)
|
||||
.to_i32_ceil();
|
||||
let touches_top = role == crate::effects::SemanticRole::Panel && geometry.loc.y == 0;
|
||||
let touches_bottom =
|
||||
role == crate::effects::SemanticRole::Panel && geometry.loc.y + geometry.size.h >= output_size.h;
|
||||
let touches_bottom = role == crate::effects::SemanticRole::Panel
|
||||
&& geometry.loc.y + geometry.size.h >= output_size.h;
|
||||
let radii = ferese_panel::CornerRadii(radii)
|
||||
.at_top_edge(touches_top)
|
||||
.at_edge(ferese_panel::Edge::Bottom, touches_bottom)
|
||||
@@ -279,14 +305,21 @@ pub(super) fn material_element_with_role(
|
||||
corners.rect.size,
|
||||
);
|
||||
let background_opacity = material.opacity;
|
||||
let blur = material_blur_radius(material.style, material.opacity, state.theme_settings.backdrop_blur);
|
||||
let blur = material_blur_radius(
|
||||
material.style,
|
||||
material.opacity,
|
||||
state.theme_settings.backdrop_blur,
|
||||
);
|
||||
let sample_geometry = expanded_blur_region(capture_geometry, blur.ceil() as i32, output_size);
|
||||
let sample_physical = sample_geometry.to_physical_precise_round(scale);
|
||||
let panel = (role == crate::effects::SemanticRole::Panel)
|
||||
.then(|| crate::effects::surface_panel_presentation(surface))
|
||||
.flatten()
|
||||
.map(|presentation| panel_matrices(&presentation, shape_origin, scale, mode.size, transform));
|
||||
let opaque = panel.is_none() && material.opacity == 1.0 && presentation_alpha == 1.0 && radius == 0.0;
|
||||
.map(|presentation| {
|
||||
panel_matrices(&presentation, shape_origin, scale, mode.size, transform)
|
||||
});
|
||||
let opaque =
|
||||
panel.is_none() && material.opacity == 1.0 && presentation_alpha == 1.0 && radius == 0.0;
|
||||
let mut parameters = MaterialParameters {
|
||||
panel,
|
||||
corner_radii,
|
||||
@@ -321,12 +354,16 @@ pub(super) fn material_element_with_role(
|
||||
.get(&capture_key)
|
||||
.is_none_or(|c| c.geometry != sample_geometry || c.texture.size() != capture_size)
|
||||
{
|
||||
let texture = Offscreen::<GlesTexture>::create_buffer(renderer, Fourcc::Abgr8888, capture_size)
|
||||
.map_err(|error| {
|
||||
tracing::warn!(%error, ?sample_physical, "backdrop capture allocation failed");
|
||||
error
|
||||
})
|
||||
.ok();
|
||||
let texture = Offscreen::<GlesTexture>::create_buffer(
|
||||
renderer,
|
||||
Fourcc::Abgr8888,
|
||||
capture_size,
|
||||
)
|
||||
.map_err(|error| {
|
||||
tracing::warn!(%error, ?sample_physical, "backdrop capture allocation failed");
|
||||
error
|
||||
})
|
||||
.ok();
|
||||
if let Some(texture) = texture {
|
||||
buffers.captures.insert(
|
||||
capture_key.clone(),
|
||||
@@ -364,7 +401,9 @@ pub(super) fn material_element_with_role(
|
||||
MaterialElement::Fill(SharedPixelShaderElement::new(
|
||||
program.0.clone(),
|
||||
geometry,
|
||||
parameters.opaque.then(|| vec![Rectangle::from_size(geometry.size)]),
|
||||
parameters
|
||||
.opaque
|
||||
.then(|| vec![Rectangle::from_size(geometry.size)]),
|
||||
1.0,
|
||||
material_uniforms(¶meters),
|
||||
RenderElementKind::Unspecified,
|
||||
@@ -404,13 +443,17 @@ pub(super) fn material_element_with_role(
|
||||
|| cached.parameters.panel != parameters.panel
|
||||
{
|
||||
cached.shadow.resize(parameters.shadow_bounds, None);
|
||||
cached.shadow.update_uniforms(decoration_uniforms(¶meters, 2.0));
|
||||
cached
|
||||
.shadow
|
||||
.update_uniforms(decoration_uniforms(¶meters, 2.0));
|
||||
}
|
||||
match &mut cached.element {
|
||||
MaterialElement::Fill(element) => {
|
||||
element.resize(
|
||||
geometry,
|
||||
parameters.opaque.then(|| vec![Rectangle::from_size(geometry.size)]),
|
||||
parameters
|
||||
.opaque
|
||||
.then(|| vec![Rectangle::from_size(geometry.size)]),
|
||||
);
|
||||
element.update_uniforms(material_uniforms(¶meters));
|
||||
}
|
||||
@@ -421,7 +464,9 @@ pub(super) fn material_element_with_role(
|
||||
let background = match &cached.element {
|
||||
// Keep the shader's opaque regions when its canvas already has the
|
||||
// shared edges. Animated modal geometry may need a physical override.
|
||||
MaterialElement::Fill(element) if element.geometry(scale.into()) == corners.rect => element.clone().into(),
|
||||
MaterialElement::Fill(element) if element.geometry(scale.into()) == corners.rect => {
|
||||
element.clone().into()
|
||||
}
|
||||
MaterialElement::Fill(element) => PhysicalShaderElement {
|
||||
inner: element.clone(),
|
||||
geometry: corners.rect,
|
||||
@@ -432,7 +477,11 @@ pub(super) fn material_element_with_role(
|
||||
Some((background, cached.shadow.clone().into()))
|
||||
}
|
||||
|
||||
pub(super) fn material_blur_radius(style: crate::config::MaterialStyle, opacity: f32, radius: f64) -> f64 {
|
||||
pub(super) fn material_blur_radius(
|
||||
style: crate::config::MaterialStyle,
|
||||
opacity: f32,
|
||||
radius: f64,
|
||||
) -> f64 {
|
||||
if style == crate::config::MaterialStyle::Translucent && opacity > 0.0 {
|
||||
radius
|
||||
} else {
|
||||
@@ -440,7 +489,10 @@ pub(super) fn material_blur_radius(style: crate::config::MaterialStyle, opacity:
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn blur_program(resources: &mut RenderResources, renderer: &mut GlesRenderer) -> Option<BlurProgram> {
|
||||
pub(super) fn blur_program(
|
||||
resources: &mut RenderResources,
|
||||
renderer: &mut GlesRenderer,
|
||||
) -> Option<BlurProgram> {
|
||||
let context = renderer.context_id().erased();
|
||||
if let Some(program) = resources
|
||||
.contexts
|
||||
@@ -449,7 +501,8 @@ pub(super) fn blur_program(resources: &mut RenderResources, renderer: &mut GlesR
|
||||
{
|
||||
return Some(program.clone());
|
||||
}
|
||||
match renderer.compile_custom_texture_shader(corner_shader(BLUR_SHADER), &blur_uniform_names()) {
|
||||
match renderer.compile_custom_texture_shader(corner_shader(BLUR_SHADER), &blur_uniform_names())
|
||||
{
|
||||
Ok(program) => {
|
||||
let program = BlurProgram(program);
|
||||
resources.contexts.entry(context).or_default().blur = Some(program.clone());
|
||||
@@ -481,8 +534,14 @@ pub(super) fn blur_uniforms(p: &MaterialParameters) -> Vec<Uniform<'static>> {
|
||||
let mut uniforms = vec![
|
||||
Uniform::new("visible_rect", p.visible_framebuffer),
|
||||
Uniform::new("material_radii", p.corner_radii),
|
||||
Uniform::new("texture_size", [p.sample_framebuffer[2], p.sample_framebuffer[3]]),
|
||||
Uniform::new("capture_origin", [p.sample_framebuffer[0], p.sample_framebuffer[1]]),
|
||||
Uniform::new(
|
||||
"texture_size",
|
||||
[p.sample_framebuffer[2], p.sample_framebuffer[3]],
|
||||
),
|
||||
Uniform::new(
|
||||
"capture_origin",
|
||||
[p.sample_framebuffer[0], p.sample_framebuffer[1]],
|
||||
),
|
||||
Uniform::new("blur_radius", p.blur),
|
||||
Uniform::new("presentation_alpha", p.presentation_alpha),
|
||||
Uniform::new("background_opacity", p.background_opacity),
|
||||
@@ -524,10 +583,14 @@ pub(super) fn material_program_for_corners(
|
||||
shape: CornerShape,
|
||||
) -> Option<MaterialProgram> {
|
||||
let context = renderer.context_id().erased();
|
||||
if let Some(program) = resources.contexts.get(&context).and_then(|programs| match shape {
|
||||
CornerShape::Circular => programs.material.as_ref(),
|
||||
CornerShape::Continuous => programs.window_material.as_ref(),
|
||||
}) {
|
||||
if let Some(program) = resources
|
||||
.contexts
|
||||
.get(&context)
|
||||
.and_then(|programs| match shape {
|
||||
CornerShape::Circular => programs.material.as_ref(),
|
||||
CornerShape::Continuous => programs.window_material.as_ref(),
|
||||
})
|
||||
{
|
||||
return Some(program.clone());
|
||||
}
|
||||
|
||||
@@ -540,7 +603,8 @@ pub(super) fn material_program_for_corners(
|
||||
UniformName::new("shadow_values", UniformType::_2f),
|
||||
];
|
||||
uniforms.extend(panel_uniform_names());
|
||||
match renderer.compile_custom_pixel_shader(corner_shader_for(MATERIAL_SHADER, shape), &uniforms) {
|
||||
match renderer.compile_custom_pixel_shader(corner_shader_for(MATERIAL_SHADER, shape), &uniforms)
|
||||
{
|
||||
Ok(program) => {
|
||||
let program = MaterialProgram(program);
|
||||
let context = resources.contexts.entry(context).or_default();
|
||||
@@ -561,7 +625,10 @@ pub(super) fn material_uniforms(parameters: &MaterialParameters) -> Vec<Uniform<
|
||||
decoration_uniforms(parameters, 0.0)
|
||||
}
|
||||
|
||||
pub(super) fn decoration_uniforms(parameters: &MaterialParameters, paint_mode: f32) -> Vec<Uniform<'static>> {
|
||||
pub(super) fn decoration_uniforms(
|
||||
parameters: &MaterialParameters,
|
||||
paint_mode: f32,
|
||||
) -> Vec<Uniform<'static>> {
|
||||
let mut uniforms = vec![
|
||||
Uniform::new("paint_mode", paint_mode),
|
||||
Uniform::new("shadow_rect", parameters.shadow_rect),
|
||||
@@ -587,9 +654,15 @@ fn panel_uniforms(parameters: &MaterialParameters) -> Vec<Uniform<'static>> {
|
||||
let mut uniforms = vec![
|
||||
Uniform::new(
|
||||
"panel_count",
|
||||
parameters.panel.as_ref().map_or(0., |p| p.shapes.len() as f32),
|
||||
parameters
|
||||
.panel
|
||||
.as_ref()
|
||||
.map_or(0., |p| p.shapes.len() as f32),
|
||||
),
|
||||
Uniform::new(
|
||||
"panel_fusion",
|
||||
parameters.panel.as_ref().map_or(0., |p| p.fusion),
|
||||
),
|
||||
Uniform::new("panel_fusion", parameters.panel.as_ref().map_or(0., |p| p.fusion)),
|
||||
];
|
||||
if let Some(panel) = ¶meters.panel
|
||||
&& !panel.shapes.is_empty()
|
||||
@@ -643,7 +716,11 @@ fn panel_matrices(
|
||||
4 => 1,
|
||||
_ => 0,
|
||||
};
|
||||
match permutation.iter().position(|i| *i == old_corner as f32).unwrap() {
|
||||
match permutation
|
||||
.iter()
|
||||
.position(|i| *i == old_corner as f32)
|
||||
.unwrap()
|
||||
{
|
||||
0 => 5,
|
||||
1 => 4,
|
||||
2 => 3,
|
||||
@@ -707,7 +784,10 @@ mod region_geometry_tests {
|
||||
runtime.panel_style,
|
||||
);
|
||||
assert_eq!(material.opacity, if reduce { 1. } else { opacity });
|
||||
assert_eq!(material.opacity, candidate.theme.panel_opacity(Some(opacity)));
|
||||
assert_eq!(
|
||||
material.opacity,
|
||||
candidate.theme.panel_opacity(Some(opacity))
|
||||
);
|
||||
assert_eq!(
|
||||
material.style,
|
||||
if reduce {
|
||||
@@ -738,9 +818,14 @@ mod region_geometry_tests {
|
||||
let source = format!(
|
||||
"theme {{ material {{ style translucent; opacity 0.6; blur-radius 20; }}; accessibility {{ reduce-transparency #{reduce}; }}; }}; panel main {{ style morph; }}"
|
||||
);
|
||||
let (_, runtime, candidate) = crate::config::prepare(&source, std::path::Path::new("/tmp")).unwrap();
|
||||
let material =
|
||||
resolve_surface_material(SemanticRole::Panel, runtime.theme_settings, None, runtime.panel_style);
|
||||
let (_, runtime, candidate) =
|
||||
crate::config::prepare(&source, std::path::Path::new("/tmp")).unwrap();
|
||||
let material = resolve_surface_material(
|
||||
SemanticRole::Panel,
|
||||
runtime.theme_settings,
|
||||
None,
|
||||
runtime.panel_style,
|
||||
);
|
||||
assert_eq!(material.shadow, [0.; 3]);
|
||||
assert_eq!(material.opacity, candidate.theme.panel_opacity(None));
|
||||
assert_eq!(
|
||||
@@ -751,7 +836,11 @@ mod region_geometry_tests {
|
||||
crate::config::MaterialStyle::Translucent
|
||||
}
|
||||
);
|
||||
let blur = material_blur_radius(material.style, material.opacity, runtime.theme_settings.backdrop_blur);
|
||||
let blur = material_blur_radius(
|
||||
material.style,
|
||||
material.opacity,
|
||||
runtime.theme_settings.backdrop_blur,
|
||||
);
|
||||
assert_eq!(blur > 0., !reduce);
|
||||
}
|
||||
}
|
||||
@@ -760,7 +849,10 @@ mod region_geometry_tests {
|
||||
fn framebuffer_transforms_preserve_each_logical_corner() {
|
||||
let radii = [1., 2., 3., 4.];
|
||||
assert_eq!(framebuffer_corner_radii(radii, Transform::Normal), radii);
|
||||
assert_eq!(framebuffer_corner_radii(radii, Transform::Flipped180), [4., 3., 2., 1.]);
|
||||
assert_eq!(
|
||||
framebuffer_corner_radii(radii, Transform::Flipped180),
|
||||
[4., 3., 2., 1.]
|
||||
);
|
||||
for (transform, expected) in [
|
||||
(Transform::Normal, [1., 2., 3., 4.]),
|
||||
(Transform::_90, [4., 1., 2., 3.]),
|
||||
@@ -781,7 +873,9 @@ mod region_geometry_tests {
|
||||
use ferese_panel::shape::PixelGrid;
|
||||
for scale in [1., 1.25, 1.5, 2.] {
|
||||
for origin in [-3, 0, 1, 3, 101] {
|
||||
for width in [149.99, 150., 150.01, 199.99, 200., 200.01, 259.99, 260., 260.01] {
|
||||
for width in [
|
||||
149.99, 150., 150.01, 199.99, 200., 200.01, 259.99, 260., 260.01,
|
||||
] {
|
||||
let scene = derive(
|
||||
Parameters {
|
||||
style: ferese_panel::PanelStyle::Morph,
|
||||
@@ -814,7 +908,8 @@ mod region_geometry_tests {
|
||||
let [body, left, right] = shapes.as_slice() else {
|
||||
panic!("expected body and shoulders")
|
||||
};
|
||||
let client_left = (scene.paint[0].bounds.x * scale).round() + (f64::from(origin) * scale).round();
|
||||
let client_left = (scene.paint[0].bounds.x * scale).round()
|
||||
+ (f64::from(origin) * scale).round();
|
||||
assert_eq!(
|
||||
body[0], client_left as f32,
|
||||
"native fill must align with the shell outline"
|
||||
@@ -864,7 +959,9 @@ mod region_geometry_tests {
|
||||
attached: true,
|
||||
edge,
|
||||
fillet: 12.,
|
||||
radius: Some(AnchoredRadii(sample.geometry.radii.map(|r| r as f32))),
|
||||
radius: Some(AnchoredRadii(
|
||||
sample.geometry.radii.map(|r| r as f32),
|
||||
)),
|
||||
..Default::default()
|
||||
},
|
||||
bounds,
|
||||
@@ -916,14 +1013,23 @@ mod region_geometry_tests {
|
||||
#[test]
|
||||
fn fractional_regions_snap_once_in_output_pixels() {
|
||||
let bounds = Rectangle::from_size((200, 100).into());
|
||||
let (_, corners) = material_region_geometry([0.4, 0.4, 100.4, 40.4, 14.0], bounds, (0, 0).into(), 1.5).unwrap();
|
||||
assert_eq!(corners.rect, Rectangle::new((1, 1).into(), (150, 60).into()));
|
||||
let (_, corners) =
|
||||
material_region_geometry([0.4, 0.4, 100.4, 40.4, 14.0], bounds, (0, 0).into(), 1.5)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
corners.rect,
|
||||
Rectangle::new((1, 1).into(), (150, 60).into())
|
||||
);
|
||||
assert_eq!(corners.radius, 21.0);
|
||||
// The old logical rounding would put this at physical (0, 0).
|
||||
let (_, clipped) =
|
||||
material_region_geometry([-0.4, -0.4, 100.4, 40.4, 100.0], bounds, (0, 0).into(), 1.5).unwrap();
|
||||
material_region_geometry([-0.4, -0.4, 100.4, 40.4, 100.0], bounds, (0, 0).into(), 1.5)
|
||||
.unwrap();
|
||||
assert_eq!(clipped.rect, Rectangle::from_size((150, 60).into()));
|
||||
assert_eq!(clipped.radius, 30.0);
|
||||
assert!(material_region_geometry([300.0, 0.0, 10.0, 10.0, 2.0], bounds, (0, 0).into(), 1.5).is_none());
|
||||
assert!(
|
||||
material_region_geometry([300.0, 0.0, 10.0, 10.0, 2.0], bounds, (0, 0).into(), 1.5)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
+155
-55
@@ -19,14 +19,17 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) use capture::{capture_resize_snapshot, capture_window_buffer, capture_window_frame, convert_window_pixels};
|
||||
pub(crate) use capture::{
|
||||
capture_resize_snapshot, capture_window_buffer, capture_window_frame, convert_window_pixels,
|
||||
};
|
||||
use decorations::*;
|
||||
use materials::*;
|
||||
use overview::*;
|
||||
pub(crate) use resources::RenderResources;
|
||||
use scene::cursor_elements;
|
||||
pub(crate) use scene::{
|
||||
animated_window_elements, capture_output_elements, frame_effect_metrics, layer_surfaces, sampled_output_elements,
|
||||
animated_window_elements, capture_output_elements, frame_effect_metrics, layer_surfaces,
|
||||
sampled_output_elements,
|
||||
};
|
||||
pub(crate) use shader::SharedPixelShaderElement;
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
@@ -36,40 +39,50 @@ use smithay::backend::renderer::element::surface::{
|
||||
WaylandSurfaceRenderElement, WaylandSurfaceTexture, render_elements_from_surface_tree,
|
||||
};
|
||||
use smithay::backend::renderer::element::utils::{
|
||||
ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement,
|
||||
constrain_render_elements,
|
||||
ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, RelocateRenderElement,
|
||||
RescaleRenderElement, constrain_render_elements,
|
||||
};
|
||||
use smithay::backend::renderer::element::{
|
||||
Element, Id, Kind as RenderElementKind, RenderElement, UnderlyingStorage, render_elements,
|
||||
};
|
||||
use smithay::backend::renderer::gles::{
|
||||
GlesError, GlesFrame, GlesPixelProgram, GlesRenderer, GlesTexProgram, GlesTexture, Uniform, UniformName,
|
||||
UniformType,
|
||||
GlesError, GlesFrame, GlesPixelProgram, GlesRenderer, GlesTexProgram, GlesTexture, Uniform,
|
||||
UniformName, UniformType,
|
||||
};
|
||||
use smithay::backend::renderer::utils::{
|
||||
CommitCounter, DamageSet, OpaqueRegions, draw_render_elements,
|
||||
};
|
||||
use smithay::backend::renderer::{
|
||||
Bind, Color32F, ErasedContextId, ExportMem, Frame, Offscreen, Renderer, Texture,
|
||||
};
|
||||
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions, draw_render_elements};
|
||||
use smithay::backend::renderer::{Bind, Color32F, ErasedContextId, ExportMem, Frame, Offscreen, Renderer, Texture};
|
||||
use smithay::desktop::space::{ConstrainBehavior, ConstrainReference, constrain_space_element};
|
||||
use smithay::desktop::{LayerSurface, PopupManager, layer_map_for_output};
|
||||
use smithay::input::pointer::{CursorImageStatus, CursorImageSurfaceData};
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_server::Resource;
|
||||
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale as RenderScale, Size, Transform};
|
||||
use smithay::utils::{
|
||||
Buffer, Logical, Physical, Point, Rectangle, Scale as RenderScale, Size, Transform,
|
||||
};
|
||||
use smithay::wayland::compositor::with_states;
|
||||
use smithay::wayland::shell::wlr_layer::Layer;
|
||||
|
||||
use crate::Ferese;
|
||||
use crate::metrics::FrameEffectMetrics;
|
||||
use crate::presentation::{
|
||||
CornerShape, NativeTextureElement, PhysicalShaderElement, RoundedRect, clamp_radius, corner_extent, physical_rect,
|
||||
CornerShape, NativeTextureElement, PhysicalShaderElement, RoundedRect, clamp_radius,
|
||||
corner_extent, physical_rect,
|
||||
};
|
||||
|
||||
type SurfaceRenderElement =
|
||||
CropRenderElement<RelocateRenderElement<RescaleRenderElement<WaylandSurfaceRenderElement<GlesRenderer>>>>;
|
||||
type SurfaceRenderElement = CropRenderElement<
|
||||
RelocateRenderElement<RescaleRenderElement<WaylandSurfaceRenderElement<GlesRenderer>>>,
|
||||
>;
|
||||
|
||||
type WindowRenderElement = CropRenderElement<RelocateRenderElement<RescaleRenderElement<WindowContentRenderElement>>>;
|
||||
type WindowRenderElement =
|
||||
CropRenderElement<RelocateRenderElement<RescaleRenderElement<WindowContentRenderElement>>>;
|
||||
|
||||
type MemoryRenderElement =
|
||||
CropRenderElement<RelocateRenderElement<RescaleRenderElement<MemoryRenderBufferRenderElement<GlesRenderer>>>>;
|
||||
type MemoryRenderElement = CropRenderElement<
|
||||
RelocateRenderElement<RescaleRenderElement<MemoryRenderBufferRenderElement<GlesRenderer>>>,
|
||||
>;
|
||||
|
||||
render_elements! {
|
||||
pub(crate) AnimatedWindowRenderElement<=GlesRenderer>;
|
||||
@@ -127,7 +140,9 @@ fn corner_shader(source: &str) -> String {
|
||||
|
||||
fn corner_shader_for(source: &str, shape: CornerShape) -> String {
|
||||
match shape {
|
||||
CornerShape::Circular => source.replace("//_CORNERS_", include_str!("shaders/corners.glsl")),
|
||||
CornerShape::Circular => {
|
||||
source.replace("//_CORNERS_", include_str!("shaders/corners.glsl"))
|
||||
}
|
||||
CornerShape::Continuous => source
|
||||
.replace(
|
||||
"//_CORNERS_",
|
||||
@@ -138,7 +153,10 @@ fn corner_shader_for(source: &str, shape: CornerShape) -> String {
|
||||
)
|
||||
.replace("precision mediump float;", "precision highp float;"),
|
||||
}
|
||||
.replace("//_MATERIAL_CORNERS_", include_str!("shaders/material_corners.glsl"))
|
||||
.replace(
|
||||
"//_MATERIAL_CORNERS_",
|
||||
include_str!("shaders/material_corners.glsl"),
|
||||
)
|
||||
.replace("//_PANEL_MASK_", include_str!("shaders/panel_mask.glsl"))
|
||||
}
|
||||
|
||||
@@ -216,7 +234,11 @@ impl Element for BlurRenderElement {
|
||||
self.geometry.to_physical_precise_round(scale)
|
||||
}
|
||||
|
||||
fn damage_since(&self, scale: RenderScale<f64>, commit: Option<CommitCounter>) -> DamageSet<i32, Physical> {
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: RenderScale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> DamageSet<i32, Physical> {
|
||||
blur_damage(self.geometry(scale).size, self.current_commit(), commit)
|
||||
}
|
||||
|
||||
@@ -517,7 +539,11 @@ impl Element for RoundedSurfaceRenderElement {
|
||||
self.inner.src()
|
||||
}
|
||||
|
||||
fn damage_since(&self, scale: RenderScale<f64>, commit: Option<CommitCounter>) -> DamageSet<i32, Physical> {
|
||||
fn damage_since(
|
||||
&self,
|
||||
scale: RenderScale<f64>,
|
||||
commit: Option<CommitCounter>,
|
||||
) -> DamageSet<i32, Physical> {
|
||||
clipped_surface_damage(self.inner.geometry(scale).size, self.clip_changed, || {
|
||||
self.inner.damage_since(scale, commit)
|
||||
})
|
||||
@@ -634,8 +660,8 @@ mod tests {
|
||||
use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform};
|
||||
|
||||
use super::{
|
||||
border_gradient_line, color_with_alpha, framebuffer_clip_rect, resize_content_behavior, rounded_visual_rect,
|
||||
scaled_visual_rect, shadow_bounds,
|
||||
border_gradient_line, color_with_alpha, framebuffer_clip_rect, resize_content_behavior,
|
||||
rounded_visual_rect, scaled_visual_rect, shadow_bounds,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -643,9 +669,14 @@ mod tests {
|
||||
fn solid_corner_mask_preserves_premultiplied_color() {
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture, Uniform, UniformName, UniformType};
|
||||
use smithay::backend::renderer::gles::{
|
||||
GlesRenderer, GlesTexture, Uniform, UniformName, UniformType,
|
||||
};
|
||||
use smithay::backend::renderer::{Bind, Color32F, ExportMem, Frame, Offscreen, Renderer};
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let device = EGLDevice::enumerate()
|
||||
.unwrap()
|
||||
.last()
|
||||
.expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let context = EGLContext::new(&display).unwrap();
|
||||
let mut renderer = unsafe { GlesRenderer::new(context).unwrap() };
|
||||
@@ -662,7 +693,9 @@ mod tests {
|
||||
let size = (32, 32).into();
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, size).unwrap();
|
||||
let rect = Rectangle::<i32, Physical>::from_size((32, 32).into());
|
||||
for (radius, corner_alpha, inset) in [(12.0f32, 0u8, 0.0f32), (0.0, 64, 0.0), (0.0, 48, 0.25)] {
|
||||
for (radius, corner_alpha, inset) in
|
||||
[(12.0f32, 0u8, 0.0f32), (0.0, 64, 0.0), (0.0, 48, 0.25)]
|
||||
{
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
{
|
||||
let mut frame = renderer
|
||||
@@ -678,7 +711,10 @@ mod tests {
|
||||
Some(&[rect]),
|
||||
0.5,
|
||||
&[
|
||||
Uniform::new("clip_rect", [inset, inset, 32.0 - 2.0 * inset, 32.0 - 2.0 * inset]),
|
||||
Uniform::new(
|
||||
"clip_rect",
|
||||
[inset, inset, 32.0 - 2.0 * inset, 32.0 - 2.0 * inset],
|
||||
),
|
||||
Uniform::new("radius", radius),
|
||||
Uniform::new("color", [0.5f32, 0.0, 0.0, 0.5]),
|
||||
],
|
||||
@@ -702,7 +738,10 @@ mod tests {
|
||||
fn shared_corner_shaders_compile() {
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let device = EGLDevice::enumerate()
|
||||
.unwrap()
|
||||
.last()
|
||||
.expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let context = EGLContext::new(&display).unwrap();
|
||||
let mut renderer = unsafe { GlesRenderer::new(context).unwrap() };
|
||||
@@ -729,22 +768,33 @@ mod tests {
|
||||
use smithay::backend::allocator::Fourcc;
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture, Uniform};
|
||||
use smithay::backend::renderer::{Bind, Color32F, ExportMem, Frame, ImportMem, Offscreen, Renderer};
|
||||
use smithay::backend::renderer::{
|
||||
Bind, Color32F, ExportMem, Frame, ImportMem, Offscreen, Renderer,
|
||||
};
|
||||
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let device = EGLDevice::enumerate()
|
||||
.unwrap()
|
||||
.last()
|
||||
.expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let context = EGLContext::new(&display).unwrap();
|
||||
let mut renderer = unsafe { GlesRenderer::new(context).unwrap() };
|
||||
let program = renderer
|
||||
.compile_custom_texture_shader(super::corner_shader(super::BLUR_SHADER), &super::blur_uniform_names())
|
||||
.compile_custom_texture_shader(
|
||||
super::corner_shader(super::BLUR_SHADER),
|
||||
&super::blur_uniform_names(),
|
||||
)
|
||||
.unwrap();
|
||||
let size = (32, 32).into();
|
||||
let mut pixels = vec![128u8; 32 * 32 * 4];
|
||||
for pixel in pixels.as_chunks_mut::<4>().0 {
|
||||
pixel[3] = 255;
|
||||
}
|
||||
let blurred = renderer.import_memory(&pixels, Fourcc::Abgr8888, size, false).unwrap();
|
||||
let mut target_texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, size).unwrap();
|
||||
let blurred = renderer
|
||||
.import_memory(&pixels, Fourcc::Abgr8888, size, false)
|
||||
.unwrap();
|
||||
let mut target_texture: GlesTexture =
|
||||
renderer.create_buffer(Fourcc::Abgr8888, size).unwrap();
|
||||
let damage = Rectangle::<i32, Physical>::from_size((32, 32).into());
|
||||
|
||||
for (opacity, strength, tint) in [
|
||||
@@ -768,7 +818,9 @@ mod tests {
|
||||
let mut frame = renderer
|
||||
.render(&mut target, (32, 32).into(), Transform::Normal)
|
||||
.unwrap();
|
||||
frame.clear(Color32F::new(0.0, 0.0, 0.0, 1.0), &[damage]).unwrap();
|
||||
frame
|
||||
.clear(Color32F::new(0.0, 0.0, 0.0, 1.0), &[damage])
|
||||
.unwrap();
|
||||
frame
|
||||
.draw_solid(
|
||||
Rectangle::new((0, 0).into(), (16, 32).into()),
|
||||
@@ -801,7 +853,8 @@ mod tests {
|
||||
left.abs_diff(right) <= 2,
|
||||
"opacity {opacity} leaked the original backdrop: {left} versus {right}"
|
||||
);
|
||||
let expected = (128.0 * (1.0 - strength * opacity) + tint * 255.0 * strength * opacity).round() as u8;
|
||||
let expected = (128.0 * (1.0 - strength * opacity) + tint * 255.0 * strength * opacity)
|
||||
.round() as u8;
|
||||
assert!(
|
||||
left.abs_diff(expected) <= 2,
|
||||
"opacity {opacity} failed to control tint: {left} versus {expected}"
|
||||
@@ -823,7 +876,8 @@ mod tests {
|
||||
Transform::Flipped270,
|
||||
] {
|
||||
let area = transform.transform_size(mode).to_f64();
|
||||
let rect: Rectangle<i32, Physical> = Rectangle::new((110, 60).into(), (800, 600).into());
|
||||
let rect: Rectangle<i32, Physical> =
|
||||
Rectangle::new((110, 60).into(), (800, 600).into());
|
||||
for (angle, first, last) in [
|
||||
(0.0, (110.0, 360.0), (910.0, 360.0)),
|
||||
(90.0, (510.0, 60.0), (510.0, 660.0)),
|
||||
@@ -836,8 +890,14 @@ mod tests {
|
||||
(point.x - f64::from(line[0])) * f64::from(line[2])
|
||||
+ (point.y - f64::from(line[1])) * f64::from(line[3])
|
||||
};
|
||||
assert!(progress(first.into()).abs() < 0.00001, "{transform:?}, {angle}");
|
||||
assert!((progress(last.into()) - 1.0).abs() < 0.00001, "{transform:?}, {angle}");
|
||||
assert!(
|
||||
progress(first.into()).abs() < 0.00001,
|
||||
"{transform:?}, {angle}"
|
||||
);
|
||||
assert!(
|
||||
(progress(last.into()) - 1.0).abs() < 0.00001,
|
||||
"{transform:?}, {angle}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -888,7 +948,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
Rectangle::from_size(self.geometry.size.to_f64().to_buffer(1.0, Transform::Normal))
|
||||
Rectangle::from_size(
|
||||
self.geometry
|
||||
.size
|
||||
.to_f64()
|
||||
.to_buffer(1.0, Transform::Normal),
|
||||
)
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
@@ -904,9 +969,9 @@ mod tests {
|
||||
if commit == Some(self.current_commit()) {
|
||||
smithay::backend::renderer::utils::DamageSet::default()
|
||||
} else {
|
||||
smithay::backend::renderer::utils::DamageSet::from_slice(&[Rectangle::from_size(
|
||||
self.geometry(scale).size,
|
||||
)])
|
||||
smithay::backend::renderer::utils::DamageSet::from_slice(&[
|
||||
Rectangle::from_size(self.geometry(scale).size),
|
||||
])
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -933,7 +998,12 @@ mod tests {
|
||||
element.clip_changed = resources.thumbnail_corner_changed(window, output, shape);
|
||||
assert_eq!(element.commit, client_commit);
|
||||
assert!(element.clip_changed);
|
||||
let damage = tracker.damage_output(1, &[&element]).unwrap().0.cloned().unwrap();
|
||||
let damage = tracker
|
||||
.damage_output(1, &[&element])
|
||||
.unwrap()
|
||||
.0
|
||||
.cloned()
|
||||
.unwrap();
|
||||
assert!(damage.iter().any(|rect| rect.contains((100, 100))));
|
||||
assert!(damage.iter().any(|rect| rect.contains((299, 259))));
|
||||
element.clip_changed = resources.thumbnail_corner_changed(window, output, shape);
|
||||
@@ -949,7 +1019,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resize_keeps_source_pixels_native_when_growing_and_shrinking() {
|
||||
use smithay::backend::renderer::element::utils::{ConstrainAlign, constrain_render_elements};
|
||||
use smithay::backend::renderer::element::utils::{
|
||||
ConstrainAlign, constrain_render_elements,
|
||||
};
|
||||
for scale in [1.0, 1.5, 1.8, 2.0] {
|
||||
for width in [300, 600, 1200] {
|
||||
let source = Rectangle::<i32, Logical>::from_size((600, 800).into());
|
||||
@@ -1020,14 +1092,18 @@ mod tests {
|
||||
Transform::Flipped180,
|
||||
Transform::Flipped270,
|
||||
] {
|
||||
let output_size =
|
||||
smithay::utils::Size::<i32, Physical>::from(((1280.0 * scale) as i32, (800.0 * scale) as i32));
|
||||
let output_size = smithay::utils::Size::<i32, Physical>::from((
|
||||
(1280.0 * scale) as i32,
|
||||
(800.0 * scale) as i32,
|
||||
));
|
||||
let element_size = transform.transform_size(output_size);
|
||||
let logical_height = f64::from(element_size.h) / scale;
|
||||
// Bar and exclusion end at 40; the 10px outer gap ends at 50.
|
||||
let geometry =
|
||||
Rectangle::<f64, Logical>::new((10.0, 50.0).into(), (400.0, logical_height - 60.0).into())
|
||||
.to_physical_precise_round(scale);
|
||||
let geometry = Rectangle::<f64, Logical>::new(
|
||||
(10.0, 50.0).into(),
|
||||
(400.0, logical_height - 60.0).into(),
|
||||
)
|
||||
.to_physical_precise_round(scale);
|
||||
let clip = framebuffer_clip_rect(geometry, output_size, transform);
|
||||
// Independently reproduce GlesFrame's orthographic projection,
|
||||
// transform matrix, GL flip, and viewport mapping for each corner.
|
||||
@@ -1048,7 +1124,10 @@ mod tests {
|
||||
let max_x = xs.iter().copied().fold(f32::NEG_INFINITY, f32::max);
|
||||
let min_y = ys.iter().copied().fold(f32::INFINITY, f32::min);
|
||||
let max_y = ys.iter().copied().fold(f32::NEG_INFINITY, f32::max);
|
||||
for (actual, expected) in clip.into_iter().zip([min_x, min_y, max_x - min_x, max_y - min_y]) {
|
||||
for (actual, expected) in
|
||||
clip.into_iter()
|
||||
.zip([min_x, min_y, max_x - min_x, max_y - min_y])
|
||||
{
|
||||
assert!(
|
||||
(actual - expected).abs() < 0.001,
|
||||
"{transform:?}, scale={scale}: {actual} != {expected}"
|
||||
@@ -1077,11 +1156,17 @@ mod tests {
|
||||
let old_window = Rectangle::<i32, Logical>::new((100, 80).into(), (400, 300).into());
|
||||
let new_window = Rectangle::<i32, Logical>::new((300, 180).into(), (400, 300).into());
|
||||
let mut shadow = DamageElement::new(shadow_bounds(old_window, 4.0, 18.0));
|
||||
let mut tracker = OutputDamageTracker::new((1_000, 800), Scale::from(1.0), Transform::Normal);
|
||||
let mut tracker =
|
||||
OutputDamageTracker::new((1_000, 800), Scale::from(1.0), Transform::Normal);
|
||||
|
||||
tracker.damage_output(0, &[&shadow]).unwrap();
|
||||
shadow.geometry = shadow_bounds(new_window, 4.0, 18.0);
|
||||
let damage = tracker.damage_output(1, &[&shadow]).unwrap().0.cloned().unwrap();
|
||||
let damage = tracker
|
||||
.damage_output(1, &[&shadow])
|
||||
.unwrap()
|
||||
.0
|
||||
.cloned()
|
||||
.unwrap();
|
||||
|
||||
assert!(damage.iter().any(|rect| rect.contains((64, 48))));
|
||||
assert!(damage.iter().any(|rect| rect.contains((735, 519))));
|
||||
@@ -1098,13 +1183,22 @@ mod tests {
|
||||
#[test]
|
||||
fn translucent_materials_blur_at_full_opacity() {
|
||||
use crate::config::MaterialStyle;
|
||||
assert_eq!(super::material_blur_radius(MaterialStyle::Translucent, 0.0, 18.0), 0.0);
|
||||
assert_eq!(super::material_blur_radius(MaterialStyle::Translucent, 1.0, 18.0), 18.0);
|
||||
assert_eq!(
|
||||
super::material_blur_radius(MaterialStyle::Translucent, 0.0, 18.0),
|
||||
0.0
|
||||
);
|
||||
assert_eq!(
|
||||
super::material_blur_radius(MaterialStyle::Translucent, 1.0, 18.0),
|
||||
18.0
|
||||
);
|
||||
assert_eq!(
|
||||
super::material_blur_radius(MaterialStyle::Translucent, 0.79, 18.0),
|
||||
18.0
|
||||
);
|
||||
assert_eq!(super::material_blur_radius(MaterialStyle::Solid, 0.79, 18.0), 0.0);
|
||||
assert_eq!(
|
||||
super::material_blur_radius(MaterialStyle::Solid, 0.79, 18.0),
|
||||
0.0
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1120,7 +1214,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn close_opacity_only_changes_the_alpha_channel() {
|
||||
assert_eq!(color_with_alpha([0.2, 0.4, 0.6, 0.8], 0.5), [0.2, 0.4, 0.6, 0.4]);
|
||||
assert_eq!(
|
||||
color_with_alpha([0.2, 0.4, 0.6, 0.8], 0.5),
|
||||
[0.2, 0.4, 0.6, 0.4]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1134,7 +1231,10 @@ mod tests {
|
||||
|
||||
for tenth in 0..=10 {
|
||||
let left = 100.0 + f64::from(tenth) / 10.0;
|
||||
let frame = rounded_visual_rect(ferese_layout::Rect::new(left, 50.0, 500.0 - left, 300.0), (0, 0).into());
|
||||
let frame = rounded_visual_rect(
|
||||
ferese_layout::Rect::new(left, 50.0, 500.0 - left, 300.0),
|
||||
(0, 0).into(),
|
||||
);
|
||||
|
||||
assert_eq!(frame.loc.x + frame.size.w, 500);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ pub(super) fn overview_chrome_element(
|
||||
let mode = output.current_mode()?;
|
||||
let parameters = BorderParameters {
|
||||
geometry,
|
||||
clip_rect: framebuffer_clip_rect(corners.rect, mode.size, output.current_transform().invert()),
|
||||
clip_rect: framebuffer_clip_rect(
|
||||
corners.rect,
|
||||
mode.size,
|
||||
output.current_transform().invert(),
|
||||
),
|
||||
radius: corners.radius,
|
||||
width: (width * scale) as f32,
|
||||
color,
|
||||
@@ -30,13 +34,19 @@ pub(super) fn overview_chrome_element(
|
||||
focus_gradient_line: [0.0; 4],
|
||||
focus_mix: 0.0,
|
||||
};
|
||||
let program = if matches!(key.1, OverviewChromePart::Outline | OverviewChromePart::TabListOutline) {
|
||||
let program = if matches!(
|
||||
key.1,
|
||||
OverviewChromePart::Outline | OverviewChromePart::TabListOutline
|
||||
) {
|
||||
rounded_clip_program(&mut state.render, renderer)?.border
|
||||
} else {
|
||||
material_program(&mut state.render, renderer)?.0
|
||||
};
|
||||
let uniforms = |p: &BorderParameters| {
|
||||
if matches!(key.1, OverviewChromePart::Outline | OverviewChromePart::TabListOutline) {
|
||||
if matches!(
|
||||
key.1,
|
||||
OverviewChromePart::Outline | OverviewChromePart::TabListOutline
|
||||
) {
|
||||
border_uniforms(p)
|
||||
} else {
|
||||
tint_uniforms(p)
|
||||
@@ -47,7 +57,9 @@ pub(super) fn overview_chrome_element(
|
||||
.render
|
||||
.outputs
|
||||
.entry(output_id)
|
||||
.or_insert_with(|| OverviewScrim { chrome: HashMap::new() })
|
||||
.or_insert_with(|| OverviewScrim {
|
||||
chrome: HashMap::new(),
|
||||
})
|
||||
.chrome;
|
||||
let cached = chrome
|
||||
.entry(key)
|
||||
@@ -129,7 +141,9 @@ pub(super) fn overview_strip_elements(
|
||||
| OverviewChromePart::TabListOutline
|
||||
| OverviewChromePart::TabList
|
||||
| OverviewChromePart::TabRow
|
||||
| OverviewChromePart::TabFlash => state.windows.window(ferese_layout::WindowId(*id)).is_some(),
|
||||
| OverviewChromePart::TabFlash => {
|
||||
state.windows.window(ferese_layout::WindowId(*id)).is_some()
|
||||
}
|
||||
OverviewChromePart::Card | OverviewChromePart::Outline => card_ids.contains(id),
|
||||
OverviewChromePart::Strip => true,
|
||||
});
|
||||
@@ -149,7 +163,11 @@ pub(super) fn overview_strip_elements(
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let Some(rect) = frame.windows.get(&id).map(|sample| sample.presentation.bounds.current) else {
|
||||
let Some(rect) = frame
|
||||
.windows
|
||||
.get(&id)
|
||||
.map(|sample| sample.presentation.bounds.current)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some((buffer, size)) = state.overview_window_label(id, scale, rect.width) else {
|
||||
@@ -163,7 +181,9 @@ pub(super) fn overview_strip_elements(
|
||||
26.0,
|
||||
);
|
||||
let location = Point::<i32, Physical>::from((
|
||||
((caption.x - f64::from(output_geometry.loc.x) + caption.width * 0.5) * scale).round() as i32 - size.w / 2,
|
||||
((caption.x - f64::from(output_geometry.loc.x) + caption.width * 0.5) * scale).round()
|
||||
as i32
|
||||
- size.w / 2,
|
||||
((caption.y - f64::from(output_geometry.loc.y) + 4.0) * scale).round() as i32,
|
||||
));
|
||||
if let Ok(element) = MemoryRenderBufferRenderElement::from_buffer(
|
||||
@@ -171,7 +191,9 @@ pub(super) fn overview_strip_elements(
|
||||
location.to_f64(),
|
||||
&buffer,
|
||||
Some(alpha),
|
||||
Some(Rectangle::from_size((f64::from(size.w), f64::from(size.h)).into())),
|
||||
Some(Rectangle::from_size(
|
||||
(f64::from(size.w), f64::from(size.h)).into(),
|
||||
)),
|
||||
Some(
|
||||
(
|
||||
(f64::from(size.w) / scale).round() as i32,
|
||||
@@ -183,9 +205,11 @@ pub(super) fn overview_strip_elements(
|
||||
) {
|
||||
let element = RescaleRenderElement::from_element(element, location, 1.0);
|
||||
let element = RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
if let Some(element) =
|
||||
CropRenderElement::from_element(element, scale, physical_rect(caption, output_geometry.loc, scale))
|
||||
{
|
||||
if let Some(element) = CropRenderElement::from_element(
|
||||
element,
|
||||
scale,
|
||||
physical_rect(caption, output_geometry.loc, scale),
|
||||
) {
|
||||
elements.push(element.into());
|
||||
}
|
||||
}
|
||||
@@ -235,13 +259,20 @@ pub(super) fn overview_strip_elements(
|
||||
let shape = window_corner_shape(window);
|
||||
|
||||
if let Some(programs) = corner_program(&mut state.render, renderer, shape) {
|
||||
let clip_changed = state
|
||||
.output_id(output)
|
||||
.is_some_and(|output| state.render.thumbnail_corner_changed(*id, output, shape));
|
||||
let Some(sample) = frame.windows.get(id) else { continue };
|
||||
let clip_changed = state.output_id(output).is_some_and(|output| {
|
||||
state.render.thumbnail_corner_changed(*id, output, shape)
|
||||
});
|
||||
let Some(sample) = frame.windows.get(id) else {
|
||||
continue;
|
||||
};
|
||||
let presentation = sample.presentation.thumbnail(*rect);
|
||||
let corners = RoundedRect::new(presentation.bounds.current, output_geometry.loc, scale, 4.0)
|
||||
.with_shape(shape);
|
||||
let corners = RoundedRect::new(
|
||||
presentation.bounds.current,
|
||||
output_geometry.loc,
|
||||
scale,
|
||||
4.0,
|
||||
)
|
||||
.with_shape(shape);
|
||||
if let Some(snapshot) = state.render.snapshot(id)
|
||||
&& snapshot.context == renderer.context_id().erased()
|
||||
&& (snapshot.scale - scale).abs() < 0.001
|
||||
@@ -277,16 +308,20 @@ pub(super) fn overview_strip_elements(
|
||||
}
|
||||
if let Some((buffer, size)) = state.overview_workspace_label(card.index, scale) {
|
||||
let location = Point::<i32, Physical>::from((
|
||||
((card.rect.x - f64::from(output_geometry.loc.x) + card.rect.width * 0.5) * scale).round() as i32
|
||||
((card.rect.x - f64::from(output_geometry.loc.x) + card.rect.width * 0.5) * scale)
|
||||
.round() as i32
|
||||
- size.w / 2,
|
||||
((card.rect.y - f64::from(output_geometry.loc.y) + card.rect.height - 20.0) * scale).round() as i32,
|
||||
((card.rect.y - f64::from(output_geometry.loc.y) + card.rect.height - 20.0) * scale)
|
||||
.round() as i32,
|
||||
));
|
||||
if let Ok(element) = MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
location.to_f64(),
|
||||
&buffer,
|
||||
Some(alpha),
|
||||
Some(Rectangle::from_size((f64::from(size.w), f64::from(size.h)).into())),
|
||||
Some(Rectangle::from_size(
|
||||
(f64::from(size.w), f64::from(size.h)).into(),
|
||||
)),
|
||||
Some(
|
||||
(
|
||||
(f64::from(size.w) / scale).round() as i32,
|
||||
@@ -297,7 +332,8 @@ pub(super) fn overview_strip_elements(
|
||||
RenderElementKind::Unspecified,
|
||||
) {
|
||||
let element = RescaleRenderElement::from_element(element, location, 1.0);
|
||||
let element = RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
let element =
|
||||
RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
if let Some(element) = CropRenderElement::from_element(
|
||||
element,
|
||||
scale,
|
||||
|
||||
@@ -60,7 +60,12 @@ impl RenderResources {
|
||||
changed
|
||||
}
|
||||
|
||||
pub(super) fn thumbnail_corner_changed(&mut self, id: WindowId, output: OutputId, shape: CornerShape) -> bool {
|
||||
pub(super) fn thumbnail_corner_changed(
|
||||
&mut self,
|
||||
id: WindowId,
|
||||
output: OutputId,
|
||||
shape: CornerShape,
|
||||
) -> bool {
|
||||
self.windows
|
||||
.entry(id)
|
||||
.or_default()
|
||||
@@ -117,7 +122,9 @@ impl RenderResources {
|
||||
}
|
||||
|
||||
pub fn snapshots(&self) -> impl Iterator<Item = &ResizeSnapshot> {
|
||||
self.windows.values().filter_map(|window| window.snapshot.as_ref())
|
||||
self.windows
|
||||
.values()
|
||||
.filter_map(|window| window.snapshot.as_ref())
|
||||
}
|
||||
|
||||
pub fn set_snapshot(&mut self, id: WindowId, snapshot: ResizeSnapshot) {
|
||||
@@ -130,7 +137,10 @@ impl RenderResources {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain_snapshots(&mut self, mut keep: impl FnMut(&WindowId, &mut ResizeSnapshot) -> bool) {
|
||||
pub fn retain_snapshots(
|
||||
&mut self,
|
||||
mut keep: impl FnMut(&WindowId, &mut ResizeSnapshot) -> bool,
|
||||
) {
|
||||
for (id, window) in &mut self.windows {
|
||||
if let Some(snapshot) = &mut window.snapshot
|
||||
&& !keep(id, snapshot)
|
||||
@@ -159,7 +169,8 @@ impl RenderResources {
|
||||
.filter(|window| &window.snapshot.context == context)
|
||||
.map(|window| window.presentation.id)
|
||||
.collect();
|
||||
self.closing.retain(|window| &window.snapshot.context != context);
|
||||
self.closing
|
||||
.retain(|window| &window.snapshot.context != context);
|
||||
for id in removed {
|
||||
self.remove_window(id);
|
||||
}
|
||||
@@ -201,8 +212,12 @@ mod tests {
|
||||
#[test]
|
||||
fn window_removal_releases_all_its_caches_without_removing_other_windows() {
|
||||
let mut resources = RenderResources::default();
|
||||
resources.windows.insert(WindowId(1), WindowBuffers::default());
|
||||
resources.windows.insert(WindowId(2), WindowBuffers::default());
|
||||
resources
|
||||
.windows
|
||||
.insert(WindowId(1), WindowBuffers::default());
|
||||
resources
|
||||
.windows
|
||||
.insert(WindowId(2), WindowBuffers::default());
|
||||
|
||||
resources.remove_window(WindowId(1));
|
||||
resources.clear_snapshot(&WindowId(1));
|
||||
@@ -217,7 +232,10 @@ mod tests {
|
||||
fn context_removal_evicts_programs_and_snapshots_without_touching_another_gpu() {
|
||||
use smithay::backend::egl::{EGLContext, EGLDevice, EGLDisplay};
|
||||
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let device = EGLDevice::enumerate()
|
||||
.unwrap()
|
||||
.last()
|
||||
.expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let mut first = unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
let mut second = unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
@@ -229,7 +247,12 @@ mod tests {
|
||||
rounded_clip_program(&mut resources, renderer).unwrap();
|
||||
material_program(&mut resources, renderer).unwrap();
|
||||
blur_program(&mut resources, renderer).unwrap();
|
||||
let texture = Offscreen::<GlesTexture>::create_buffer(renderer, Fourcc::Abgr8888, (16, 16).into()).unwrap();
|
||||
let texture = Offscreen::<GlesTexture>::create_buffer(
|
||||
renderer,
|
||||
Fourcc::Abgr8888,
|
||||
(16, 16).into(),
|
||||
)
|
||||
.unwrap();
|
||||
resources.set_snapshot(
|
||||
id,
|
||||
ResizeSnapshot {
|
||||
|
||||
@@ -10,7 +10,10 @@ pub(crate) fn animated_window_elements(
|
||||
output_elements(state, renderer, output, true)
|
||||
}
|
||||
|
||||
pub(crate) fn frame_effect_metrics(_elements: &[AnimatedWindowRenderElement], _scale: f64) -> FrameEffectMetrics {
|
||||
pub(crate) fn frame_effect_metrics(
|
||||
_elements: &[AnimatedWindowRenderElement],
|
||||
_scale: f64,
|
||||
) -> FrameEffectMetrics {
|
||||
FrameEffectMetrics
|
||||
}
|
||||
|
||||
@@ -69,9 +72,16 @@ fn scene_elements(
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let background = state.session_lock.backgrounds.entry(output.clone()).or_insert_with(|| {
|
||||
smithay::backend::renderer::element::solid::SolidColorBuffer::new(geometry.size, [0.0, 0.0, 0.0, 1.0])
|
||||
});
|
||||
let background = state
|
||||
.session_lock
|
||||
.backgrounds
|
||||
.entry(output.clone())
|
||||
.or_insert_with(|| {
|
||||
smithay::backend::renderer::element::solid::SolidColorBuffer::new(
|
||||
geometry.size,
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
)
|
||||
});
|
||||
background.resize(geometry.size);
|
||||
let opacity = state.session_lock.idle_opacity;
|
||||
if opacity > 0.0 {
|
||||
@@ -87,8 +97,14 @@ fn scene_elements(
|
||||
});
|
||||
overlay.resize(geometry.size);
|
||||
elements.push(
|
||||
SolidColorRenderElement::from_buffer(overlay, (0, 0), scale, opacity, RenderElementKind::Unspecified)
|
||||
.into(),
|
||||
SolidColorRenderElement::from_buffer(
|
||||
overlay,
|
||||
(0, 0),
|
||||
scale,
|
||||
opacity,
|
||||
RenderElementKind::Unspecified,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
if let Some(surface) = state
|
||||
@@ -98,7 +114,10 @@ fn scene_elements(
|
||||
.filter(|surface| surface.alive())
|
||||
{
|
||||
elements.extend(
|
||||
render_elements_from_surface_tree::<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>(
|
||||
render_elements_from_surface_tree::<
|
||||
GlesRenderer,
|
||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||
>(
|
||||
renderer,
|
||||
surface.wl_surface(),
|
||||
(0, 0),
|
||||
@@ -111,7 +130,14 @@ fn scene_elements(
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
SolidColorRenderElement::from_buffer(background, (0, 0), scale, 1.0, RenderElementKind::Unspecified).into(),
|
||||
SolidColorRenderElement::from_buffer(
|
||||
background,
|
||||
(0, 0),
|
||||
scale,
|
||||
1.0,
|
||||
RenderElementKind::Unspecified,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
return elements;
|
||||
}
|
||||
@@ -126,11 +152,12 @@ fn scene_elements(
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let upper_layers: &[Layer] = if !state.layer_visible_for_frame(output, Layer::Top, frame.overview.is_presenting()) {
|
||||
&[Layer::Overlay]
|
||||
} else {
|
||||
&[Layer::Overlay, Layer::Top]
|
||||
};
|
||||
let upper_layers: &[Layer] =
|
||||
if !state.layer_visible_for_frame(output, Layer::Top, frame.overview.is_presenting()) {
|
||||
&[Layer::Overlay]
|
||||
} else {
|
||||
&[Layer::Overlay, Layer::Top]
|
||||
};
|
||||
elements.extend(layer_elements(state, renderer, output, upper_layers));
|
||||
if frame.overview.is_presenting() {
|
||||
elements.extend(overview_strip_elements(
|
||||
@@ -196,7 +223,9 @@ fn scene_elements(
|
||||
if let Some(group) = closing.remove(&Some(id)) {
|
||||
elements.extend(group);
|
||||
}
|
||||
elements.extend(super::tabs::tab_elements(state, renderer, output, frame, id, capture));
|
||||
elements.extend(super::tabs::tab_elements(
|
||||
state, renderer, output, frame, id, capture,
|
||||
));
|
||||
let constrain = rounded_visual_rect(visual, output_geometry.loc);
|
||||
let material_surface = window
|
||||
.toplevel()
|
||||
@@ -211,7 +240,8 @@ fn scene_elements(
|
||||
state.theme_settings.window_radius
|
||||
} * decoration_progress;
|
||||
let shape = window_corner_shape(&window);
|
||||
let corners = RoundedRect::new(visual, output_geometry.loc, scale, window_radius).with_shape(shape);
|
||||
let corners =
|
||||
RoundedRect::new(visual, output_geometry.loc, scale, window_radius).with_shape(shape);
|
||||
let corner_shape_changed = state.render.prepare_window_corners(id, shape);
|
||||
let rounded_clip_program = corner_program(&mut state.render, renderer, shape);
|
||||
// Only overview/close intentionally scale the complete application.
|
||||
@@ -241,7 +271,8 @@ fn scene_elements(
|
||||
let shadow_color = state.theme_settings.shadow_color.0;
|
||||
let focus = sample.presentation.focus();
|
||||
let border_width = state.theme_settings.border_width
|
||||
+ (state.theme_settings.focus_ring_width - state.theme_settings.border_width) * focus;
|
||||
+ (state.theme_settings.focus_ring_width - state.theme_settings.border_width)
|
||||
* focus;
|
||||
let border_color = state.theme_settings.border_color.0;
|
||||
let gradient = state.theme_settings.border_gradient;
|
||||
|
||||
@@ -273,7 +304,10 @@ fn scene_elements(
|
||||
scale,
|
||||
shadow_offset_y * offset_factor,
|
||||
shadow_blur * blur_factor,
|
||||
shadow_opacity * opacity_factor * f64::from(presentation_alpha) * decoration_progress,
|
||||
shadow_opacity
|
||||
* opacity_factor
|
||||
* f64::from(presentation_alpha)
|
||||
* decoration_progress,
|
||||
shadow_color,
|
||||
output,
|
||||
&programs,
|
||||
@@ -313,9 +347,16 @@ fn scene_elements(
|
||||
{
|
||||
let mut color = state.theme_settings.surface_base_color.0;
|
||||
color[3] = presentation_alpha;
|
||||
if let Some(fill) =
|
||||
window_tint_element(&mut state.render, renderer, id, constrain, corners, color, true, output)
|
||||
{
|
||||
if let Some(fill) = window_tint_element(
|
||||
&mut state.render,
|
||||
renderer,
|
||||
id,
|
||||
constrain,
|
||||
corners,
|
||||
color,
|
||||
true,
|
||||
output,
|
||||
) {
|
||||
// Front-to-back: fill uncovered strips behind the native
|
||||
// content instead of stretching it or exposing wallpaper.
|
||||
elements.push(fill.into());
|
||||
@@ -342,8 +383,13 @@ fn scene_elements(
|
||||
elements.push(background);
|
||||
}
|
||||
if material_surface.is_none()
|
||||
&& let Some(blur) =
|
||||
super::window_blur::window_blur_element(state, renderer, output, sample.presentation, corners)
|
||||
&& let Some(blur) = super::window_blur::window_blur_element(
|
||||
state,
|
||||
renderer,
|
||||
output,
|
||||
sample.presentation,
|
||||
corners,
|
||||
)
|
||||
{
|
||||
elements.push(blur);
|
||||
}
|
||||
@@ -420,20 +466,22 @@ pub(super) fn layer_elements(
|
||||
requested_layers
|
||||
.iter()
|
||||
.flat_map(|requested| {
|
||||
map.layers_on(*requested)
|
||||
.rev()
|
||||
.filter_map(|layer| map.layer_geometry(layer).map(|geometry| (geometry, layer.clone())))
|
||||
map.layers_on(*requested).rev().filter_map(|layer| {
|
||||
map.layer_geometry(layer)
|
||||
.map(|geometry| (geometry, layer.clone()))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
state.render.surfaces.retain(|surface, _| {
|
||||
surface.is_alive()
|
||||
|| state
|
||||
.render
|
||||
.closing
|
||||
.iter()
|
||||
.any(|window| window.material.as_ref().is_some_and(|(key, ..)| key == surface))
|
||||
|| state.render.closing.iter().any(|window| {
|
||||
window
|
||||
.material
|
||||
.as_ref()
|
||||
.is_some_and(|(key, ..)| key == surface)
|
||||
})
|
||||
});
|
||||
|
||||
let mut elements = Vec::new();
|
||||
@@ -486,7 +534,8 @@ pub(super) fn cursor_elements(
|
||||
let Some(pointer) = state.seat.get_pointer() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let output_crop = Rectangle::<i32, Logical>::from_size(output_geometry.size).to_physical_precise_round(scale);
|
||||
let output_crop =
|
||||
Rectangle::<i32, Logical>::from_size(output_geometry.size).to_physical_precise_round(scale);
|
||||
let origin = Point::<i32, Physical>::default();
|
||||
let pointer_location = pointer.current_location() - output_geometry.loc.to_f64();
|
||||
match &state.cursor_status {
|
||||
@@ -498,9 +547,13 @@ pub(super) fn cursor_elements(
|
||||
.map(|attributes| attributes.lock().unwrap().hotspot)
|
||||
.unwrap_or_default()
|
||||
});
|
||||
let physical_location = (pointer_location - hotspot.to_f64()).to_physical_precise_round(scale);
|
||||
let physical_location =
|
||||
(pointer_location - hotspot.to_f64()).to_physical_precise_round(scale);
|
||||
|
||||
render_elements_from_surface_tree::<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>(
|
||||
render_elements_from_surface_tree::<
|
||||
GlesRenderer,
|
||||
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||
>(
|
||||
renderer,
|
||||
surface,
|
||||
physical_location,
|
||||
@@ -511,7 +564,8 @@ pub(super) fn cursor_elements(
|
||||
.into_iter()
|
||||
.filter_map(|element| {
|
||||
let element = RescaleRenderElement::from_element(element, origin, 1.0);
|
||||
let element = RelocateRenderElement::from_element(element, origin, Relocate::Relative);
|
||||
let element =
|
||||
RelocateRenderElement::from_element(element, origin, Relocate::Relative);
|
||||
|
||||
CropRenderElement::from_element(element, scale, output_crop).map(Into::into)
|
||||
})
|
||||
@@ -573,8 +627,9 @@ mod layer_crop_tests {
|
||||
[1.0; 4],
|
||||
RenderElementKind::Unspecified,
|
||||
);
|
||||
let crop = CropRenderElement::from_element(panel, 1.0, layer_output_crop(mode, transform))
|
||||
.expect("bottom panel must remain visible");
|
||||
let crop =
|
||||
CropRenderElement::from_element(panel, 1.0, layer_output_crop(mode, transform))
|
||||
.expect("bottom panel must remain visible");
|
||||
assert_eq!(crop.geometry(1.0.into()), geometry, "{transform:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement};
|
||||
use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesPixelProgram, GlesRenderer, Uniform};
|
||||
use smithay::backend::renderer::gles::{
|
||||
GlesError, GlesFrame, GlesPixelProgram, GlesRenderer, Uniform,
|
||||
};
|
||||
use smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};
|
||||
use smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Transform};
|
||||
|
||||
@@ -47,7 +49,11 @@ impl SharedPixelShaderElement {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resize(&mut self, area: Rectangle<i32, Logical>, opaque: Option<Vec<Rectangle<i32, Logical>>>) {
|
||||
pub(crate) fn resize(
|
||||
&mut self,
|
||||
area: Rectangle<i32, Logical>,
|
||||
opaque: Option<Vec<Rectangle<i32, Logical>>>,
|
||||
) {
|
||||
let opaque = opaque.unwrap_or_default();
|
||||
|
||||
if self.state.area != area || self.state.opaque != opaque {
|
||||
@@ -75,7 +81,13 @@ impl Element for SharedPixelShaderElement {
|
||||
}
|
||||
|
||||
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||
Rectangle::from_size(self.state.area.size.to_f64().to_buffer(1.0, Transform::Normal))
|
||||
Rectangle::from_size(
|
||||
self.state
|
||||
.area
|
||||
.size
|
||||
.to_f64()
|
||||
.to_buffer(1.0, Transform::Normal),
|
||||
)
|
||||
}
|
||||
|
||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||
@@ -159,9 +171,13 @@ mod tests {
|
||||
#[test]
|
||||
#[ignore = "requires an EGL rendering device"]
|
||||
fn shared_shader_matches_smithay_and_keeps_old_frames_immutable() {
|
||||
let device = EGLDevice::enumerate().unwrap().last().expect("an EGL device");
|
||||
let device = EGLDevice::enumerate()
|
||||
.unwrap()
|
||||
.last()
|
||||
.expect("an EGL device");
|
||||
let display = unsafe { EGLDisplay::new(device).unwrap() };
|
||||
let mut renderer = unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
let mut renderer =
|
||||
unsafe { GlesRenderer::new(EGLContext::new(&display).unwrap()).unwrap() };
|
||||
let program = renderer
|
||||
.compile_custom_pixel_shader(
|
||||
super::super::corner_shader(super::super::ROUNDED_SOLID_SHADER),
|
||||
@@ -191,7 +207,14 @@ mod tests {
|
||||
uniforms(red),
|
||||
Kind::Unspecified,
|
||||
);
|
||||
let mut shared = SharedPixelShaderElement::new(program, area, opaque, 0.5, uniforms(red), Kind::Unspecified);
|
||||
let mut shared = SharedPixelShaderElement::new(
|
||||
program,
|
||||
area,
|
||||
opaque,
|
||||
0.5,
|
||||
uniforms(red),
|
||||
Kind::Unspecified,
|
||||
);
|
||||
let old = shared.clone();
|
||||
let old_commit = old.current_commit();
|
||||
|
||||
@@ -205,14 +228,20 @@ mod tests {
|
||||
);
|
||||
|
||||
for scale in [1.0, 1.25, 2.0] {
|
||||
assert_eq!(shared.geometry(scale.into()), baseline.geometry(scale.into()));
|
||||
assert_eq!(
|
||||
shared.geometry(scale.into()),
|
||||
baseline.geometry(scale.into())
|
||||
);
|
||||
assert_eq!(
|
||||
&*shared.opaque_regions(scale.into()),
|
||||
&*baseline.opaque_regions(scale.into())
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(pixels(&mut renderer, &shared), pixels(&mut renderer, &baseline));
|
||||
assert_eq!(
|
||||
pixels(&mut renderer, &shared),
|
||||
pixels(&mut renderer, &baseline)
|
||||
);
|
||||
shared.resize(area, Some(vec![Rectangle::from_size((20, 20).into())]));
|
||||
assert!(Arc::ptr_eq(&shared.state, &old.state));
|
||||
shared.update_uniforms(uniforms(green));
|
||||
@@ -220,12 +249,21 @@ mod tests {
|
||||
assert_eq!(shared.id(), old.id());
|
||||
assert_eq!(old.current_commit(), old_commit);
|
||||
assert_ne!(shared.current_commit(), old_commit);
|
||||
assert_eq!(pixels(&mut renderer, &old), pixels(&mut renderer, &baseline));
|
||||
assert_ne!(pixels(&mut renderer, &shared), pixels(&mut renderer, &baseline));
|
||||
assert_eq!(
|
||||
pixels(&mut renderer, &old),
|
||||
pixels(&mut renderer, &baseline)
|
||||
);
|
||||
assert_ne!(
|
||||
pixels(&mut renderer, &shared),
|
||||
pixels(&mut renderer, &baseline)
|
||||
);
|
||||
|
||||
let retained = shared.clone();
|
||||
shared.resize(Rectangle::from_size((16, 16).into()), None);
|
||||
assert_eq!(retained.geometry(1.0.into()), area.to_physical_precise_round(1.0));
|
||||
assert_eq!(
|
||||
retained.geometry(1.0.into()),
|
||||
area.to_physical_precise_round(1.0)
|
||||
);
|
||||
assert_eq!(shared.geometry(1.0.into()).size, (16, 16).into());
|
||||
assert!(shared.opaque_regions(1.0.into()).is_empty());
|
||||
assert_eq!(shared.alpha(), baseline.alpha());
|
||||
|
||||
@@ -13,14 +13,23 @@ fn text_element(
|
||||
centered: bool,
|
||||
) -> Option<AnimatedWindowRenderElement> {
|
||||
let settings = state.tab_indicator_settings;
|
||||
let color = settings.text_color.unwrap_or(state.theme_settings.text_primary_color.0);
|
||||
let color = settings
|
||||
.text_color
|
||||
.unwrap_or(state.theme_settings.text_primary_color.0);
|
||||
let available = (rect.width - 16.0).max(0.0);
|
||||
let mut title: String = text.chars().filter(|c| !c.is_control()).take(1024).collect();
|
||||
if state.indicator_text_width(&title, settings.font_size) > available || text.chars().count() > 1024 {
|
||||
let mut title: String = text
|
||||
.chars()
|
||||
.filter(|c| !c.is_control())
|
||||
.take(1024)
|
||||
.collect();
|
||||
if state.indicator_text_width(&title, settings.font_size) > available
|
||||
|| text.chars().count() > 1024
|
||||
{
|
||||
let mut boundaries: Vec<_> = title.char_indices().map(|(index, _)| index).collect();
|
||||
boundaries.push(title.len());
|
||||
let fits = boundaries.partition_point(|&end| {
|
||||
state.indicator_text_width(&format!("{}…", &title[..end]), settings.font_size) <= available
|
||||
state.indicator_text_width(&format!("{}…", &title[..end]), settings.font_size)
|
||||
<= available
|
||||
});
|
||||
if fits == 0 {
|
||||
title.clear();
|
||||
@@ -37,14 +46,17 @@ fn text_element(
|
||||
};
|
||||
let location = Point::<i32, Physical>::from((
|
||||
((rect.x - f64::from(output_origin.x) + x) * scale).round() as i32,
|
||||
((rect.y - f64::from(output_origin.y) + rect.height * 0.5) * scale).round() as i32 - size.h / 2,
|
||||
((rect.y - f64::from(output_origin.y) + rect.height * 0.5) * scale).round() as i32
|
||||
- size.h / 2,
|
||||
));
|
||||
let element = MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
location.to_f64(),
|
||||
&buffer,
|
||||
Some(alpha),
|
||||
Some(Rectangle::from_size((f64::from(size.w), f64::from(size.h)).into())),
|
||||
Some(Rectangle::from_size(
|
||||
(f64::from(size.w), f64::from(size.h)).into(),
|
||||
)),
|
||||
Some(
|
||||
(
|
||||
(f64::from(size.w) / scale).round() as i32,
|
||||
@@ -57,12 +69,23 @@ fn text_element(
|
||||
.ok()?;
|
||||
let element = RescaleRenderElement::from_element(element, location, 1.0);
|
||||
let element = RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
let clip = Rect::new(rect.x + 8.0, rect.y, (rect.width - 16.0).max(0.0), rect.height);
|
||||
CropRenderElement::from_element(element, scale, physical_rect(clip, output_origin, scale)).map(Into::into)
|
||||
let clip = Rect::new(
|
||||
rect.x + 8.0,
|
||||
rect.y,
|
||||
(rect.width - 16.0).max(0.0),
|
||||
rect.height,
|
||||
);
|
||||
CropRenderElement::from_element(element, scale, physical_rect(clip, output_origin, scale))
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
fn title(state: &Ferese, id: WindowId, capture: bool) -> String {
|
||||
if capture && state.windows.window(id).is_none_or(|w| state.capture_protected(w)) {
|
||||
if capture
|
||||
&& state
|
||||
.windows
|
||||
.window(id)
|
||||
.is_none_or(|w| state.capture_protected(w))
|
||||
{
|
||||
return "Protected window".into();
|
||||
}
|
||||
state
|
||||
@@ -99,8 +122,12 @@ pub(super) fn elements(
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
let settings = state.tab_indicator_settings;
|
||||
let anchor = bar.group.windows[0];
|
||||
let accent = settings.active_color.unwrap_or(state.theme_settings.accent_color.0);
|
||||
let inactive = settings.inactive_color.unwrap_or(state.theme_settings.border_color.0);
|
||||
let accent = settings
|
||||
.active_color
|
||||
.unwrap_or(state.theme_settings.accent_color.0);
|
||||
let inactive = settings
|
||||
.inactive_color
|
||||
.unwrap_or(state.theme_settings.border_color.0);
|
||||
let mut background = settings
|
||||
.background_color
|
||||
.unwrap_or(state.theme_settings.surface_raised_color.0);
|
||||
@@ -109,7 +136,8 @@ pub(super) fn elements(
|
||||
state.theme_settings.material_style,
|
||||
state.theme_settings.shell_opacity as f32,
|
||||
);
|
||||
background[3] = if state.theme_settings.reduce_transparency || material.style == crate::config::MaterialStyle::Solid
|
||||
background[3] = if state.theme_settings.reduce_transparency
|
||||
|| material.style == crate::config::MaterialStyle::Solid
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
@@ -135,7 +163,15 @@ pub(super) fn elements(
|
||||
.tab_indicator
|
||||
.first_row
|
||||
.min(bar.group.windows.len().saturating_sub(g.rows));
|
||||
for (row, id) in bar.group.windows.iter().copied().skip(first).take(g.rows).enumerate() {
|
||||
for (row, id) in bar
|
||||
.group
|
||||
.windows
|
||||
.iter()
|
||||
.copied()
|
||||
.skip(first)
|
||||
.take(g.rows)
|
||||
.enumerate()
|
||||
{
|
||||
let rect = g.row(row);
|
||||
let label = title(state, id, capture);
|
||||
if let Some(element) = text_element(
|
||||
@@ -153,7 +189,9 @@ pub(super) fn elements(
|
||||
if id == bar.group.active {
|
||||
// Match the control center's selected shell buttons: a quiet
|
||||
// foreground tint, with accent reserved for the active rail.
|
||||
let mut fill = settings.text_color.unwrap_or(state.theme_settings.text_primary_color.0);
|
||||
let mut fill = settings
|
||||
.text_color
|
||||
.unwrap_or(state.theme_settings.text_primary_color.0);
|
||||
fill[3] = 0.14 * alpha * hover;
|
||||
if let Some(element) = overview_chrome_element(
|
||||
state,
|
||||
@@ -172,7 +210,9 @@ pub(super) fn elements(
|
||||
}
|
||||
}
|
||||
if settings.outline_width > 0.0 {
|
||||
let color = settings.outline_color.unwrap_or(state.theme_settings.border_color.0);
|
||||
let color = settings
|
||||
.outline_color
|
||||
.unwrap_or(state.theme_settings.border_color.0);
|
||||
if let Some(element) = overview_chrome_element(
|
||||
state,
|
||||
renderer,
|
||||
@@ -224,7 +264,8 @@ pub(super) fn elements(
|
||||
output_geometry.size.h as f64,
|
||||
);
|
||||
let rect = Rect::new(
|
||||
(content.x + (content.width - width) * 0.5).clamp(bounds.x, bounds.x + bounds.width - width),
|
||||
(content.x + (content.width - width) * 0.5)
|
||||
.clamp(bounds.x, bounds.x + bounds.width - width),
|
||||
(content.y + settings.padding).clamp(bounds.y, bounds.y + bounds.height - g.row_height),
|
||||
width,
|
||||
g.row_height,
|
||||
@@ -263,7 +304,11 @@ pub(super) fn elements(
|
||||
g.rail.width,
|
||||
g.segment_height,
|
||||
);
|
||||
let color = if id == bar.group.active { accent } else { inactive };
|
||||
let color = if id == bar.group.active {
|
||||
accent
|
||||
} else {
|
||||
inactive
|
||||
};
|
||||
if let Some(element) = overview_chrome_element(
|
||||
state,
|
||||
renderer,
|
||||
|
||||
@@ -48,7 +48,12 @@ pub(super) fn tab_elements(
|
||||
};
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
let width = content.width / bar.group.windows.len() as f64;
|
||||
let shell = ferese_layout::Rect::new(content.x, content.y - bar.rect.height, content.width, bar.rect.height);
|
||||
let shell = ferese_layout::Rect::new(
|
||||
content.x,
|
||||
content.y - bar.rect.height,
|
||||
content.width,
|
||||
bar.rect.height,
|
||||
);
|
||||
let radius = state
|
||||
.theme_settings
|
||||
.material_radius
|
||||
@@ -89,14 +94,18 @@ pub(super) fn tab_elements(
|
||||
+ ((rect.width - f64::from(size.w) / scale) * 0.5).max(8.0))
|
||||
* scale)
|
||||
.round() as i32,
|
||||
((rect.y - f64::from(output_geometry.loc.y) + rect.height * 0.5) * scale).round() as i32 - size.h / 2,
|
||||
((rect.y - f64::from(output_geometry.loc.y) + rect.height * 0.5) * scale).round()
|
||||
as i32
|
||||
- size.h / 2,
|
||||
));
|
||||
if let Ok(element) = MemoryRenderBufferRenderElement::from_buffer(
|
||||
renderer,
|
||||
location.to_f64(),
|
||||
&buffer,
|
||||
Some(alpha),
|
||||
Some(Rectangle::from_size((f64::from(size.w), f64::from(size.h)).into())),
|
||||
Some(Rectangle::from_size(
|
||||
(f64::from(size.w), f64::from(size.h)).into(),
|
||||
)),
|
||||
Some(
|
||||
(
|
||||
(f64::from(size.w) / scale).round() as i32,
|
||||
@@ -107,11 +116,19 @@ pub(super) fn tab_elements(
|
||||
RenderElementKind::Unspecified,
|
||||
) {
|
||||
let element = RescaleRenderElement::from_element(element, location, 1.0);
|
||||
let element = RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
let clip = ferese_layout::Rect::new(rect.x + 8.0, rect.y, (rect.width - 16.0).max(0.0), rect.height);
|
||||
if let Some(element) =
|
||||
CropRenderElement::from_element(element, scale, physical_rect(clip, output_geometry.loc, scale))
|
||||
{
|
||||
let element =
|
||||
RelocateRenderElement::from_element(element, (0, 0), Relocate::Relative);
|
||||
let clip = ferese_layout::Rect::new(
|
||||
rect.x + 8.0,
|
||||
rect.y,
|
||||
(rect.width - 16.0).max(0.0),
|
||||
rect.height,
|
||||
);
|
||||
if let Some(element) = CropRenderElement::from_element(
|
||||
element,
|
||||
scale,
|
||||
physical_rect(clip, output_geometry.loc, scale),
|
||||
) {
|
||||
elements.push(element.into());
|
||||
}
|
||||
}
|
||||
@@ -126,7 +143,10 @@ pub(super) fn tab_elements(
|
||||
// The shell palette supplies the surface and accent. Keep selection
|
||||
// inside the shell so its corners follow the same continuous profile.
|
||||
let mut fill = state.theme_settings.surface_raised_color.0;
|
||||
for (channel, accent) in fill[..3].iter_mut().zip(state.theme_settings.accent_color.0) {
|
||||
for (channel, accent) in fill[..3]
|
||||
.iter_mut()
|
||||
.zip(state.theme_settings.accent_color.0)
|
||||
{
|
||||
*channel += (accent - *channel) * 0.16;
|
||||
}
|
||||
fill[3] = alpha * opacity;
|
||||
|
||||
@@ -68,22 +68,34 @@ pub(super) fn window_blur_element(
|
||||
};
|
||||
let key = (renderer.context_id().erased(), state.output_id(output)?);
|
||||
let program = blur_program(&mut state.render, renderer)?;
|
||||
let buffers = &mut state.render.windows.entry(presentation.id).or_default().blur;
|
||||
let buffers = &mut state
|
||||
.render
|
||||
.windows
|
||||
.entry(presentation.id)
|
||||
.or_default()
|
||||
.blur;
|
||||
let capture_rect = framebuffer_capture_rect(parameters.sample_framebuffer);
|
||||
let capture_size = Size::from((capture_rect[2], capture_rect[3]));
|
||||
if buffers
|
||||
.get(&key)
|
||||
.is_none_or(|cached| cached.element.texture.size() != capture_size)
|
||||
{
|
||||
let texture = match Offscreen::<GlesTexture>::create_buffer(renderer, Fourcc::Abgr8888, capture_size) {
|
||||
Ok(texture) => texture,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "window blur allocation failed");
|
||||
buffers.remove(&key);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let element = BlurRenderElement::new(texture, program.0, ¶meters, Arc::new(AtomicBool::new(true)));
|
||||
let texture =
|
||||
match Offscreen::<GlesTexture>::create_buffer(renderer, Fourcc::Abgr8888, capture_size)
|
||||
{
|
||||
Ok(texture) => texture,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "window blur allocation failed");
|
||||
buffers.remove(&key);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let element = BlurRenderElement::new(
|
||||
texture,
|
||||
program.0,
|
||||
¶meters,
|
||||
Arc::new(AtomicBool::new(true)),
|
||||
);
|
||||
buffers.insert(
|
||||
key.clone(),
|
||||
WindowBlur {
|
||||
|
||||
@@ -34,7 +34,11 @@ pub(super) fn snapshot_element(
|
||||
.into(),
|
||||
)
|
||||
.intersection(pixels)?;
|
||||
let clip = framebuffer_clip_rect(pixels, output.current_mode()?.size, output.current_transform().invert());
|
||||
let clip = framebuffer_clip_rect(
|
||||
pixels,
|
||||
output.current_mode()?.size,
|
||||
output.current_transform().invert(),
|
||||
);
|
||||
Some(NativeTextureElement {
|
||||
id: snapshot.id.clone(),
|
||||
commit: snapshot.commit,
|
||||
@@ -46,7 +50,10 @@ pub(super) fn snapshot_element(
|
||||
))),
|
||||
alpha: presentation.alpha() * handoff,
|
||||
program: Some(programs.texture.clone()),
|
||||
uniforms: vec![Uniform::new("clip_rect", clip), Uniform::new("radius", corners.radius)],
|
||||
uniforms: vec![
|
||||
Uniform::new("clip_rect", clip),
|
||||
Uniform::new("radius", corners.radius),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,7 +91,8 @@ mod tests {
|
||||
Some((0, 0).into()),
|
||||
);
|
||||
let mut resources = RenderResources::default();
|
||||
let programs = corner_program(&mut resources, &mut renderer, CornerShape::Continuous).unwrap();
|
||||
let programs =
|
||||
corner_program(&mut resources, &mut renderer, CornerShape::Continuous).unwrap();
|
||||
for (scale, focus_alpha) in [1.0, 1.25, 1.5, 2.0]
|
||||
.into_iter()
|
||||
.flat_map(|scale| [1.0, 0.8].map(|alpha| (scale, alpha)))
|
||||
@@ -100,17 +108,22 @@ mod tests {
|
||||
scale_content: false,
|
||||
native_size: None,
|
||||
};
|
||||
let corners =
|
||||
RoundedRect::new(live.bounds.current, (0, 0).into(), scale, 8.0).with_shape(CornerShape::Continuous);
|
||||
let corners = RoundedRect::new(live.bounds.current, (0, 0).into(), scale, 8.0)
|
||||
.with_shape(CornerShape::Continuous);
|
||||
let size = corners.rect.size;
|
||||
let mut texture: GlesTexture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, (size.w, size.h).into())
|
||||
.unwrap();
|
||||
{
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
let mut frame = renderer.render(&mut target, size, Transform::Normal).unwrap();
|
||||
let mut frame = renderer
|
||||
.render(&mut target, size, Transform::Normal)
|
||||
.unwrap();
|
||||
frame
|
||||
.clear(Color32F::new(1.0, 0.0, 0.0, 1.0), &[Rectangle::from_size(size)])
|
||||
.clear(
|
||||
Color32F::new(1.0, 0.0, 0.0, 1.0),
|
||||
&[Rectangle::from_size(size)],
|
||||
)
|
||||
.unwrap();
|
||||
let _ = frame.finish().unwrap();
|
||||
}
|
||||
@@ -127,10 +140,20 @@ mod tests {
|
||||
closing.close();
|
||||
let mut pixels = Vec::new();
|
||||
for presentation in [live, closing] {
|
||||
let element =
|
||||
snapshot_element(&snapshot, presentation, corners, scale, &output, &programs, 1.0).unwrap();
|
||||
let element = snapshot_element(
|
||||
&snapshot,
|
||||
presentation,
|
||||
corners,
|
||||
scale,
|
||||
&output,
|
||||
&programs,
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(element.geometry, corners.rect);
|
||||
let mut texture: GlesTexture = renderer.create_buffer(Fourcc::Abgr8888, (128, 128).into()).unwrap();
|
||||
let mut texture: GlesTexture = renderer
|
||||
.create_buffer(Fourcc::Abgr8888, (128, 128).into())
|
||||
.unwrap();
|
||||
{
|
||||
let mut target = renderer.bind(&mut texture).unwrap();
|
||||
let mut frame = renderer
|
||||
@@ -148,14 +171,23 @@ mod tests {
|
||||
let _ = frame.finish().unwrap();
|
||||
}
|
||||
let mapping = renderer
|
||||
.copy_texture(&texture, Rectangle::from_size((128, 128).into()), Fourcc::Abgr8888)
|
||||
.copy_texture(
|
||||
&texture,
|
||||
Rectangle::from_size((128, 128).into()),
|
||||
Fourcc::Abgr8888,
|
||||
)
|
||||
.unwrap();
|
||||
pixels.push(renderer.map_texture(&mapping).unwrap().to_vec());
|
||||
}
|
||||
assert_eq!(pixels[0], pixels[1], "close handoff changed pixels at scale {scale}");
|
||||
assert_eq!(
|
||||
pixels[0], pixels[1],
|
||||
"close handoff changed pixels at scale {scale}"
|
||||
);
|
||||
let expected = (focus_alpha * 255.0).round() as u8;
|
||||
assert!(
|
||||
pixels[0].chunks_exact(4).any(|pixel| pixel[3].abs_diff(expected) <= 1),
|
||||
pixels[0]
|
||||
.chunks_exact(4)
|
||||
.any(|pixel| pixel[3].abs_diff(expected) <= 1),
|
||||
"snapshot alpha must be applied once"
|
||||
);
|
||||
assert!(
|
||||
@@ -183,7 +215,10 @@ mod tests {
|
||||
assert_eq!(predicted.presentation.focus_alpha, focus_alpha);
|
||||
let faded = &predicted.handoff.as_ref().unwrap().0;
|
||||
assert_eq!(faded.elapsed, Duration::from_millis(40));
|
||||
assert_eq!(retained.handoff.as_ref().unwrap().0.elapsed, Duration::from_millis(20));
|
||||
assert_eq!(
|
||||
retained.handoff.as_ref().unwrap().0.elapsed,
|
||||
Duration::from_millis(20)
|
||||
);
|
||||
let element = snapshot_element(
|
||||
faded,
|
||||
predicted.presentation,
|
||||
@@ -200,7 +235,10 @@ mod tests {
|
||||
assert!(retained.handoff.is_none());
|
||||
let thumbnail = live.thumbnail(ferese_layout::Rect::new(4.25, 3.75, 16.5, 12.25));
|
||||
let corners = RoundedRect::new(thumbnail.bounds.current, (0, 0).into(), scale, 4.0);
|
||||
let element = snapshot_element(&snapshot, thumbnail, corners, scale, &output, &programs, 1.0).unwrap();
|
||||
let element = snapshot_element(
|
||||
&snapshot, thumbnail, corners, scale, &output, &programs, 1.0,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(element.geometry, corners.rect);
|
||||
assert!((element.source.size.w - f64::from(size.w)).abs() < 1e-9);
|
||||
assert!((element.source.size.h - f64::from(size.h)).abs() < 1e-9);
|
||||
|
||||
Reference in new issue
Block a user