Properly scale surfaces with fractional scale
This commit is contained in:
parent
ef4ffc9fd2
commit
555f9492ad
7 changed files with 242 additions and 77 deletions
|
|
@ -35,3 +35,4 @@ Additionally, satellite can *optionally* take advantage of the following protoco
|
||||||
- XDG foreign
|
- XDG foreign
|
||||||
- Pointer constraints
|
- Pointer constraints
|
||||||
- Tablet input
|
- Tablet input
|
||||||
|
- Fractional scale
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1:
|
||||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1;
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1;
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
|
fractional_scale::v1::client::{
|
||||||
|
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||||
|
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||||
|
},
|
||||||
linux_dmabuf::zv1::client::{
|
linux_dmabuf::zv1::client::{
|
||||||
self as dmabuf,
|
self as dmabuf,
|
||||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1 as DmabufFeedback,
|
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1 as DmabufFeedback,
|
||||||
|
|
@ -143,6 +147,7 @@ delegate_noop!(Globals: ZwpPointerConstraintsV1);
|
||||||
delegate_noop!(Globals: ZwpTabletManagerV2);
|
delegate_noop!(Globals: ZwpTabletManagerV2);
|
||||||
delegate_noop!(Globals: XdgActivationV1);
|
delegate_noop!(Globals: XdgActivationV1);
|
||||||
delegate_noop!(Globals: ZxdgDecorationManagerV1);
|
delegate_noop!(Globals: ZxdgDecorationManagerV1);
|
||||||
|
delegate_noop!(Globals: WpFractionalScaleManagerV1);
|
||||||
delegate_noop!(Globals: ignore ZxdgToplevelDecorationV1);
|
delegate_noop!(Globals: ignore ZxdgToplevelDecorationV1);
|
||||||
|
|
||||||
impl Dispatch<WlRegistry, GlobalListContents> for Globals {
|
impl Dispatch<WlRegistry, GlobalListContents> for Globals {
|
||||||
|
|
@ -232,6 +237,7 @@ push_events!(XdgOutput);
|
||||||
push_events!(WlTouch);
|
push_events!(WlTouch);
|
||||||
push_events!(ZwpConfinedPointerV1);
|
push_events!(ZwpConfinedPointerV1);
|
||||||
push_events!(ZwpLockedPointerV1);
|
push_events!(ZwpLockedPointerV1);
|
||||||
|
push_events!(WpFractionalScaleV1);
|
||||||
|
|
||||||
pub(crate) struct LateInitObjectKey<P: Proxy> {
|
pub(crate) struct LateInitObjectKey<P: Proxy> {
|
||||||
key: OnceLock<ObjectKey>,
|
key: OnceLock<ObjectKey>,
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,10 @@ impl<C: XConnection>
|
||||||
let viewport = state.viewporter.get_viewport(&client, &state.qh, ());
|
let viewport = state.viewporter.get_viewport(&client, &state.qh, ());
|
||||||
surface_id = Some(server.id().protocol_id());
|
surface_id = Some(server.id().protocol_id());
|
||||||
debug!("new surface with key {key:?} ({surface_id:?})");
|
debug!("new surface with key {key:?} ({surface_id:?})");
|
||||||
|
let fractional = state
|
||||||
|
.fractional_scale
|
||||||
|
.as_ref()
|
||||||
|
.map(|f| f.get_fractional_scale(&client, &state.qh, key));
|
||||||
|
|
||||||
SurfaceData {
|
SurfaceData {
|
||||||
client,
|
client,
|
||||||
|
|
@ -242,8 +246,9 @@ impl<C: XConnection>
|
||||||
xwl: None,
|
xwl: None,
|
||||||
window: None,
|
window: None,
|
||||||
output_key: None,
|
output_key: None,
|
||||||
scale_factor: 1,
|
scale_factor: 1.0,
|
||||||
viewport,
|
viewport,
|
||||||
|
fractional,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use std::os::fd::AsFd;
|
||||||
use wayland_client::{protocol as client, Proxy};
|
use wayland_client::{protocol as client, Proxy};
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
|
fractional_scale::v1::client::wp_fractional_scale_v1,
|
||||||
pointer_constraints::zv1::{
|
pointer_constraints::zv1::{
|
||||||
client::{
|
client::{
|
||||||
zwp_confined_pointer_v1::{self, ZwpConfinedPointerV1 as ConfinedPointerClient},
|
zwp_confined_pointer_v1::{self, ZwpConfinedPointerV1 as ConfinedPointerClient},
|
||||||
|
|
@ -63,6 +64,7 @@ pub(crate) enum SurfaceEvents {
|
||||||
XdgSurface(xdg_surface::Event),
|
XdgSurface(xdg_surface::Event),
|
||||||
Toplevel(xdg_toplevel::Event),
|
Toplevel(xdg_toplevel::Event),
|
||||||
Popup(xdg_popup::Event),
|
Popup(xdg_popup::Event),
|
||||||
|
FractionalScale(wp_fractional_scale_v1::Event),
|
||||||
}
|
}
|
||||||
macro_rules! impl_from {
|
macro_rules! impl_from {
|
||||||
($type:ty, $variant:ident) => {
|
($type:ty, $variant:ident) => {
|
||||||
|
|
@ -77,6 +79,7 @@ impl_from!(client::wl_surface::Event, WlSurface);
|
||||||
impl_from!(xdg_surface::Event, XdgSurface);
|
impl_from!(xdg_surface::Event, XdgSurface);
|
||||||
impl_from!(xdg_toplevel::Event, Toplevel);
|
impl_from!(xdg_toplevel::Event, Toplevel);
|
||||||
impl_from!(xdg_popup::Event, Popup);
|
impl_from!(xdg_popup::Event, Popup);
|
||||||
|
impl_from!(wp_fractional_scale_v1::Event, FractionalScale);
|
||||||
|
|
||||||
impl HandleEvent for SurfaceData {
|
impl HandleEvent for SurfaceData {
|
||||||
type Event = SurfaceEvents;
|
type Event = SurfaceEvents;
|
||||||
|
|
@ -86,6 +89,20 @@ impl HandleEvent for SurfaceData {
|
||||||
SurfaceEvents::XdgSurface(event) => self.xdg_event(event, state),
|
SurfaceEvents::XdgSurface(event) => self.xdg_event(event, state),
|
||||||
SurfaceEvents::Toplevel(event) => self.toplevel_event(event, state),
|
SurfaceEvents::Toplevel(event) => self.toplevel_event(event, state),
|
||||||
SurfaceEvents::Popup(event) => self.popup_event(event, state),
|
SurfaceEvents::Popup(event) => self.popup_event(event, state),
|
||||||
|
SurfaceEvents::FractionalScale(event) => match event {
|
||||||
|
wp_fractional_scale_v1::Event::PreferredScale { scale } => {
|
||||||
|
self.scale_factor = scale as f64 / 120.0;
|
||||||
|
log::debug!("{} scale factor: {}", self.server.id(), self.scale_factor);
|
||||||
|
if let Some(win_data) = self
|
||||||
|
.window
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|win| state.windows.get_mut(win))
|
||||||
|
{
|
||||||
|
self.update_viewport(win_data.attrs.dims, win_data.attrs.size_hints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,8 +126,8 @@ impl SurfaceData {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_viewport(&self, dims: WindowDims, size_hints: Option<WmNormalHints>) {
|
fn update_viewport(&self, dims: WindowDims, size_hints: Option<WmNormalHints>) {
|
||||||
let width = dims.width as i32 / self.scale_factor;
|
let width = (dims.width as f64 / self.scale_factor) as i32;
|
||||||
let height = dims.height as i32 / self.scale_factor;
|
let height = (dims.height as f64 / self.scale_factor) as i32;
|
||||||
self.viewport.set_destination(width, height);
|
self.viewport.set_destination(width, height);
|
||||||
debug!("{} viewport: {width}x{height}", self.server.id());
|
debug!("{} viewport: {width}x{height}", self.server.id());
|
||||||
if let Some(hints) = size_hints {
|
if let Some(hints) = size_hints {
|
||||||
|
|
@ -121,16 +138,17 @@ impl SurfaceData {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(min) = hints.min_size {
|
if let Some(min) = hints.min_size {
|
||||||
data.toplevel.set_min_size(
|
data.toplevel.set_min_size(
|
||||||
min.width / self.scale_factor,
|
(min.width as f64 / self.scale_factor) as i32,
|
||||||
min.height / self.scale_factor,
|
(min.height as f64 / self.scale_factor) as i32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(max) = hints.max_size {
|
if let Some(max) = hints.max_size {
|
||||||
data.toplevel.set_max_size(
|
data.toplevel.set_max_size(
|
||||||
max.width / self.scale_factor,
|
(max.width as f64 / self.scale_factor) as i32,
|
||||||
max.height / self.scale_factor,
|
(max.height as f64 / self.scale_factor) as i32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -151,13 +169,17 @@ impl SurfaceData {
|
||||||
};
|
};
|
||||||
let output: &mut Output = object.as_mut();
|
let output: &mut Output = object.as_mut();
|
||||||
self.server.enter(&output.server);
|
self.server.enter(&output.server);
|
||||||
self.scale_factor = output.scale;
|
if state.fractional_scale.is_none() {
|
||||||
|
self.scale_factor = output.scale as f64;
|
||||||
|
}
|
||||||
self.output_key = Some(key);
|
self.output_key = Some(key);
|
||||||
debug!("{} entered {}", self.server.id(), output.server.id());
|
debug!("{} entered {}", self.server.id(), output.server.id());
|
||||||
|
|
||||||
let windows = &mut state.windows;
|
let windows = &mut state.windows;
|
||||||
if let Some(win_data) = self.window.as_ref().and_then(|win| windows.get_mut(win)) {
|
if let Some(win_data) = self.window.as_ref().and_then(|win| windows.get_mut(win)) {
|
||||||
|
if state.fractional_scale.is_none() {
|
||||||
self.update_viewport(win_data.attrs.dims, win_data.attrs.size_hints);
|
self.update_viewport(win_data.attrs.dims, win_data.attrs.size_hints);
|
||||||
|
}
|
||||||
win_data.update_output_offset(
|
win_data.update_output_offset(
|
||||||
key,
|
key,
|
||||||
WindowOutputOffset {
|
WindowOutputOffset {
|
||||||
|
|
@ -205,15 +227,15 @@ impl SurfaceData {
|
||||||
if let Some(pending) = xdg.pending.take() {
|
if let Some(pending) = xdg.pending.take() {
|
||||||
let window = state.associated_windows[self.key];
|
let window = state.associated_windows[self.key];
|
||||||
let window = state.windows.get_mut(&window).unwrap();
|
let window = state.windows.get_mut(&window).unwrap();
|
||||||
let x = pending.x * self.scale_factor + window.output_offset.x;
|
let x = (pending.x as f64 * self.scale_factor) as i32 + window.output_offset.x;
|
||||||
let y = pending.y * self.scale_factor + window.output_offset.y;
|
let y = (pending.y as f64 * self.scale_factor) as i32 + window.output_offset.y;
|
||||||
let width = if pending.width > 0 {
|
let width = if pending.width > 0 {
|
||||||
(pending.width * self.scale_factor) as u16
|
(pending.width as f64 * self.scale_factor) as u16
|
||||||
} else {
|
} else {
|
||||||
window.attrs.dims.width
|
window.attrs.dims.width
|
||||||
};
|
};
|
||||||
let height = if pending.height > 0 {
|
let height = if pending.height > 0 {
|
||||||
(pending.height * self.scale_factor) as u16
|
(pending.height as f64 * self.scale_factor) as u16
|
||||||
} else {
|
} else {
|
||||||
window.attrs.dims.height
|
window.attrs.dims.height
|
||||||
};
|
};
|
||||||
|
|
@ -394,7 +416,7 @@ pub struct Pointer {
|
||||||
server: WlPointer,
|
server: WlPointer,
|
||||||
pub client: client::wl_pointer::WlPointer,
|
pub client: client::wl_pointer::WlPointer,
|
||||||
pending_enter: PendingEnter,
|
pending_enter: PendingEnter,
|
||||||
scale: i32,
|
scale: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pointer {
|
impl Pointer {
|
||||||
|
|
@ -403,7 +425,7 @@ impl Pointer {
|
||||||
server,
|
server,
|
||||||
client,
|
client,
|
||||||
pending_enter: PendingEnter(None),
|
pending_enter: PendingEnter(None),
|
||||||
scale: 1,
|
scale: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -445,8 +467,8 @@ impl HandleEvent for Pointer {
|
||||||
self.server.enter(
|
self.server.enter(
|
||||||
serial,
|
serial,
|
||||||
&surface_data.server,
|
&surface_data.server,
|
||||||
surface_x * self.scale as f64,
|
surface_x * self.scale,
|
||||||
surface_y * self.scale as f64,
|
surface_y * self.scale,
|
||||||
);
|
);
|
||||||
let window = surface_data.window.unwrap();
|
let window = surface_data.window.unwrap();
|
||||||
state.connection.as_mut().unwrap().raise_to_top(window);
|
state.connection.as_mut().unwrap().raise_to_top(window);
|
||||||
|
|
@ -524,11 +546,8 @@ impl HandleEvent for Pointer {
|
||||||
warn!("could not move pointer to surface ({serial}): stale surface");
|
warn!("could not move pointer to surface ({serial}): stale surface");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.server.motion(
|
self.server
|
||||||
time,
|
.motion(time, surface_x * self.scale, surface_y * self.scale);
|
||||||
surface_x * self.scale as f64,
|
|
||||||
surface_y * self.scale as f64,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => simple_event_shunt! {
|
_ => simple_event_shunt! {
|
||||||
|
|
@ -983,6 +1002,7 @@ impl Output {
|
||||||
Event::Scale { factor } => {
|
Event::Scale { factor } => {
|
||||||
debug!("{} scale: {factor}", self.server.id());
|
debug!("{} scale: {factor}", self.server.id());
|
||||||
self.scale = factor;
|
self.scale = factor;
|
||||||
|
if state.fractional_scale.is_none() {
|
||||||
self.windows.retain(|window| {
|
self.windows.retain(|window| {
|
||||||
let Some(data): Option<&WindowData> = state.windows.get(window) else {
|
let Some(data): Option<&WindowData> = state.windows.get(window) else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -993,7 +1013,7 @@ impl Output {
|
||||||
.and_then(|key| state.objects.get_mut(key))
|
.and_then(|key| state.objects.get_mut(key))
|
||||||
.map(AsMut::as_mut)
|
.map(AsMut::as_mut)
|
||||||
{
|
{
|
||||||
surface.scale_factor = factor;
|
surface.scale_factor = factor as f64;
|
||||||
surface.update_viewport(data.attrs.dims, data.attrs.size_hints);
|
surface.update_viewport(data.attrs.dims, data.attrs.size_hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1002,6 +1022,7 @@ impl Output {
|
||||||
|
|
||||||
self.server.scale(factor);
|
self.server.scale(factor);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_ => simple_event_shunt! {
|
_ => simple_event_shunt! {
|
||||||
self.server, event: Event => [
|
self.server, event: Event => [
|
||||||
Name {
|
Name {
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,14 @@ use std::os::fd::{AsFd, BorrowedFd};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use wayland_client::{globals::Global, protocol as client, Proxy};
|
use wayland_client::{globals::Global, protocol as client, Proxy};
|
||||||
|
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
||||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{
|
||||||
self, ZxdgToplevelDecorationV1,
|
self, ZxdgToplevelDecorationV1,
|
||||||
};
|
};
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
|
fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||||
linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf},
|
linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf},
|
||||||
pointer_constraints::zv1::server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
|
pointer_constraints::zv1::server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
|
||||||
relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
||||||
|
|
@ -188,8 +190,9 @@ pub struct SurfaceData {
|
||||||
xwl: Option<XwaylandSurfaceV1>,
|
xwl: Option<XwaylandSurfaceV1>,
|
||||||
window: Option<x::Window>,
|
window: Option<x::Window>,
|
||||||
output_key: Option<ObjectKey>,
|
output_key: Option<ObjectKey>,
|
||||||
scale_factor: i32,
|
scale_factor: f64,
|
||||||
viewport: WpViewport,
|
viewport: WpViewport,
|
||||||
|
fractional: Option<WpFractionalScaleV1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceData {
|
impl SurfaceData {
|
||||||
|
|
@ -515,6 +518,7 @@ pub struct ServerState<C: XConnection> {
|
||||||
|
|
||||||
xdg_wm_base: XdgWmBase,
|
xdg_wm_base: XdgWmBase,
|
||||||
viewporter: WpViewporter,
|
viewporter: WpViewporter,
|
||||||
|
fractional_scale: Option<WpFractionalScaleManagerV1>,
|
||||||
clipboard_data: Option<ClipboardData<C::X11Selection>>,
|
clipboard_data: Option<ClipboardData<C::X11Selection>>,
|
||||||
last_kb_serial: Option<(client::wl_seat::WlSeat, u32)>,
|
last_kb_serial: Option<(client::wl_seat::WlSeat, u32)>,
|
||||||
activation_state: Option<ActivationState>,
|
activation_state: Option<ActivationState>,
|
||||||
|
|
@ -542,6 +546,10 @@ impl<C: XConnection> ServerState<C> {
|
||||||
.bind::<WpViewporter, _, _>(&qh, 1..=1, ())
|
.bind::<WpViewporter, _, _>(&qh, 1..=1, ())
|
||||||
.expect("Could not bind wp_viewporter");
|
.expect("Could not bind wp_viewporter");
|
||||||
|
|
||||||
|
let fractional_scale = clientside.global_list.bind::<WpFractionalScaleManagerV1, _, _>(&qh, 1..=1, ())
|
||||||
|
.inspect_err(|e| warn!("Couldn't bind fractional scale manager: {e}. Fractional scaling will not work."))
|
||||||
|
.ok();
|
||||||
|
|
||||||
let manager = DataDeviceManagerState::bind(&clientside.global_list, &qh)
|
let manager = DataDeviceManagerState::bind(&clientside.global_list, &qh)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
warn!("Could not bind data device manager ({e:?}). Clipboard will not work.")
|
warn!("Could not bind data device manager ({e:?}). Clipboard will not work.")
|
||||||
|
|
@ -590,6 +598,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
associated_windows: Default::default(),
|
associated_windows: Default::default(),
|
||||||
xdg_wm_base,
|
xdg_wm_base,
|
||||||
viewporter,
|
viewporter,
|
||||||
|
fractional_scale,
|
||||||
clipboard_data,
|
clipboard_data,
|
||||||
last_kb_serial: None,
|
last_kb_serial: None,
|
||||||
activation_state,
|
activation_state,
|
||||||
|
|
@ -731,14 +740,14 @@ impl<C: XConnection> ServerState<C> {
|
||||||
if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role {
|
if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role {
|
||||||
if let Some(min_size) = &hints.min_size {
|
if let Some(min_size) = &hints.min_size {
|
||||||
data.toplevel.set_min_size(
|
data.toplevel.set_min_size(
|
||||||
min_size.width / surface.scale_factor,
|
(min_size.width as f64 / surface.scale_factor) as i32,
|
||||||
min_size.height / surface.scale_factor,
|
(min_size.height as f64 / surface.scale_factor) as i32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(max_size) = &hints.max_size {
|
if let Some(max_size) = &hints.max_size {
|
||||||
data.toplevel.set_max_size(
|
data.toplevel.set_max_size(
|
||||||
max_size.width / surface.scale_factor,
|
(max_size.width as f64 / surface.scale_factor) as i32,
|
||||||
max_size.height / surface.scale_factor,
|
(max_size.height as f64 / surface.scale_factor) as i32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -831,12 +840,12 @@ impl<C: XConnection> ServerState<C> {
|
||||||
match &data.role {
|
match &data.role {
|
||||||
Some(SurfaceRole::Popup(Some(popup))) => {
|
Some(SurfaceRole::Popup(Some(popup))) => {
|
||||||
popup.positioner.set_offset(
|
popup.positioner.set_offset(
|
||||||
(event.x() as i32 - win.output_offset.x) / data.scale_factor,
|
((event.x() as i32 - win.output_offset.x) as f64 / data.scale_factor) as i32,
|
||||||
(event.y() as i32 - win.output_offset.y) / data.scale_factor,
|
((event.y() as i32 - win.output_offset.y) as f64 / data.scale_factor) as i32,
|
||||||
);
|
);
|
||||||
popup.positioner.set_size(
|
popup.positioner.set_size(
|
||||||
event.width() as i32 / data.scale_factor,
|
(event.width() as f64 / data.scale_factor) as i32,
|
||||||
event.height() as i32 / data.scale_factor,
|
(event.height() as f64 / data.scale_factor) as i32,
|
||||||
);
|
);
|
||||||
popup.popup.reposition(&popup.positioner, 0);
|
popup.popup.reposition(&popup.positioner, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -1199,19 +1208,19 @@ impl<C: XConnection> ServerState<C> {
|
||||||
|
|
||||||
let positioner = self.xdg_wm_base.create_positioner(&self.qh, ());
|
let positioner = self.xdg_wm_base.create_positioner(&self.qh, ());
|
||||||
positioner.set_size(
|
positioner.set_size(
|
||||||
1.max(window.attrs.dims.width as i32 / initial_scale),
|
1.max((window.attrs.dims.width as f64 / initial_scale) as i32),
|
||||||
1.max(window.attrs.dims.height as i32 / initial_scale),
|
1.max((window.attrs.dims.height as f64 / initial_scale) as i32),
|
||||||
);
|
);
|
||||||
let x = (window.attrs.dims.x - parent_dims.x) as i32 / initial_scale;
|
let x = ((window.attrs.dims.x - parent_dims.x) as f64 / initial_scale) as i32;
|
||||||
let y = (window.attrs.dims.y - parent_dims.y) as i32 / initial_scale;
|
let y = ((window.attrs.dims.y - parent_dims.y) as f64 / initial_scale) as i32;
|
||||||
positioner.set_offset(x, y);
|
positioner.set_offset(x, y);
|
||||||
positioner.set_anchor(Anchor::TopLeft);
|
positioner.set_anchor(Anchor::TopLeft);
|
||||||
positioner.set_gravity(Gravity::BottomRight);
|
positioner.set_gravity(Gravity::BottomRight);
|
||||||
positioner.set_anchor_rect(
|
positioner.set_anchor_rect(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
parent_window.attrs.dims.width as i32 / initial_scale,
|
(parent_window.attrs.dims.width as f64 / initial_scale) as i32,
|
||||||
parent_window.attrs.dims.height as i32 / initial_scale,
|
(parent_window.attrs.dims.height as f64 / initial_scale) as i32,
|
||||||
);
|
);
|
||||||
let popup = xdg_surface.get_popup(
|
let popup = xdg_surface.get_popup(
|
||||||
Some(&parent_surface.xdg().unwrap().surface),
|
Some(&parent_surface.xdg().unwrap().surface),
|
||||||
|
|
@ -1230,7 +1239,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
};
|
};
|
||||||
SurfaceRole::Popup(Some(popup))
|
SurfaceRole::Popup(Some(popup))
|
||||||
} else {
|
} else {
|
||||||
initial_scale = 1;
|
initial_scale = 1.0;
|
||||||
let data = self.create_toplevel(window, surface_key, xdg_surface, fullscreen);
|
let data = self.create_toplevel(window, surface_key, xdg_surface, fullscreen);
|
||||||
SurfaceRole::Toplevel(Some(data))
|
SurfaceRole::Toplevel(Some(data))
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::{ServerState, WindowDims};
|
use super::{ServerState, WindowDims};
|
||||||
use crate::xstate::{SetState, WinSize, WmName};
|
use crate::xstate::{SetState, WinSize, WmName};
|
||||||
use rustix::event::{poll, PollFd, PollFlags};
|
use rustix::event::{poll, PollFd, PollFlags};
|
||||||
|
use smithay_client_toolkit::compositor::Surface;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::fd::{AsRawFd, BorrowedFd};
|
use std::os::fd::{AsRawFd, BorrowedFd};
|
||||||
|
|
@ -23,6 +24,7 @@ use wayland_client::{
|
||||||
},
|
},
|
||||||
Connection, Proxy, WEnum,
|
Connection, Proxy, WEnum,
|
||||||
};
|
};
|
||||||
|
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1;
|
||||||
|
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
|
|
@ -269,7 +271,7 @@ struct PopupBuilder {
|
||||||
parent_window: Window,
|
parent_window: Window,
|
||||||
parent_surface: testwl::SurfaceId,
|
parent_surface: testwl::SurfaceId,
|
||||||
dims: WindowDims,
|
dims: WindowDims,
|
||||||
scale: i32,
|
scale: f64,
|
||||||
check_size_and_pos: bool,
|
check_size_and_pos: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,7 +287,7 @@ impl PopupBuilder {
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
||||||
},
|
},
|
||||||
scale: 1,
|
scale: 1.0,
|
||||||
check_size_and_pos: true,
|
check_size_and_pos: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,14 +317,14 @@ impl PopupBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale(mut self, scale: i32) -> Self {
|
fn scale(mut self, scale: impl Into<f64>) -> Self {
|
||||||
self.scale = scale;
|
self.scale = scale.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestFixture {
|
impl TestFixture {
|
||||||
fn new() -> Self {
|
fn new_pre_connect(pre_connect: impl FnOnce(&mut testwl::Server)) -> Self {
|
||||||
INIT.call_once(|| {
|
INIT.call_once(|| {
|
||||||
env_logger::builder()
|
env_logger::builder()
|
||||||
.is_test(true)
|
.is_test(true)
|
||||||
|
|
@ -332,6 +334,7 @@ impl TestFixture {
|
||||||
|
|
||||||
let (client_s, server_s) = UnixStream::pair().unwrap();
|
let (client_s, server_s) = UnixStream::pair().unwrap();
|
||||||
let mut testwl = testwl::Server::new(true);
|
let mut testwl = testwl::Server::new(true);
|
||||||
|
pre_connect(&mut testwl);
|
||||||
let display = Display::<FakeServerState>::new().unwrap();
|
let display = Display::<FakeServerState>::new().unwrap();
|
||||||
testwl.connect(server_s);
|
testwl.connect(server_s);
|
||||||
// Handle initial globals roundtrip setup requirement
|
// Handle initial globals roundtrip setup requirement
|
||||||
|
|
@ -369,6 +372,10 @@ impl TestFixture {
|
||||||
f
|
f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::new_pre_connect(|_| {})
|
||||||
|
}
|
||||||
|
|
||||||
fn new_with_compositor() -> (Self, Compositor) {
|
fn new_with_compositor() -> (Self, Compositor) {
|
||||||
let mut f = Self::new();
|
let mut f = Self::new();
|
||||||
let compositor = f.compositor();
|
let compositor = f.compositor();
|
||||||
|
|
@ -512,6 +519,39 @@ impl TestFixture {
|
||||||
(output, self.testwl.last_created_output())
|
(output, self.testwl.last_created_output())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enable_fractional_scale(&mut self) -> TestObject<WpFractionalScaleManagerV1> {
|
||||||
|
self.testwl.enable_fractional_scale();
|
||||||
|
self.run();
|
||||||
|
self.run();
|
||||||
|
|
||||||
|
let mut events = std::mem::take(&mut *self.registry.data.events.lock().unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
events.len(),
|
||||||
|
1,
|
||||||
|
"Unexpected number of global events after enabling fractional scale"
|
||||||
|
);
|
||||||
|
let event = events.pop().unwrap();
|
||||||
|
let Ev::<WlRegistry>::Global {
|
||||||
|
name,
|
||||||
|
interface,
|
||||||
|
version,
|
||||||
|
} = event
|
||||||
|
else {
|
||||||
|
panic!("Unexpected event: {event:?}");
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(interface, WpFractionalScaleManagerV1::interface().name);
|
||||||
|
let man = TestObject::<WpFractionalScaleManagerV1>::from_request(
|
||||||
|
&self.registry.obj,
|
||||||
|
Req::<WlRegistry>::Bind {
|
||||||
|
name,
|
||||||
|
id: (WpFractionalScaleManagerV1::interface(), version),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.run();
|
||||||
|
man
|
||||||
|
}
|
||||||
|
|
||||||
fn enable_xdg_output(&mut self) -> TestObject<ZxdgOutputManagerV1> {
|
fn enable_xdg_output(&mut self) -> TestObject<ZxdgOutputManagerV1> {
|
||||||
self.testwl.enable_xdg_output_manager();
|
self.testwl.enable_xdg_output_manager();
|
||||||
self.run();
|
self.run();
|
||||||
|
|
@ -755,8 +795,8 @@ impl TestFixture {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pos.size.as_ref().unwrap(),
|
pos.size.as_ref().unwrap(),
|
||||||
&testwl::Vec2 {
|
&testwl::Vec2 {
|
||||||
x: 50 / scale,
|
x: (dims.width as f64 / scale) as i32,
|
||||||
y: 50 / scale
|
y: (dims.height as f64 / scale) as i32
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -765,8 +805,8 @@ impl TestFixture {
|
||||||
pos.anchor_rect.as_ref().unwrap(),
|
pos.anchor_rect.as_ref().unwrap(),
|
||||||
&testwl::Rect {
|
&testwl::Rect {
|
||||||
size: testwl::Vec2 {
|
size: testwl::Vec2 {
|
||||||
x: parent_win.dims.width as i32 / scale,
|
x: (parent_win.dims.width as f64 / scale) as i32,
|
||||||
y: parent_win.dims.height as i32 / scale
|
y: (parent_win.dims.height as f64 / scale) as i32
|
||||||
},
|
},
|
||||||
offset: testwl::Vec2::default()
|
offset: testwl::Vec2::default()
|
||||||
}
|
}
|
||||||
|
|
@ -774,8 +814,8 @@ impl TestFixture {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pos.offset,
|
pos.offset,
|
||||||
testwl::Vec2 {
|
testwl::Vec2 {
|
||||||
x: (dims.x - parent_win.dims.x) as i32 / scale,
|
x: ((dims.x - parent_win.dims.x) as f64 / scale) as i32,
|
||||||
y: (dims.y - parent_win.dims.y) as i32 / scale
|
y: ((dims.y - parent_win.dims.y) as f64 / scale) as i32
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1955,6 +1995,45 @@ fn scaled_output_popup() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fractional_scale_popup() {
|
||||||
|
let mut f = TestFixture::new_pre_connect(|testwl| {
|
||||||
|
testwl.enable_fractional_scale();
|
||||||
|
});
|
||||||
|
let comp = f.compositor();
|
||||||
|
|
||||||
|
let toplevel = unsafe { Window::new(1) };
|
||||||
|
let (_, toplevel_id) = f.create_toplevel(&comp, toplevel);
|
||||||
|
let surface_data = f
|
||||||
|
.testwl
|
||||||
|
.get_surface_data(toplevel_id)
|
||||||
|
.expect("No surface data");
|
||||||
|
let fractional = surface_data
|
||||||
|
.fractional
|
||||||
|
.as_ref()
|
||||||
|
.expect("No fractional scale for surface");
|
||||||
|
|
||||||
|
fractional.preferred_scale(180); // 1.5 scale
|
||||||
|
f.run();
|
||||||
|
f.run();
|
||||||
|
|
||||||
|
let popup = unsafe { Window::new(2) };
|
||||||
|
let builder = PopupBuilder::new(popup, toplevel, toplevel_id)
|
||||||
|
.x(60)
|
||||||
|
.y(60)
|
||||||
|
.width(60)
|
||||||
|
.height(60)
|
||||||
|
.scale(1.5);
|
||||||
|
let initial_dims = builder.dims;
|
||||||
|
f.create_popup(&comp, builder);
|
||||||
|
f.run();
|
||||||
|
assert_eq!(
|
||||||
|
initial_dims,
|
||||||
|
f.connection().window(popup).dims,
|
||||||
|
"X11 dimensions changed after configure"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scaled_output_small_popup() {
|
fn scaled_output_small_popup() {
|
||||||
let (mut f, comp) = TestFixture::new_with_compositor();
|
let (mut f, comp) = TestFixture::new_with_compositor();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex, OnceLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
|
fractional_scale::v1::server::{
|
||||||
|
wp_fractional_scale_manager_v1::{self, WpFractionalScaleManagerV1},
|
||||||
|
wp_fractional_scale_v1::{self, WpFractionalScaleV1},
|
||||||
|
},
|
||||||
linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
|
linux_dmabuf::zv1::server::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
|
||||||
pointer_constraints::zv1::server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
|
pointer_constraints::zv1::server::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
|
||||||
relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
relative_pointer::zv1::server::zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
||||||
|
|
@ -88,6 +92,7 @@ pub struct SurfaceData {
|
||||||
pub last_damage: Option<BufferDamage>,
|
pub last_damage: Option<BufferDamage>,
|
||||||
pub role: Option<SurfaceRole>,
|
pub role: Option<SurfaceRole>,
|
||||||
pub last_enter_serial: Option<u32>,
|
pub last_enter_serial: Option<u32>,
|
||||||
|
pub fractional: Option<WpFractionalScaleV1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceData {
|
impl SurfaceData {
|
||||||
|
|
@ -732,6 +737,12 @@ impl Server {
|
||||||
xdg.done();
|
xdg.done();
|
||||||
self.display.flush_clients().unwrap();
|
self.display.flush_clients().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn enable_fractional_scale(&mut self) {
|
||||||
|
self.dh
|
||||||
|
.create_global::<State, WpFractionalScaleManagerV1, _>(1, ());
|
||||||
|
self.display.flush_clients().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
|
@ -746,6 +757,8 @@ simple_global_dispatch!(XdgWmBase);
|
||||||
simple_global_dispatch!(ZxdgOutputManagerV1);
|
simple_global_dispatch!(ZxdgOutputManagerV1);
|
||||||
simple_global_dispatch!(ZwpTabletManagerV2);
|
simple_global_dispatch!(ZwpTabletManagerV2);
|
||||||
simple_global_dispatch!(ZxdgDecorationManagerV1);
|
simple_global_dispatch!(ZxdgDecorationManagerV1);
|
||||||
|
simple_global_dispatch!(WpViewporter);
|
||||||
|
simple_global_dispatch!(WpFractionalScaleManagerV1);
|
||||||
|
|
||||||
impl Dispatch<ZwpTabletManagerV2, ()> for State {
|
impl Dispatch<ZwpTabletManagerV2, ()> for State {
|
||||||
fn request(
|
fn request(
|
||||||
|
|
@ -1505,6 +1518,7 @@ impl Dispatch<WlCompositor, ()> for State {
|
||||||
last_damage: None,
|
last_damage: None,
|
||||||
role: None,
|
role: None,
|
||||||
last_enter_serial: None,
|
last_enter_serial: None,
|
||||||
|
fractional: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
state.last_surface_id = Some(SurfaceId(id));
|
state.last_surface_id = Some(SurfaceId(id));
|
||||||
|
|
@ -1812,19 +1826,6 @@ impl Dispatch<ZxdgToplevelDecorationV1, SurfaceId> for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalDispatch<WpViewporter, ()> for State {
|
|
||||||
fn bind(
|
|
||||||
_: &mut Self,
|
|
||||||
_: &DisplayHandle,
|
|
||||||
_: &Client,
|
|
||||||
resource: wayland_server::New<WpViewporter>,
|
|
||||||
_: &(),
|
|
||||||
data_init: &mut wayland_server::DataInit<'_, Self>,
|
|
||||||
) {
|
|
||||||
data_init.init(resource, ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Dispatch<WpViewporter, ()> for State {
|
impl Dispatch<WpViewporter, ()> for State {
|
||||||
fn request(
|
fn request(
|
||||||
_: &mut Self,
|
_: &mut Self,
|
||||||
|
|
@ -1858,3 +1859,46 @@ impl Dispatch<WpViewport, ()> for State {
|
||||||
//todo!()
|
//todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WpFractionalScaleManagerV1, ()> for State {
|
||||||
|
fn request(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &Client,
|
||||||
|
_: &WpFractionalScaleManagerV1,
|
||||||
|
request: <WpFractionalScaleManagerV1 as Resource>::Request,
|
||||||
|
_: &(),
|
||||||
|
_: &DisplayHandle,
|
||||||
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
wp_fractional_scale_manager_v1::Request::GetFractionalScale { id, surface } => {
|
||||||
|
let surface_id = SurfaceId(surface.id().protocol_id());
|
||||||
|
let fractional = data_init.init(id, surface_id);
|
||||||
|
let surface_data = state.surfaces.get_mut(&surface_id).unwrap();
|
||||||
|
surface_data.fractional = Some(fractional);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WpFractionalScaleV1, SurfaceId> for State {
|
||||||
|
fn request(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &Client,
|
||||||
|
_: &WpFractionalScaleV1,
|
||||||
|
request: <WpFractionalScaleV1 as Resource>::Request,
|
||||||
|
data: &SurfaceId,
|
||||||
|
_: &DisplayHandle,
|
||||||
|
_: &mut wayland_server::DataInit<'_, Self>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
wp_fractional_scale_v1::Request::Destroy => {
|
||||||
|
if let Some(surface_data) = state.surfaces.get_mut(data) {
|
||||||
|
surface_data.fractional.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue