Skip to main content

libsurfer/
system_state.rs

1use ahash::AHashMap;
2use egui::{Pos2, Rect};
3use eyre::Result;
4use num::BigInt;
5use std::{
6    cell::RefCell,
7    collections::VecDeque,
8    rc::Rc,
9    sync::{Arc, atomic::AtomicBool},
10};
11use surfer_translation_types::translator::VariableNameInfo;
12use tokio::task::JoinHandle;
13
14use crate::{
15    CachedDrawData, CanvasState, Channels, WcpClientCapabilities, command_prompt,
16    displayed_item::DisplayedItemRef,
17    file_history::FileHistory,
18    frame_buffer::{FrameBufferArrayCache, FrameBufferContent, FrameBufferPixelCache},
19    hierarchy::{AllVariableCacheKey, ScopeExpandType, VariableListRow},
20    memory_viewer::{MemoryViewerCache, MemoryViewerState},
21    message::Message,
22    mousegestures::AnnotationKind,
23    state::UserState,
24    time::TimeInputState,
25    translation::{TranslatorList, all_translators},
26    wave_container::VariableRef,
27    wave_source::{LoadOptions, LoadProgress},
28};
29
30type VariableInfoCache = AHashMap<VariableRef, Option<VariableNameInfo>>;
31type FlattenedRowsCache = AHashMap<DisplayedItemRef, (u64, Rc<Vec<crate::view::VariableFieldRow>>)>;
32
33#[cfg(feature = "performance_plot")]
34use crate::benchmark::Timing;
35pub struct SystemState {
36    pub user: UserState,
37    pub(crate) file_history: FileHistory,
38    /// Which translator to use for each variable
39    pub(crate) translators: TranslatorList,
40    /// Channels for messages generated by other threads
41    pub channels: Channels,
42
43    /// Tracks progress of file/variable loading operations.
44    pub(crate) progress_tracker: Option<LoadProgress>,
45
46    /// Buffer for the command input
47    pub(crate) command_prompt: command_prompt::CommandPrompt,
48
49    /// The context to egui, we need this to change the visual settings when the config is reloaded
50    pub(crate) context: Option<Arc<egui::Context>>,
51
52    /// List of batch messages which will executed as soon as possible
53    pub(crate) batch_messages: VecDeque<Message>,
54    pub(crate) batch_messages_completed: bool,
55
56    /// The WCP server
57    #[allow(unused)]
58    pub(crate) wcp_server_thread: Option<JoinHandle<()>>,
59    #[allow(unused)]
60    pub(crate) wcp_server_address: Option<String>,
61    #[allow(unused)]
62    pub(crate) wcp_stop_signal: Arc<AtomicBool>,
63    #[allow(unused)]
64    pub(crate) wcp_running_signal: Arc<AtomicBool>,
65    pub(crate) wcp_greeted_signal: Arc<AtomicBool>,
66    pub(crate) wcp_client_capabilities: WcpClientCapabilities,
67
68    /// The draw commands for every variable currently selected
69    // For performance reasons, these need caching so we have them in a RefCell for interior
70    // mutability
71    pub(crate) draw_data: RefCell<Vec<Option<CachedDrawData>>>,
72
73    pub(crate) variable_name_info_cache: RefCell<VariableInfoCache>,
74
75    /// Cached flattened compound-field rows per variable, keyed by a signature of that
76    /// variable's fold state (`DisplayedVariable::unfolded_fields`); avoids re-walking the
77    /// (possibly large) field tree every frame when nothing changed.
78    pub(crate) flattened_rows_cache: RefCell<FlattenedRowsCache>,
79
80    /// Monotonically increasing counter incremented when translators reload, to invalidate
81    /// the `all_variable_rows_cache` when name info changes without a waveform reload.
82    pub(crate) translator_generation: u64,
83    /// Cached result of `build_variable_rows` for `draw_all_variables`; rebuilt only when the
84    /// key changes (filter settings, wave data, or translator state).
85    pub(crate) all_variable_rows_cache: Option<(AllVariableCacheKey, Rc<Vec<VariableListRow>>)>,
86
87    pub(crate) gesture_start_location: Option<Pos2>,
88    pub(crate) gesture_start_time: Option<BigInt>,
89
90    pub(crate) measure_start_location: Option<Pos2>,
91
92    pub(crate) annotation_kind: Option<AnnotationKind>,
93
94    // Egui requires a place to store text field content between frames
95    pub(crate) url: RefCell<String>,
96    pub(crate) command_prompt_text: RefCell<String>,
97    pub(crate) last_canvas_rect: RefCell<Option<Rect>>,
98    pub(crate) surver_selected_file: RefCell<Option<usize>>,
99    pub(crate) surver_load_options: RefCell<LoadOptions>,
100
101    /// Character to add to the command prompt if it is visible.
102    ///
103    /// This is only needed for presentations at them moment.
104    pub(crate) char_to_add_to_prompt: RefCell<Option<char>>,
105    /// This item works with the expand scope feature to determine what hierarchys to open.
106    pub scope_ref_to_expand: RefCell<Option<ScopeExpandType>>,
107
108    pub(crate) time_widgets: RefCell<AHashMap<String, TimeInputState>>,
109    /// Map of widget id -> focused state.
110    pub(crate) text_edit_focused: AHashMap<String, bool>,
111    /// Map of widget id -> one-shot request focus flag.
112    pub(crate) text_edit_request_focus: AHashMap<String, bool>,
113    pub(crate) frame_buffer_content: Option<FrameBufferContent>,
114    pub(crate) frame_buffer_array_cache: Option<FrameBufferArrayCache>,
115    pub(crate) frame_buffer_pixel_cache: Option<FrameBufferPixelCache>,
116    pub(crate) memory_viewer: MemoryViewerState,
117    pub(crate) memory_viewer_cache: Option<MemoryViewerCache>,
118    // Benchmarking stuff
119    /// Invalidate draw commands every frame to make performance comparison easier
120    pub(crate) continuous_redraw: bool,
121    #[cfg(feature = "performance_plot")]
122    pub(crate) rendering_cpu_times: VecDeque<f32>,
123    #[cfg(feature = "performance_plot")]
124    pub(crate) timing: RefCell<Timing>,
125
126    // Undo and Redo stacks
127    pub(crate) undo_stack: Vec<CanvasState>,
128    pub(crate) redo_stack: Vec<CanvasState>,
129
130    // Toolbar group drag state
131    pub(crate) toolbar_dragging_group: Option<String>,
132    pub(crate) toolbar_drop_row: Option<usize>,
133    pub(crate) toolbar_drop_index: Option<usize>,
134    pub(crate) toolbar_drop_new_row: bool,
135
136    pub(crate) url_callback: Option<Box<dyn Fn(String) -> Message + Send + 'static>>,
137
138    // Only used for testing
139    pub(crate) expand_parameter_section: bool,
140
141    pub(crate) annotation_id_source: u64,
142    pub(crate) click_handled: bool,
143}
144
145impl SystemState {
146    pub fn new() -> Result<SystemState> {
147        Self::new_inner(false)
148    }
149
150    #[cfg(test)]
151    pub(crate) fn new_default_config() -> Result<SystemState> {
152        Self::new_inner(true)
153    }
154
155    fn new_inner(force_default_config: bool) -> Result<SystemState> {
156        let channels = Channels::new();
157        let user = UserState::new(force_default_config)?;
158        let file_history = FileHistory::load(user.config.behavior.file_history_size());
159
160        // Basic translators that we can load quickly
161        let translators = all_translators();
162
163        let result = SystemState {
164            user,
165            file_history,
166            translators,
167            channels,
168            progress_tracker: None,
169            command_prompt: Default::default(),
170            context: None,
171            wcp_server_thread: None,
172            wcp_server_address: None,
173            wcp_stop_signal: Arc::new(AtomicBool::new(false)),
174            wcp_running_signal: Arc::new(AtomicBool::new(false)),
175            wcp_greeted_signal: Arc::new(AtomicBool::new(false)),
176            wcp_client_capabilities: WcpClientCapabilities::new(),
177            gesture_start_location: None,
178            gesture_start_time: None,
179
180            measure_start_location: None,
181            batch_messages: VecDeque::new(),
182            batch_messages_completed: false,
183            url: RefCell::new(String::new()),
184            command_prompt_text: RefCell::new(String::new()),
185            draw_data: RefCell::new(vec![None]),
186            variable_name_info_cache: RefCell::new(AHashMap::new()),
187            flattened_rows_cache: RefCell::new(AHashMap::new()),
188            translator_generation: 0,
189            all_variable_rows_cache: None,
190            last_canvas_rect: RefCell::new(None),
191
192            char_to_add_to_prompt: RefCell::new(None),
193            scope_ref_to_expand: RefCell::new(None),
194            surver_selected_file: RefCell::new(None),
195            surver_load_options: RefCell::new(LoadOptions::Clear),
196            expand_parameter_section: false,
197            time_widgets: RefCell::new(AHashMap::new()),
198            text_edit_focused: AHashMap::new(),
199            text_edit_request_focus: AHashMap::new(),
200            frame_buffer_content: None,
201            frame_buffer_array_cache: None,
202            frame_buffer_pixel_cache: None,
203            memory_viewer: MemoryViewerState::default(),
204            memory_viewer_cache: None,
205            url_callback: None,
206            continuous_redraw: false,
207            #[cfg(feature = "performance_plot")]
208            rendering_cpu_times: VecDeque::new(),
209            #[cfg(feature = "performance_plot")]
210            timing: RefCell::new(Timing::new()),
211            undo_stack: vec![],
212            redo_stack: vec![],
213            annotation_kind: None,
214            annotation_id_source: 0,
215            click_handled: false,
216            toolbar_dragging_group: None,
217            toolbar_drop_row: None,
218            toolbar_drop_index: None,
219            toolbar_drop_new_row: false,
220        };
221
222        Ok(result)
223    }
224}
225
226impl From<UserState> for SystemState {
227    fn from(serializable_state: UserState) -> SystemState {
228        let mut state = SystemState::new().unwrap();
229        state.user = serializable_state;
230        state
231    }
232}