fix: ceil integer scale for better text rendering on fractional scaling

This commit is contained in:
drendog 2026-01-19 02:43:52 +01:00
parent c430938bf1
commit 3eea09b2f6
Signed by: dwenya
GPG key ID: 8DD77074645332D0
4 changed files with 121 additions and 22 deletions

View file

@ -10,6 +10,46 @@ pub enum RenderState {
Dirty,
}
#[derive(Debug, Clone, Copy)]
pub struct FractionalScaleConfig {
pub render_scale: f32,
pub render_physical_size: PhysicalSize,
pub logical_width: f32,
pub logical_height: f32,
}
impl FractionalScaleConfig {
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[must_use]
pub fn new(logical_width: f32, logical_height: f32, scale_factor: f32) -> Self {
let render_scale = Self::render_scale(scale_factor);
Self {
render_scale,
render_physical_size: PhysicalSize::new(
(logical_width * render_scale) as u32,
(logical_height * render_scale) as u32,
),
logical_width,
logical_height,
}
}
#[must_use]
pub fn render_scale(scale_factor: f32) -> f32 {
scale_factor.ceil()
}
pub fn apply_to<W: RenderableWindow + ?Sized>(&self, window: &W) {
window.set_scale_factor(self.render_scale);
window.set_size_with_exact_logical(
self.render_physical_size,
self.logical_width,
self.logical_height,
);
}
}
pub trait RenderableWindow: WindowAdapter {
fn render_frame_if_dirty(&self) -> Result<()>;
fn set_scale_factor(&self, scale_factor: f32);
@ -31,4 +71,16 @@ pub trait RenderableWindow: WindowAdapter {
size: size.to_logical(self.scale_factor()),
});
}
fn set_size_with_exact_logical(
&self,
physical: slint::PhysicalSize,
logical_width: f32,
logical_height: f32,
) {
self.size_cell().set(physical);
self.window().dispatch_event(WindowEvent::Resized {
size: slint::LogicalSize::new(logical_width, logical_height),
});
}
}

View file

@ -1,7 +1,7 @@
use super::callbacks::{LockCallbackContext, LockCallbackExt, LockPropertyOperationExt};
use crate::errors::Result;
use crate::rendering::femtovg::main_window::FemtoVGWindow;
use crate::rendering::femtovg::renderable_window::RenderableWindow;
use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
use crate::rendering::slint_integration::platform::CustomSlintPlatform;
use crate::wayland::session_lock::lock_surface::LockSurface;
use crate::wayland::surfaces::component_state::ComponentState;
@ -308,7 +308,22 @@ impl ActiveLockSurface {
scale_factor: f32,
) {
match mode {
LockScalingMode::FractionalWithViewport | LockScalingMode::FractionalOnly => {
LockScalingMode::FractionalWithViewport => {
let config = FractionalScaleConfig::new(
dimensions.logical_width() as f32,
dimensions.logical_height() as f32,
scale_factor,
);
info!(
"Lock FractionalWithViewport: render scale {} (from {}), physical {}x{}",
config.render_scale,
scale_factor,
config.render_physical_size.width,
config.render_physical_size.height
);
config.apply_to(self.window.as_ref());
}
LockScalingMode::FractionalOnly => {
RenderableWindow::set_scale_factor(self.window.as_ref(), scale_factor);
self.window.set_size(WindowSize::Logical(LogicalSize::new(
dimensions.logical_width() as f32,

View file

@ -1,6 +1,7 @@
use crate::errors::{LayerShikaError, Result};
use crate::rendering::egl::context_factory::RenderContextFactory;
use crate::rendering::femtovg::{popup_window::PopupWindow, renderable_window::RenderableWindow};
use crate::rendering::femtovg::popup_window::PopupWindow;
use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
use crate::wayland::surfaces::display_metrics::{DisplayMetrics, SharedDisplayMetrics};
use layer_shika_domain::dimensions::LogicalSize as DomainLogicalSize;
use layer_shika_domain::surface_dimensions::SurfaceDimensions;
@ -9,7 +10,7 @@ use layer_shika_domain::value_objects::popup_behavior::ConstraintAdjustment;
use layer_shika_domain::value_objects::popup_config::PopupConfig;
use layer_shika_domain::value_objects::popup_position::PopupPosition;
use log::info;
use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize, WindowSize};
use slint::{platform::femtovg_renderer::FemtoVGRenderer, PhysicalSize};
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
@ -200,8 +201,9 @@ impl PopupManager {
pub fn update_scale_factor(&self, scale_factor: f32) {
self.scale_factor.set(scale_factor);
let render_scale = FractionalScaleConfig::render_scale(scale_factor);
for popup in self.state.borrow().popups.values() {
popup.window.set_scale_factor(scale_factor);
popup.window.set_scale_factor(render_scale);
}
self.mark_all_popups_dirty();
}
@ -322,11 +324,16 @@ impl PopupManager {
let popup_window = PopupWindow::new_with_callback(renderer, on_close);
popup_window.set_popup_id(popup_id.to_handle());
popup_window.set_scale_factor(scale_factor);
popup_window.set_size(WindowSize::Logical(slint::LogicalSize::new(
params.width,
params.height,
)));
let config = FractionalScaleConfig::new(params.width, params.height, scale_factor);
info!(
"Popup using render scale {} (from {}), render_physical {}x{}",
config.render_scale,
scale_factor,
config.render_physical_size.width,
config.render_physical_size.height
);
config.apply_to(popup_window.as_ref());
let mut state = self.state.borrow_mut();
state.popups.insert(
@ -489,7 +496,6 @@ impl PopupManager {
ActiveWindow::None
}
#[allow(clippy::cast_precision_loss)]
pub fn update_scale_for_fractional_scale_object(
&self,
fractional_scale_proxy: &WpFractionalScaleV1,
@ -500,8 +506,12 @@ impl PopupManager {
if let Some(popup_key) = self.find_popup_key_by_fractional_scale_id(&fractional_scale_id) {
if let Some(popup_surface) = self.get_popup_window(popup_key) {
let new_scale_factor = DisplayMetrics::scale_factor_from_120ths(scale_120ths);
info!("Updating popup scale factor to {new_scale_factor} ({scale_120ths}x)");
popup_surface.set_scale_factor(new_scale_factor);
let render_scale = FractionalScaleConfig::render_scale(new_scale_factor);
info!(
"Updating popup scale factor to {} (render scale {}, from {}x)",
new_scale_factor, render_scale, scale_120ths
);
popup_surface.set_scale_factor(render_scale);
popup_surface.request_redraw();
}
}

View file

@ -1,5 +1,5 @@
use crate::errors::Result;
use crate::rendering::femtovg::renderable_window::RenderableWindow;
use crate::rendering::femtovg::renderable_window::{FractionalScaleConfig, RenderableWindow};
use crate::wayland::managed_proxies::{
ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport,
};
@ -92,12 +92,19 @@ impl<W: RenderableWindow> SurfaceRenderer<W> {
) {
match mode {
ScalingMode::FractionalWithViewport => {
self.window.set_scale_factor(scale_factor);
self.window
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new(
let config = FractionalScaleConfig::new(
dimensions.logical_width() as f32,
dimensions.logical_height() as f32,
)));
scale_factor,
);
info!(
"FractionalWithViewport: render scale {} (from {}), physical {}x{}",
config.render_scale,
scale_factor,
config.render_physical_size.width,
config.render_physical_size.height
);
config.apply_to(self.window.as_ref());
}
ScalingMode::FractionalOnly => {
self.window
@ -154,15 +161,30 @@ impl<W: RenderableWindow> SurfaceRenderer<W> {
self.apply_surface_dimensions(dimensions, scale_factor);
}
#[allow(clippy::cast_precision_loss)]
pub fn apply_surface_dimensions(&mut self, dimensions: SurfaceDimensions, scale_factor: f32) {
let scaling_mode = self.determine_scaling_mode();
let render_physical_size = match scaling_mode {
ScalingMode::FractionalWithViewport => {
FractionalScaleConfig::new(
dimensions.logical_width() as f32,
dimensions.logical_height() as f32,
scale_factor,
)
.render_physical_size
}
_ => dimensions.to_slint_physical_size(),
};
info!(
"Updating window size: logical {}x{}, physical {}x{}, scale {}, buffer_scale {}, mode {:?}",
"Updating window size: logical {}x{}, physical {}x{}, render_physical {}x{}, scale {}, buffer_scale {}, mode {:?}",
dimensions.logical_width(),
dimensions.logical_height(),
dimensions.physical_width(),
dimensions.physical_height(),
render_physical_size.width,
render_physical_size.height,
scale_factor,
dimensions.buffer_scale(),
scaling_mode
@ -173,7 +195,7 @@ impl<W: RenderableWindow> SurfaceRenderer<W> {
info!("Window physical size: {:?}", self.window.size());
self.size = dimensions.to_slint_physical_size();
self.size = render_physical_size;
self.logical_size = dimensions.to_slint_logical_size();
RenderableWindow::request_redraw(self.window.as_ref());
}