1
0
Fork 0
forked from wry/wry

Merge pull request #287 from mahkoh/jorth/xwayland-downscaling

xwayland: allow windows to scale themselves
This commit is contained in:
mahkoh 2024-10-10 12:10:24 +02:00 committed by GitHub
commit 3f9b75e470
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 800 additions and 80 deletions

View file

@ -12,10 +12,14 @@ pub mod screenshot;
mod seat_test;
mod set_log_level;
mod unlock;
mod xwayland;
use {
crate::{
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
cli::{
damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs,
xwayland::XwaylandArgs,
},
compositor::start_compositor,
format::{ref_formats, Format},
portal,
@ -72,6 +76,8 @@ pub enum Cmd {
/// Modify damage tracking settings. (Only for debugging.)
#[clap(hide = true)]
DamageTracking(DamageTrackingArgs),
/// Inspect/modify xwayland settings.
Xwayland(XwaylandArgs),
#[cfg(feature = "it")]
RunTests,
}
@ -259,6 +265,7 @@ pub fn main() {
Cmd::Randr(a) => randr::main(cli.global, a),
Cmd::Input(a) => input::main(cli.global, a),
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
Cmd::Xwayland(a) => xwayland::main(cli.global, a),
#[cfg(feature = "it")]
Cmd::RunTests => crate::it::run_tests(),
}

106
src/cli/xwayland.rs Normal file
View file

@ -0,0 +1,106 @@
use {
crate::{
cli::GlobalArgs,
tools::tool_client::{with_tool_client, Handle, ToolClient},
wire::{jay_compositor, jay_xwayland, JayXwaylandId},
},
clap::{Args, Subcommand, ValueEnum},
jay_config::xwayland::XScalingMode,
std::{cell::Cell, rc::Rc},
};
#[derive(Args, Debug)]
pub struct XwaylandArgs {
#[clap(subcommand)]
pub command: Option<XwaylandCmd>,
}
#[derive(Subcommand, Debug, Default)]
pub enum XwaylandCmd {
/// Print the Xwayland status.
#[default]
Status,
/// Set the Xwayland scaling mode.
SetScalingMode(SetScalingModeArgs),
}
#[derive(Args, Debug)]
pub struct SetScalingModeArgs {
#[clap(value_enum)]
pub mode: CliScalingMode,
}
#[derive(ValueEnum, Debug, Copy, Clone, Hash, PartialEq)]
pub enum CliScalingMode {
/// The default mode.
Default,
/// Windows are rendered at the highest integer scale and then downscaled.
Downscaled,
}
pub fn main(global: GlobalArgs, args: XwaylandArgs) {
with_tool_client(global.log_level.into(), |tc| async move {
let xwayland = Xwayland { tc: tc.clone() };
xwayland.run(args).await;
});
}
struct Xwayland {
tc: Rc<ToolClient>,
}
impl Xwayland {
async fn run(self, args: XwaylandArgs) {
let tc = &self.tc;
let comp = tc.jay_compositor().await;
let xwayland = tc.id();
tc.send(jay_compositor::GetXwayland {
self_id: comp,
id: xwayland,
});
match args.command.unwrap_or_default() {
XwaylandCmd::Status => self.status(xwayland).await,
XwaylandCmd::SetScalingMode(args) => self.set_scaling_mode(xwayland, args).await,
}
}
async fn status(self, xwayland: JayXwaylandId) {
let tc = &self.tc;
tc.send(jay_xwayland::GetScaling { self_id: xwayland });
let mode = Rc::new(Cell::new(0));
let scale = Rc::new(Cell::new(None));
jay_xwayland::ScalingMode::handle(tc, xwayland, mode.clone(), |iv, msg| {
iv.set(msg.mode);
});
jay_xwayland::ImpliedScale::handle(tc, xwayland, scale.clone(), |iv, msg| {
iv.set(Some(msg.scale));
});
tc.round_trip().await;
let mode_str;
let mode = match XScalingMode(mode.get()) {
XScalingMode::DEFAULT => "default",
XScalingMode::DOWNSCALED => "downscaled",
o => {
mode_str = format!("unknown ({})", o.0);
&mode_str
}
};
println!("scaling mode: {}", mode);
if let Some(scale) = scale.get() {
println!("implied scale: {}", scale);
}
}
async fn set_scaling_mode(self, xwayland: JayXwaylandId, args: SetScalingModeArgs) {
let tc = &self.tc;
let mode = match args.mode {
CliScalingMode::Default => XScalingMode::DEFAULT,
CliScalingMode::Downscaled => XScalingMode::DOWNSCALED,
};
tc.send(jay_xwayland::SetScalingMode {
self_id: xwayland,
mode: mode.0,
});
tc.round_trip().await;
}
}

View file

@ -172,6 +172,7 @@ impl Clients {
&global.wait_for_sync_obj,
&global.ring,
)),
wire_scale: Default::default(),
});
track!(data, data);
let display = Rc::new(WlDisplay::new(&data));
@ -282,6 +283,7 @@ pub struct Client {
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
pub commit_timelines: Rc<CommitTimelines>,
pub wire_scale: Cell<Option<i32>>,
}
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;

View file

@ -211,6 +211,8 @@ fn start_compositor2(
handler: Default::default(),
queue: Default::default(),
ipc_device_ids: Default::default(),
use_wire_scale: Default::default(),
wire_scale: Default::default(),
},
acceptor: Default::default(),
serial: Default::default(),

View file

@ -53,6 +53,7 @@ use {
Connector, DrmDevice, Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode,
Transform, VrrMode as ConfigVrrMode,
},
xwayland::XScalingMode,
Axis, Direction, Workspace,
},
libloading::Library,
@ -759,6 +760,17 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_set_x_scaling_mode(&self, mode: XScalingMode) -> Result<(), CphError> {
let use_wire_scale = match mode {
XScalingMode::DEFAULT => false,
XScalingMode::DOWNSCALED => true,
_ => return Err(CphError::UnknownXScalingMode(mode)),
};
self.state.xwayland.use_wire_scale.set(use_wire_scale);
self.state.update_xwayland_wire_scale();
Ok(())
}
fn handle_set_ui_drag_enabled(&self, enabled: bool) {
self.state.ui_drag_enabled.set(enabled);
}
@ -1965,6 +1977,9 @@ impl ConfigProxyHandler {
ClientMessage::SetUiDragThreshold { threshold } => {
self.handle_set_ui_drag_threshold(threshold)
}
ClientMessage::SetXScalingMode { mode } => self
.handle_set_x_scaling_mode(mode)
.wrn("set_x_scaling_mode")?,
}
Ok(())
}
@ -2034,6 +2049,8 @@ enum CphError {
UnknownTearingMode(ConfigTearingMode),
#[error("The format {0:?} is unknown")]
UnknownFormat(ConfigFormat),
#[error("Unknown x scaling mode {0:?}")]
UnknownXScalingMode(XScalingMode),
}
trait WithRequestName {

View file

@ -1,7 +1,7 @@
use std::{
cmp::Ordering,
fmt::{Debug, Display, Formatter},
ops::{Add, AddAssign, Sub, SubAssign},
ops::{Add, AddAssign, Div, Mul, Sub, SubAssign},
};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
@ -108,6 +108,22 @@ impl Add<i32> for Fixed {
}
}
impl Mul<i32> for Fixed {
type Output = Self;
fn mul(self, rhs: i32) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Div<i32> for Fixed {
type Output = Self;
fn div(self, rhs: i32) -> Self::Output {
Self(self.0 / rhs)
}
}
impl AddAssign for Fixed {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;

View file

@ -24,6 +24,7 @@ pub mod jay_select_workspace;
pub mod jay_toplevel;
pub mod jay_workspace;
pub mod jay_workspace_watcher;
pub mod jay_xwayland;
pub mod org_kde_kwin_server_decoration;
pub mod org_kde_kwin_server_decoration_manager;
pub mod wl_buffer;

View file

@ -18,6 +18,7 @@ use {
jay_select_toplevel::{JaySelectToplevel, JayToplevelSelector},
jay_select_workspace::{JaySelectWorkspace, JayWorkspaceSelector},
jay_workspace_watcher::JayWorkspaceWatcher,
jay_xwayland::JayXwayland,
},
leaks::Tracker,
object::{Object, Version},
@ -70,7 +71,7 @@ impl Global for JayCompositorGlobal {
}
fn version(&self) -> u32 {
10
11
}
fn required_caps(&self) -> ClientCaps {
@ -409,6 +410,18 @@ impl JayCompositorRequestHandler for JayCompositor {
self.client.add_client_obj(&obj)?;
Ok(())
}
fn get_xwayland(&self, req: GetXwayland, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let obj = Rc::new(JayXwayland {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
version: self.version,
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
Ok(())
}
}
object_base! {

84
src/ifs/jay_xwayland.rs Normal file
View file

@ -0,0 +1,84 @@
use {
crate::{
client::{Client, ClientError},
leaks::Tracker,
object::{Object, Version},
wire::{jay_xwayland::*, JayXwaylandId},
},
jay_config::xwayland::XScalingMode,
std::rc::Rc,
thiserror::Error,
};
pub struct JayXwayland {
pub id: JayXwaylandId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl JayXwayland {
pub fn send_scaling_mode(&self) {
let xw = &self.client.state.xwayland;
self.client.event(ScalingMode {
self_id: self.id,
mode: match xw.use_wire_scale.get() {
false => XScalingMode::DEFAULT.0,
true => XScalingMode::DOWNSCALED.0,
},
});
}
pub fn send_implied_scale(&self) {
let xw = &self.client.state.xwayland;
if let Some(scale) = xw.wire_scale.get() {
self.client.event(ImpliedScale {
self_id: self.id,
scale,
});
}
}
}
impl JayXwaylandRequestHandler for JayXwayland {
type Error = JayXwaylandError;
fn get_scaling(&self, _req: GetScaling, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.send_scaling_mode();
self.send_implied_scale();
Ok(())
}
fn set_scaling_mode(&self, req: SetScalingMode, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let use_wire_scale = match XScalingMode(req.mode) {
XScalingMode::DEFAULT => false,
XScalingMode::DOWNSCALED => true,
_ => return Err(JayXwaylandError::UnknownMode(req.mode)),
};
self.client
.state
.xwayland
.use_wire_scale
.set(use_wire_scale);
self.client.state.update_xwayland_wire_scale();
Ok(())
}
}
object_base! {
self = JayXwayland;
version = self.version;
}
impl Object for JayXwayland {}
simple_add_obj!(JayXwayland);
#[derive(Debug, Error)]
pub enum JayXwaylandError {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Unknown scaling mode {}", .0)]
UnknownMode(u32),
}
efrom!(JayXwaylandError, ClientError);

View file

@ -12,7 +12,10 @@ use {
rect::Rect,
state::{ConnectorData, State},
tree::{calculate_logical_size, OutputNode, TearingMode, VrrMode},
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, transform_ext::TransformExt},
utils::{
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
transform_ext::TransformExt,
},
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
},
ahash::AHashMap,
@ -196,15 +199,7 @@ impl WlOutputGlobal {
let bindings = self.bindings.borrow_mut();
for binding in bindings.values() {
for binding in binding.values() {
binding.send_geometry();
binding.send_mode();
binding.send_scale();
binding.send_done();
let xdg = binding.xdg_outputs.lock();
for xdg in xdg.values() {
xdg.send_updates();
}
// binding.client.flush();
binding.send_updates();
}
}
}
@ -283,15 +278,33 @@ pub const SEND_SCALE_SINCE: Version = Version(2);
pub const SEND_NAME_SINCE: Version = Version(4);
impl WlOutput {
pub fn send_updates(&self) {
self.send_geometry();
self.send_mode();
if self.version >= SEND_SCALE_SINCE {
self.send_scale();
}
if self.version >= SEND_DONE_SINCE {
self.send_done();
}
let xdg = self.xdg_outputs.lock();
for xdg in xdg.values() {
xdg.send_updates();
}
}
fn send_geometry(&self) {
let Some(global) = self.global.get() else {
return;
};
let pos = global.pos.get();
let mut x = pos.x1();
let mut y = pos.y1();
logical_to_client_wire_scale!(self.client, x, y);
let event = Geometry {
self_id: self.id,
x: pos.x1(),
y: pos.y1(),
x,
y,
physical_width: global.width_mm,
physical_height: global.height_mm,
subpixel: SP_UNKNOWN,
@ -306,7 +319,8 @@ impl WlOutput {
let Some(global) = self.global.get() else {
return;
};
let mode = global.mode.get();
let mut mode = global.mode.get();
logical_to_client_wire_scale!(self.client, mode.width, mode.height);
let event = Mode {
self_id: self.id,
flags: MODE_CURRENT,
@ -317,13 +331,17 @@ impl WlOutput {
self.client.event(event);
}
fn send_scale(self: &Rc<Self>) {
fn send_scale(&self) {
let Some(global) = self.global.get() else {
return;
};
let factor = match self.client.wire_scale.is_some() {
true => 1,
false => global.legacy_scale.get() as _,
};
let event = Scale {
self_id: self.id,
factor: global.legacy_scale.get() as _,
factor,
};
self.client.event(event);
}

View file

@ -124,7 +124,8 @@ impl ZwpTabletToolV2 {
self.client.event(Up { self_id: self.id });
}
pub fn send_motion(&self, x: Fixed, y: Fixed) {
pub fn send_motion(&self, mut x: Fixed, mut y: Fixed) {
logical_to_client_wire_scale!(self.client, x, y);
self.client.event(Motion {
self_id: self.id,
x,
@ -199,7 +200,7 @@ impl ZwpTabletToolV2 {
impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 {
type Error = ZwpTabletToolV2Error;
fn set_cursor(&self, req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
fn set_cursor(&self, mut req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let Some(tool) = self.tool.get() else {
return Ok(());
};
@ -209,6 +210,7 @@ impl ZwpTabletToolV2RequestHandler for ZwpTabletToolV2 {
}
let mut cursor_opt = None;
if req.surface.is_some() {
client_wire_scale_to_logical!(self.client, req.hotspot_x, req.hotspot_y);
let surface = self.seat.client.lookup(req.surface)?;
let cursor = surface.get_cursor(&tool.cursor)?;
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);

View file

@ -86,8 +86,9 @@ impl WlPointer {
}
}
pub fn send_enter(&self, serial: u32, surface: WlSurfaceId, x: Fixed, y: Fixed) {
pub fn send_enter(&self, serial: u32, surface: WlSurfaceId, mut x: Fixed, mut y: Fixed) {
self.last_motion.set((x, y));
logical_to_client_wire_scale!(self.seat.client, x, y);
self.seat.client.event(Enter {
self_id: self.id,
serial,
@ -105,10 +106,11 @@ impl WlPointer {
})
}
pub fn send_motion(&self, time: u32, x: Fixed, y: Fixed) {
pub fn send_motion(&self, time: u32, mut x: Fixed, mut y: Fixed) {
if self.last_motion.replace((x, y)) == (x, y) {
return;
}
logical_to_client_wire_scale!(self.seat.client, x, y);
self.seat.client.event(Motion {
self_id: self.id,
time,
@ -135,7 +137,8 @@ impl WlPointer {
})
}
pub fn send_axis(&self, time: u32, axis: u32, value: Fixed) {
pub fn send_axis(&self, time: u32, axis: u32, mut value: Fixed) {
logical_to_client_wire_scale!(self.seat.client, value);
self.seat.client.event(Axis {
self_id: self.id,
time,
@ -183,13 +186,14 @@ impl WlPointer {
impl WlPointerRequestHandler for WlPointer {
type Error = WlPointerError;
fn set_cursor(&self, req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
fn set_cursor(&self, mut req: SetCursor, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if !self.seat.client.valid_serial(req.serial) {
log::warn!("Client tried to set_cursor with an invalid serial");
return Ok(());
}
let mut cursor_opt = None;
if req.surface.is_some() {
client_wire_scale_to_logical!(self.seat.client, req.hotspot_x, req.hotspot_y);
let surface = self.seat.client.lookup(req.surface)?;
let cursor = surface.get_cursor(&self.seat.global.pointer_cursor)?;
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);

View file

@ -37,9 +37,10 @@ impl WlTouch {
time: u32,
surface: WlSurfaceId,
id: i32,
x: Fixed,
y: Fixed,
mut x: Fixed,
mut y: Fixed,
) {
logical_to_client_wire_scale!(self.seat.client, x, y);
self.seat.client.event(Down {
self_id: self.id,
serial,
@ -60,7 +61,8 @@ impl WlTouch {
})
}
pub fn send_motion(&self, time: u32, id: i32, x: Fixed, y: Fixed) {
pub fn send_motion(&self, time: u32, id: i32, mut x: Fixed, mut y: Fixed) {
logical_to_client_wire_scale!(self.seat.client, x, y);
self.seat.client.event(Motion {
self_id: self.id,
time,

View file

@ -12,7 +12,7 @@ use {
},
leaks::Tracker,
object::{Object, Version},
rect::Region,
rect::{Rect, Region},
utils::clonecell::CloneCell,
wire::{
zwp_pointer_constraints_v1::*, WlPointerId, WlRegionId, WlSurfaceId,
@ -125,11 +125,7 @@ impl SeatConstraint {
}
fn set_region(&self, region: WlRegionId) -> Result<(), ZwpPointerConstraintsV1Error> {
let region = if region.is_some() {
Some(self.client.lookup(region)?.region())
} else {
None
};
let region = get_region(&self.client, region)?;
self.region.set(region);
Ok(())
}
@ -166,6 +162,35 @@ impl ZwpPointerConstraintsV1Global {
}
}
fn get_region(
client: &Client,
region: WlRegionId,
) -> Result<Option<Rc<Region>>, ZwpPointerConstraintsV1Error> {
let region = if region.is_some() {
let mut region = client.lookup(region)?.region();
if let Some(scale) = client.wire_scale.get() {
let rects: Vec<_> = region
.rects()
.iter()
.map(|r| {
Rect::new_sized(
r.x1() / scale,
r.y1() / scale,
r.width() / scale,
r.height() / scale,
)
.unwrap()
})
.collect();
region = Region::from_rects(&rects);
}
Some(region)
} else {
None
};
Ok(region)
}
impl ZwpPointerConstraintsV1 {
fn create_constraint(
&self,
@ -181,11 +206,7 @@ impl ZwpPointerConstraintsV1 {
if surface.constraints.contains(&seat.id) {
return Err(ZwpPointerConstraintsV1Error::AlreadyConstrained);
}
let region = if region.is_some() {
Some(self.client.lookup(region)?.region())
} else {
None
};
let region = get_region(&self.client, region)?;
let one_shot = match lifetime {
LT_ONESHOT => true,
LT_PERSISTENT => false,

View file

@ -23,11 +23,12 @@ impl ZwpRelativePointerV1 {
pub fn send_relative_motion(
&self,
time_usec: u64,
dx: Fixed,
dy: Fixed,
mut dx: Fixed,
mut dy: Fixed,
dx_unaccelerated: Fixed,
dy_unaccelerated: Fixed,
) {
logical_to_client_wire_scale!(self.client, dx, dy);
self.client.event(RelativeMotion {
self_id: self.id,
utime_hi: (time_usec >> 32) as u32,

View file

@ -769,9 +769,13 @@ impl WlSurface {
pub fn send_preferred_buffer_scale(&self) {
if self.version >= BUFFER_SCALE_SINCE {
let factor = match self.client.wire_scale.is_some() {
true => 1,
false => self.output.get().global.legacy_scale.get() as _,
};
self.client.event(PreferredBufferScale {
self_id: self.id,
factor: self.output.get().global.legacy_scale.get() as _,
factor,
});
}
}
@ -909,6 +913,22 @@ impl WlSurface {
}
Ok(())
}
pub fn handle_xwayland_wire_scale_change(&self) {
self.send_preferred_buffer_scale();
if let Some(fs) = self.fractional_scale.get() {
fs.send_preferred_scale();
}
if let Some(xsurface) = self.ext.get().into_xsurface() {
if let Some(window) = xsurface.xwindow.get() {
self.client
.state
.xwayland
.queue
.push(XWaylandEvent::Configure(window));
}
}
}
}
const MAX_DAMAGE: usize = 32;
@ -1093,7 +1113,7 @@ impl WlSurface {
scale_changed || buffer_transform_changed || viewport_changed || alpha_changed;
let mut buffer_changed = false;
let mut old_raw_size = None;
let (dx, dy) = mem::take(&mut pending.offset);
let (mut dx, mut dy) = mem::take(&mut pending.offset);
if let Some(buffer_change) = pending.buffer.take() {
buffer_changed = true;
if let Some(buffer) = self.buffer.take() {
@ -1132,6 +1152,8 @@ impl WlSurface {
}
}
if self.buffer.is_some() && (dx, dy) != (0, 0) {
// This is somewhat problematic since we don't accumulate small changes.
client_wire_scale_to_logical!(self.client, dx, dy);
self.buf_x.fetch_add(dx);
self.buf_y.fetch_add(dy);
self.need_extents_update.set(true);
@ -1210,7 +1232,8 @@ impl WlSurface {
buffer_transform: self.buffer_transform.get(),
};
let (buffer_width, buffer_height) = buffer.buffer.rect.size();
let (dst_width, dst_height) = new_size.unwrap_or_default();
let (mut dst_width, mut dst_height) = new_size.unwrap_or_default();
client_wire_scale_to_logical!(self.client, dst_width, dst_height);
let damage_matrix = DamageMatrix::new(
self.buffer_transform.get(),
self.buffer_scale.get(),
@ -1223,7 +1246,8 @@ impl WlSurface {
self.damage_matrix.set(damage_matrix);
}
}
let (width, height) = new_size.unwrap_or_default();
let (mut width, mut height) = new_size.unwrap_or_default();
client_wire_scale_to_logical!(self.client, width, height);
let (old_width, old_height) = buffer_abs_pos.size();
if (width, height) != (old_width, old_height) {
self.need_extents_update.set(true);
@ -1360,6 +1384,13 @@ impl WlSurface {
}
for damage in &pending.surface_damage {
let mut damage = damage.move_(pos.x1(), pos.y1());
if let Some(scale) = self.client.wire_scale.get() {
let x1 = damage.x1() / scale;
let y1 = damage.y1() / scale;
let x2 = (damage.x2() + scale - 1) / scale;
let y2 = (damage.y2() + scale - 1) / scale;
damage = Rect::new(x1, y1, x2, y2).unwrap();
}
damage = damage.intersect(bounds.unwrap_or(pos));
self.client.state.damage(damage);
}
@ -1406,12 +1437,13 @@ impl WlSurface {
}
}
fn accepts_input_at(&self, x: i32, y: i32) -> bool {
fn accepts_input_at(&self, mut x: i32, mut y: i32) -> bool {
let rect = self.buffer_abs_pos.get().at_point(0, 0);
if !rect.contains(x, y) {
return false;
}
if let Some(ir) = self.input_region.get() {
logical_to_client_wire_scale!(self.client, x, y);
if !ir.contains(x, y) {
return false;
}

View file

@ -135,7 +135,8 @@ impl WlSubsurface {
v.pending.set(false);
self.node.borrow_mut().replace(v);
}
if let Some((x, y)) = pending.position.take() {
if let Some((mut x, mut y)) = pending.position.take() {
client_wire_scale_to_logical!(self.surface.client, x, y);
self.position
.set(self.surface.buffer_abs_pos.get().at_point(x, y));
let (parent_x, parent_y) = self.parent.buffer_abs_pos.get().position();

View file

@ -4,6 +4,8 @@ use {
ifs::wl_surface::WlSurface,
leaks::Tracker,
object::{Object, Version},
scale::Scale,
utils::cell_ext::CellExt,
wire::{wp_fractional_scale_v1::*, WpFractionalScaleV1Id},
},
std::rc::Rc,
@ -38,17 +40,13 @@ impl WpFractionalScaleV1 {
}
pub fn send_preferred_scale(&self) {
let scale = match self.client.wire_scale.is_some() {
true => Scale::from_int(1),
false => self.surface.output.get().global.persistent.scale.get(),
};
self.client.event(PreferredScale {
self_id: self.id,
scale: self
.surface
.output
.get()
.global
.persistent
.scale
.get()
.to_wl(),
scale: scale.to_wl(),
});
}
}

View file

@ -140,13 +140,12 @@ pub struct Xwindow {
impl XwindowData {
pub fn new(state: &Rc<State>, event: &CreateNotify, client: &Rc<Client>) -> Self {
let extents = Rect::new_sized(
event.x as _,
event.y as _,
event.width as _,
event.height as _,
)
.unwrap();
let mut x = event.x as i32;
let mut y = event.y as i32;
let mut width = event.width as i32;
let mut height = event.height as i32;
client_wire_scale_to_logical!(client, x, y, width, height);
let extents = Rect::new_sized(x, y, width, height).unwrap();
// log::info!("xwin {} new {:?} or {}", event.window, extents, event.override_redirect);
Self {
state: state.clone(),

View file

@ -24,7 +24,8 @@ pub struct ZxdgOutputV1 {
}
impl ZxdgOutputV1 {
pub fn send_logical_position(&self, x: i32, y: i32) {
pub fn send_logical_position(&self, mut x: i32, mut y: i32) {
logical_to_client_wire_scale!(self.client, x, y);
self.client.event(LogicalPosition {
self_id: self.id,
x,
@ -32,7 +33,8 @@ impl ZxdgOutputV1 {
});
}
pub fn send_logical_size(&self, width: i32, height: i32) {
pub fn send_logical_size(&self, mut width: i32, mut height: i32) {
logical_to_client_wire_scale!(self.client, width, height);
self.client.event(LogicalSize {
self_id: self.id,
width,

View file

@ -737,3 +737,23 @@ macro_rules! ei_object_base {
}
};
}
macro_rules! logical_to_client_wire_scale {
($client:expr, $($field:expr),+ $(,)?) => {
if let Some(scale) = $client.wire_scale.get() {
$(
$field = $field * scale;
)+
}
};
}
macro_rules! client_wire_scale_to_logical {
($client:expr, $($field:expr),+ $(,)?) => {
if let Some(scale) = $client.wire_scale.get() {
$(
$field = $field / scale;
)+
}
};
}

View file

@ -244,6 +244,8 @@ pub struct XWaylandState {
pub handler: RefCell<Option<SpawnedFuture<()>>>,
pub queue: Rc<AsyncQueue<XWaylandEvent>>,
pub ipc_device_ids: XIpcDeviceIds,
pub use_wire_scale: Cell<bool>,
pub wire_scale: Cell<Option<i32>>,
}
pub struct IdleState {
@ -415,6 +417,7 @@ impl State {
fn output_scales_changed(&self) {
UpdateTextTexturesVisitor.visit_display(&self.root);
self.reload_cursors();
self.update_xwayland_wire_scale();
}
fn cursor_sizes_changed(&self) {
@ -1215,6 +1218,36 @@ impl State {
let dy = y1 - y2;
dx * dx + dy * dy > self.ui_drag_threshold_squared.get()
}
pub fn update_xwayland_wire_scale(&self) {
let scale = self
.scales
.lock()
.iter()
.map(|v| v.0.round_up())
.max()
.unwrap_or(1);
let wire_scale = match self.xwayland.use_wire_scale.get() {
true => Some(scale as i32),
false => None,
};
self.xwayland.wire_scale.set(wire_scale);
for client in self.clients.clients.borrow().values() {
let client = &client.data;
if !client.is_xwayland {
continue;
}
if client.wire_scale.replace(wire_scale) == wire_scale {
continue;
}
for output in client.objects.outputs.lock().values() {
output.send_updates();
}
for surface in client.objects.surfaces.lock().values() {
surface.handle_xwayland_wire_scale_change();
}
}
}
}
#[derive(Debug, Error)]

View file

@ -332,7 +332,7 @@ impl ToolClient {
self_id: s.registry,
name: s.jay_compositor.0,
interface: JayCompositor.name(),
version: s.jay_compositor.1.min(10),
version: s.jay_compositor.1.min(11),
id: id.into(),
});
self.jay_compositor.set(Some(id));

View file

@ -182,6 +182,7 @@ async fn run(
Ok(c) => c,
Err(e) => return Err(XWaylandError::SpawnClient(e)),
};
state.update_xwayland_wire_scale();
state.ring.readable(&Rc::new(dfdread)).await?;
state.xwayland.queue.clear();
{

View file

@ -913,13 +913,18 @@ impl Wm {
async fn send_configure(&mut self, window: Rc<Xwindow>) {
let extents = window.data.info.extents.get();
// log::info!("xwin {} send_configure {:?}", window.data.window_id, extents);
let mut x = extents.x1();
let mut y = extents.y1();
let mut width = extents.width();
let mut height = extents.height();
logical_to_client_wire_scale!(self.client, x, y, width, height);
let cw = ConfigureWindow {
window: window.data.window_id,
values: ConfigureWindowValues {
x: Some(extents.x1()),
y: Some(extents.y1()),
width: Some(extents.width() as u32),
height: Some(extents.height() as u32),
x: Some(x),
y: Some(y),
width: Some(width as u32),
height: Some(height as u32),
border_width: Some(0),
..Default::default()
},
@ -2134,13 +2139,18 @@ impl Wm {
if pending.width() > 0 && pending.height() > 0 {
let dummy = Rect::new_sized(0, 0, 1, 1).unwrap();
for rect in [dummy, pending] {
let mut x = rect.x1();
let mut y = rect.y1();
let mut width = rect.width();
let mut height = rect.height();
logical_to_client_wire_scale!(self.client, x, y, width, height);
let cw = ConfigureWindow {
window: data.window_id,
values: ConfigureWindowValues {
x: Some(rect.x1()),
y: Some(rect.y1()),
width: Some(rect.width() as _),
height: Some(rect.height() as _),
x: Some(x),
y: Some(y),
width: Some(width as _),
height: Some(height as _),
..Default::default()
},
};
@ -2213,13 +2223,12 @@ impl Wm {
};
self.update_override_redirect(data, event.override_redirect);
if data.info.override_redirect.get() {
let extents = Rect::new_sized(
event.x as _,
event.y as _,
event.width as _,
event.height as _,
)
.unwrap();
let mut x = event.x as i32;
let mut y = event.y as i32;
let mut width = event.width as i32;
let mut height = event.height as i32;
client_wire_scale_to_logical!(self.client, x, y, width, height);
let extents = Rect::new_sized(x, y, width, height).unwrap();
if let Some(window) = data.window.get() {
window.tl_change_extents(&extents);
self.state.tree_changed();
@ -2248,15 +2257,19 @@ impl Wm {
let mut height = de.height();
if event.value_mask.contains(CONFIG_WINDOW_X) {
x1 = event.x as _;
client_wire_scale_to_logical!(self.client, x1);
}
if event.value_mask.contains(CONFIG_WINDOW_Y) {
y1 = event.y as _;
client_wire_scale_to_logical!(self.client, y1);
}
if event.value_mask.contains(CONFIG_WINDOW_WIDTH) {
width = event.width as _;
client_wire_scale_to_logical!(self.client, width);
}
if event.value_mask.contains(CONFIG_WINDOW_HEIGHT) {
height = event.height as _;
client_wire_scale_to_logical!(self.client, height);
}
data.info
.pending_extents