Commit 4e490b7685b8
tarball4 files changed+342-317
@@ -0,0 +1,119 @@
1
+//! The last frames windows leave behind when they close or hide to a tray, so2
+//! their animations have something to show.3
+4
+use crate::anim::Ghost;5
+use crate::state::{State, now_ns};6
+use crate::wm::{LastFrame, root};7
+use monolith_layout::{OutputId, Rect, WindowId};8
+use monolith_render_vk::VulkanTexture;9
+use smithay::backend::renderer::Renderer;10
+use smithay::backend::renderer::utils::{import_surface_tree, with_renderer_surface_state};11
+use smithay::desktop::Window;12
+use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;13
+use smithay::wayland::compositor::{BufferAssignment, SurfaceAttributes, with_states};14
+15
+impl State {16
+ /// Called before a commit applies. A null-buffer commit makes smithay17
+ /// drop the surface's textures before `on_buffer` runs, so the frame a18
+ /// hiding window leaves behind has to be taken here.19
+ pub fn keep_frame_for_hide(&mut self, surface: &WlSurface) {20
+ let Some(id) = self.window_id(surface) else {21
+ return;22
+ };23
+ let removes = with_states(surface, |s| {24
+ let mut attrs = s.cached_state.get::<SurfaceAttributes>();25
+ matches!(attrs.pending().buffer, Some(BufferAssignment::Removed))26
+ });27
+ let Some(s) = self.wm.get(id).filter(|s| removes && s.drawn) else {28
+ return;29
+ };30
+ let window = s.window.clone();31
+ let frame = self.last_frame(&window);32
+ if let Some(s) = self.wm.get_mut(id) {33
+ s.hide_frame = frame;34
+ }35
+ }36
+37
+ /// Keeps a visible tiled window's last frame shrinking to its seed while38
+ /// its neighbours close over it.39
+ pub(crate) fn leave_ghost(40
+ &mut self,41
+ id: WindowId,42
+ window: &Window,43
+ saved: Option<LastFrame>,44
+ opacity: f32,45
+ ) {46
+ let Some(o) = self.wm.layout.window(id).and_then(|w| w.output) else {47
+ return;48
+ };49
+ let tiled = self50
+ .wm51
+ .layout52
+ .output(o)53
+ .is_some_and(|out| out.tree.contains(id));54
+ if !tiled || !self.wm.layout.visible(id) {55
+ return;56
+ }57
+ let Some(seed) = self58
+ .wm59
+ .layout60
+ .arrange_hiding(o, &[id])61
+ .tiled62
+ .into_iter()63
+ .find(|p| p.window == id)64
+ else {65
+ return;66
+ };67
+ if let Some(frame) = saved.or_else(|| self.last_frame(window)) {68
+ self.wm.last_frames.insert(id, frame);69
+ self.wm.anim.add_ghost(Ghost {70
+ id,71
+ output: o,72
+ seed: seed.rect,73
+ born: now_ns(),74
+ fixed: None,75
+ opacity,76
+ });77
+ }78
+ }79
+80
+ /// Fades a window that just left the screen where it was, like a81
+ /// dismissed scratchpad.82
+ pub fn fade_out(&mut self, id: WindowId, rect: Rect, output: OutputId) {83
+ let Some(window) = self.wm.window(id).cloned() else {84
+ return;85
+ };86
+ let opacity = self.wm.get(id).and_then(|s| s.rule.opacity).unwrap_or(1.0);87
+ if let Some(frame) = self.last_frame(&window) {88
+ self.wm.last_frames.insert(id, frame);89
+ self.wm.anim.add_ghost(Ghost {90
+ id,91
+ output,92
+ seed: rect,93
+ born: now_ns(),94
+ fixed: Some(rect),95
+ opacity,96
+ });97
+ }98
+ }99
+100
+ fn last_frame(&mut self, window: &Window) -> Option<LastFrame> {101
+ let surface = root(window)?;102
+ let _ = import_surface_tree(&mut self.renderer, &surface);103
+ let context = self.renderer.context_id();104
+ // The geometry lookup locks the surface's state, which the closure105
+ // below already holds.106
+ let geometry = window.geometry();107
+ with_renderer_surface_state(&surface, |s| {108
+ let texture = s.texture::<VulkanTexture>(context.clone())?.clone();109
+ Some(LastFrame {110
+ texture,111
+ scale: s.buffer_scale(),112
+ transform: s.buffer_transform(),113
+ geometry,114
+ view: s.view()?,115
+ })116
+ })117
+ .flatten()118
+ }119
+}@@ -11,6 +11,7 @@ mod drm;
1111
mod fifo;1212
mod focus;1313
mod frame;14
+mod ghosts;1415
mod globals;1516
mod idle;1617
mod import;@@ -30,6 +31,7 @@ mod scale;
3031
mod scene;3132
mod seat;3233
mod signals;34
+mod sizing;3335
mod state;3436
mod stats;3537
mod timing;@@ -0,0 +1,217 @@
1
+//! Sizing floating windows. Clients are asked what size they want, and rules2
+//! can pick the size and spot.3
+4
+use crate::state::{State, now_ns};5
+use crate::wm::Kind;6
+use monolith_config::At;7
+use monolith_layout::{Rect, WindowId, clamp_float};8
+use smithay::reexports::calloop::timer::{TimeoutAction, Timer};9
+use smithay::utils::Serial;10
+use std::time::Duration;11
+12
+impl State {13
+ /// Gives floats that landed on the layout's default rect their own size,14
+ /// as when a tiled window is toggled. The client is told to pick one, and15
+ /// its next commit after acking says which.16
+ pub fn size_new_floats(&mut self) {17
+ let stale: Vec<WindowId> = self18
+ .wm19
+ .iter()20
+ .filter(|(id, s)| {21
+ s.natural_size && !self.wm.layout.window(*id).is_some_and(|w| w.floating)22
+ })23
+ .map(|(id, _)| id)24
+ .collect();25
+ for id in stale {26
+ if let Some(s) = self.wm.get_mut(id) {27
+ s.natural_size = false;28
+ s.natural_after = None;29
+ }30
+ }31
+ let defaults: Vec<WindowId> = self32
+ .wm33
+ .layout34
+ .windows()35
+ .filter(|(_, w)| w.floating && w.float_default && w.scratchpad.is_none() && !w.hidden)36
+ .map(|(id, _)| id)37
+ .collect();38
+ for id in defaults {39
+ self.ask_natural_size(id);40
+ }41
+ }42
+43
+ fn ask_natural_size(&mut self, id: WindowId) {44
+ let Some(window) = self45
+ .wm46
+ .get(id)47
+ .filter(|s| !s.natural_size)48
+ .map(|s| s.window.clone())49
+ else {50
+ return;51
+ };52
+ let geometry = window.geometry().size;53
+ let current = (geometry.w > 0 && geometry.h > 0).then_some(geometry);54
+ let fixed = window.toplevel().and_then(crate::xdg::fixed_size);55
+ let size = match (window.x11_surface(), window.toplevel()) {56
+ (Some(x), _) => crate::xwayland::fixed_size(x),57
+ (_, Some(_)) if fixed.is_some() => fixed,58
+ (_, Some(t)) => {59
+ let held = self.visual_rect(id);60
+ let serial = crate::wm::configure_free(t);61
+ if let Some(w) = self.wm.get_mut(id)62
+ && let Some(serial) = serial63
+ {64
+ w.natural_size = true;65
+ w.natural_after = Some(serial);66
+ if let Some(rect) = held {67
+ self.wm.layout.hold_float_rect(id, rect);68
+ }69
+ self.await_natural_size(id, serial);70
+ return;71
+ }72
+ current73
+ }74
+ _ => None,75
+ };76
+ match size {77
+ Some(size) => self.float_at_natural_size(id, f64::from(size.w), f64::from(size.h)),78
+ None => {79
+ let rect = self.wm.layout.window(id).and_then(|w| w.float_rect);80
+ if let Some(rect) = rect {81
+ self.wm.layout.set_float_rect(id, rect);82
+ }83
+ }84
+ }85
+ }86
+87
+ /// Where a placed window is drawn right now.88
+ fn visual_rect(&self, id: WindowId) -> Option<Rect> {89
+ let now = now_ns();90
+ let (kind, rect, o) = self.wm.get(id)?.placed?;91
+ self.wm92
+ .anim93
+ .gliding(id, now)94
+ .or_else(|| {95
+ (kind == Kind::Tiled)96
+ .then(|| self.wm.anim.tiled(o, id, now))97
+ .flatten()98
+ })99
+ .or(Some(rect))100
+ }101
+102
+ /// A client that never answers a request for its size takes whatever103
+ /// size it has.104
+ fn await_natural_size(&mut self, id: WindowId, serial: Serial) {105
+ let timeout = Timer::from_duration(Duration::from_millis(300));106
+ let _ = self.core.handle.insert_source(timeout, move |_, _, state| {107
+ state.give_up_natural_size(id, serial);108
+ TimeoutAction::Drop109
+ });110
+ }111
+112
+ fn give_up_natural_size(&mut self, id: WindowId, serial: Serial) {113
+ let Some(w) = self.wm.get_mut(id) else {114
+ return;115
+ };116
+ if !w.natural_size || w.natural_after != Some(serial) {117
+ return;118
+ }119
+ w.natural_size = false;120
+ w.natural_after = None;121
+ let size = w.window.geometry().size;122
+ if size.w > 0 && size.h > 0 {123
+ self.float_at_natural_size(id, f64::from(size.w), f64::from(size.h));124
+ } else if let Some(rect) = self.wm.layout.window(id).and_then(|w| w.float_rect) {125
+ self.wm.layout.set_float_rect(id, rect);126
+ }127
+ self.apply_layout();128
+ }129
+130
+ /// Gives a floating window the size and spot its rules ask for, keeping131
+ /// whichever of the two they leave out. One still waiting for its client132
+ /// to pick a size gets its spot when the size arrives, unless the rule133
+ /// has a size to give it right away.134
+ pub(crate) fn float_by_rule(&mut self, id: WindowId) {135
+ let Some(win) = self.wm.layout.window(id).filter(|win| win.floating) else {136
+ return;137
+ };138
+ let (held, fullscreen) = (win.float_rect.map(|r| (r.w, r.h)), win.fullscreen);139
+ let Some(s) = self.wm.get_mut(id) else {140
+ return;141
+ };142
+ let rule = &s.rule;143
+ let size = rule.size.map(|(w, h)| (f64::from(w), f64::from(h)));144
+ let shaped = rule.at.is_some() || rule.min_size.is_some() || rule.max_size.is_some();145
+ if size.is_some() {146
+ s.natural_size = false;147
+ s.natural_after = None;148
+ } else if !shaped || s.natural_size || fullscreen {149
+ return;150
+ }151
+ let Some((w, h)) = size.or(held) else {152
+ return;153
+ };154
+ if let Some(rect) = self.float_rect_for(id, w, h) {155
+ self.wm.layout.set_float_rect(id, rect);156
+ }157
+ }158
+159
+ /// Where a floating window of this size goes. A rule can pick the spot,160
+ /// otherwise it is centred over its parent, or else its output, and kept161
+ /// clear of the bars.162
+ fn float_rect_for(&self, id: WindowId, w: f64, h: f64) -> Option<Rect> {163
+ let rule = self.wm.get(id).map(|s| &s.rule);164
+ let (w, h) = rule.map_or((w, h), |r| r.clamp_size(w, h));165
+ let parent = self166
+ .wm167
+ .layout168
+ .window(id)169
+ .and_then(|win| win.parent)170
+ .and_then(|p| self.wm.get(p)?.placed)171
+ .map(|(_, r, _)| r);172
+ let output = self173
+ .wm174
+ .layout175
+ .window(id)176
+ .and_then(|win| win.output)177
+ .and_then(|o| self.wm.layout.output(o))?;178
+ let usable = output.usable();179
+ let at = rule.and_then(|r| r.at);180
+ let around = if at.is_some() {181
+ usable182
+ } else {183
+ parent.unwrap_or(usable)184
+ };185
+ let (x, y) = match at {186
+ Some(At::Position(x, y)) => {187
+ return Some(Rect::new(188
+ output.area.x + f64::from(x),189
+ output.area.y + f64::from(y),190
+ w,191
+ h,192
+ ));193
+ }194
+ _ => (195
+ around.x + (around.w - w) / 2.0,196
+ around.y + (around.h - h) / 2.0,197
+ ),198
+ };199
+ Some(clamp_float(Rect::new(x, y, w, h), usable))200
+ }201
+202
+ /// Sizes a window to what its client picked. One already floating glides203
+ /// there from where it is drawn.204
+ pub(crate) fn float_at_natural_size(&mut self, id: WindowId, w: f64, h: f64) {205
+ if let Some(rect) = self.float_rect_for(id, w, h) {206
+ self.wm.layout.set_float_rect(id, rect);207
+ if let Some((Kind::Floating, from, o)) = self.wm.get(id).and_then(|s| s.placed) {208
+ let now = now_ns();209
+ let from = self.wm.anim.gliding(id, now).unwrap_or(from);210
+ self.wm.anim.glide(id, from, rect, now);211
+ if let Some(s) = self.wm.get_mut(id) {212
+ s.placed = Some((Kind::Floating, rect, o));213
+ }214
+ }215
+ }216
+ }217
+}@@ -1,18 +1,14 @@
1
-use crate::anim::Ghost;21
use crate::state::{State, now_ns};3
-use crate::wm::{Kind, LastFrame, identity, root};4
-use monolith_config::At;5
-use monolith_layout::{OutputId, Rect, TagId, WindowId, clamp_float};6
-use monolith_render_vk::VulkanTexture;7
-use smithay::backend::renderer::Renderer;8
-use smithay::backend::renderer::utils::{import_surface_tree, with_renderer_surface_state};2
+use crate::wm::{identity, root};3
+use monolith_layout::{OutputId, Rect, TagId, WindowId};4
+use smithay::backend::renderer::utils::with_renderer_surface_state;95
use smithay::desktop::Window;106
use smithay::reexports::calloop::timer::{TimeoutAction, Timer};117
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState;128
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;139
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;1410
use smithay::utils::{Logical, Point, Rectangle, Serial};15
-use smithay::wayland::compositor::{BufferAssignment, SurfaceAttributes, with_states};11
+use smithay::wayland::compositor::with_states;1612
use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;1713
use std::time::Duration;1814
@@ -239,27 +235,6 @@ impl State {
239235
self.apply_layout();240236
}241237
242
- /// Called before a commit applies. A null-buffer commit makes smithay243
- /// drop the surface's textures before `on_buffer` runs, so the frame a244
- /// hiding window leaves behind has to be taken here.245
- pub fn keep_frame_for_hide(&mut self, surface: &WlSurface) {246
- let Some(id) = self.window_id(surface) else {247
- return;248
- };249
- let removes = with_states(surface, |s| {250
- let mut attrs = s.cached_state.get::<SurfaceAttributes>();251
- matches!(attrs.pending().buffer, Some(BufferAssignment::Removed))252
- });253
- let Some(s) = self.wm.get(id).filter(|s| removes && s.drawn) else {254
- return;255
- };256
- let window = s.window.clone();257
- let frame = self.last_frame(&window);258
- if let Some(s) = self.wm.get_mut(id) {259
- s.hide_frame = frame;260
- }261
- }262
-263238
fn first_buffer(&mut self, id: WindowId) {264239
let Some(w) = self.wm.get_mut(id) else {265240
return;@@ -324,211 +299,6 @@ impl State {
324299
true325300
}326301
327
- /// Gives floats that landed on the layout's default rect their own size,328
- /// as when a tiled window is toggled. The client is told to pick one, and329
- /// its next commit after acking says which.330
- pub fn size_new_floats(&mut self) {331
- let stale: Vec<WindowId> = self332
- .wm333
- .iter()334
- .filter(|(id, s)| {335
- s.natural_size && !self.wm.layout.window(*id).is_some_and(|w| w.floating)336
- })337
- .map(|(id, _)| id)338
- .collect();339
- for id in stale {340
- if let Some(s) = self.wm.get_mut(id) {341
- s.natural_size = false;342
- s.natural_after = None;343
- }344
- }345
- let defaults: Vec<WindowId> = self346
- .wm347
- .layout348
- .windows()349
- .filter(|(_, w)| w.floating && w.float_default && w.scratchpad.is_none() && !w.hidden)350
- .map(|(id, _)| id)351
- .collect();352
- for id in defaults {353
- self.ask_natural_size(id);354
- }355
- }356
-357
- fn ask_natural_size(&mut self, id: WindowId) {358
- let Some(window) = self359
- .wm360
- .get(id)361
- .filter(|s| !s.natural_size)362
- .map(|s| s.window.clone())363
- else {364
- return;365
- };366
- let geometry = window.geometry().size;367
- let current = (geometry.w > 0 && geometry.h > 0).then_some(geometry);368
- let fixed = window.toplevel().and_then(crate::xdg::fixed_size);369
- let size = match (window.x11_surface(), window.toplevel()) {370
- (Some(x), _) => crate::xwayland::fixed_size(x),371
- (_, Some(_)) if fixed.is_some() => fixed,372
- (_, Some(t)) => {373
- let held = self.visual_rect(id);374
- let serial = crate::wm::configure_free(t);375
- if let Some(w) = self.wm.get_mut(id)376
- && let Some(serial) = serial377
- {378
- w.natural_size = true;379
- w.natural_after = Some(serial);380
- if let Some(rect) = held {381
- self.wm.layout.hold_float_rect(id, rect);382
- }383
- self.await_natural_size(id, serial);384
- return;385
- }386
- current387
- }388
- _ => None,389
- };390
- match size {391
- Some(size) => self.float_at_natural_size(id, f64::from(size.w), f64::from(size.h)),392
- None => {393
- let rect = self.wm.layout.window(id).and_then(|w| w.float_rect);394
- if let Some(rect) = rect {395
- self.wm.layout.set_float_rect(id, rect);396
- }397
- }398
- }399
- }400
-401
- /// Where a placed window is drawn right now.402
- fn visual_rect(&self, id: WindowId) -> Option<Rect> {403
- let now = now_ns();404
- let (kind, rect, o) = self.wm.get(id)?.placed?;405
- self.wm406
- .anim407
- .gliding(id, now)408
- .or_else(|| {409
- (kind == Kind::Tiled)410
- .then(|| self.wm.anim.tiled(o, id, now))411
- .flatten()412
- })413
- .or(Some(rect))414
- }415
-416
- /// A client that never answers a request for its size takes whatever417
- /// size it has.418
- fn await_natural_size(&mut self, id: WindowId, serial: Serial) {419
- let timeout = Timer::from_duration(Duration::from_millis(300));420
- let _ = self.core.handle.insert_source(timeout, move |_, _, state| {421
- state.give_up_natural_size(id, serial);422
- TimeoutAction::Drop423
- });424
- }425
-426
- fn give_up_natural_size(&mut self, id: WindowId, serial: Serial) {427
- let Some(w) = self.wm.get_mut(id) else {428
- return;429
- };430
- if !w.natural_size || w.natural_after != Some(serial) {431
- return;432
- }433
- w.natural_size = false;434
- w.natural_after = None;435
- let size = w.window.geometry().size;436
- if size.w > 0 && size.h > 0 {437
- self.float_at_natural_size(id, f64::from(size.w), f64::from(size.h));438
- } else if let Some(rect) = self.wm.layout.window(id).and_then(|w| w.float_rect) {439
- self.wm.layout.set_float_rect(id, rect);440
- }441
- self.apply_layout();442
- }443
-444
- /// Gives a floating window the size and spot its rules ask for, keeping445
- /// whichever of the two they leave out. One still waiting for its client446
- /// to pick a size gets its spot when the size arrives, unless the rule447
- /// has a size to give it right away.448
- pub(crate) fn float_by_rule(&mut self, id: WindowId) {449
- let Some(win) = self.wm.layout.window(id).filter(|win| win.floating) else {450
- return;451
- };452
- let (held, fullscreen) = (win.float_rect.map(|r| (r.w, r.h)), win.fullscreen);453
- let Some(s) = self.wm.get_mut(id) else {454
- return;455
- };456
- let rule = &s.rule;457
- let size = rule.size.map(|(w, h)| (f64::from(w), f64::from(h)));458
- let shaped = rule.at.is_some() || rule.min_size.is_some() || rule.max_size.is_some();459
- if size.is_some() {460
- s.natural_size = false;461
- s.natural_after = None;462
- } else if !shaped || s.natural_size || fullscreen {463
- return;464
- }465
- let Some((w, h)) = size.or(held) else {466
- return;467
- };468
- if let Some(rect) = self.float_rect_for(id, w, h) {469
- self.wm.layout.set_float_rect(id, rect);470
- }471
- }472
-473
- /// Where a floating window of this size goes. A rule can pick the spot,474
- /// otherwise it is centred over its parent, or else its output, and kept475
- /// clear of the bars.476
- fn float_rect_for(&self, id: WindowId, w: f64, h: f64) -> Option<Rect> {477
- let rule = self.wm.get(id).map(|s| &s.rule);478
- let (w, h) = rule.map_or((w, h), |r| r.clamp_size(w, h));479
- let parent = self480
- .wm481
- .layout482
- .window(id)483
- .and_then(|win| win.parent)484
- .and_then(|p| self.wm.get(p)?.placed)485
- .map(|(_, r, _)| r);486
- let output = self487
- .wm488
- .layout489
- .window(id)490
- .and_then(|win| win.output)491
- .and_then(|o| self.wm.layout.output(o))?;492
- let usable = output.usable();493
- let at = rule.and_then(|r| r.at);494
- let around = if at.is_some() {495
- usable496
- } else {497
- parent.unwrap_or(usable)498
- };499
- let (x, y) = match at {500
- Some(At::Position(x, y)) => {501
- return Some(Rect::new(502
- output.area.x + f64::from(x),503
- output.area.y + f64::from(y),504
- w,505
- h,506
- ));507
- }508
- _ => (509
- around.x + (around.w - w) / 2.0,510
- around.y + (around.h - h) / 2.0,511
- ),512
- };513
- Some(clamp_float(Rect::new(x, y, w, h), usable))514
- }515
-516
- /// Sizes a window to what its client picked. One already floating glides517
- /// there from where it is drawn.518
- fn float_at_natural_size(&mut self, id: WindowId, w: f64, h: f64) {519
- if let Some(rect) = self.float_rect_for(id, w, h) {520
- self.wm.layout.set_float_rect(id, rect);521
- if let Some((Kind::Floating, from, o)) = self.wm.get(id).and_then(|s| s.placed) {522
- let now = now_ns();523
- let from = self.wm.anim.gliding(id, now).unwrap_or(from);524
- self.wm.anim.glide(id, from, rect, now);525
- if let Some(s) = self.wm.get_mut(id) {526
- s.placed = Some((Kind::Floating, rect, o));527
- }528
- }529
- }530
- }531
-532302
pub fn unmap_window(&mut self, surface: &WlSurface) {533303
self.wm534304
.unmapped@@ -550,89 +320,6 @@ impl State {
550320
state.map(|s| s.window)551321
}552322
553
- /// Keeps a visible tiled window's last frame shrinking to its seed while554
- /// its neighbours close over it.555
- fn leave_ghost(556
- &mut self,557
- id: WindowId,558
- window: &Window,559
- saved: Option<LastFrame>,560
- opacity: f32,561
- ) {562
- let Some(o) = self.wm.layout.window(id).and_then(|w| w.output) else {563
- return;564
- };565
- let tiled = self566
- .wm567
- .layout568
- .output(o)569
- .is_some_and(|out| out.tree.contains(id));570
- if !tiled || !self.wm.layout.visible(id) {571
- return;572
- }573
- let Some(seed) = self574
- .wm575
- .layout576
- .arrange_hiding(o, &[id])577
- .tiled578
- .into_iter()579
- .find(|p| p.window == id)580
- else {581
- return;582
- };583
- if let Some(frame) = saved.or_else(|| self.last_frame(window)) {584
- self.wm.last_frames.insert(id, frame);585
- self.wm.anim.add_ghost(Ghost {586
- id,587
- output: o,588
- seed: seed.rect,589
- born: now_ns(),590
- fixed: None,591
- opacity,592
- });593
- }594
- }595
-596
- /// Fades a window that just left the screen where it was, like a597
- /// dismissed scratchpad.598
- pub fn fade_out(&mut self, id: WindowId, rect: Rect, output: OutputId) {599
- let Some(window) = self.wm.window(id).cloned() else {600
- return;601
- };602
- let opacity = self.wm.get(id).and_then(|s| s.rule.opacity).unwrap_or(1.0);603
- if let Some(frame) = self.last_frame(&window) {604
- self.wm.last_frames.insert(id, frame);605
- self.wm.anim.add_ghost(Ghost {606
- id,607
- output,608
- seed: rect,609
- born: now_ns(),610
- fixed: Some(rect),611
- opacity,612
- });613
- }614
- }615
-616
- fn last_frame(&mut self, window: &Window) -> Option<LastFrame> {617
- let surface = root(window)?;618
- let _ = import_surface_tree(&mut self.renderer, &surface);619
- let context = self.renderer.context_id();620
- // The geometry lookup locks the surface's state, which the closure621
- // below already holds.622
- let geometry = window.geometry();623
- with_renderer_surface_state(&surface, |s| {624
- let texture = s.texture::<VulkanTexture>(context.clone())?.clone();625
- Some(LastFrame {626
- texture,627
- scale: s.buffer_scale(),628
- transform: s.buffer_transform(),629
- geometry,630
- view: s.view()?,631
- })632
- })633
- .flatten()634
- }635
-636323
/// Picks up a changed title or app id, which clients send at any time.637324
pub fn refresh_identity(&mut self, id: WindowId) {638325
let Some(w) = self.wm.get_mut(id) else {