Format Rust sources at 100 columns

This commit is contained in:
Samuel Onoja committed 2026-10-22 01:28:03 +01:00
1 parent 4661f595f8
commit 610478f8c7
336 files changed
+23497 -7892

No files matched your search

+61 -13
View File
@@ -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]
+35 -10
View File
@@ -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
]
);
}
}
+19 -6
View File
@@ -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);
}
+113 -30
View File
@@ -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;
+86 -32
View File
@@ -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(&parameters),
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(&parameters),
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,
+169 -63
View File
@@ -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 &regions {
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(&parameters),
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(&parameters, 2.0));
cached
.shadow
.update_uniforms(decoration_uniforms(&parameters, 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(&parameters));
}
@@ -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) = &parameters.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
View File
@@ -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);
}
+57 -21
View File
@@ -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,
+31 -8
View File
@@ -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 {
+91 -36
View File
@@ -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:?}");
}
}
+49 -11
View File
@@ -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());
+62 -17
View File
@@ -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,
+29 -9
View File
@@ -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;
+22 -10
View File
@@ -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, &parameters, 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,
&parameters,
Arc::new(AtomicBool::new(true)),
);
buffers.insert(
key.clone(),
WindowBlur {
+53 -15
View File
@@ -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);