file manager
git clone https://git.lucas.co/cce-files.git
The preview texture belongs to a renderer, not to the process
An image id names an entry in one renderer's image table, and a
renderer does not outlive its session: cce-ui's window_runner repairs a
lost Wayland transport by opening a new session around the same
Application, which rebuilds the VkRenderer and with it the image table.
A draw for an id that table does not hold is skipped rather than
reported.
PreviewPane holds exactly such an id, and it is only replaced when the
selection changes — so a reconnected window kept the file's name, size,
permissions and modified date beside an empty preview well, until the
user clicked a different file. Reproduced in a shadow session with
CCE_UI_FAULT_RECONNECT: with a PNG selected the well shows the image
before the injected drop and is blank after it.
The decoded pixels are not kept here (they are moved into the upload),
so the repair is the round trip the selection already makes:
renderer_init drops the dead texture and re-sends
FsRequest::ReadPreview for the shown path, and PreviewLoaded uploads it
into the live renderer. Only when there was a texture to lose — a text
or directory preview needs nothing. Same shadow run with the fix: the
image is still there after the drop.
Not on the first renderer: nothing has been uploaded yet, and the
initial directory load is already in flight. window_runner documents
the contract above `run`; cce-grid and cce-status-interface (73ab926)
are the same shape.
src/main.rs | 40 ++++++++++++++++++++++++++++++++++++++++
src/preview_pane.rs | 16 ++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/src/main.rs b/src/main.rs
index 9eb671c..cc8ed10 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -431,6 +431,10 @@ struct FilesystemApp {
ui_context: cce_ui::context::UiContext,
watcher: Option<notify::RecommendedWatcher>,
fs_service: services::fs::FsService,
+ /// Whether a renderer has been handed over yet — the first one is the
+ /// process's own, any later one is a replacement after a reconnect. See
+ /// `renderer_init`.
+ seen_renderer: bool,
context_menu: ContextMenu,
open_with_dialog: Option<(std::path::PathBuf, cce_ui::widget::Adapted<cce_ui::widget::TextBox>)>,
browse_split: SplitPane,
@@ -1295,6 +1299,7 @@ impl Application for FilesystemApp {
ui_context: cce_ui::context::UiContext::new(),
watcher: None,
fs_service,
+ seen_renderer: false,
context_menu: ContextMenu {
visible: false,
x: 0.0,
@@ -1578,6 +1583,41 @@ impl Application for FilesystemApp {
}
}
+ /// Re-request the shown file's preview when the renderer is replaced.
+ ///
+ /// `PreviewPane` holds a **renderer** image id, and a renderer does not
+ /// outlive its session: `cce-ui`'s `window_runner` repairs a lost Wayland
+ /// transport by opening a new session around the same `Application`, which
+ /// rebuilds the renderer and with it the image table. The cached id then
+ /// names an image that no longer exists, and a draw for an unknown id is
+ /// skipped rather than reported — so a reconnected window kept the file's
+ /// name, size and permissions and showed an empty well where the picture
+ /// was, until the user selected a different file.
+ ///
+ /// The pixels are not kept here (they are moved into the upload), so the
+ /// repair is the same round trip the selection makes: drop the dead
+ /// texture, ask `FsService` for the preview again, and let
+ /// `PreviewMessage::PreviewLoaded` upload it into the live renderer.
+ ///
+ /// Not on the first renderer: nothing has been uploaded yet, and the
+ /// initial directory load is already in flight.
+ fn renderer_init(&mut self, _renderer: &mut cce_ui::vk::VkRenderer) {
+ if !std::mem::replace(&mut self.seen_renderer, true) {
+ return;
+ }
+ let had_texture = self.preview.drop_texture();
+ if let Some(path) = self.preview.path.clone() {
+ if had_texture {
+ log::info!(
+ "[preview] renderer replaced; re-reading the preview of {}",
+ path.display()
+ );
+ self.fs_service.send(services::fs::FsRequest::ReadPreview(path));
+ }
+ }
+ self.needs_rebuild = true;
+ }
+
fn display_list(&mut self, size: LogicalSize, scale: f64) -> Option<cce_ui::scene::paint::DisplayList> {
// Phase 6 single paint path: the whole frame — geometry and text — is this one list.
// rebuild_layout flattens every source (browse/network page, popovers, context menu,
diff --git a/src/preview_pane.rs b/src/preview_pane.rs
index 22f29d7..678eb24 100644
--- a/src/preview_pane.rs
+++ b/src/preview_pane.rs
@@ -126,6 +126,22 @@ impl PreviewPane {
}
}
+ /// Forget the uploaded texture, freeing it, and say whether there was one.
+ ///
+ /// For the renderer-replaced path only (see `FilesystemApp::renderer_init`):
+ /// the id belongs to a renderer that no longer exists, so this is a drop
+ /// rather than a clear — everything else about the shown file stays, and
+ /// the caller re-requests the preview to get a live texture back.
+ pub fn drop_texture(&mut self) -> bool {
+ match self.image_tex.take() {
+ Some((id, _, _)) => {
+ cce_ui::vk::free_image(id);
+ true
+ }
+ None => false,
+ }
+ }
+
pub fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
self.rect = (x, y, w, h);
}