mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-10-28 14:14:23 +00:00
refactor: better fractional scaling management
This commit is contained in:
parent
238bd150ad
commit
2cd5df6ee1
6 changed files with 281 additions and 35 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -4012,9 +4012,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.3.3"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953"
|
||||
checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"wayland-backend",
|
||||
|
|
|
|||
|
|
@ -38,4 +38,4 @@ slint-interpreter = { path = "../slint/internal/interpreter", default-features =
|
|||
smithay-client-toolkit = "0.20.0"
|
||||
thiserror = "2.0.17"
|
||||
wayland-client = "0.31.11"
|
||||
wayland-protocols = { version = "0.32.6", features = ["client", "staging"] }
|
||||
wayland-protocols = { version = "0.32.9", features = ["client", "staging"] }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@ use wayland_client::{
|
|||
},
|
||||
Connection, EventQueue, Proxy, QueueHandle,
|
||||
};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||
};
|
||||
use wayland_protocols::wp::viewporter::client::{
|
||||
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
||||
};
|
||||
|
||||
pub mod builder;
|
||||
mod config;
|
||||
|
|
@ -44,14 +51,16 @@ impl WindowingSystem {
|
|||
Rc::new(Connection::connect_to_env().map_err(LayerShikaError::WaylandConnection)?);
|
||||
let event_queue = connection.new_event_queue();
|
||||
|
||||
let (compositor, output, layer_shell, seat) =
|
||||
let (compositor, output, layer_shell, seat, fractional_scale_manager, viewporter) =
|
||||
Self::initialize_globals(&connection, &event_queue.handle())
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
|
||||
|
||||
let (surface, layer_surface) = Self::setup_surface(
|
||||
let (surface, layer_surface, fractional_scale, viewport) = Self::setup_surface(
|
||||
&compositor,
|
||||
&output,
|
||||
&layer_shell,
|
||||
fractional_scale_manager.as_ref(),
|
||||
viewporter.as_ref(),
|
||||
&event_queue.handle(),
|
||||
config,
|
||||
);
|
||||
|
|
@ -63,7 +72,7 @@ impl WindowingSystem {
|
|||
LayerShikaError::WindowConfiguration("Component definition is required".to_string())
|
||||
})?;
|
||||
|
||||
let state = WindowStateBuilder::new()
|
||||
let mut builder = WindowStateBuilder::new()
|
||||
.with_component_definition(component_definition)
|
||||
.with_surface(Rc::clone(&surface))
|
||||
.with_layer_surface(Rc::clone(&layer_surface))
|
||||
|
|
@ -71,7 +80,17 @@ impl WindowingSystem {
|
|||
.with_scale_factor(config.scale_factor)
|
||||
.with_height(config.height)
|
||||
.with_exclusive_zone(config.exclusive_zone)
|
||||
.with_window(window)
|
||||
.with_window(window);
|
||||
|
||||
if let Some(fs) = fractional_scale {
|
||||
builder = builder.with_fractional_scale(fs);
|
||||
}
|
||||
|
||||
if let Some(vp) = viewport {
|
||||
builder = builder.with_viewport(vp);
|
||||
}
|
||||
|
||||
let state = builder
|
||||
.build()
|
||||
.map_err(|e| LayerShikaError::WindowConfiguration(e.to_string()))?;
|
||||
|
||||
|
|
@ -89,7 +108,17 @@ impl WindowingSystem {
|
|||
fn initialize_globals(
|
||||
connection: &Connection,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
) -> Result<(WlCompositor, WlOutput, ZwlrLayerShellV1, WlSeat), LayerShikaError> {
|
||||
) -> Result<
|
||||
(
|
||||
WlCompositor,
|
||||
WlOutput,
|
||||
ZwlrLayerShellV1,
|
||||
WlSeat,
|
||||
Option<WpFractionalScaleManagerV1>,
|
||||
Option<WpViewporter>,
|
||||
),
|
||||
LayerShikaError,
|
||||
> {
|
||||
let global_list = registry_queue_init::<WindowState>(connection)
|
||||
.map(|(global_list, _)| global_list)
|
||||
.map_err(|e| LayerShikaError::GlobalInitialization(e.to_string()))?;
|
||||
|
|
@ -97,22 +126,52 @@ impl WindowingSystem {
|
|||
let (compositor, output, layer_shell, seat) = bind_globals!(
|
||||
&global_list,
|
||||
queue_handle,
|
||||
(WlCompositor, compositor, 1..=1),
|
||||
(WlOutput, output, 1..=1),
|
||||
(ZwlrLayerShellV1, layer_shell, 1..=1),
|
||||
(WlSeat, seat, 1..=1)
|
||||
(WlCompositor, compositor, 3..=6),
|
||||
(WlOutput, output, 1..=4),
|
||||
(ZwlrLayerShellV1, layer_shell, 1..=5),
|
||||
(WlSeat, seat, 1..=9)
|
||||
)?;
|
||||
|
||||
Ok((compositor, output, layer_shell, seat))
|
||||
let fractional_scale_manager = global_list
|
||||
.bind::<WpFractionalScaleManagerV1, _, _>(queue_handle, 1..=1, ())
|
||||
.ok();
|
||||
|
||||
let viewporter = global_list
|
||||
.bind::<WpViewporter, _, _>(queue_handle, 1..=1, ())
|
||||
.ok();
|
||||
|
||||
if fractional_scale_manager.is_none() {
|
||||
info!("Fractional scale protocol not available, using integer scaling");
|
||||
}
|
||||
|
||||
if viewporter.is_none() {
|
||||
info!("Viewporter protocol not available");
|
||||
}
|
||||
|
||||
Ok((
|
||||
compositor,
|
||||
output,
|
||||
layer_shell,
|
||||
seat,
|
||||
fractional_scale_manager,
|
||||
viewporter,
|
||||
))
|
||||
}
|
||||
|
||||
fn setup_surface(
|
||||
compositor: &WlCompositor,
|
||||
output: &WlOutput,
|
||||
layer_shell: &ZwlrLayerShellV1,
|
||||
fractional_scale_manager: Option<&WpFractionalScaleManagerV1>,
|
||||
viewporter: Option<&WpViewporter>,
|
||||
queue_handle: &QueueHandle<WindowState>,
|
||||
config: &WindowConfig,
|
||||
) -> (Rc<WlSurface>, Rc<ZwlrLayerSurfaceV1>) {
|
||||
) -> (
|
||||
Rc<WlSurface>,
|
||||
Rc<ZwlrLayerSurfaceV1>,
|
||||
Option<Rc<WpFractionalScaleV1>>,
|
||||
Option<Rc<WpViewport>>,
|
||||
) {
|
||||
let surface = Rc::new(compositor.create_surface(queue_handle, ()));
|
||||
let layer_surface = Rc::new(layer_shell.get_layer_surface(
|
||||
&surface,
|
||||
|
|
@ -123,9 +182,21 @@ impl WindowingSystem {
|
|||
(),
|
||||
));
|
||||
|
||||
let fractional_scale = fractional_scale_manager.map(|manager| {
|
||||
info!("Creating fractional scale object for surface");
|
||||
Rc::new(manager.get_fractional_scale(&surface, queue_handle, ()))
|
||||
});
|
||||
|
||||
let viewport = viewporter.map(|vp| {
|
||||
info!("Creating viewport for surface");
|
||||
Rc::new(vp.get_viewport(&surface, queue_handle, ()))
|
||||
});
|
||||
|
||||
Self::configure_layer_surface(&layer_surface, &surface, config);
|
||||
|
||||
(surface, layer_surface)
|
||||
surface.set_buffer_scale(1);
|
||||
|
||||
(surface, layer_surface, fractional_scale, viewport)
|
||||
}
|
||||
|
||||
fn configure_layer_surface(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ use slint::PhysicalSize;
|
|||
use slint_interpreter::ComponentDefinition;
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||
use wayland_client::protocol::{wl_pointer::WlPointer, wl_surface::WlSurface};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use crate::{errors::LayerShikaError, rendering::{femtovg_window::FemtoVGWindow, slint_platform::CustomSlintPlatform}};
|
||||
|
||||
use super::WindowState;
|
||||
|
|
@ -11,6 +13,8 @@ pub struct WindowStateBuilder {
|
|||
pub component_definition: Option<ComponentDefinition>,
|
||||
pub surface: Option<Rc<WlSurface>>,
|
||||
pub layer_surface: Option<Rc<ZwlrLayerSurfaceV1>>,
|
||||
pub fractional_scale: Option<Rc<WpFractionalScaleV1>>,
|
||||
pub viewport: Option<Rc<WpViewport>>,
|
||||
pub size: Option<PhysicalSize>,
|
||||
pub output_size: Option<PhysicalSize>,
|
||||
pub pointer: Option<Rc<WlPointer>>,
|
||||
|
|
@ -86,6 +90,18 @@ impl WindowStateBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_fractional_scale(mut self, fractional_scale: Rc<WpFractionalScaleV1>) -> Self {
|
||||
self.fractional_scale = Some(fractional_scale);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_viewport(mut self, viewport: Rc<WpViewport>) -> Self {
|
||||
self.viewport = Some(viewport);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<WindowState, LayerShikaError> {
|
||||
let platform = CustomSlintPlatform::new(Rc::clone(
|
||||
self.window
|
||||
|
|
@ -106,6 +122,8 @@ impl Default for WindowStateBuilder {
|
|||
component_definition: None,
|
||||
surface: None,
|
||||
layer_surface: None,
|
||||
fractional_scale: None,
|
||||
viewport: None,
|
||||
size: None,
|
||||
output_size: None,
|
||||
pointer: None,
|
||||
|
|
|
|||
|
|
@ -21,10 +21,20 @@ use wayland_client::{
|
|||
},
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||
wp_fractional_scale_v1::{self, WpFractionalScaleV1},
|
||||
};
|
||||
use wayland_protocols::wp::viewporter::client::{
|
||||
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
||||
};
|
||||
|
||||
use super::WindowState;
|
||||
|
||||
impl Dispatch<ZwlrLayerSurfaceV1, ()> for WindowState {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
layer_surface: &ZwlrLayerSurfaceV1,
|
||||
|
|
@ -39,14 +49,49 @@ impl Dispatch<ZwlrLayerSurfaceV1, ()> for WindowState {
|
|||
width,
|
||||
height,
|
||||
} => {
|
||||
info!("Layer surface configured with size: {}x{}", width, height);
|
||||
info!(
|
||||
"Layer surface configured with compositor size: {}x{}",
|
||||
width, height
|
||||
);
|
||||
layer_surface.ack_configure(serial);
|
||||
if width > 0 && height > 0 {
|
||||
state.update_size(state.output_size().width, state.height());
|
||||
|
||||
let output_width = state.output_size().width;
|
||||
let scale_factor = state.scale_factor();
|
||||
|
||||
let target_width = if width == 0 || (width == 1 && output_width > 1) {
|
||||
if scale_factor > 1.0 {
|
||||
(output_width as f32 / scale_factor).round() as u32
|
||||
} else {
|
||||
output_width
|
||||
}
|
||||
} else {
|
||||
let current_size = state.output_size();
|
||||
state.update_size(current_size.width, current_size.height);
|
||||
}
|
||||
width
|
||||
};
|
||||
|
||||
let target_height = if height > 0 {
|
||||
height
|
||||
} else {
|
||||
let h = state.height();
|
||||
if scale_factor > 1.0 {
|
||||
(h as f32 / scale_factor).round() as u32
|
||||
} else {
|
||||
h
|
||||
}
|
||||
};
|
||||
|
||||
let clamped_width = target_width.min(output_width);
|
||||
|
||||
info!(
|
||||
"Using dimensions: {}x{} (clamped from {}x{}, output: {}x{})",
|
||||
clamped_width,
|
||||
target_height,
|
||||
target_width,
|
||||
target_height,
|
||||
output_width,
|
||||
state.output_size().height
|
||||
);
|
||||
|
||||
state.update_size(clamped_width, target_height);
|
||||
}
|
||||
zwlr_layer_surface_v1::Event::Closed => {
|
||||
info!("Layer surface closed");
|
||||
|
|
@ -153,10 +198,31 @@ impl Dispatch<WlPointer, ()> for WindowState {
|
|||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpFractionalScaleV1, ()> for WindowState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_proxy: &WpFractionalScaleV1,
|
||||
event: wp_fractional_scale_v1::Event,
|
||||
_data: &(),
|
||||
_conn: &Connection,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let scale_float = scale as f32 / 120.0;
|
||||
info!("Fractional scale received: {scale_float} ({scale}x)");
|
||||
state.update_scale_factor(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_empty_dispatch!(
|
||||
(WlRegistry, GlobalListContents),
|
||||
(WlCompositor, ()),
|
||||
(WlSurface, ()),
|
||||
(ZwlrLayerShellV1, ()),
|
||||
(WlSeat, ())
|
||||
(WlSeat, ()),
|
||||
(WpFractionalScaleManagerV1, ()),
|
||||
(WpViewporter, ()),
|
||||
(WpViewport, ())
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
|
|||
use slint_interpreter::ComponentInstance;
|
||||
use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
|
||||
use wayland_client::protocol::wl_surface::WlSurface;
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use crate::rendering::femtovg_window::FemtoVGWindow;
|
||||
use crate::errors::LayerShikaError;
|
||||
|
||||
|
|
@ -15,7 +17,10 @@ pub struct WindowState {
|
|||
component_instance: ComponentInstance,
|
||||
surface: Rc<WlSurface>,
|
||||
layer_surface: Rc<ZwlrLayerSurfaceV1>,
|
||||
fractional_scale: Option<Rc<WpFractionalScaleV1>>,
|
||||
viewport: Option<Rc<WpViewport>>,
|
||||
size: PhysicalSize,
|
||||
logical_size: PhysicalSize,
|
||||
output_size: PhysicalSize,
|
||||
window: Rc<FemtoVGWindow>,
|
||||
current_pointer_position: LogicalPosition,
|
||||
|
|
@ -39,7 +44,6 @@ impl WindowState {
|
|||
.show()
|
||||
.map_err(|e| LayerShikaError::SlintComponentCreation(e.to_string()))?;
|
||||
|
||||
// Request initial redraw to ensure the first frame is rendered
|
||||
window.request_redraw();
|
||||
|
||||
Ok(Self {
|
||||
|
|
@ -50,7 +54,10 @@ impl WindowState {
|
|||
layer_surface: builder
|
||||
.layer_surface
|
||||
.ok_or_else(|| LayerShikaError::InvalidInput("Layer surface is required".into()))?,
|
||||
fractional_scale: builder.fractional_scale,
|
||||
viewport: builder.viewport,
|
||||
size: builder.size.unwrap_or_default(),
|
||||
logical_size: PhysicalSize::default(),
|
||||
output_size: builder.output_size.unwrap_or_default(),
|
||||
window,
|
||||
current_pointer_position: LogicalPosition::default(),
|
||||
|
|
@ -61,27 +68,92 @@ impl WindowState {
|
|||
}
|
||||
|
||||
pub fn update_size(&mut self, width: u32, height: u32) {
|
||||
let new_size = PhysicalSize::new(width, height);
|
||||
info!("Updating window size to {}x{}", width, height);
|
||||
self.window.set_size(slint::WindowSize::Physical(new_size));
|
||||
self.window.set_scale_factor(self.scale_factor);
|
||||
if width == 0 || height == 0 {
|
||||
info!(
|
||||
"Skipping update_size with zero dimension: {}x{}",
|
||||
width, height
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let physical_width = (width as f32 * self.scale_factor).round() as u32;
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let physical_height = (height as f32 * self.scale_factor).round() as u32;
|
||||
|
||||
let new_physical_size = PhysicalSize::new(physical_width, physical_height);
|
||||
let new_logical_size = PhysicalSize::new(width, height);
|
||||
|
||||
info!(
|
||||
"Updating window size: buffer {}x{}, physical {}x{}, scale {}, has_viewport: {}",
|
||||
width,
|
||||
height,
|
||||
physical_width,
|
||||
physical_height,
|
||||
self.scale_factor,
|
||||
self.viewport.is_some()
|
||||
);
|
||||
|
||||
if self.fractional_scale.is_some() && self.viewport.is_some() {
|
||||
self.surface.set_buffer_scale(1);
|
||||
self.window
|
||||
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new(
|
||||
width as f32,
|
||||
height as f32,
|
||||
)));
|
||||
self.window.set_scale_factor(self.scale_factor);
|
||||
|
||||
if let Some(viewport) = &self.viewport {
|
||||
viewport.set_destination(width as i32, height as i32);
|
||||
}
|
||||
} else if self.fractional_scale.is_some() {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let buffer_scale = self.scale_factor.round() as i32;
|
||||
self.surface.set_buffer_scale(buffer_scale);
|
||||
|
||||
self.window
|
||||
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new(
|
||||
width as f32,
|
||||
height as f32,
|
||||
)));
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
self.window.set_scale_factor(buffer_scale as f32);
|
||||
} else {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let buffer_scale = self.scale_factor.round() as i32;
|
||||
self.surface.set_buffer_scale(buffer_scale);
|
||||
|
||||
self.window
|
||||
.set_size(slint::WindowSize::Physical(new_physical_size));
|
||||
self.window.set_scale_factor(self.scale_factor);
|
||||
}
|
||||
|
||||
info!("Window physical size: {:?}", self.window.size());
|
||||
|
||||
info!("Updating layer surface size to {}x{}", width, height);
|
||||
self.layer_surface.set_size(width, height);
|
||||
self.layer_surface.set_exclusive_zone(self.exclusive_zone);
|
||||
|
||||
self.surface.commit();
|
||||
self.size = new_size;
|
||||
|
||||
self.size = new_physical_size;
|
||||
self.logical_size = new_logical_size;
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn set_current_pointer_position(&mut self, physical_x: f64, physical_y: f64) {
|
||||
let scale_factor = self.scale_factor;
|
||||
let logical_position = LogicalPosition::new(
|
||||
physical_x as f32 / scale_factor,
|
||||
physical_y as f32 / scale_factor,
|
||||
);
|
||||
let logical_position = if self.fractional_scale.is_some() {
|
||||
LogicalPosition::new(physical_x as f32, physical_y as f32)
|
||||
} else {
|
||||
let scale_factor = self.scale_factor;
|
||||
LogicalPosition::new(
|
||||
physical_x as f32 / scale_factor,
|
||||
physical_y as f32 / scale_factor,
|
||||
)
|
||||
};
|
||||
self.current_pointer_position = logical_position;
|
||||
}
|
||||
|
||||
|
|
@ -120,4 +192,23 @@ impl WindowState {
|
|||
pub const fn component_instance(&self) -> &ComponentInstance {
|
||||
&self.component_instance
|
||||
}
|
||||
|
||||
pub fn update_scale_factor(&mut self, scale_120ths: u32) {
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
let new_scale_factor = scale_120ths as f32 / 120.0;
|
||||
info!(
|
||||
"Updating scale factor from {} to {} ({}x)",
|
||||
self.scale_factor, new_scale_factor, scale_120ths
|
||||
);
|
||||
self.scale_factor = new_scale_factor;
|
||||
|
||||
let current_logical_size = self.logical_size;
|
||||
if current_logical_size.width > 0 && current_logical_size.height > 0 {
|
||||
self.update_size(current_logical_size.width, current_logical_size.height);
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn scale_factor(&self) -> f32 {
|
||||
self.scale_factor
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue