parent
799027d1ae
commit
b98fa84524
6 changed files with 269 additions and 151 deletions
|
|
@ -1350,11 +1350,11 @@ impl<C: XConnection> GlobalDispatch<WlOutput, Global> for ServerState<C> {
|
||||||
(
|
(
|
||||||
server,
|
server,
|
||||||
client,
|
client,
|
||||||
event::OutputScaleFactor(1),
|
event::OutputScaleFactor::Output(1),
|
||||||
event::OutputDimensions::default(),
|
event::OutputDimensions::default(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
state.output_scales_updated = true;
|
state.updated_outputs.push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global_dispatch_with_events!(WlDrmServer, WlDrmClient);
|
global_dispatch_with_events!(WlDrmServer, WlDrmClient);
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,15 @@ use wayland_server::protocol::{
|
||||||
wl_seat::WlSeat, wl_touch::WlTouch,
|
wl_seat::WlSeat, wl_touch::WlTouch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(super) struct SurfaceScaleFactor(pub f64);
|
||||||
|
|
||||||
#[derive(hecs::Bundle)]
|
#[derive(hecs::Bundle)]
|
||||||
pub(super) struct SurfaceBundle {
|
pub(super) struct SurfaceBundle {
|
||||||
pub client: client::wl_surface::WlSurface,
|
pub client: client::wl_surface::WlSurface,
|
||||||
pub server: WlSurface,
|
pub server: WlSurface,
|
||||||
pub scale: SurfaceScaleFactor,
|
|
||||||
pub viewport: WpViewport,
|
pub viewport: WpViewport,
|
||||||
|
pub scale: SurfaceScaleFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -86,12 +89,22 @@ impl Event for SurfaceEvents {
|
||||||
wp_fractional_scale_v1::Event::PreferredScale { scale } => {
|
wp_fractional_scale_v1::Event::PreferredScale { scale } => {
|
||||||
let entity = state.world.entity(target).unwrap();
|
let entity = state.world.entity(target).unwrap();
|
||||||
let factor = scale as f64 / 120.0;
|
let factor = scale as f64 / 120.0;
|
||||||
entity.get::<&mut SurfaceScaleFactor>().unwrap().0 = factor;
|
debug!(
|
||||||
log::debug!(
|
|
||||||
"{} scale factor: {}",
|
"{} scale factor: {}",
|
||||||
entity.get::<&WlSurface>().unwrap().id(),
|
entity.get::<&WlSurface>().unwrap().id(),
|
||||||
factor
|
factor
|
||||||
);
|
);
|
||||||
|
|
||||||
|
entity.get::<&mut SurfaceScaleFactor>().unwrap().0 = factor;
|
||||||
|
|
||||||
|
if let Some(OnOutput(output)) = entity.get::<&OnOutput>().as_deref().copied() {
|
||||||
|
if update_output_scale(
|
||||||
|
state.world.query_one(output).unwrap(),
|
||||||
|
OutputScaleFactor::Fractional(factor),
|
||||||
|
) {
|
||||||
|
state.updated_outputs.push(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
if entity.has::<WindowData>() {
|
if entity.has::<WindowData>() {
|
||||||
update_surface_viewport(state.world.query_one(target).unwrap());
|
update_surface_viewport(state.world.query_one(target).unwrap());
|
||||||
}
|
}
|
||||||
|
|
@ -126,11 +139,6 @@ impl SurfaceEvents {
|
||||||
surface.enter(&output);
|
surface.enter(&output);
|
||||||
let on_output = OnOutput(output_entity);
|
let on_output = OnOutput(output_entity);
|
||||||
|
|
||||||
if state.fractional_scale.is_none() {
|
|
||||||
data.get::<&mut SurfaceScaleFactor>().unwrap().0 =
|
|
||||||
output_data.get::<&OutputScaleFactor>().unwrap().0 as f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("{} entered {}", surface.id(), output.id());
|
debug!("{} entered {}", surface.id(), output.id());
|
||||||
|
|
||||||
let mut query = data.query::<(&x::Window, &mut WindowData)>();
|
let mut query = data.query::<(&x::Window, &mut WindowData)>();
|
||||||
|
|
@ -151,9 +159,19 @@ impl SurfaceEvents {
|
||||||
conn.focus_window(*window, output);
|
conn.focus_window(*window, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(query);
|
|
||||||
if state.fractional_scale.is_none() {
|
if state.fractional_scale.is_none() {
|
||||||
|
let output_scale = output_data.get::<&OutputScaleFactor>().unwrap().get();
|
||||||
|
data.get::<&mut SurfaceScaleFactor>().unwrap().0 = output_scale;
|
||||||
|
drop(query);
|
||||||
update_surface_viewport(state.world.query_one(target).unwrap());
|
update_surface_viewport(state.world.query_one(target).unwrap());
|
||||||
|
} else {
|
||||||
|
let scale = data.get::<&SurfaceScaleFactor>().unwrap();
|
||||||
|
if update_output_scale(
|
||||||
|
state.world.query_one(on_output.0).unwrap(),
|
||||||
|
OutputScaleFactor::Fractional(scale.0),
|
||||||
|
) {
|
||||||
|
state.updated_outputs.push(on_output.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd.insert_one(target, on_output);
|
cmd.insert_one(target, on_output);
|
||||||
|
|
@ -200,20 +218,19 @@ impl SurfaceEvents {
|
||||||
drop(xdg);
|
drop(xdg);
|
||||||
|
|
||||||
if let Some(pending) = pending {
|
if let Some(pending) = pending {
|
||||||
let mut query = data.query::<(&mut SurfaceScaleFactor, &x::Window, &mut WindowData)>();
|
let mut query = data.query::<(&SurfaceScaleFactor, &x::Window, &mut WindowData)>();
|
||||||
let (scale_factor, window, window_data) = query.get().unwrap();
|
let (scale_factor, window, window_data) = query.get().unwrap();
|
||||||
let scale_factor = scale_factor.0;
|
|
||||||
|
|
||||||
let window = *window;
|
let window = *window;
|
||||||
let x = (pending.x as f64 * scale_factor) as i32 + window_data.output_offset.x;
|
let x = (pending.x as f64 * scale_factor.0) as i32 + window_data.output_offset.x;
|
||||||
let y = (pending.y as f64 * scale_factor) as i32 + window_data.output_offset.y;
|
let y = (pending.y as f64 * scale_factor.0) as i32 + window_data.output_offset.y;
|
||||||
let width = if pending.width > 0 {
|
let width = if pending.width > 0 {
|
||||||
(pending.width as f64 * scale_factor) as u16
|
(pending.width as f64 * scale_factor.0) as u16
|
||||||
} else {
|
} else {
|
||||||
window_data.attrs.dims.width
|
window_data.attrs.dims.width
|
||||||
};
|
};
|
||||||
let height = if pending.height > 0 {
|
let height = if pending.height > 0 {
|
||||||
(pending.height as f64 * scale_factor) as u16
|
(pending.height as f64 * scale_factor.0) as u16
|
||||||
} else {
|
} else {
|
||||||
window_data.attrs.dims.height
|
window_data.attrs.dims.height
|
||||||
};
|
};
|
||||||
|
|
@ -452,7 +469,7 @@ impl Event for client::wl_pointer::Event {
|
||||||
|
|
||||||
let surface_is_popup = matches!(role, SurfaceRole::Popup(_));
|
let surface_is_popup = matches!(role, SurfaceRole::Popup(_));
|
||||||
let mut do_enter = || {
|
let mut do_enter = || {
|
||||||
debug!("pointer entering {} ({serial})", surface.id());
|
debug!("pointer entering {} ({serial} {})", surface.id(), scale.0);
|
||||||
server.enter(serial, surface, surface_x * scale.0, surface_y * scale.0);
|
server.enter(serial, surface, surface_x * scale.0, surface_y * scale.0);
|
||||||
state.connection.as_mut().unwrap().raise_to_top(*window);
|
state.connection.as_mut().unwrap().raise_to_top(*window);
|
||||||
if !surface_is_popup {
|
if !surface_is_popup {
|
||||||
|
|
@ -488,6 +505,7 @@ impl Event for client::wl_pointer::Event {
|
||||||
cmd.run_on(&mut state.world);
|
cmd.run_on(&mut state.world);
|
||||||
}
|
}
|
||||||
client::wl_pointer::Event::Leave { serial, surface } => {
|
client::wl_pointer::Event::Leave { serial, surface } => {
|
||||||
|
let _ = state.world.remove_one::<PendingEnter>(target);
|
||||||
if !surface.is_alive() {
|
if !surface.is_alive() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -720,13 +738,47 @@ impl Event for client::wl_touch::Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub(super) struct OnOutput(pub Entity);
|
pub(super) struct OnOutput(pub Entity);
|
||||||
struct OutputName(String);
|
struct OutputName(String);
|
||||||
fn get_output_name(output: Option<&OnOutput>, world: &World) -> Option<String> {
|
fn get_output_name(output: Option<&OnOutput>, world: &World) -> Option<String> {
|
||||||
output.map(|o| world.get::<&OutputName>(o.0).unwrap().0.clone())
|
output.map(|o| world.get::<&OutputName>(o.0).unwrap().0.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct OutputScaleFactor(pub i32);
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub(super) enum OutputScaleFactor {
|
||||||
|
Output(i32),
|
||||||
|
Fractional(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputScaleFactor {
|
||||||
|
pub(super) fn get(&self) -> f64 {
|
||||||
|
match *self {
|
||||||
|
Self::Output(o) => o as _,
|
||||||
|
Self::Fractional(f) => f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn update_output_scale(
|
||||||
|
mut output_scale: hecs::QueryOne<&mut OutputScaleFactor>,
|
||||||
|
factor: OutputScaleFactor,
|
||||||
|
) -> bool {
|
||||||
|
let output_scale = output_scale.get().unwrap();
|
||||||
|
if matches!(output_scale, OutputScaleFactor::Fractional(..))
|
||||||
|
&& matches!(factor, OutputScaleFactor::Output(..))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *output_scale != factor {
|
||||||
|
*output_scale = factor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
enum OutputDimensionsSource {
|
enum OutputDimensionsSource {
|
||||||
// The data in this variant is the values needed for the wl_output.geometry event.
|
// The data in this variant is the values needed for the wl_output.geometry event.
|
||||||
|
|
@ -1012,26 +1064,15 @@ impl OutputEvent {
|
||||||
"{} scale: {factor}",
|
"{} scale: {factor}",
|
||||||
state.world.get::<&WlOutput>(target).unwrap().id()
|
state.world.get::<&WlOutput>(target).unwrap().id()
|
||||||
);
|
);
|
||||||
state.world.get::<&mut OutputScaleFactor>(target).unwrap().0 = factor;
|
if update_output_scale(
|
||||||
|
state.world.query_one(target).unwrap(),
|
||||||
|
OutputScaleFactor::Output(factor),
|
||||||
|
) {
|
||||||
|
state.updated_outputs.push(target);
|
||||||
|
}
|
||||||
if state.fractional_scale.is_none() {
|
if state.fractional_scale.is_none() {
|
||||||
let mut surfaces = vec![];
|
|
||||||
for (entity, (scale, _)) in state
|
|
||||||
.world
|
|
||||||
.query_mut::<(&mut SurfaceScaleFactor, &OnOutput)>()
|
|
||||||
.with::<&WindowData>()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(_, (_, o))| o.0 == target)
|
|
||||||
{
|
|
||||||
surfaces.push(entity);
|
|
||||||
scale.0 = factor as f64;
|
|
||||||
}
|
|
||||||
for entity in surfaces {
|
|
||||||
update_surface_viewport(state.world.query_one(entity).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
state.world.get::<&WlOutput>(target).unwrap().scale(factor);
|
state.world.get::<&WlOutput>(target).unwrap().scale(factor);
|
||||||
}
|
}
|
||||||
state.output_scales_updated = true;
|
|
||||||
}
|
}
|
||||||
Event::Name { name } => {
|
Event::Name { name } => {
|
||||||
state
|
state
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,6 @@ struct SurfaceAttach {
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
struct SurfaceSerial([u32; 2]);
|
struct SurfaceSerial([u32; 2]);
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct SurfaceScaleFactor(f64);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum SurfaceRole {
|
enum SurfaceRole {
|
||||||
Toplevel(Option<ToplevelData>),
|
Toplevel(Option<ToplevelData>),
|
||||||
|
|
@ -418,8 +415,8 @@ pub struct ServerState<C: XConnection> {
|
||||||
activation_state: Option<ActivationState>,
|
activation_state: Option<ActivationState>,
|
||||||
global_output_offset: GlobalOutputOffset,
|
global_output_offset: GlobalOutputOffset,
|
||||||
global_offset_updated: bool,
|
global_offset_updated: bool,
|
||||||
output_scales_updated: bool,
|
updated_outputs: Vec<Entity>,
|
||||||
new_scale: Option<i32>,
|
new_scale: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: XConnection> ServerState<C> {
|
impl<C: XConnection> ServerState<C> {
|
||||||
|
|
@ -507,7 +504,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
global_offset_updated: false,
|
global_offset_updated: false,
|
||||||
output_scales_updated: false,
|
updated_outputs: Vec::new(),
|
||||||
new_scale: None,
|
new_scale: None,
|
||||||
decoration_manager,
|
decoration_manager,
|
||||||
world: MyWorld::new(global_list),
|
world: MyWorld::new(global_list),
|
||||||
|
|
@ -999,19 +996,42 @@ impl<C: XConnection> ServerState<C> {
|
||||||
self.global_offset_updated = false;
|
self.global_offset_updated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.output_scales_updated {
|
if !self.updated_outputs.is_empty() {
|
||||||
|
for output in self.updated_outputs.drain(..) {
|
||||||
|
let output_scale = self.world.get::<&OutputScaleFactor>(output).unwrap();
|
||||||
|
if matches!(*output_scale, OutputScaleFactor::Output(..)) {
|
||||||
|
let mut surface_query = self
|
||||||
|
.world
|
||||||
|
.query::<(&OnOutput, &mut SurfaceScaleFactor)>()
|
||||||
|
.with::<(&WindowData, &WlSurface)>();
|
||||||
|
|
||||||
|
let mut surfaces = vec![];
|
||||||
|
for (surface, (OnOutput(s_output), surface_scale)) in surface_query.iter() {
|
||||||
|
if *s_output == output {
|
||||||
|
surface_scale.0 = output_scale.get();
|
||||||
|
surfaces.push(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(surface_query);
|
||||||
|
for surface in surfaces {
|
||||||
|
update_surface_viewport(self.world.query_one(surface).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut mixed_scale = false;
|
let mut mixed_scale = false;
|
||||||
let mut scale;
|
let mut scale;
|
||||||
|
|
||||||
let mut outputs = self.world.query_mut::<&OutputScaleFactor>().into_iter();
|
let mut outputs = self.world.query_mut::<&OutputScaleFactor>().into_iter();
|
||||||
let (_, output_scale) = outputs.next().unwrap();
|
let (_, output_scale) = outputs.next().unwrap();
|
||||||
|
|
||||||
scale = output_scale.0;
|
scale = output_scale.get();
|
||||||
|
|
||||||
for (_, output_scale) in outputs {
|
for (_, output_scale) in outputs {
|
||||||
if output_scale.0 != scale {
|
if output_scale.get() != scale {
|
||||||
mixed_scale = true;
|
mixed_scale = true;
|
||||||
scale = scale.min(output_scale.0);
|
scale = scale.min(output_scale.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1021,8 +1041,6 @@ impl<C: XConnection> ServerState<C> {
|
||||||
|
|
||||||
debug!("Using new scale {scale}");
|
debug!("Using new scale {scale}");
|
||||||
self.new_scale = Some(scale);
|
self.new_scale = Some(scale);
|
||||||
|
|
||||||
self.output_scales_updated = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -1049,7 +1067,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
.expect("Failed flushing clientside events");
|
.expect("Failed flushing clientside events");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_global_scale(&mut self) -> Option<i32> {
|
pub fn new_global_scale(&mut self) -> Option<f64> {
|
||||||
self.new_scale.take()
|
self.new_scale.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1156,26 +1174,18 @@ impl<C: XConnection> ServerState<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let initial_scale;
|
|
||||||
let role = if let Some(parent) = popup_for {
|
let role = if let Some(parent) = popup_for {
|
||||||
let data;
|
let data = self.create_popup(entity, xdg_surface, parent);
|
||||||
(initial_scale, data) = self.create_popup(entity, xdg_surface, parent);
|
|
||||||
SurfaceRole::Popup(Some(data))
|
SurfaceRole::Popup(Some(data))
|
||||||
} else {
|
} else {
|
||||||
initial_scale = 1.0;
|
|
||||||
let data = self.create_toplevel(entity, xdg_surface, fullscreen);
|
let data = self.create_toplevel(entity, xdg_surface, fullscreen);
|
||||||
SurfaceRole::Toplevel(Some(data))
|
SurfaceRole::Toplevel(Some(data))
|
||||||
};
|
};
|
||||||
|
|
||||||
let (surface_role, scale_factor, client) = self
|
let (surface_role, client) = self
|
||||||
.world
|
.world
|
||||||
.query_one_mut::<(
|
.query_one_mut::<(Option<&SurfaceRole>, &client::wl_surface::WlSurface)>(entity)
|
||||||
Option<&SurfaceRole>,
|
|
||||||
&mut SurfaceScaleFactor,
|
|
||||||
&client::wl_surface::WlSurface,
|
|
||||||
)>(entity)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
scale_factor.0 = initial_scale;
|
|
||||||
|
|
||||||
let new_role_type = std::mem::discriminant(&role);
|
let new_role_type = std::mem::discriminant(&role);
|
||||||
if let Some(role) = surface_role {
|
if let Some(role) = surface_role {
|
||||||
|
|
@ -1294,13 +1304,13 @@ impl<C: XConnection> ServerState<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_popup(
|
fn create_popup(&mut self, entity: Entity, xdg: XdgSurface, parent: x::Window) -> PopupData {
|
||||||
&mut self,
|
let mut query = self
|
||||||
entity: Entity,
|
.world
|
||||||
xdg: XdgSurface,
|
.query_one::<(&WindowData, &mut SurfaceScaleFactor)>(entity)
|
||||||
parent: x::Window,
|
.unwrap();
|
||||||
) -> (f64, PopupData) {
|
|
||||||
let window = self.world.get::<&WindowData>(entity).unwrap();
|
let (window, scale) = query.get().unwrap();
|
||||||
let mut parent_query = self
|
let mut parent_query = self
|
||||||
.world
|
.world
|
||||||
.query_one::<(&WindowData, &SurfaceScaleFactor, &SurfaceRole)>(self.windows[&parent])
|
.query_one::<(&WindowData, &SurfaceScaleFactor, &SurfaceRole)>(self.windows[&parent])
|
||||||
|
|
@ -1308,6 +1318,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
let (parent_window, parent_scale, parent_role) = parent_query.get().unwrap();
|
let (parent_window, parent_scale, parent_role) = parent_query.get().unwrap();
|
||||||
let parent_dims = parent_window.attrs.dims;
|
let parent_dims = parent_window.attrs.dims;
|
||||||
let initial_scale = parent_scale.0;
|
let initial_scale = parent_scale.0;
|
||||||
|
*scale = *parent_scale;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"creating popup ({:?}) {:?} {:?} {:?} {entity:?} (scale: {initial_scale})",
|
"creating popup ({:?}) {:?} {:?} {:?} {entity:?} (scale: {initial_scale})",
|
||||||
|
|
@ -1340,18 +1351,15 @@ impl<C: XConnection> ServerState<C> {
|
||||||
entity,
|
entity,
|
||||||
);
|
);
|
||||||
|
|
||||||
(
|
PopupData {
|
||||||
initial_scale,
|
popup,
|
||||||
PopupData {
|
positioner,
|
||||||
popup,
|
xdg: XdgSurfaceData {
|
||||||
positioner,
|
surface: xdg,
|
||||||
xdg: XdgSurfaceData {
|
configured: false,
|
||||||
surface: xdg,
|
pending: None,
|
||||||
configured: false,
|
|
||||||
pending: None,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_x_window(&mut self, window: x::Window) {
|
fn close_x_window(&mut self, window: x::Window) {
|
||||||
|
|
|
||||||
|
|
@ -1965,6 +1965,7 @@ fn fractional_scale_popup() {
|
||||||
testwl.enable_fractional_scale();
|
testwl.enable_fractional_scale();
|
||||||
});
|
});
|
||||||
let comp = f.compositor();
|
let comp = f.compositor();
|
||||||
|
let (_, output) = f.new_output(0, 0);
|
||||||
|
|
||||||
let toplevel = unsafe { Window::new(1) };
|
let toplevel = unsafe { Window::new(1) };
|
||||||
let (_, toplevel_id) = f.create_toplevel(&comp, toplevel);
|
let (_, toplevel_id) = f.create_toplevel(&comp, toplevel);
|
||||||
|
|
@ -1978,6 +1979,7 @@ fn fractional_scale_popup() {
|
||||||
.expect("No fractional scale for surface");
|
.expect("No fractional scale for surface");
|
||||||
|
|
||||||
fractional.preferred_scale(180); // 1.5 scale
|
fractional.preferred_scale(180); // 1.5 scale
|
||||||
|
f.testwl.move_surface_to_output(toplevel_id, &output);
|
||||||
f.run();
|
f.run();
|
||||||
f.run();
|
f.run();
|
||||||
|
|
||||||
|
|
@ -2038,6 +2040,7 @@ fn fractional_scale_small_popup() {
|
||||||
});
|
});
|
||||||
let comp = f.compositor();
|
let comp = f.compositor();
|
||||||
|
|
||||||
|
let (_, output) = f.new_output(0, 0);
|
||||||
let toplevel = unsafe { Window::new(1) };
|
let toplevel = unsafe { Window::new(1) };
|
||||||
let (_, toplevel_id) = f.create_toplevel(&comp, toplevel);
|
let (_, toplevel_id) = f.create_toplevel(&comp, toplevel);
|
||||||
let data = f.testwl.get_surface_data(toplevel_id).unwrap();
|
let data = f.testwl.get_surface_data(toplevel_id).unwrap();
|
||||||
|
|
@ -2046,6 +2049,7 @@ fn fractional_scale_small_popup() {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Missing fracitonal scale data");
|
.expect("Missing fracitonal scale data");
|
||||||
fractional.preferred_scale(180); // 1.5 scale
|
fractional.preferred_scale(180); // 1.5 scale
|
||||||
|
f.testwl.move_surface_to_output(toplevel_id, &output);
|
||||||
f.run();
|
f.run();
|
||||||
f.run();
|
f.run();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ impl XState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_global_scale(&mut self, scale: i32) {
|
pub(crate) fn update_global_scale(&mut self, scale: f64) {
|
||||||
self.settings.set_scale(scale);
|
self.settings.set_scale(scale);
|
||||||
self.connection
|
self.connection
|
||||||
.send_and_check_request(&x::ChangeProperty {
|
.send_and_check_request(&x::ChangeProperty {
|
||||||
|
|
@ -57,6 +57,7 @@ pub(super) struct Settings {
|
||||||
settings: HashMap<&'static str, IntSetting>,
|
settings: HashMap<&'static str, IntSetting>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
struct IntSetting {
|
struct IntSetting {
|
||||||
value: i32,
|
value: i32,
|
||||||
last_change_serial: u32,
|
last_change_serial: u32,
|
||||||
|
|
@ -160,17 +161,26 @@ impl Settings {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_scale(&mut self, scale: i32) {
|
fn set_scale(&mut self, scale: f64) {
|
||||||
self.serial += 1;
|
self.serial += 1;
|
||||||
|
|
||||||
self.settings.entry(XFT_DPI).insert_entry(IntSetting {
|
let setting = IntSetting {
|
||||||
value: scale * DEFAULT_DPI * DPI_SCALE_FACTOR,
|
value: (scale * DEFAULT_DPI as f64 * DPI_SCALE_FACTOR as f64).round() as i32,
|
||||||
last_change_serial: self.serial,
|
last_change_serial: self.serial,
|
||||||
});
|
};
|
||||||
|
self.settings.entry(XFT_DPI).insert_entry(setting);
|
||||||
|
// Gdk/WindowScalingFactor + Gdk/UnscaledDPI is identical to setting
|
||||||
|
// GDK_SCALE = scale and then GDK_DPI_SCALE = 1 / scale.
|
||||||
|
self.settings
|
||||||
|
.entry(GDK_UNSCALED_DPI)
|
||||||
|
.insert_entry(IntSetting {
|
||||||
|
value: setting.value / scale as i32,
|
||||||
|
last_change_serial: self.serial,
|
||||||
|
});
|
||||||
self.settings
|
self.settings
|
||||||
.entry(GDK_WINDOW_SCALE)
|
.entry(GDK_WINDOW_SCALE)
|
||||||
.insert_entry(IntSetting {
|
.insert_entry(IntSetting {
|
||||||
value: scale,
|
value: scale as i32,
|
||||||
last_change_serial: self.serial,
|
last_change_serial: self.serial,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,6 +329,15 @@ xcb::atoms_struct! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
serial: u32,
|
||||||
|
data: HashMap<String, Setting>,
|
||||||
|
}
|
||||||
|
struct Setting {
|
||||||
|
value: i32,
|
||||||
|
last_change: u32,
|
||||||
|
}
|
||||||
|
|
||||||
struct Connection {
|
struct Connection {
|
||||||
inner: xcb::Connection,
|
inner: xcb::Connection,
|
||||||
pollfd: PollFd<'static>,
|
pollfd: PollFd<'static>,
|
||||||
|
|
@ -564,6 +573,56 @@ impl Connection {
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_xsettings(&self) -> Settings {
|
||||||
|
let owner = self
|
||||||
|
.get_reply(&x::GetSelectionOwner {
|
||||||
|
selection: self.atoms.xsettings,
|
||||||
|
})
|
||||||
|
.owner();
|
||||||
|
|
||||||
|
let reply = self.get_reply(&x::GetProperty {
|
||||||
|
delete: false,
|
||||||
|
window: owner,
|
||||||
|
property: self.atoms.xsettings_setting,
|
||||||
|
r#type: self.atoms.xsettings_setting,
|
||||||
|
long_offset: 0,
|
||||||
|
long_length: 60,
|
||||||
|
});
|
||||||
|
assert_eq!(reply.r#type(), self.atoms.xsettings_setting);
|
||||||
|
|
||||||
|
let data = reply.value::<u8>();
|
||||||
|
|
||||||
|
let byte_order = data[0];
|
||||||
|
assert_eq!(byte_order, 0);
|
||||||
|
let serial = u32::from_le_bytes(data[4..8].try_into().unwrap());
|
||||||
|
let num_settings = u32::from_le_bytes(data[8..12].try_into().unwrap());
|
||||||
|
|
||||||
|
let mut current_idx = 12;
|
||||||
|
let mut settings = HashMap::new();
|
||||||
|
for _ in 0..num_settings {
|
||||||
|
assert_eq!(&data[current_idx..current_idx + 2], &[0, 0]);
|
||||||
|
let name_len =
|
||||||
|
u16::from_le_bytes(data[current_idx + 2..current_idx + 4].try_into().unwrap());
|
||||||
|
|
||||||
|
let padding_start = current_idx + 4 + name_len as usize;
|
||||||
|
let name = String::from_utf8(data[current_idx + 4..padding_start].to_vec()).unwrap();
|
||||||
|
let num_padding_bytes = (4 - (name_len as usize % 4)) % 4;
|
||||||
|
let data_start = padding_start + num_padding_bytes;
|
||||||
|
let last_change =
|
||||||
|
u32::from_le_bytes(data[data_start..data_start + 4].try_into().unwrap());
|
||||||
|
let value =
|
||||||
|
i32::from_le_bytes(data[data_start + 4..data_start + 8].try_into().unwrap());
|
||||||
|
|
||||||
|
settings.insert(name, Setting { value, last_change });
|
||||||
|
current_idx = data_start + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings {
|
||||||
|
serial,
|
||||||
|
data: settings,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1627,6 +1686,8 @@ fn forced_1x_scale_consistent_x11_size() {
|
||||||
// Update scale
|
// Update scale
|
||||||
output.scale(3);
|
output.scale(3);
|
||||||
output.done();
|
output.done();
|
||||||
|
f.wait_and_dispatch();
|
||||||
|
|
||||||
f.testwl
|
f.testwl
|
||||||
.configure_toplevel(surface, 100, 100, vec![xdg_toplevel::State::Activated]);
|
.configure_toplevel(surface, 100, 100, vec![xdg_toplevel::State::Activated]);
|
||||||
f.testwl.focus_toplevel(surface);
|
f.testwl.focus_toplevel(surface);
|
||||||
|
|
@ -1736,66 +1797,7 @@ fn xsettings_scale() {
|
||||||
let connection = Connection::new(&f.display);
|
let connection = Connection::new(&f.display);
|
||||||
f.testwl.enable_xdg_output_manager();
|
f.testwl.enable_xdg_output_manager();
|
||||||
|
|
||||||
struct Settings {
|
let settings = connection.get_xsettings();
|
||||||
serial: u32,
|
|
||||||
data: HashMap<String, Setting>,
|
|
||||||
}
|
|
||||||
struct Setting {
|
|
||||||
value: i32,
|
|
||||||
last_change: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
let owner = connection
|
|
||||||
.get_reply(&x::GetSelectionOwner {
|
|
||||||
selection: connection.atoms.xsettings,
|
|
||||||
})
|
|
||||||
.owner();
|
|
||||||
|
|
||||||
let get_settings = || -> Settings {
|
|
||||||
let reply = connection.get_reply(&x::GetProperty {
|
|
||||||
delete: false,
|
|
||||||
window: owner,
|
|
||||||
property: connection.atoms.xsettings_setting,
|
|
||||||
r#type: connection.atoms.xsettings_setting,
|
|
||||||
long_offset: 0,
|
|
||||||
long_length: 60,
|
|
||||||
});
|
|
||||||
assert_eq!(reply.r#type(), connection.atoms.xsettings_setting);
|
|
||||||
|
|
||||||
let data = reply.value::<u8>();
|
|
||||||
|
|
||||||
let byte_order = data[0];
|
|
||||||
assert_eq!(byte_order, 0);
|
|
||||||
let serial = u32::from_le_bytes(data[4..8].try_into().unwrap());
|
|
||||||
let num_settings = u32::from_le_bytes(data[8..12].try_into().unwrap());
|
|
||||||
|
|
||||||
let mut current_idx = 12;
|
|
||||||
let mut settings = HashMap::new();
|
|
||||||
for _ in 0..num_settings {
|
|
||||||
assert_eq!(&data[current_idx..current_idx + 2], &[0, 0]);
|
|
||||||
let name_len =
|
|
||||||
u16::from_le_bytes(data[current_idx + 2..current_idx + 4].try_into().unwrap());
|
|
||||||
|
|
||||||
let padding_start = current_idx + 4 + name_len as usize;
|
|
||||||
let name = String::from_utf8(data[current_idx + 4..padding_start].to_vec()).unwrap();
|
|
||||||
let num_padding_bytes = (4 - (name_len as usize % 4)) % 4;
|
|
||||||
let data_start = padding_start + num_padding_bytes;
|
|
||||||
let last_change =
|
|
||||||
u32::from_le_bytes(data[data_start..data_start + 4].try_into().unwrap());
|
|
||||||
let value =
|
|
||||||
i32::from_le_bytes(data[data_start + 4..data_start + 8].try_into().unwrap());
|
|
||||||
|
|
||||||
settings.insert(name, Setting { value, last_change });
|
|
||||||
current_idx = data_start + 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings {
|
|
||||||
serial,
|
|
||||||
data: settings,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let settings = get_settings();
|
|
||||||
let settings_serial = settings.serial;
|
let settings_serial = settings.serial;
|
||||||
assert_eq!(settings.data["Xft/DPI"].value, 96 * 1024);
|
assert_eq!(settings.data["Xft/DPI"].value, 96 * 1024);
|
||||||
let dpi_serial = settings.data["Xft/DPI"].last_change;
|
let dpi_serial = settings.data["Xft/DPI"].last_change;
|
||||||
|
|
@ -1809,20 +1811,17 @@ fn xsettings_scale() {
|
||||||
output.done();
|
output.done();
|
||||||
f.wait_and_dispatch();
|
f.wait_and_dispatch();
|
||||||
|
|
||||||
let settings = get_settings();
|
let settings = connection.get_xsettings();
|
||||||
assert!(settings.serial > settings_serial);
|
assert!(settings.serial > settings_serial);
|
||||||
assert_eq!(settings.data["Xft/DPI"].value, 2 * 96 * 1024);
|
assert_eq!(settings.data["Xft/DPI"].value, 2 * 96 * 1024);
|
||||||
assert!(settings.data["Xft/DPI"].last_change > dpi_serial);
|
assert!(settings.data["Xft/DPI"].last_change > dpi_serial);
|
||||||
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 2);
|
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 2);
|
||||||
assert!(settings.data["Gdk/WindowScalingFactor"].last_change > window_serial);
|
assert!(settings.data["Gdk/WindowScalingFactor"].last_change > window_serial);
|
||||||
assert_eq!(settings.data["Gdk/UnscaledDPI"].value, 96 * 1024);
|
assert_eq!(settings.data["Gdk/UnscaledDPI"].value, 96 * 1024);
|
||||||
assert_eq!(
|
assert!(settings.data["Gdk/UnscaledDPI"].last_change > unscaled_serial);
|
||||||
settings.data["Gdk/UnscaledDPI"].last_change,
|
|
||||||
unscaled_serial
|
|
||||||
);
|
|
||||||
|
|
||||||
let output2 = f.create_output(0, 0);
|
let output2 = f.create_output(0, 0);
|
||||||
let settings = get_settings();
|
let settings = connection.get_xsettings();
|
||||||
assert_eq!(settings.data["Xft/DPI"].value, 96 * 1024);
|
assert_eq!(settings.data["Xft/DPI"].value, 96 * 1024);
|
||||||
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 1);
|
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 1);
|
||||||
assert_eq!(settings.data["Gdk/UnscaledDPI"].value, 96 * 1024);
|
assert_eq!(settings.data["Gdk/UnscaledDPI"].value, 96 * 1024);
|
||||||
|
|
@ -1832,12 +1831,68 @@ fn xsettings_scale() {
|
||||||
f.testwl.dispatch();
|
f.testwl.dispatch();
|
||||||
std::thread::sleep(Duration::from_millis(1));
|
std::thread::sleep(Duration::from_millis(1));
|
||||||
|
|
||||||
let settings = get_settings();
|
let settings = connection.get_xsettings();
|
||||||
assert_eq!(settings.data["Xft/DPI"].value, 2 * 96 * 1024);
|
assert_eq!(settings.data["Xft/DPI"].value, 2 * 96 * 1024);
|
||||||
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 2);
|
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 2);
|
||||||
assert_eq!(settings.data["Gdk/UnscaledDPI"].value, 96 * 1024);
|
assert_eq!(settings.data["Gdk/UnscaledDPI"].value, 96 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn xsettings_fractional_scale() {
|
||||||
|
let mut f = Fixture::new_preset(|testwl| {
|
||||||
|
testwl.new_output(0, 0); // WL-1
|
||||||
|
testwl.enable_fractional_scale();
|
||||||
|
});
|
||||||
|
let mut connection = Connection::new(&f.display);
|
||||||
|
f.testwl.enable_xdg_output_manager();
|
||||||
|
|
||||||
|
let output = f.testwl.last_created_output();
|
||||||
|
|
||||||
|
let window = connection.new_window(connection.root, 0, 0, 20, 20, false);
|
||||||
|
let surface = f.map_as_toplevel(&mut connection, window);
|
||||||
|
|
||||||
|
let data = f
|
||||||
|
.testwl
|
||||||
|
.get_surface_data(surface)
|
||||||
|
.expect("Missing surface data");
|
||||||
|
let fractional = data
|
||||||
|
.fractional
|
||||||
|
.as_ref()
|
||||||
|
.expect("No fractional scale for surface");
|
||||||
|
|
||||||
|
fractional.preferred_scale(180); // 1.5 scale
|
||||||
|
f.testwl.move_surface_to_output(surface, &output);
|
||||||
|
|
||||||
|
f.wait_and_dispatch();
|
||||||
|
let settings = connection.get_xsettings();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
settings.data["Xft/DPI"].value,
|
||||||
|
(1.5 * 96_f64 * 1024_f64).round() as i32
|
||||||
|
);
|
||||||
|
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 1);
|
||||||
|
assert_eq!(
|
||||||
|
settings.data["Gdk/UnscaledDPI"].value,
|
||||||
|
(1.5 * 96_f64 * 1024_f64).round() as i32
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = f.testwl.get_surface_data(surface).unwrap();
|
||||||
|
let fractional = data.fractional.as_ref().unwrap();
|
||||||
|
fractional.preferred_scale(300); // 2.5 scale
|
||||||
|
f.wait_and_dispatch();
|
||||||
|
|
||||||
|
let settings = connection.get_xsettings();
|
||||||
|
assert_eq!(
|
||||||
|
settings.data["Xft/DPI"].value,
|
||||||
|
(2.5 * 96_f64 * 1024_f64).round() as i32
|
||||||
|
);
|
||||||
|
assert_eq!(settings.data["Gdk/WindowScalingFactor"].value, 2);
|
||||||
|
assert_eq!(
|
||||||
|
settings.data["Gdk/UnscaledDPI"].value,
|
||||||
|
(2.5 / 2.0 * 96_f64 * 1024_f64).round() as i32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn xsettings_switch_owner() {
|
fn xsettings_switch_owner() {
|
||||||
let f = Fixture::new();
|
let f = Fixture::new();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue