1
0
Fork 0
forked from wry/wry

wayland: implement scaling

This involves many subsystems:

- config:
    - allow setting the connector scale
    - allow setting the cursor size
- cursors:
    - load server cursors for all requested sizes and scales
- wl_surface:
    - track the output the surface belongs to
    - send wl_surface.enter/leave
- wl_output:
    - implement wl_output.scale
- text:
    - pre-render texts for all used scales
- renderer:
    - properly align scale textures and rectangles
- wp_fractional_scale:
    - new interface for fractional scaling
This commit is contained in:
Julian Orth 2022-05-30 17:00:25 +02:00
parent 16aec8f87e
commit e52a60b3b6
41 changed files with 1417 additions and 364 deletions

View file

@ -5,8 +5,8 @@ use {
format::XRGB8888,
globals::{Global, GlobalName},
ifs::{
wl_buffer::WlBufferStorage, zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
zxdg_output_v1::ZxdgOutputV1,
wl_buffer::WlBufferStorage, wl_surface::WlSurface,
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, zxdg_output_v1::ZxdgOutputV1,
},
leaks::Tracker,
object::Object,
@ -72,6 +72,7 @@ pub struct WlOutputGlobal {
pub unused_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
pub pending_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
pub destroyed: Cell<bool>,
pub legacy_scale: Cell<i32>,
}
#[derive(Eq, PartialEq)]
@ -117,6 +118,7 @@ impl WlOutputGlobal {
unused_captures: Default::default(),
pending_captures: Default::default(),
destroyed: Cell::new(false),
legacy_scale: Cell::new(1),
}
}
@ -124,6 +126,27 @@ impl WlOutputGlobal {
self.pos.get()
}
pub fn for_each_binding<F: FnMut(&Rc<WlOutput>)>(&self, client: ClientId, mut f: F) {
let bindings = self.bindings.borrow_mut();
if let Some(bindings) = bindings.get(&client) {
for binding in bindings.values() {
f(binding);
}
}
}
pub fn send_enter(&self, surface: &WlSurface) {
self.for_each_binding(surface.client.id, |b| {
surface.send_enter(b.id);
})
}
pub fn send_leave(&self, surface: &WlSurface) {
self.for_each_binding(surface.client.id, |b| {
surface.send_leave(b.id);
})
}
pub fn send_mode(&self) {
let bindings = self.bindings.borrow_mut();
for binding in bindings.values() {
@ -293,7 +316,7 @@ impl WlOutput {
fn send_scale(self: &Rc<Self>) {
let event = Scale {
self_id: self.id,
factor: 1,
factor: self.global.legacy_scale.get(),
};
self.client.event(event);
}

View file

@ -149,11 +149,20 @@ pub struct WlSeatGlobal {
output: CloneCell<Rc<OutputNode>>,
desired_known_cursor: Cell<Option<KnownCursor>>,
changes: NumCell<u32>,
cursor_size: Cell<u32>,
}
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
const CHANGE_TREE: u32 = 1 << 1;
const DEFAULT_CURSOR_SIZE: u32 = 16;
impl Drop for WlSeatGlobal {
fn drop(&mut self) {
self.state.remove_cursor_size(self.cursor_size.get());
}
}
impl WlSeatGlobal {
pub fn new(name: GlobalName, seat_name: &str, state: &Rc<State>) -> Rc<Self> {
let slf = Rc::new(Self {
@ -192,7 +201,9 @@ impl WlSeatGlobal {
output: CloneCell::new(state.dummy_output.get().unwrap()),
desired_known_cursor: Cell::new(None),
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
cursor_size: Cell::new(DEFAULT_CURSOR_SIZE),
});
state.add_cursor_size(DEFAULT_CURSOR_SIZE);
let seat = slf.clone();
let future = state.eng.spawn(async move {
loop {
@ -207,6 +218,15 @@ impl WlSeatGlobal {
slf
}
pub fn set_cursor_size(&self, size: u32) {
let old = self.cursor_size.replace(size);
if size != old {
self.state.remove_cursor_size(old);
self.state.add_cursor_size(size);
self.reload_known_cursor();
}
}
pub fn add_data_device(&self, device: &Rc<WlDataDevice>) {
let mut dd = self.data_devices.borrow_mut();
dd.entry(device.client.id)
@ -342,15 +362,25 @@ impl WlSeatGlobal {
pub fn set_position(&self, x: i32, y: i32) {
self.pos.set((Fixed::from_int(x), Fixed::from_int(y)));
self.trigger_tree_changed();
'set_output: {
let output = 'set_output: {
let outputs = self.state.outputs.lock();
for output in outputs.values() {
if output.node.global.pos.get().contains(x, y) {
self.output.set(output.node.clone());
break 'set_output;
break 'set_output output.node.clone();
}
}
self.output.set(self.state.dummy_output.get().unwrap());
self.state.dummy_output.get().unwrap()
};
self.set_output(&output);
}
fn set_output(&self, output: &Rc<OutputNode>) {
self.output.set(output.clone());
if let Some(cursor) = self.cursor.get() {
cursor.set_output(output);
}
if let Some(dnd) = self.pointer_owner.dnd_icon() {
dnd.set_output(output);
}
}
@ -547,6 +577,9 @@ impl WlSeatGlobal {
icon: Option<Rc<WlSurface>>,
serial: u32,
) -> Result<(), WlSeatError> {
if let Some(icon) = &icon {
icon.set_output(&self.output.get());
}
self.pointer_owner
.start_drag(self, origin, source, icon, serial)
}
@ -604,6 +637,12 @@ impl WlSeatGlobal {
self.set_selection_::<PrimarySelectionIpc>(&self.primary_selection, selection)
}
pub fn reload_known_cursor(&self) {
if let Some(kc) = self.desired_known_cursor.get() {
self.set_known_cursor(kc);
}
}
pub fn set_known_cursor(&self, cursor: KnownCursor) {
self.desired_known_cursor.set(Some(cursor));
let cursors = match self.state.cursors.get() {
@ -622,7 +661,7 @@ impl WlSeatGlobal {
KnownCursor::ResizeBottomLeft => &cursors.resize_bottom_left,
KnownCursor::ResizeBottomRight => &cursors.resize_bottom_right,
};
self.set_cursor2(Some(tpl.instantiate()));
self.set_cursor2(Some(tpl.instantiate(self.cursor_size.get())));
}
pub fn set_app_cursor(&self, cursor: Option<Rc<dyn Cursor>>) {
@ -640,8 +679,7 @@ impl WlSeatGlobal {
old.handle_unset();
}
if let Some(cursor) = cursor.as_ref() {
let (x, y) = self.pos.get();
cursor.set_position(x.round_down(), y.round_down());
cursor.set_output(&self.output.get());
}
self.cursor.set(cursor);
}
@ -654,6 +692,10 @@ impl WlSeatGlobal {
self.pointer_owner.remove_dnd_icon();
}
pub fn get_position(&self) -> (Fixed, Fixed) {
self.pos.get()
}
pub fn get_cursor(&self) -> Option<Rc<dyn Cursor>> {
self.cursor.get()
}

View file

@ -212,7 +212,7 @@ impl WlSeatGlobal {
Some(o) => o,
_ => return,
};
self.output.set(output.node.clone());
self.set_output(&output.node);
let pos = output.node.global.pos.get();
x += Fixed::from_int(pos.x1());
y += Fixed::from_int(pos.y1());
@ -247,7 +247,7 @@ impl WlSeatGlobal {
let outputs = self.state.outputs.lock();
for output in outputs.values() {
if output.node.global.pos.get().contains(x_int, y_int) {
self.output.set(output.node.clone());
self.set_output(&output.node);
break 'warp;
}
}
@ -261,8 +261,8 @@ impl WlSeatGlobal {
} else if y_int >= pos.y2() {
y_int = pos.y2() - 1;
}
x = x.apply_fract(x_int);
y = y.apply_fract(y_int);
x = Fixed::from_int(x_int);
y = Fixed::from_int(y_int);
}
}
self.set_new_position(time_usec, x, y);
@ -480,9 +480,6 @@ impl WlSeatGlobal {
fn set_new_position(self: &Rc<Self>, time_usec: u64, x: Fixed, y: Fixed) {
self.pos_time_usec.set(time_usec);
self.pos.set((x, y));
if let Some(cursor) = self.cursor.get() {
cursor.set_position(x.round_down(), y.round_down());
}
self.changes.or_assign(CHANGE_CURSOR_MOVED);
self.apply_changes();
}

View file

@ -1,6 +1,7 @@
pub mod cursor;
pub mod ext_session_lock_surface_v1;
pub mod wl_subsurface;
pub mod wp_fractional_scale_v1;
pub mod wp_viewport;
pub mod xdg_surface;
pub mod xwindow;
@ -21,7 +22,8 @@ use {
},
wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, SeatId, WlSeatGlobal},
wl_surface::{
cursor::CursorSurface, wl_subsurface::WlSubsurface, wp_viewport::WpViewport,
cursor::CursorSurface, wl_subsurface::WlSubsurface,
wp_fractional_scale_v1::WpFractionalScaleV1, wp_viewport::WpViewport,
xdg_surface::XdgSurfaceError, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error,
},
wp_presentation_feedback::WpPresentationFeedback,
@ -31,7 +33,10 @@ use {
rect::{Rect, Region},
render::Renderer,
state::DeviceHandlerData,
tree::{FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelNode},
tree::{
FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase, OutputNode,
ToplevelNode,
},
utils::{
buffd::{MsgParser, MsgParserError},
clonecell::CloneCell,
@ -207,6 +212,14 @@ impl SurfaceRole {
}
}
pub struct SurfaceSendPreferredScaleVisitor(pub Fixed);
impl NodeVisitorBase for SurfaceSendPreferredScaleVisitor {
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
node.send_preferred_scale();
node.node_visit_children(self);
}
}
pub struct WlSurface {
pub id: WlSurfaceId,
pub node_id: SurfaceNodeId,
@ -239,6 +252,8 @@ pub struct WlSurface {
pub tracker: Tracker<Self>,
idle_inhibitors: CopyHashMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>>,
viewporter: CloneCell<Option<Rc<WpViewport>>>,
output: CloneCell<Rc<OutputNode>>,
fractional_scale: CloneCell<Option<Rc<WpFractionalScaleV1>>>,
}
impl Debug for WlSurface {
@ -370,6 +385,34 @@ impl WlSurface {
tracker: Default::default(),
idle_inhibitors: Default::default(),
viewporter: Default::default(),
output: CloneCell::new(client.state.dummy_output.get().unwrap()),
fractional_scale: Default::default(),
}
}
pub fn set_output(&self, output: &Rc<OutputNode>) {
let old = self.output.set(output.clone());
if old.id == output.id {
return;
}
output.global.send_enter(self);
old.global.send_leave(self);
if old.preferred_scale.get() != output.preferred_scale.get() {
if let Some(fs) = self.fractional_scale.get() {
fs.send_preferred_scale();
}
}
let children = self.children.borrow_mut();
if let Some(children) = &*children {
for ss in children.subsurfaces.values() {
ss.surface.set_output(output);
}
}
}
pub fn send_preferred_scale(&self) {
if let Some(fs) = self.fractional_scale.get() {
fs.send_preferred_scale();
}
}
@ -423,13 +466,20 @@ impl WlSurface {
}
}
fn send_enter(&self, output: WlOutputId) {
pub fn send_enter(&self, output: WlOutputId) {
self.client.event(Enter {
self_id: self.id,
output,
})
}
pub fn send_leave(&self, output: WlOutputId) {
self.client.event(Leave {
self_id: self.id,
output,
})
}
fn set_toplevel(&self, tl: Option<Rc<dyn ToplevelNode>>) {
let ch = self.children.borrow();
if let Some(ch) = &*ch {
@ -967,6 +1017,7 @@ impl Object for WlSurface {
self.pending.presentation_feedback.borrow_mut().clear();
self.presentation_feedback.borrow_mut().clear();
self.viewporter.take();
self.fractional_scale.take();
}
}

View file

@ -1,10 +1,12 @@
use {
crate::{
cursor::Cursor,
fixed::Fixed,
ifs::{wl_seat::WlSeatGlobal, wl_surface::WlSurface},
leaks::Tracker,
rect::Rect,
render::Renderer,
tree::OutputNode,
},
std::{cell::Cell, rc::Rc},
};
@ -13,7 +15,6 @@ pub struct CursorSurface {
seat: Rc<WlSeatGlobal>,
surface: Rc<WlSurface>,
hotspot: Cell<(i32, i32)>,
pos: Cell<(i32, i32)>,
extents: Cell<Rect>,
pub tracker: Tracker<Self>,
}
@ -24,25 +25,16 @@ impl CursorSurface {
seat: seat.clone(),
surface: surface.clone(),
hotspot: Cell::new((0, 0)),
pos: Cell::new((0, 0)),
extents: Cell::new(Default::default()),
tracker: Default::default(),
}
}
fn update_extents(&self) {
let (pos_x, pos_y) = self.pos.get();
let extents = self.extents.get();
let (hot_x, hot_y) = self.hotspot.get();
self.extents.set(
Rect::new_sized(
pos_x - hot_x,
pos_y - hot_y,
extents.width(),
extents.height(),
)
.unwrap(),
);
self.extents
.set(Rect::new_sized(-hot_x, -hot_y, extents.width(), extents.height()).unwrap());
}
pub fn handle_surface_destroy(&self) {
@ -69,21 +61,25 @@ impl CursorSurface {
}
impl Cursor for CursorSurface {
fn set_position(&self, x: i32, y: i32) {
self.pos.set((x, y));
self.update_extents();
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed) {
let extents = self.extents.get().move_(x.round_down(), y.round_down());
if extents.intersects(&renderer.logical_extents()) {
let scale = renderer.scale();
if scale != 1 {
let scale = scale.to_f64();
let (hot_x, hot_y) = self.hotspot.get();
let (hot_x, hot_y) = (Fixed::from_int(hot_x), Fixed::from_int(hot_y));
let x = ((x - hot_x).to_f64() * scale).round() as _;
let y = ((y - hot_y).to_f64() * scale).round() as _;
renderer.render_surface_scaled(&self.surface, x, y, None);
} else {
renderer.render_surface(&self.surface, extents.x1(), extents.y1());
}
}
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_surface(&self.surface, x, y);
}
fn get_hotspot(&self) -> (i32, i32) {
self.hotspot.get()
}
fn extents(&self) -> Rect {
self.extents.get()
fn set_output(&self, output: &Rc<OutputNode>) {
self.surface.set_output(output);
}
fn handle_unset(&self) {

View file

@ -0,0 +1,78 @@
use {
crate::{
client::{Client, ClientError},
ifs::wl_surface::WlSurface,
leaks::Tracker,
object::Object,
utils::buffd::{MsgParser, MsgParserError},
wire::{wp_fractional_scale_v1::*, WpFractionalScaleV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpFractionalScaleV1 {
pub id: WpFractionalScaleV1Id,
pub client: Rc<Client>,
pub surface: Rc<WlSurface>,
pub tracker: Tracker<Self>,
}
impl WpFractionalScaleV1 {
pub fn new(id: WpFractionalScaleV1Id, surface: &Rc<WlSurface>) -> Self {
Self {
id,
client: surface.client.clone(),
surface: surface.clone(),
tracker: Default::default(),
}
}
pub fn install(self: &Rc<Self>) -> Result<(), WpFractionalScaleError> {
if self.surface.fractional_scale.get().is_some() {
return Err(WpFractionalScaleError::Exists);
}
self.surface.fractional_scale.set(Some(self.clone()));
Ok(())
}
pub fn send_preferred_scale(&self) {
self.client.event(PreferredScale {
self_id: self.id,
scale: self.surface.output.get().preferred_scale.get(),
});
}
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpFractionalScaleError> {
let _req: Destroy = self.client.parse(self, msg)?;
self.surface.fractional_scale.take();
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
WpFractionalScaleV1;
DESTROY => destroy,
}
impl Object for WpFractionalScaleV1 {
fn num_requests(&self) -> u32 {
DESTROY + 1
}
}
simple_add_obj!(WpFractionalScaleV1);
#[derive(Debug, Error)]
pub enum WpFractionalScaleError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("The surface already has a fractional scale extension attached")]
Exists,
}
efrom!(WpFractionalScaleError, MsgParserError);
efrom!(WpFractionalScaleError, ClientError);

View file

@ -124,6 +124,7 @@ impl XdgSurface {
fn set_workspace(&self, ws: &Rc<WorkspaceNode>) {
self.workspace.set(Some(ws.clone()));
self.surface.set_output(&ws.output.get());
let pu = self.popups.lock();
for pu in pu.values() {
pu.xdg.set_workspace(ws);

View file

@ -456,8 +456,7 @@ impl ToplevelNode for XdgToplevel {
Some(self.xdg.surface.clone())
}
fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
self.toplevel_data.workspace.set(Some(ws.clone()));
fn tl_set_workspace_ext(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
self.xdg.set_workspace(ws);
}
@ -548,12 +547,7 @@ impl XdgSurfaceExt for XdgToplevel {
self.extents_changed();
if let Some(workspace) = self.xdg.workspace.get() {
let output = workspace.output.get();
let bindings = output.global.bindings.borrow_mut();
if let Some(binding) = bindings.get(&self.xdg.surface.client.id) {
for binding in binding.values() {
self.xdg.surface.send_enter(binding.id);
}
}
surface.set_output(&output);
}
// {
// let seats = surface.client.state.globals.lock_seats();

View file

@ -12,7 +12,7 @@ use {
state::State,
tree::{
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, StackedNode,
ToplevelData, ToplevelNode,
ToplevelData, ToplevelNode, WorkspaceNode,
},
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
wire::WlSurfaceId,
@ -401,6 +401,10 @@ impl ToplevelNode for Xwindow {
Some(self.surface.clone())
}
fn tl_set_workspace_ext(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
self.surface.set_output(&ws.output.get());
}
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
// log::info!("xwin {} change_extents {:?}", self.data.window_id, rect);
let old = self.data.info.extents.replace(*rect);

View file

@ -0,0 +1,112 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::wl_surface::wp_fractional_scale_v1::{WpFractionalScaleError, WpFractionalScaleV1},
leaks::Tracker,
object::Object,
utils::buffd::{MsgParser, MsgParserError},
wire::{wp_fractional_scale_manager_v1::*, WpFractionalScaleManagerV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpFractionalScaleManagerV1Global {
pub name: GlobalName,
}
pub struct WpFractionalScaleManagerV1 {
pub id: WpFractionalScaleManagerV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
}
impl WpFractionalScaleManagerV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: WpFractionalScaleManagerV1Id,
client: &Rc<Client>,
_version: u32,
) -> Result<(), WpFractionalScaleManagerError> {
let obj = Rc::new(WpFractionalScaleManagerV1 {
id,
client: client.clone(),
tracker: Default::default(),
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}
global_base!(
WpFractionalScaleManagerV1Global,
WpFractionalScaleManagerV1,
WpFractionalScaleManagerError
);
impl Global for WpFractionalScaleManagerV1Global {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
}
simple_add_global!(WpFractionalScaleManagerV1Global);
impl WpFractionalScaleManagerV1 {
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpFractionalScaleManagerError> {
let _req: Destroy = self.client.parse(self, msg)?;
self.client.remove_obj(self)?;
Ok(())
}
fn get_fractional_scale(
&self,
msg: MsgParser<'_, '_>,
) -> Result<(), WpFractionalScaleManagerError> {
let req: GetFractionalScale = self.client.parse(self, msg)?;
let surface = self.client.lookup(req.surface)?;
let fs = Rc::new(WpFractionalScaleV1::new(req.id, &surface));
track!(self.client, fs);
fs.install()?;
self.client.add_client_obj(&fs)?;
fs.send_preferred_scale();
Ok(())
}
}
object_base! {
WpFractionalScaleManagerV1;
DESTROY => destroy,
GET_FRACTIONAL_SCALE => get_fractional_scale,
}
impl Object for WpFractionalScaleManagerV1 {
fn num_requests(&self) -> u32 {
GET_FRACTIONAL_SCALE + 1
}
}
simple_add_obj!(WpFractionalScaleManagerV1);
#[derive(Debug, Error)]
pub enum WpFractionalScaleManagerError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
WpFractionalScaleError(#[from] WpFractionalScaleError),
}
efrom!(WpFractionalScaleManagerError, MsgParserError);
efrom!(WpFractionalScaleManagerError, ClientError);