1
0
Fork 0
forked from wry/wry

xwayland: allow windows to scale themselves

This commit is contained in:
Julian Orth 2024-10-08 11:14:13 +02:00
parent cc8db84289
commit 19b07fa7dc
40 changed files with 800 additions and 80 deletions

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,