From 22dafe5c8b5526791b117a87eb9f871c28f06d3a Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Mon, 17 Aug 2026 21:34:10 +0200 Subject: [PATCH] viewer: fix a startup panic in the Save File browser (Bevy B0002) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit draw_save_ui held both an EventReader (to know when the View menu opened it) and an EventWriter (so its own "Open savedata…" button could re-trigger the file dialog). Bevy rejects a system that accesses one event type both ways, and does so at schedule-validation time -- so the app panicked on startup, before any window content. The button now sets `SaveBrowser::request_open` and handle_save_open_request treats that flag as equivalent to the event, with no path (dialog). Audited every system in the viewer for the same shape; this was the only one. Verified by running the binary to steady state rather than by compiling alone, since a param conflict is invisible to the type checker. Co-Authored-By: Claude Opus 5 (1M context) --- crates/sylpheed-viewer/src/iso_loader.rs | 15 +++++++++++++-- crates/sylpheed-viewer/src/ui.rs | 5 +++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/sylpheed-viewer/src/iso_loader.rs b/crates/sylpheed-viewer/src/iso_loader.rs index d6463ae6..b420e287 100644 --- a/crates/sylpheed-viewer/src/iso_loader.rs +++ b/crates/sylpheed-viewer/src/iso_loader.rs @@ -786,6 +786,11 @@ pub struct SaveBrowser { pub result: Option>, /// Show the fields that are still unidentified. pub show_unknown: bool, + /// Set by the window's own "Open savedata…" button. It cannot send + /// [`RequestSaveOpen`] itself: a system may not hold an `EventWriter` and an + /// `EventReader` for the same event type, and `draw_save_ui` already reads + /// this one (Bevy B0002). + pub request_open: bool, } /// Ask the loader to open + parse a `savedata` file. `None` = show a file @@ -4267,7 +4272,14 @@ fn handle_save_open_request( channels: Res, mut saves: ResMut, ) { - let Some(req) = events.read().last() else { + // Either the View-menu event, or the in-window button's flag. + let want = match events.read().last() { + Some(req) => Some(req.path.clone()), + None if saves.request_open => Some(None), + None => None, + }; + saves.request_open = false; + let Some(want) = want else { return; }; if saves.loading { @@ -4275,7 +4287,6 @@ fn handle_save_open_request( } saves.loading = true; saves.open = true; - let want = req.path.clone(); let sender = channels.sender.clone(); std::thread::spawn(move || { let path = match want { diff --git a/crates/sylpheed-viewer/src/ui.rs b/crates/sylpheed-viewer/src/ui.rs index d1045c2c..d31a4e1b 100644 --- a/crates/sylpheed-viewer/src/ui.rs +++ b/crates/sylpheed-viewer/src/ui.rs @@ -2122,7 +2122,6 @@ fn draw_save_ui( mut contexts: EguiContexts, mut saves: ResMut, mut requests: EventReader, - mut open_save: EventWriter, ) { if requests.read().next().is_some() { saves.open = true; @@ -2289,8 +2288,10 @@ fn draw_save_ui( }); }); + // A flag, not a self-send: holding both an EventReader and an EventWriter + // for RequestSaveOpen in one system is a Bevy B0002 panic at startup. if reopen { - open_save.send(RequestSaveOpen::default()); + saves.request_open = true; } saves.open &= open; }