1
0
Fork 0
forked from wry/wry

all: implement damage tracking

This commit is contained in:
Julian Orth 2024-07-10 19:58:17 +02:00
parent 76a3c50560
commit bb66abb817
28 changed files with 473 additions and 82 deletions

View file

@ -74,7 +74,7 @@ impl ExtSessionLockManagerV1RequestHandler for ExtSessionLockManagerV1 {
state.lock.locked.set(true);
state.lock.lock.set(Some(new.clone()));
state.tree_changed();
state.damage();
state.damage(state.root.extents.get());
new.send_locked();
} else {
new.finish();

View file

@ -327,9 +327,6 @@ impl JayScreencast {
Target::Toplevel(tl) => {
let data = tl.tl_data();
data.jay_screencasts.remove(&(self.client.id, self.id));
if data.jay_screencasts.is_empty() {
self.client.state.damage();
}
}
}
}
@ -418,10 +415,16 @@ impl JayScreencast {
fn damage(&self) {
if let Some(target) = self.target.get() {
match target {
Target::Output(o) => o.global.connector.connector.damage(),
Target::Toplevel(_) => self.client.state.damage(),
}
let rect = match target {
Target::Output(o) => o.global.pos.get(),
Target::Toplevel(t) => {
if !t.node_visible() {
return;
}
t.node_absolute_position()
}
};
self.client.state.damage(rect);
}
}
}
@ -535,9 +538,6 @@ impl JayScreencastRequestHandler for JayScreencast {
}
let t = t.toplevel.clone();
let data = t.tl_data();
if data.jay_screencasts.is_empty() {
data.state.damage();
}
data.jay_screencasts
.set((self.client.id, self.id), slf.clone());
new_target = Some(Target::Toplevel(t));
@ -577,6 +577,10 @@ impl JayScreencastRequestHandler for JayScreencast {
}
}
if self.running.get() {
self.damage();
}
Ok(())
}

View file

@ -561,7 +561,6 @@ impl WlSeatGlobal {
if let Some(parent) = tl.tl_data().parent.get() {
if let Some(tl) = parent.node_toplevel() {
self.focus_node(tl.tl_into_node());
self.state.damage();
}
}
}

View file

@ -411,10 +411,18 @@ impl WlSeatGlobal {
}
fn set_pointer_cursor_position(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
let dnd_icon = self.pointer_owner.dnd_icon();
if let Some(dnd_icon) = &dnd_icon {
let (x_old, y_old) = self.pointer_cursor.position_int();
dnd_icon.damage_at(x_old, y_old);
}
let (x, y) = self.pointer_cursor.set_position(x, y);
let x_int = x.round_down();
let y_int = y.round_down();
if let Some(dnd_icon) = &dnd_icon {
dnd_icon.damage_at(x_int, y_int);
}
if let Some(td) = self.pointer_owner.toplevel_drag() {
let x_int = x.round_down();
let y_int = y.round_down();
td.move_(x_int, y_int);
}
(x, y)
@ -894,7 +902,6 @@ impl WlSeatGlobal {
}
pub(super) fn apply_changes(self: &Rc<Self>) {
self.state.damage();
self.pointer_owner.apply_changes(self);
if self.changes.get().contains(CHANGE_TREE) {
self.tablet_apply_changes();

View file

@ -385,7 +385,6 @@ impl<T: SimplePointerOwnerUsecase> PointerOwner for SimplePointerOwner<T> {
if !T::IS_DEFAULT {
seat.pointer_owner.set_default_pointer_owner(seat);
seat.trigger_tree_changed();
seat.state.damage();
}
}
@ -763,21 +762,17 @@ impl<S: ToplevelSelector> NodeSelectorUsecase for SelectToplevelUsecase<S> {
}
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
let mut damage = false;
let tl = node.clone().node_into_toplevel();
if let Some(tl) = &tl {
tl.tl_data().render_highlight.fetch_add(1);
if !tl.tl_admits_children() {
seat.pointer_cursor().set_known(KnownCursor::Pointer);
}
damage = true;
seat.state.damage(tl.node_absolute_position());
}
if let Some(prev) = self.latest.set(tl) {
prev.tl_data().render_highlight.fetch_sub(1);
damage = true;
}
if damage {
seat.state.damage();
seat.state.damage(prev.node_absolute_position());
}
}
}
@ -787,7 +782,7 @@ impl<S: ?Sized> Drop for SelectToplevelUsecase<S> {
if let Some(prev) = self.latest.take() {
prev.tl_data().render_highlight.fetch_sub(1);
if let Some(seat) = self.seat.upgrade() {
seat.state.damage();
seat.state.damage(prev.node_absolute_position());
}
}
}
@ -812,19 +807,15 @@ impl<S: WorkspaceSelector> NodeSelectorUsecase for SelectWorkspaceUsecase<S> {
}
fn node_focus(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, node: &Rc<dyn Node>) {
let mut damage = false;
let ws = node.clone().node_into_workspace();
if let Some(ws) = &ws {
ws.render_highlight.fetch_add(1);
seat.pointer_cursor().set_known(KnownCursor::Pointer);
damage = true;
seat.state.damage(ws.position.get());
}
if let Some(prev) = self.latest.set(ws) {
prev.render_highlight.fetch_sub(1);
damage = true;
}
if damage {
seat.state.damage();
seat.state.damage(prev.position.get());
}
}
}
@ -834,7 +825,7 @@ impl<S: ?Sized> Drop for SelectWorkspaceUsecase<S> {
if let Some(prev) = self.latest.take() {
prev.render_highlight.fetch_sub(1);
if let Some(seat) = self.seat.upgrade() {
seat.state.damage();
seat.state.damage(prev.position.get());
}
}
}

View file

@ -257,6 +257,7 @@ pub struct WlSurface {
opaque_region: Cell<Option<Rc<Region>>>,
buffer_points: RefCell<BufferPoints>,
pub buffer_points_norm: RefCell<SampleRect>,
damage_matrix: Cell<DamageMatrix>,
buffer_transform: Cell<Transform>,
buffer_scale: Cell<i32>,
src_rect: Cell<Option<[Fixed; 4]>>,
@ -568,6 +569,7 @@ impl WlSurface {
opaque_region: Default::default(),
buffer_points: Default::default(),
buffer_points_norm: Default::default(),
damage_matrix: Default::default(),
buffer_transform: Cell::new(Transform::None),
buffer_scale: Cell::new(1),
src_rect: Cell::new(None),
@ -669,8 +671,13 @@ impl WlSurface {
}
fn set_absolute_position(&self, x1: i32, y1: i32) {
self.buffer_abs_pos
.set(self.buffer_abs_pos.get().at_point(x1, y1));
let old_pos = self.buffer_abs_pos.get();
let new_pos = old_pos.at_point(x1, y1);
if self.visible.get() && self.toplevel.is_none() {
self.client.state.damage(old_pos);
self.client.state.damage(new_pos);
}
self.buffer_abs_pos.set(new_pos);
if let Some(children) = self.children.borrow_mut().deref_mut() {
for ss in children.subsurfaces.values() {
let pos = ss.position.get();
@ -843,6 +850,9 @@ impl WlSurface {
fn unset_dnd_icons(&self) {
while let Some((_, dnd_icon)) = self.dnd_icons.pop() {
dnd_icon.seat.remove_dnd_icon();
if self.visible.get() {
dnd_icon.damage();
}
}
}
@ -1169,6 +1179,18 @@ impl WlSurface {
y2,
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 damage_matrix = DamageMatrix::new(
self.buffer_transform.get(),
self.buffer_scale.get(),
buffer_width,
buffer_height,
self.src_rect.get(),
dst_width,
dst_height,
);
self.damage_matrix.set(damage_matrix);
}
}
let (width, height) = new_size.unwrap_or_default();
@ -1232,15 +1254,65 @@ impl WlSurface {
fr.send_done(now);
let _ = fr.client.remove_obj(&*fr);
}
} else {
self.apply_damage(pending);
}
}
self.client.state.damage();
pending.buffer_damage.clear();
pending.surface_damage.clear();
pending.damage_full = false;
Ok(())
}
fn apply_damage(&self, pending: &PendingState) {
let bounds = self.toplevel.get().map(|tl| tl.node_absolute_position());
let pos = self.buffer_abs_pos.get();
let apply_damage = |pos: Rect| {
if pending.damage_full {
let mut damage = pos;
if let Some(bounds) = bounds {
damage = damage.intersect(bounds);
}
self.client.state.damage(damage);
} else {
let matrix = self.damage_matrix.get();
if let Some(buffer) = self.buffer.get() {
for damage in &pending.buffer_damage {
let mut damage =
matrix.apply(pos.x1(), pos.y1(), damage.intersect(buffer.buffer.rect));
if let Some(bounds) = bounds {
damage = damage.intersect(bounds);
}
self.client.state.damage(damage);
}
}
for damage in &pending.surface_damage {
let mut damage = damage.move_(pos.x1(), pos.y1());
damage = damage.intersect(bounds.unwrap_or(pos));
self.client.state.damage(damage);
}
}
};
match self.role.get() {
SurfaceRole::Cursor => {
for (_, cursor) in &self.cursors {
if cursor.needs_damage_tracking() {
let (x, y) = cursor.surface_position();
apply_damage(pos.at_point(x, y));
}
}
}
SurfaceRole::DndIcon => {
for (_, dnd_icon) in &self.dnd_icons {
let (x, y) = dnd_icon.seat.pointer_cursor().position_int();
let (x, y) = dnd_icon.surface_position(x, y);
apply_damage(pos.at_point(x, y));
}
}
_ => apply_damage(pos),
}
}
fn verify_explicit_sync(&self, pending: &mut PendingState) -> Result<(), WlSurfaceError> {
pending.explicit_sync = self.sync_obj_surface.is_some();
if !pending.explicit_sync {
@ -1378,8 +1450,8 @@ impl WlSurface {
}
}
self.seat_state.destroy_node(self);
if self.visible.get() {
self.client.state.damage();
if self.visible.get() && self.toplevel.is_none() {
self.client.state.damage(self.buffer_abs_pos.get());
}
if set_invisible {
self.visible.set(false);
@ -1757,3 +1829,114 @@ efrom!(WlSurfaceError, ClientError);
efrom!(WlSurfaceError, XdgSurfaceError);
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
efrom!(WlSurfaceError, CommitTimelineError);
#[derive(Copy, Clone, Debug)]
struct DamageMatrix {
transform: Transform,
mx: f64,
my: f64,
dx: f64,
dy: f64,
smear: i32,
}
impl Default for DamageMatrix {
fn default() -> Self {
Self {
transform: Default::default(),
mx: 1.0,
my: 1.0,
dx: 0.0,
dy: 0.0,
smear: 0,
}
}
}
impl DamageMatrix {
fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
let x1 = rect.x1() - self.smear;
let x2 = rect.x2() + self.smear;
let y1 = rect.y1() - self.smear;
let y2 = rect.y2() + self.smear;
let [x1, y1, x2, y2] = match self.transform {
Transform::None => [x1, y1, x2, y2],
Transform::Rotate90 => [-y2, x1, -y1, x2],
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
Transform::Rotate270 => [y1, -x2, y2, -x1],
Transform::Flip => [-x2, y1, -x1, y2],
Transform::FlipRotate90 => [y1, x1, y2, x2],
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
};
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
Rect::new(x1, y1, x2, y2).unwrap()
}
fn new(
transform: Transform,
legacy_scale: i32,
buffer_width: i32,
buffer_height: i32,
viewport: Option<[Fixed; 4]>,
dst_width: i32,
dst_height: i32,
) -> DamageMatrix {
let mut buffer_width = buffer_width as f64;
let mut buffer_height = buffer_height as f64;
let dst_width = dst_width as f64;
let dst_height = dst_height as f64;
let mut mx = 1.0;
let mut my = 1.0;
if legacy_scale != 1 {
let scale_inv = 1.0 / (legacy_scale as f64);
mx = scale_inv;
my = scale_inv;
buffer_width *= scale_inv;
buffer_height *= scale_inv;
}
let (mut buffer_width, mut buffer_height) =
transform.maybe_swap((buffer_width, buffer_height));
let (mut dx, mut dy) = match transform {
Transform::None => (0.0, 0.0),
Transform::Rotate90 => (buffer_width, 0.0),
Transform::Rotate180 => (buffer_width, buffer_height),
Transform::Rotate270 => (0.0, buffer_height),
Transform::Flip => (buffer_width, 0.0),
Transform::FlipRotate90 => (0.0, 0.0),
Transform::FlipRotate180 => (0.0, buffer_height),
Transform::FlipRotate270 => (buffer_width, buffer_height),
};
if let Some([x, y, w, h]) = viewport {
dx -= x.to_f64();
dy -= y.to_f64();
buffer_width = w.to_f64();
buffer_height = h.to_f64();
}
let mut smear = false;
if dst_width != buffer_width {
let scale = dst_width / buffer_width;
mx *= scale;
dx *= scale;
smear |= dst_width > buffer_width;
}
if dst_height != buffer_height {
let scale = dst_height / buffer_height;
my *= scale;
dy *= scale;
smear |= dst_height > buffer_height;
}
DamageMatrix {
transform,
mx,
my,
dx,
dy,
smear: smear as _,
}
}
}

View file

@ -60,6 +60,16 @@ impl CursorSurface {
pub fn update_hardware_cursor(&self) {
self.user.update_hardware_cursor();
}
pub fn needs_damage_tracking(&self) -> bool {
self.user.software_cursor()
}
pub fn surface_position(&self) -> (i32, i32) {
let (x, y) = self.user.position();
let (dx, dy) = self.hotspot.get();
(x.to_int() - dx, y.to_int() - dy)
}
}
impl Cursor for CursorSurface {

View file

@ -18,9 +18,13 @@ impl DndIcon {
}
fn update_visible(&self) {
let was_visible = self.surface.visible.get();
let is_visible =
self.surface.dnd_icons.is_not_empty() && self.surface.client.state.root_visible();
self.surface.set_visible(is_visible);
if was_visible != is_visible {
self.damage();
}
}
pub fn enable(self: &Rc<Self>) {
@ -45,6 +49,16 @@ impl DndIcon {
self.surface.extents.get().move_(x, y)
}
pub fn damage(&self) {
let (x, y) = self.seat.pointer_cursor().position_int();
self.damage_at(x, y);
}
pub fn damage_at(&self, x: i32, y: i32) {
let extents = self.extents(x, y);
self.surface.client.state.damage(extents);
}
pub fn render(&self, renderer: &mut Renderer<'_>, cursor_rect: &Rect, x: i32, y: i32) {
let extents = self.extents(x, y);
if extents.intersects(&cursor_rect) {

View file

@ -352,6 +352,9 @@ impl SurfaceExt for WlSubsurface {
if self.had_buffer.replace(has_buffer) != has_buffer {
if has_buffer {
if self.parent.visible.get() {
let (x, y) = self.surface.buffer_abs_pos.get().position();
let extents = self.surface.extents.get();
self.surface.client.state.damage(extents.move_(x, y));
self.surface.set_visible(true);
}
} else {

View file

@ -252,6 +252,7 @@ impl Xwindow {
pub fn map_status_changed(self: &Rc<Self>) {
let map_change = self.map_change();
let override_redirect = self.data.info.override_redirect.get();
match map_change {
Change::None => return,
Change::Unmap => {
@ -261,7 +262,7 @@ impl Xwindow {
.set(self.data.info.extents.take());
self.tl_destroy();
}
Change::Map if self.data.info.override_redirect.get() => {
Change::Map if override_redirect => {
self.clone()
.tl_change_extents(&self.data.info.pending_extents.get());
*self.display_link.borrow_mut() =
@ -284,12 +285,17 @@ impl Xwindow {
match map_change {
Change::Unmap => self.tl_set_visible(false),
Change::Map => {
self.tl_set_visible(true);
if override_redirect {
self.tl_set_visible(true);
}
self.toplevel_data.broadcast(self.clone());
}
Change::None => {}
}
self.data.state.tree_changed();
if override_redirect {
self.data.state.damage(self.data.info.pending_extents.get());
}
}
}
@ -414,7 +420,10 @@ impl ToplevelNodeBase for Xwindow {
// log::info!("xwin {} change_extents {:?}", self.data.window_id, rect);
let old = self.data.info.extents.replace(*rect);
if old != *rect {
if !self.data.info.override_redirect.get() {
if self.data.info.override_redirect.get() {
self.data.state.damage(old);
self.data.state.damage(*rect);
} else {
self.data
.state
.xwayland

View file

@ -167,6 +167,10 @@ pub trait XdgSurfaceExt: Debug {
fn extents_changed(&self) {
// nothing
}
fn geometry_changed(&self) {
// nothing
}
}
impl XdgSurface {
@ -258,6 +262,12 @@ impl XdgSurface {
}
}
pub fn damage(&self) {
let (x, y) = self.surface.buffer_abs_pos.get().position();
let extents = self.surface.extents.get();
self.surface.client.state.damage(extents.move_(x, y));
}
pub fn geometry(&self) -> Option<Rect> {
self.geometry.get()
}
@ -497,6 +507,9 @@ impl SurfaceExt for XdgSurface {
if prev != Some(geometry) {
self.update_extents();
self.update_surface_position();
if let Some(ext) = self.ext.get() {
ext.geometry_changed();
}
}
}
}

View file

@ -407,11 +407,13 @@ impl XdgToplevel {
}
self.toplevel_data.broadcast(self.clone());
self.tl_set_visible(self.state.root_visible());
self.xdg.damage();
}
self.extents_changed();
} else {
if self.is_mapped.replace(false) {
self.tl_set_visible(false);
self.xdg.damage();
}
}
return;
@ -677,6 +679,14 @@ impl XdgSurfaceExt for XdgToplevel {
self.toplevel_data.pos.set(self.xdg.extents.get());
self.tl_extents_changed();
}
fn geometry_changed(&self) {
self.xdg
.surface
.client
.state
.damage(self.node_absolute_position());
}
}
#[derive(Debug, Error)]

View file

@ -588,6 +588,11 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 {
}
if self.mapped.get() != was_mapped {
output.update_visible();
if self.mapped.get() {
let (x, y) = self.surface.buffer_abs_pos.get().position();
let extents = self.surface.extents.get().move_(x, y);
self.client.state.damage(extents);
}
}
if self.mapped.get() {
match self.keyboard_interactivity.get() {

View file

@ -44,17 +44,24 @@ pub async fn input_popup_positioning(state: Rc<State>) {
}
impl ZwpInputPopupSurfaceV2 {
fn damage(&self) {
let (x, y) = self.surface.buffer_abs_pos.get().position();
let extents = self.surface.extents.get();
self.client.state.damage(extents.move_(x, y));
}
pub fn update_visible(self: &Rc<Self>) {
let was_visible = self.surface.visible.get();
let is_visible = self.surface.buffer.is_some()
&& self.input_method.connection.is_some()
&& self.client.state.root_visible();
self.surface.set_visible(is_visible);
if was_visible || is_visible {
self.client.state.damage();
}
if !was_visible && is_visible {
self.schedule_positioning();
if was_visible != is_visible {
if is_visible {
self.schedule_positioning();
} else {
self.damage();
}
}
}
@ -132,6 +139,9 @@ impl ZwpInputPopupSurfaceV2 {
}
fn detach(&self) {
if self.surface.visible.get() {
self.damage();
}
self.surface.destroy_node();
self.surface.unset_ext();
self.input_method.popups.remove(&self.id);

View file

@ -9,7 +9,7 @@ use {
object::{Object, Version},
rect::Rect,
renderer::Renderer,
tree::ToplevelNode,
tree::{Node, ToplevelNode},
utils::clonecell::CloneCell,
wire::{xdg_toplevel_drag_v1::*, XdgToplevelDragV1Id},
},
@ -53,16 +53,22 @@ impl XdgToplevelDragV1 {
}
}
fn move2(&self, x: i32, y: i32) {
fn move2(&self, x: i32, y: i32, damage_initial: bool) {
if let Some(tl) = self.toplevel.get() {
if damage_initial && tl.node_visible() {
tl.xdg.damage();
}
let extents = tl.xdg.absolute_desired_extents.get();
let extents = extents.at_point(x - self.x_off.get(), y - self.y_off.get());
tl.clone().tl_change_extents(&extents);
if tl.node_visible() {
tl.xdg.damage();
}
}
}
pub fn move_(&self, x: i32, y: i32) {
self.move2(x, y);
self.move2(x, y, true);
}
pub fn render(&self, renderer: &mut Renderer<'_>, cursor_rect: &Rect, x: i32, y: i32) {
@ -122,7 +128,7 @@ impl XdgToplevelDragV1 {
self.client.state.tree_changed();
if let Some(seat) = self.source.data.seat.get() {
let (x, y) = seat.pointer_cursor().position_int();
self.move2(x, y)
self.move2(x, y, false)
}
}