diff --git a/release-notes.md b/release-notes.md index 701c2dae..ec924a06 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Add fine-grained damage tracking. + # 1.4.0 (2024-07-07) - Add window management mode. diff --git a/src/compositor.rs b/src/compositor.rs index bdb4925d..2617eb9b 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -468,9 +468,11 @@ fn create_dummy_output(state: &Rc) { screencasts: Default::default(), hardware_cursor_needs_render: Cell::new(false), screencopies: Default::default(), + title_visible: Cell::new(false), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), + state: state.clone(), is_dummy: true, output: CloneCell::new(dummy_output.clone()), position: Default::default(), diff --git a/src/config/handler.rs b/src/config/handler.rs index e53f4fde..755f8e4c 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -720,8 +720,6 @@ impl ConfigProxyHandler { if let Some(ws) = self.state.workspaces.get(name.as_str()) { ws.may_capture.set(capture); ws.update_has_captures(); - ws.output.get().schedule_update_render_data(); - self.state.damage(); } Ok(()) } @@ -863,7 +861,6 @@ impl ConfigProxyHandler { move_ws_to_output(&link, &output, config); ws.desired_output.set(output.global.output_id.clone()); self.state.tree_changed(); - self.state.damage(); Ok(()) } @@ -1032,7 +1029,6 @@ impl ConfigProxyHandler { let scale = Scale::from_f64(scale); let connector = self.get_output_node(connector)?; connector.set_preferred_scale(scale); - self.state.damage(); Ok(()) } @@ -1043,7 +1039,6 @@ impl ConfigProxyHandler { ) -> Result<(), CphError> { let connector = self.get_output_node(connector)?; connector.update_transform(transform); - self.state.damage(); Ok(()) } @@ -1371,6 +1366,7 @@ impl ConfigProxyHandler { } } self.state.root.clone().node_visit(&mut V); + self.state.damage(self.state.root.extents.get()); } fn colors_changed(&self) { @@ -1386,6 +1382,7 @@ impl ConfigProxyHandler { } } self.state.root.clone().node_visit(&mut V); + self.state.damage(self.state.root.extents.get()); } fn get_sized(&self, sized: Resizable) -> Result { diff --git a/src/cursor_user.rs b/src/cursor_user.rs index 9459e393..96a0e992 100644 --- a/src/cursor_user.rs +++ b/src/cursor_user.rs @@ -3,6 +3,7 @@ use { cursor::{Cursor, KnownCursor, DEFAULT_CURSOR_SIZE}, fixed::Fixed, rect::Rect, + scale::Scale, state::State, tree::OutputNode, utils::{ @@ -74,13 +75,26 @@ impl CursorUserGroup { group } + fn damage_active(&self) { + if let Some(active) = self.active.get() { + if let Some(cursor) = active.cursor.get() { + let (x, y) = active.pos.get(); + let x_int = x.round_down(); + let y_int = y.round_down(); + let extents = cursor.extents_at_scale(Scale::default()); + self.state.damage(extents.move_(x_int, y_int)); + } + } + } + pub fn deactivate(&self) { if self.hardware_cursor.get() { self.remove_hardware_cursor(); + } else { + self.damage_active(); } self.active_id.take(); self.active.take(); - self.state.damage(); } pub fn latest_output(&self) -> Rc { @@ -150,6 +164,7 @@ impl CursorUserGroup { if self.hardware_cursor.replace(hardware_cursor) == hardware_cursor { return; } + self.damage_active(); if hardware_cursor { let prev = self .state @@ -157,6 +172,7 @@ impl CursorUserGroup { .set(Some(self.clone())); if let Some(prev) = prev { prev.hardware_cursor.set(false); + prev.damage_active(); } match self.active.get() { None => self.remove_hardware_cursor(), @@ -230,9 +246,7 @@ impl CursorUser { self.owner.take(); self.group.users.remove(&self.id); if self.group.active_id.get() == Some(self.id) { - self.group.active_id.take(); - self.group.active.take(); - self.group.state.damage(); + self.group.deactivate(); } } @@ -240,10 +254,15 @@ impl CursorUser { if self.group.active_id.replace(Some(self.id)) == Some(self.id) { return; } + if self.software_cursor() { + self.group.damage_active(); + } self.group.latest_output.set(self.output.get()); self.group.active.set(Some(self.clone())); self.update_hardware_cursor(); - self.group.state.damage(); + if self.software_cursor() { + self.group.damage_active(); + } } #[cfg_attr(not(feature = "it"), allow(dead_code))] @@ -339,6 +358,9 @@ impl CursorUser { } } old.handle_unset(); + if self.software_cursor() { + self.group.damage_active(); + } } if let Some(cursor) = cursor.as_ref() { cursor.clone().handle_set(); @@ -346,6 +368,9 @@ impl CursorUser { } self.cursor.set(cursor.clone()); self.update_hardware_cursor(); + if self.software_cursor() { + self.group.damage_active(); + } } pub fn position(&self) -> (Fixed, Fixed) { @@ -366,6 +391,16 @@ impl CursorUser { x = x.apply_fract(x_tmp); y = y.apply_fract(y_tmp); } + if self.software_cursor() { + if let Some(cursor) = self.cursor.get() { + let (old_x, old_y) = self.pos.get(); + let old_x_int = old_x.round_down(); + let old_y_int = old_y.round_down(); + let extents = cursor.extents_at_scale(Scale::default()); + self.group.state.damage(extents.move_(old_x_int, old_y_int)); + self.group.state.damage(extents.move_(x_int, y_int)); + } + } self.pos.set((x, y)); self.update_hardware_cursor_(false); (x, y) @@ -379,6 +414,10 @@ impl CursorUser { self.is_active() && self.group.hardware_cursor.get() } + pub fn software_cursor(&self) -> bool { + self.is_active() && !self.group.hardware_cursor.get() + } + fn update_hardware_cursor_(&self, render: bool) { if !self.hardware_cursor() { return; diff --git a/src/ifs/ext_session_lock_manager_v1.rs b/src/ifs/ext_session_lock_manager_v1.rs index b2981ddf..3d80fd17 100644 --- a/src/ifs/ext_session_lock_manager_v1.rs +++ b/src/ifs/ext_session_lock_manager_v1.rs @@ -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(); diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index 344a1b06..9c4d7301 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -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(()) } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 08847360..365263a3 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -562,7 +562,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(); } } } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 51bcb5a4..5cbbf231 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -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.state.damage(); self.pointer_owner.apply_changes(self); if self.changes.get().contains(CHANGE_TREE) { self.tablet_apply_changes(); diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 5a436f2a..b5e760c7 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -385,7 +385,6 @@ impl PointerOwner for SimplePointerOwner { if !T::IS_DEFAULT { seat.pointer_owner.set_default_pointer_owner(seat); seat.trigger_tree_changed(); - seat.state.damage(); } } @@ -763,21 +762,17 @@ impl NodeSelectorUsecase for SelectToplevelUsecase { } fn node_focus(self: &Rc, seat: &Rc, node: &Rc) { - 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 Drop for SelectToplevelUsecase { 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 NodeSelectorUsecase for SelectWorkspaceUsecase { } fn node_focus(self: &Rc, seat: &Rc, node: &Rc) { - 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 Drop for SelectWorkspaceUsecase { 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()); } } } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index c319354c..072a4e2a 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -82,7 +82,7 @@ use { xwayland::XWaylandEvent, }, ahash::AHashMap, - isnt::std_1::primitive::IsntSliceExt, + isnt::std_1::{primitive::IsntSliceExt, vec::IsntVecExt}, jay_config::video::Transform, std::{ cell::{Cell, RefCell}, @@ -257,6 +257,7 @@ pub struct WlSurface { opaque_region: Cell>>, buffer_points: RefCell, pub buffer_points_norm: RefCell, + damage_matrix: Cell, buffer_transform: Cell, buffer_scale: Cell, src_rect: Cell>, @@ -563,6 +564,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), @@ -663,8 +665,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(); @@ -837,6 +844,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(); + } } } @@ -1160,6 +1170,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().map(|v| (v[0], v[1])), + dst_width, + dst_height, + ); + self.damage_matrix.set(damage_matrix); } } let (width, height) = new_size.unwrap_or_default(); @@ -1216,13 +1238,64 @@ impl WlSurface { } } self.ext.get().after_apply_commit(); - self.client.state.damage(); + if self.visible.get() { + self.apply_damage(pending); + } 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 { @@ -1354,8 +1427,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); @@ -1733,3 +1806,105 @@ 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, +} + +impl Default for DamageMatrix { + fn default() -> Self { + Self { + transform: Default::default(), + mx: 1.0, + my: 1.0, + dx: 0.0, + dy: 0.0, + } + } +} + +impl DamageMatrix { + fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect { + let x1 = rect.x1(); + let x2 = rect.x2(); + let y1 = rect.y1(); + let y2 = rect.y2(); + 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_pos: Option<(Fixed, Fixed)>, + 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 (buffer_width, 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 => (0.0, 0.0), + }; + if let Some((x, y)) = viewport_pos { + dx -= x.to_f64(); + dy -= y.to_f64(); + } + if dst_width != buffer_width { + let scale = dst_width / buffer_width; + mx *= scale; + dx *= scale; + } + if dst_height != buffer_height { + let scale = dst_height / buffer_height; + my *= scale; + dy *= scale; + } + DamageMatrix { + transform, + mx, + my, + dx, + dy, + } + } +} diff --git a/src/ifs/wl_surface/cursor.rs b/src/ifs/wl_surface/cursor.rs index 2d6208c5..52a16414 100644 --- a/src/ifs/wl_surface/cursor.rs +++ b/src/ifs/wl_surface/cursor.rs @@ -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 { diff --git a/src/ifs/wl_surface/dnd_icon.rs b/src/ifs/wl_surface/dnd_icon.rs index e41f11aa..ef1bc180 100644 --- a/src/ifs/wl_surface/dnd_icon.rs +++ b/src/ifs/wl_surface/dnd_icon.rs @@ -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) { @@ -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) { diff --git a/src/ifs/wl_surface/x_surface/xwindow.rs b/src/ifs/wl_surface/x_surface/xwindow.rs index d9506fbe..49110421 100644 --- a/src/ifs/wl_surface/x_surface/xwindow.rs +++ b/src/ifs/wl_surface/x_surface/xwindow.rs @@ -252,6 +252,7 @@ impl Xwindow { pub fn map_status_changed(self: &Rc) { 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 diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index 645f79d1..3cf3d575 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -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 { 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(); + } } } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 71f96da3..6b608d5e 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -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)] diff --git a/src/ifs/wl_surface/zwp_input_popup_surface_v2.rs b/src/ifs/wl_surface/zwp_input_popup_surface_v2.rs index 022a35c6..9f326e35 100644 --- a/src/ifs/wl_surface/zwp_input_popup_surface_v2.rs +++ b/src/ifs/wl_surface/zwp_input_popup_surface_v2.rs @@ -44,17 +44,24 @@ pub async fn input_popup_positioning(state: Rc) { } 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) { 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); diff --git a/src/ifs/xdg_toplevel_drag_v1.rs b/src/ifs/xdg_toplevel_drag_v1.rs index df34ec09..e41f5f25 100644 --- a/src/ifs/xdg_toplevel_drag_v1.rs +++ b/src/ifs/xdg_toplevel_drag_v1.rs @@ -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) } } diff --git a/src/state.rs b/src/state.rs index 448548a9..ce524591 100644 --- a/src/state.rs +++ b/src/state.rs @@ -661,7 +661,6 @@ impl State { ws.flush_jay_workspaces(); output.schedule_update_render_data(); self.tree_changed(); - self.damage(); // let seats = self.globals.seats.lock(); // for seat in seats.values() { // seat.workspace_changed(&output); @@ -688,7 +687,6 @@ impl State { for output in outputs.values() { output.set_status(&status); } - self.damage(); } pub fn input_occurred(&self) { @@ -730,10 +728,11 @@ impl State { serial as _ } - pub fn damage(&self) { - for connector in self.connectors.lock().values() { - if connector.connected.get() { - connector.connector.damage(); + pub fn damage(&self, rect: Rect) { + self.damage_visualizer.add(rect); + for output in self.root.outputs.lock().values() { + if output.global.pos.get().intersects(&rect) { + output.global.connector.connector.damage(); } } } @@ -747,7 +746,7 @@ impl State { } } self.tree_changed(); - self.damage(); + self.damage(self.root.extents.get()); } pub fn clear(&self) { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 09184858..dabeb8f5 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -172,7 +172,9 @@ impl ConnectorHandler { update_render_data_scheduled: Cell::new(false), hardware_cursor_needs_render: Cell::new(false), screencopies: Default::default(), + title_visible: Default::default(), }); + on.update_visible(); on.update_rects(); self.state .add_output_scale(on.global.persistent.scale.get()); @@ -295,7 +297,7 @@ impl ConnectorHandler { .remove_output_scale(on.global.persistent.scale.get()); let _ = self.state.remove_global(&*global); self.state.tree_changed(); - self.state.damage(); + self.state.damage(self.state.root.extents.get()); } async fn handle_non_desktop_connected(&self, monitor_info: MonitorInfo) { diff --git a/src/tree/container.rs b/src/tree/container.rs index 7396c6d5..6f0c6563 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -355,9 +355,24 @@ impl ContainerNode { self.schedule_compute_render_data(); } + fn damage(&self) { + self.state.damage( + Rect::new_sized( + self.abs_x1.get(), + self.abs_y1.get(), + self.width.get(), + self.height.get(), + ) + .unwrap(), + ); + } + fn schedule_layout(self: &Rc) { if !self.layout_scheduled.replace(true) { self.state.pending_container_layout.push(self.clone()); + if self.toplevel_data.visible.get() { + self.damage(); + } } } @@ -677,8 +692,13 @@ impl ContainerNode { let split = self.split.get(); let have_active = self.children.iter().any(|c| c.active.get()); let scales = self.state.scales.lock(); + let abs_x = self.abs_x1.get(); + let abs_y = self.abs_y1.get(); for (i, child) in self.children.iter().enumerate() { let rect = child.title_rect.get(); + if self.toplevel_data.visible.get() { + self.state.damage(rect.move_(abs_x, abs_y)); + } if i > 0 { let rect = if mono { Rect::new_sized(rect.x1() - bw, 0, bw, th) @@ -1471,6 +1491,7 @@ impl ContainingNode for ContainerNode { if let Some(body) = body { let body = body.move_(self.abs_x1.get(), self.abs_y1.get()); new.clone().tl_change_extents(&body); + self.state.damage(body); } } diff --git a/src/tree/display.rs b/src/tree/display.rs index 87c3cf61..0507372d 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -79,6 +79,9 @@ impl DisplayNode { for seat in state.globals.seats.lock().values() { seat.set_visible(visible); } + if visible { + state.damage(self.extents.get()); + } } } diff --git a/src/tree/float.rs b/src/tree/float.rs index a14a5194..1fc4350f 100644 --- a/src/tree/float.rs +++ b/src/tree/float.rs @@ -135,6 +135,9 @@ impl FloatNode { child.tl_set_visible(floater.visible.get()); child.tl_restack_popups(); floater.schedule_layout(); + if floater.visible.get() { + state.damage(position); + } floater } @@ -199,11 +202,12 @@ impl FloatNode { _ => return, }; let scales = self.state.scales.lock(); + let tr = Rect::new_sized(pos.x1() + bw, pos.y1() + bw, pos.width() - 2 * bw, th).unwrap(); for (scale, _) in scales.iter() { let old_tex = self.title_textures.remove(scale); - let mut th = th; + let mut th = tr.height(); let mut scalef = None; - let mut width = pos.width() - 2 * bw; + let mut width = tr.width(); if *scale != 1 { let scale = scale.to_f64(); th = (th as f64 * scale).round() as _; @@ -222,6 +226,9 @@ impl FloatNode { }; self.title_textures.set(*scale, texture); } + if self.visible.get() { + self.state.damage(tr); + } } fn pointer_move( @@ -307,8 +314,12 @@ impl FloatNode { y2 = y2.max(y1 + 2 * bw + th + 1); } } - self.position.set(Rect::new(x1, y1, x2, y2).unwrap()); - self.state.damage(); + let new_pos = Rect::new(x1, y1, x2, y2).unwrap(); + self.position.set(new_pos); + if self.visible.get() { + self.state.damage(pos); + self.state.damage(new_pos); + } self.schedule_layout(); return; } @@ -684,6 +695,9 @@ impl ContainingNode for FloatNode { self.pull_child_properties(); new.tl_set_visible(self.visible.get()); self.schedule_layout(); + if self.visible.get() { + self.state.damage(self.position.get()); + } } fn cnode_remove_child2(self: Rc, _child: &dyn Node, _preserve_focus: bool) { @@ -691,6 +705,9 @@ impl ContainingNode for FloatNode { self.child.set(None); self.display_link.borrow_mut().take(); self.workspace_link.set(None); + if self.visible.get() { + self.state.damage(self.position.get()); + } } fn cnode_accepts_child(&self, _node: &dyn Node) -> bool { @@ -716,8 +733,10 @@ impl ContainingNode for FloatNode { let (x, y) = (x - bw, y - th - bw - 1); let pos = self.position.get(); if pos.position() != (x, y) { - self.position.set(pos.at_point(x, y)); - self.state.damage(); + let new_pos = pos.at_point(x, y); + self.position.set(new_pos); + self.state.damage(pos); + self.state.damage(new_pos); self.schedule_layout(); } } @@ -753,7 +772,10 @@ impl ContainingNode for FloatNode { let new_pos = Rect::new(x1, y1, x2, y2).unwrap(); if new_pos != pos { self.position.set(new_pos); - self.state.damage(); + if self.visible.get() { + self.state.damage(pos); + self.state.damage(new_pos); + } self.schedule_layout(); } } diff --git a/src/tree/output.rs b/src/tree/output.rs index a9652984..c82d7efb 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -77,6 +77,7 @@ pub struct OutputNode { pub update_render_data_scheduled: Cell, pub screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc>, pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc>, + pub title_visible: Cell, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -115,6 +116,9 @@ impl OutputNode { if let Some(c) = self.workspace.get() { c.change_extents(&self.workspace_rect.get()); } + if self.node_visible() { + self.state.damage(self.global.pos.get()); + } } } @@ -308,7 +312,8 @@ impl OutputNode { texture_height = (th as f64 * scale).round() as _; } let active_id = self.workspace.get().map(|w| w.id); - let output_width = self.non_exclusive_rect.get().width(); + let non_exclusive_rect = self.non_exclusive_rect.get(); + let output_width = non_exclusive_rect.width(); rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap(); for ws in self.workspaces.iter() { let old_tex = ws.title_texture.take(); @@ -415,7 +420,16 @@ impl OutputNode { tex: title, }); } - self.state.damage(); + if self.title_visible.get() { + let title_rect = Rect::new_sized( + non_exclusive_rect.x1(), + non_exclusive_rect.y1(), + non_exclusive_rect.width(), + th, + ) + .unwrap(); + self.state.damage(title_rect); + } } pub fn ensure_workspace(self: &Rc) -> Rc { @@ -461,12 +475,16 @@ impl OutputNode { for seat in seats { ws.clone().node_do_focus(&seat, Direction::Unspecified); } + if self.node_visible() { + self.state.damage(self.global.pos.get()); + } true } pub fn create_workspace(self: &Rc, name: &str) -> Rc { let ws = Rc::new(WorkspaceNode { id: self.state.node_ids.next(), + state: self.state.clone(), is_dummy: false, output: CloneCell::new(self.clone()), position: Cell::new(Default::default()), @@ -583,6 +601,11 @@ impl OutputNode { } fn change_extents_(self: &Rc, rect: &Rect) { + if self.node_visible() { + let old_pos = self.global.pos.get(); + self.state.damage(old_pos); + self.state.damage(*rect); + } self.global.persistent.pos.set((rect.x1(), rect.y1())); self.global.pos.set(*rect); self.state.root.update_extents(); @@ -703,6 +726,13 @@ impl OutputNode { prev } + pub fn fullscreen_changed(&self) { + self.update_visible(); + if self.node_visible() { + self.state.damage(self.global.pos.get()); + } + } + pub fn update_visible(&self) { let mut visible = self.state.root_visible(); if self.state.lock.locked.get() { @@ -723,6 +753,7 @@ impl OutputNode { have_fullscreen = ws.fullscreen.is_some(); } let lower_visible = visible && !have_fullscreen; + self.title_visible.set(lower_visible); set_layer_visible!(self.layers[0], lower_visible); set_layer_visible!(self.layers[1], lower_visible); if let Some(ws) = self.workspace.get() { @@ -823,7 +854,7 @@ impl Node for OutputNode { } fn node_visible(&self) -> bool { - true + self.state.root_visible() } fn node_absolute_position(&self) -> Rect { diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index ee6ecdfc..5f9ccbaf 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -435,7 +435,6 @@ impl ToplevelData { .tl_into_node() .node_do_focus(&seat, Direction::Unspecified); } - state.damage(); } pub fn unset_fullscreen(&self, state: &Rc, node: Rc) { @@ -480,7 +479,6 @@ impl ToplevelData { fd.placeholder .node_seat_state() .destroy_node(fd.placeholder.deref()); - state.damage(); } pub fn set_visible(&self, node: &dyn Node, visible: bool) { @@ -496,7 +494,6 @@ impl ToplevelData { if let Some(parent) = self.parent.get() { parent.cnode_child_attention_request_changed(node, false); } - self.state.damage(); } pub fn request_attention(&self, node: &dyn Node) { @@ -510,6 +507,5 @@ impl ToplevelData { if let Some(parent) = self.parent.get() { parent.cnode_child_attention_request_changed(node, true); } - self.state.damage(); } } diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index ed2835a5..7e8c1647 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -11,6 +11,7 @@ use { }, rect::Rect, renderer::Renderer, + state::State, text::TextTexture, tree::{ container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction, @@ -38,6 +39,7 @@ tree_id!(WorkspaceNodeId); pub struct WorkspaceNode { pub id: WorkspaceNodeId, + pub state: Rc, pub is_dummy: bool, pub output: CloneCell>, pub position: Cell, @@ -85,6 +87,7 @@ impl WorkspaceNode { } if self.has_capture.replace(has_capture) != has_capture { output.schedule_update_render_data(); + output.state.damage(output.global.pos.get()); } } @@ -117,6 +120,7 @@ impl WorkspaceNode { container.tl_set_parent(self.clone()); container.tl_set_visible(self.container_visible()); self.container.set(Some(container.clone())); + self.state.damage(self.position.get()); } pub fn is_empty(&self) -> bool { @@ -168,7 +172,7 @@ impl WorkspaceNode { } self.pull_child_properties(&**node); if self.visible.get() { - self.output.get().update_visible(); + self.output.get().fullscreen_changed(); } else { node.tl_set_visible(false); } @@ -183,7 +187,7 @@ impl WorkspaceNode { if let Some(node) = self.fullscreen.take() { self.discard_child_properties(&*node); if self.visible.get() { - self.output.get().update_visible(); + self.output.get().fullscreen_changed(); } if let Some(surface) = node.tl_scanout_surface() { if let Some(fb) = surface.client.state.drm_feedback.get() { @@ -324,6 +328,7 @@ impl ContainingNode for WorkspaceNode { if container.node_id() == child.node_id() { self.discard_child_properties(&*container); self.container.set(None); + self.state.damage(self.position.get()); return; } } @@ -387,4 +392,10 @@ pub fn move_ws_to_output( if !source.is_dummy { source.schedule_update_render_data(); } + if source.node_visible() { + target.state.damage(source.global.pos.get()); + } + if target.node_visible() { + target.state.damage(target.global.pos.get()); + } }