1use std::{
2 collections::{HashMap, HashSet, VecDeque},
3 mem,
4};
5
6use camino::Utf8PathBuf;
7
8use crate::{
9 CanvasState, StartupParams,
10 annotation_list::{AnnotationGroup, DEFAULT_GROUP_NAME},
11 clock_highlighting::ClockHighlightType,
12 config::{
13 ArrowKeyBindings, AutoLoad, FocusHighlight, PrimaryMouseDrag, SurferConfig, TransitionValue,
14 },
15 data_container::DataContainer,
16 dialog::{OpenSiblingStateFileDialog, ReloadWaveformDialog},
17 displayed_item_tree::{DisplayedItemTree, VisibleItemIndex},
18 frame_buffer::FrameBufferSettings,
19 hierarchy::{HierarchyStyle, ParameterDisplayLocation},
20 message::Message,
21 system_state::SystemState,
22 time::{TimeStringFormatting, TimeUnit},
23 trace_style::TraceStyle,
24 transaction_container::TransactionContainer,
25 variable_filter::VariableFilter,
26 viewport::Viewport,
27 wave_container::{ScopeRef, VariableRef, WaveContainer},
28 wave_data::{TimeRange, WaveData},
29 wave_source::{LoadOptions, WaveFormat, WaveSource},
30};
31use egui::{
32 Visuals,
33 style::{Selection, WidgetVisuals, Widgets},
34};
35use epaint::{CornerRadius, Stroke};
36use eyre::{Result, WrapErr as _};
37use itertools::Itertools;
38use serde::{Deserialize, Serialize};
39use surfer_translation_types::Translator;
40use surver::SurverFileInfo;
41use tracing::{error, info, trace, warn};
42
43#[derive(Serialize, Deserialize)]
45#[serde(default)]
46pub struct UserState {
47 #[serde(skip)]
48 pub config: SurferConfig,
49
50 pub(crate) show_hierarchy: Option<bool>,
54 pub(crate) show_menu: Option<bool>,
55 pub(crate) show_ticks: Option<bool>,
56 pub(crate) show_toolbar: Option<bool>,
57 pub(crate) show_tooltip: Option<bool>,
58 pub(crate) show_scope_tooltip: Option<bool>,
59 pub(crate) show_default_timeline: Option<bool>,
60 pub(crate) show_overview: Option<bool>,
61 pub(crate) show_statusbar: Option<bool>,
62 pub(crate) align_names_right: Option<bool>,
63 pub(crate) show_variable_indices: Option<bool>,
64 pub(crate) show_variable_direction: Option<bool>,
65 pub(crate) show_empty_scopes: Option<bool>,
66 pub(crate) show_hierarchy_icons: Option<bool>,
67 pub(crate) show_parameters_in_scopes: Option<bool>,
68 #[serde(default)]
69 pub(crate) parameter_display_location: Option<ParameterDisplayLocation>,
70 #[serde(default)]
71 pub(crate) fill_high_values: Option<bool>,
72 #[serde(default)]
73 pub(crate) primary_button_drag_behavior: Option<PrimaryMouseDrag>,
74 #[serde(default)]
75 pub(crate) arrow_key_bindings: Option<ArrowKeyBindings>,
76 #[serde(default)]
77 pub(crate) clock_highlight_type: Option<ClockHighlightType>,
78 #[serde(default)]
79 pub(crate) hierarchy_style: Option<HierarchyStyle>,
80 #[serde(default)]
81 pub(crate) autoload_sibling_state_files: Option<AutoLoad>,
82 #[serde(default)]
83 pub(crate) autoreload_files: Option<AutoLoad>,
84
85 pub(crate) waves: Option<WaveData>,
86 pub(crate) drag_started: bool,
87 pub(crate) drag_source_idx: Option<VisibleItemIndex>,
88 pub(crate) drag_target_idx: Option<crate::displayed_item_tree::TargetPosition>,
89
90 pub(crate) previous_waves: Option<WaveData>,
91
92 pub(crate) count: Option<String>,
94
95 pub(crate) blacklisted_translators: HashSet<(VariableRef, String)>,
97
98 pub(crate) show_about: bool,
99 pub(crate) show_keys: bool,
100 pub(crate) show_gestures: bool,
101 pub(crate) show_quick_start: bool,
102 pub(crate) show_license: bool,
103 pub(crate) show_performance: bool,
104 pub(crate) show_logs: bool,
105 pub(crate) show_cursor_window: bool,
106 #[serde(default)]
107 pub(crate) frame_buffer: FrameBufferSettings,
108 pub(crate) wanted_timeunit: TimeUnit,
109 pub(crate) time_string_format: Option<TimeStringFormatting>,
110 pub(crate) show_url_entry: bool,
111 #[serde(skip, default)]
114 pub(crate) show_reload_suggestion: Option<ReloadWaveformDialog>,
115 #[serde(skip, default)]
116 pub(crate) show_open_sibling_state_file_suggestion: Option<OpenSiblingStateFileDialog>,
117 pub(crate) variable_name_filter_focused: bool,
119 pub(crate) variable_filter: VariableFilter,
120 pub(crate) sidepanel_width: Option<f32>,
122 pub(crate) ui_zoom_factor: Option<f32>,
124 #[serde(default)]
125 pub(crate) animation_enabled: Option<bool>,
126 #[serde(skip, default)]
127 pub(crate) use_dinotrace_style: Option<bool>,
128 #[serde(default)]
129 pub(crate) trace_style: Option<TraceStyle>,
130 #[serde(skip, default)]
131 pub(crate) show_server_file_window: bool,
132 #[serde(skip, default)]
133 pub(crate) selected_server_file_index: Option<usize>,
134 #[serde(skip, default)]
135 pub(crate) surver_file_infos: Option<Vec<SurverFileInfo>>,
136 #[serde(skip, default)]
137 pub(crate) surver_url: Option<String>,
138 #[serde(default)]
139 pub(crate) transition_value: Option<TransitionValue>,
140 #[serde(default)]
141 pub(crate) toolbar_group_enabled: HashMap<String, Option<bool>>,
142 #[serde(default)]
143 pub(crate) toolbar_group_rows: Vec<Vec<String>>,
144 #[serde(default)]
145 pub(crate) draw_vector_unknowns_as_line: Option<bool>,
146 #[serde(default)]
147 pub(crate) focus_highlight: Option<FocusHighlight>,
148
149 #[serde(skip)]
155 pub state_file: Option<Utf8PathBuf>,
156
157 pub(crate) show_annotation_list: bool,
158 #[serde(default)]
159 pub(crate) enable_time_offset: Option<bool>,
160}
161
162impl std::fmt::Debug for UserState {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(f, "SharedState {{ <snipped> }}")
167 }
168}
169
170impl UserState {
171 pub fn new(force_default_config: bool) -> Result<UserState> {
172 let config = SurferConfig::new(force_default_config)
173 .with_context(|| "Failed to load config file")?;
174
175 Ok(UserState {
176 config,
177 ..Default::default()
178 })
179 }
180}
181
182impl Default for UserState {
183 fn default() -> Self {
184 Self {
185 config: SurferConfig::default(),
186 show_hierarchy: None,
187 show_menu: None,
188 show_ticks: None,
189 show_toolbar: None,
190 show_tooltip: None,
191 show_scope_tooltip: None,
192 show_default_timeline: None,
193 show_overview: None,
194 show_statusbar: None,
195 align_names_right: None,
196 show_variable_indices: None,
197 show_variable_direction: None,
198 show_empty_scopes: None,
199 show_hierarchy_icons: None,
200 show_parameters_in_scopes: None,
201 parameter_display_location: None,
202 fill_high_values: None,
203 primary_button_drag_behavior: None,
204 arrow_key_bindings: None,
205 clock_highlight_type: None,
206 hierarchy_style: None,
207 autoload_sibling_state_files: None,
208 autoreload_files: None,
209 enable_time_offset: None,
210 waves: None,
211 drag_started: false,
212 drag_source_idx: None,
213 drag_target_idx: None,
214 previous_waves: None,
215 count: None,
216 blacklisted_translators: HashSet::new(),
217 show_about: false,
218 show_keys: false,
219 show_gestures: false,
220 show_quick_start: false,
221 show_license: false,
222 show_performance: false,
223 show_logs: false,
224 show_cursor_window: false,
225 frame_buffer: FrameBufferSettings::default(),
226 wanted_timeunit: TimeUnit::None,
227 time_string_format: None,
228 show_url_entry: false,
229 show_reload_suggestion: None,
230 show_open_sibling_state_file_suggestion: None,
231 variable_name_filter_focused: false,
232 variable_filter: VariableFilter::new(),
233 sidepanel_width: None,
234 ui_zoom_factor: None,
235 state_file: None,
236 animation_enabled: None,
237 use_dinotrace_style: None,
238 trace_style: None,
239 selected_server_file_index: None,
240 show_server_file_window: false,
241 surver_file_infos: None,
242 surver_url: None,
243 transition_value: None,
244 show_annotation_list: false,
245 toolbar_group_enabled: HashMap::new(),
246 toolbar_group_rows: Vec::new(),
247 draw_vector_unknowns_as_line: None,
248 focus_highlight: None,
249 }
250 }
251}
252
253impl SystemState {
254 pub fn with_params(mut self, args: StartupParams) -> Self {
255 self.user.previous_waves = self.user.waves;
256 self.user.waves = None;
257
258 self.batch_messages = VecDeque::new();
260
261 match args.waves {
262 Some(WaveSource::Url(url)) => {
263 self.add_batch_message(Message::LoadWaveformFileFromUrl(url, LoadOptions::KeepAll));
264 }
265 Some(WaveSource::File(file)) => {
266 self.add_batch_message(Message::LoadFile(file, LoadOptions::KeepAll));
267 }
268 Some(WaveSource::Data) => error!("Attempted to load data at startup"),
269 Some(WaveSource::Cxxrtl(url)) => {
270 self.add_batch_message(Message::SetupCxxrtl(url));
271 }
272 Some(WaveSource::DragAndDrop(_)) => {
273 error!("Attempted to load from drag and drop at startup (how?)");
274 }
275 None => {}
276 }
277
278 if let Some(port) = args.wcp_initiate {
279 let addr = format!("127.0.0.1:{port}");
280 self.add_batch_message(Message::StartWcpServer {
281 address: Some(addr),
282 initiate: true,
283 });
284 }
285
286 self.add_batch_commands(args.startup_commands);
287
288 self
289 }
290
291 pub fn wcp(&mut self) {
292 self.handle_wcp_commands();
293 }
294
295 pub(crate) fn get_scope(&mut self, scope: &ScopeRef, recursive: bool) -> Vec<VariableRef> {
296 let Some(waves) = self.user.waves.as_mut() else {
297 return vec![];
298 };
299
300 let wave_cont = waves.inner.as_waves().unwrap();
301
302 let children = wave_cont.child_scopes(scope);
303 let mut variables = wave_cont
304 .variables_in_scope(scope)
305 .iter()
306 .sorted_by(|a, b| numeric_sort::cmp(&a.name, &b.name))
307 .cloned()
308 .collect_vec();
309
310 if recursive && let Ok(children) = children {
311 for child in children {
312 variables.append(&mut self.get_scope(&child, true));
313 }
314 }
315
316 variables
317 }
318
319 pub(crate) fn on_waves_loaded(
320 &mut self,
321 filename: WaveSource,
322 format: WaveFormat,
323 new_waves: Box<WaveContainer>,
324 load_options: LoadOptions,
325 ) {
326 let filename_for_title = filename.clone();
327 info!("{format} file loaded");
328 let viewport = Viewport::new();
329 let viewports = [viewport].to_vec();
330
331 for translator in self.translators.all_translators() {
332 translator.set_wave_source(Some(filename.into_translation_type()));
333 }
334
335 let ((new_wave, load_commands), is_reload) =
336 if load_options != LoadOptions::Clear && self.user.waves.is_some() {
337 (
338 self.user.waves.take().unwrap().update_with_waves(
339 new_waves,
340 filename,
341 format,
342 &self.translators,
343 load_options == LoadOptions::KeepAll,
344 ),
345 true,
346 )
347 } else if let Some(old) = self.user.previous_waves.take() {
348 (
349 old.update_with_waves(
350 new_waves,
351 filename,
352 format,
353 &self.translators,
354 load_options == LoadOptions::KeepAll,
355 ),
356 true,
357 )
358 } else {
359 (
360 (
361 WaveData {
362 inner: DataContainer::Waves(*new_waves),
363 source: filename,
364 format,
365 active_scope: None,
366 items_tree: DisplayedItemTree::default(),
367 displayed_items: HashMap::new(),
368 viewports,
369 cursor: None,
370 markers: HashMap::new(),
371 annotations: Vec::new(),
372 selected_annotation: None,
373 annotation_counter: 0,
374 last_active_viewport_idx: 0,
375 annotation_menu_pos: None,
376 annotation_menu_time: None,
377 focused_item: None,
378 focused_transaction: (None, None),
379 default_variable_name_type: self.user.config.default_variable_name_type,
380 display_variable_indices: self.show_variable_indices(),
381 scroll_offset: 0.,
382 drawing_infos: vec![],
383 drawing_infos_signature: None,
384 total_height: 0.,
385 display_item_ref_counter: 0,
386 old_max_timestamp: None,
387 graphics: HashMap::new(),
388 cache_generation: 0,
389 inflight_caches: HashMap::new(),
390 annotation_groups: vec![],
391 annotation_list_visible: false,
392 cached_time_range: TimeRange::default(),
393 },
394 None,
395 ),
396 false,
397 )
398 };
399
400 if let Some(cmd) = load_commands {
401 self.load_variables(cmd);
402 }
403 self.invalidate_draw_commands();
404
405 let enable_time_offset = self.enable_time_offset();
407 self.user.waves = Some(new_wave);
408
409 if let Some(waves) = &mut self.user.waves {
411 waves.refresh_time_range(enable_time_offset);
412
413 let title = waves.window_title();
415 if let Some(ctx) = self.context.as_ref() {
416 ctx.send_viewport_cmd(egui::ViewportCommand::Title(title.clone()));
417 }
418 #[cfg(target_arch = "wasm32")]
421 if let Some(document) = web_sys::window().and_then(|w| w.document()) {
422 document.set_title(&title);
423 }
424 }
425
426 self.record_file_history(&filename_for_title);
427
428 if !is_reload && let Some(waves) = &mut self.user.waves {
429 self.user.wanted_timeunit = waves.inner.metadata().timescale.unit;
431
432 let ungrouped = AnnotationGroup {
433 name: String::from(DEFAULT_GROUP_NAME),
434 cycle_counter: 0,
435 annotations: Vec::new(),
436 };
437
438 waves.annotation_groups.push(ungrouped);
439
440 if waves.source.sibling_state_file().is_some() {
442 self.update(Message::SuggestOpenSiblingStateFile);
443 }
444 }
445 }
446
447 pub(crate) fn on_transaction_streams_loaded(
448 &mut self,
449 filename: WaveSource,
450 format: WaveFormat,
451 new_ftr: TransactionContainer,
452 _loaded_options: LoadOptions,
453 ) {
454 info!("Transaction streams are loaded.");
455 self.record_file_history(&filename);
456
457 let viewport = Viewport::new();
458 let viewports = [viewport].to_vec();
459
460 let mut new_transaction_streams = WaveData {
461 inner: DataContainer::Transactions(new_ftr),
462 source: filename,
463 format,
464 active_scope: None,
465 items_tree: DisplayedItemTree::default(),
466 displayed_items: HashMap::new(),
467 viewports,
468 cursor: None,
469 markers: HashMap::new(),
470 annotations: Vec::new(),
471 selected_annotation: None,
472 annotation_counter: 1,
473 last_active_viewport_idx: 0,
474 annotation_menu_pos: None,
475 annotation_menu_time: None,
476 focused_item: None,
477 focused_transaction: (None, None),
478 default_variable_name_type: self.user.config.default_variable_name_type,
479 display_variable_indices: self.show_variable_indices(),
480 scroll_offset: 0.,
481 drawing_infos: vec![],
482 drawing_infos_signature: None,
483 total_height: 0.,
484 display_item_ref_counter: 0,
485 old_max_timestamp: None,
486 graphics: HashMap::new(),
487 cache_generation: 0,
488 inflight_caches: HashMap::new(),
489 annotation_groups: vec![],
490 annotation_list_visible: false,
491 cached_time_range: TimeRange::default(),
492 };
493
494 let enable_time_offset = self.enable_time_offset();
495 new_transaction_streams.refresh_time_range(enable_time_offset);
496
497 self.invalidate_draw_commands();
498
499 self.user.config.theme.alt_frequency = 0;
500 self.user.wanted_timeunit = new_transaction_streams.inner.metadata().timescale.unit;
501 self.user.waves = Some(new_transaction_streams);
502 }
503
504 fn record_file_history(&mut self, source: &WaveSource) {
505 if let Some(path) = source.path() {
506 self.file_history.add(path);
507 }
508 }
509
510 #[cfg(test)]
511 pub(crate) fn handle_async_messages(&mut self) {
512 let mut msgs = vec![];
513 loop {
514 match self.channels.msg_receiver.try_recv() {
515 Ok(msg) => msgs.push(msg),
516 Err(std::sync::mpsc::TryRecvError::Empty) => break,
517 Err(std::sync::mpsc::TryRecvError::Disconnected) => {
518 trace!("Message sender disconnected");
519 break;
520 }
521 }
522 }
523
524 while let Some(msg) = msgs.pop() {
525 self.update(msg);
526 }
527 }
528
529 pub(crate) fn push_async_messages(&mut self, msgs: &mut Vec<Message>) {
530 loop {
531 match self.channels.msg_receiver.try_recv() {
532 Ok(msg) => msgs.push(msg),
533 Err(std::sync::mpsc::TryRecvError::Empty) => break,
534 Err(std::sync::mpsc::TryRecvError::Disconnected) => {
535 trace!("Message sender disconnected");
536 break;
537 }
538 }
539 }
540 }
541
542 pub fn get_visuals(&self) -> Visuals {
543 let widget_style = WidgetVisuals {
544 bg_fill: self.user.config.theme.secondary_ui_color.background,
545 fg_stroke: Stroke {
546 color: self.user.config.theme.secondary_ui_color.foreground,
547 width: 1.0,
548 },
549 weak_bg_fill: self.user.config.theme.secondary_ui_color.background,
550 bg_stroke: Stroke {
551 color: self.user.config.theme.border_color,
552 width: 1.0,
553 },
554 corner_radius: CornerRadius::same(2),
555 expansion: 0.0,
556 };
557
558 Visuals {
559 override_text_color: Some(self.user.config.theme.foreground),
560 extreme_bg_color: self.user.config.theme.secondary_ui_color.background,
561 panel_fill: self.user.config.theme.secondary_ui_color.background,
562 window_fill: self.user.config.theme.primary_ui_color.background,
563 window_stroke: Stroke {
564 width: 1.0,
565 color: self.user.config.theme.border_color,
566 },
567 selection: Selection {
568 bg_fill: self.user.config.theme.selected_elements_colors.background,
569 stroke: Stroke {
570 color: self.user.config.theme.selected_elements_colors.foreground,
571 width: 1.0,
572 },
573 },
574 widgets: Widgets {
575 noninteractive: widget_style,
576 inactive: widget_style,
577 hovered: widget_style,
578 active: widget_style,
579 open: widget_style,
580 },
581 ..Visuals::dark()
582 }
583 }
584
585 pub(crate) fn load_state(
586 &mut self,
587 mut loaded_state: Box<UserState>,
588 path: Option<Utf8PathBuf>,
589 ) {
590 mem::swap(&mut self.user, &mut loaded_state);
592
593 mem::swap(&mut loaded_state.waves, &mut self.user.waves);
596 let load_commands = if let (Some(waves), Some(new_waves)) =
597 (&mut self.user.waves, &mut loaded_state.waves)
598 {
599 mem::swap(&mut waves.active_scope, &mut new_waves.active_scope);
600 let items = std::mem::take(&mut new_waves.displayed_items);
601 let items_tree = std::mem::take(&mut new_waves.items_tree);
602 let load_commands = waves.update_with_items(&items, items_tree, &self.translators);
603
604 mem::swap(&mut waves.viewports, &mut new_waves.viewports);
605 mem::swap(&mut waves.cursor, &mut new_waves.cursor);
606 mem::swap(&mut waves.markers, &mut new_waves.markers);
607 mem::swap(&mut waves.focused_item, &mut new_waves.focused_item);
608
609 mem::swap(&mut waves.annotations, &mut new_waves.annotations);
610 mem::swap(
612 &mut waves.annotation_groups,
613 &mut new_waves.annotation_groups,
614 );
615 mem::swap(
616 &mut waves.annotation_list_visible,
617 &mut new_waves.annotation_list_visible,
618 );
619 mem::swap(
620 &mut waves.annotation_counter,
621 &mut new_waves.annotation_counter,
622 );
623 mem::swap(
624 &mut waves.selected_annotation,
625 &mut new_waves.selected_annotation,
626 );
627 waves.default_variable_name_type = new_waves.default_variable_name_type;
628 waves.scroll_offset = new_waves.scroll_offset;
629 load_commands
630 } else {
631 None
632 };
633 if let Some(load_commands) = load_commands {
634 self.load_variables(load_commands);
635 }
636
637 self.user.drag_started = false;
639 self.user.drag_source_idx = None;
640 self.user.drag_target_idx = None;
641
642 self.user.previous_waves = None;
644 self.user.count = None;
645
646 self.user.state_file = path;
648
649 let enable_time_offset = self.enable_time_offset();
650 self.invalidate_draw_commands();
651 if let Some(waves) = &mut self.user.waves {
652 waves.refresh_time_range(enable_time_offset);
653 waves.update_viewports();
654 }
655 }
656
657 pub fn waves_fully_loaded(&self) -> bool {
661 self.user
662 .waves
663 .as_ref()
664 .is_some_and(|w| w.inner.is_fully_loaded())
665 }
666
667 pub fn analog_caches_ready(&self) -> bool {
669 self.user
670 .waves
671 .as_ref()
672 .is_none_or(|w| w.inflight_caches.is_empty())
673 }
674
675 pub(crate) fn current_canvas_state(waves: &WaveData, message: String) -> CanvasState {
677 CanvasState {
678 message,
679 focused_item: waves.focused_item,
680 focused_transaction: waves.focused_transaction.clone(),
681 items_tree: waves.items_tree.clone(),
682 displayed_items: waves.displayed_items.clone(),
683 markers: waves.markers.clone(),
684 annotations: waves.annotations.clone(),
685 annotation_group: waves.annotation_groups.clone(),
686 annotation_list: waves.annotation_list_visible,
687 selected_annotation: waves.selected_annotation,
688 annotation_counter: waves.annotation_counter,
689 }
690 }
691
692 pub(crate) fn save_current_canvas(&mut self, message: String) {
694 if let Some(waves) = &self.user.waves {
695 self.undo_stack
696 .push(SystemState::current_canvas_state(waves, message));
697
698 if self.undo_stack.len() > self.user.config.undo_stack_size {
699 self.undo_stack.remove(0);
700 }
701 self.redo_stack.clear();
702 }
703 }
704
705 #[cfg(not(target_arch = "wasm32"))]
706 pub(crate) fn start_wcp_server(&mut self, address: Option<String>, initiate: bool) {
707 use wcp::wcp_server::WcpServer;
708
709 use crate::wcp;
710
711 if self.wcp_server_thread.as_ref().is_some()
712 || self
713 .wcp_running_signal
714 .load(std::sync::atomic::Ordering::Relaxed)
715 {
716 warn!("WCP HTTP server is already running");
717 return;
718 }
719 let (wcp_s2c_sender, wcp_s2c_receiver) = tokio::sync::mpsc::channel(100);
721 let (wcp_c2s_sender, wcp_c2s_receiver) = tokio::sync::mpsc::channel(100);
722
723 self.channels.wcp_c2s_receiver = Some(wcp_c2s_receiver);
724 self.channels.wcp_s2c_sender = Some(wcp_s2c_sender);
725 let stop_signal_copy = self.wcp_stop_signal.clone();
726 stop_signal_copy.store(false, std::sync::atomic::Ordering::Relaxed);
727 let running_signal_copy = self.wcp_running_signal.clone();
728 running_signal_copy.store(true, std::sync::atomic::Ordering::Relaxed);
729 let greeted_signal_copy = self.wcp_greeted_signal.clone();
730 greeted_signal_copy.store(true, std::sync::atomic::Ordering::Relaxed);
731
732 let ctx = self.context.clone();
733 let address = address.unwrap_or(self.user.config.wcp.address.clone());
734 self.wcp_server_address = Some(address.clone());
735 self.wcp_server_thread = Some(tokio::spawn(async move {
736 let server = WcpServer::new(
737 address,
738 initiate,
739 wcp_c2s_sender,
740 wcp_s2c_receiver,
741 stop_signal_copy,
742 running_signal_copy,
743 greeted_signal_copy,
744 ctx,
745 )
746 .await;
747 match server {
748 Ok(mut server) => server.run().await,
749 Err(m) => {
750 error!("Could not start WCP server. {m:?}");
751 }
752 }
753 }));
754 }
755
756 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
757 pub(crate) fn stop_wcp_server(&mut self) {
758 if self.wcp_server_address.is_some() && self.wcp_server_thread.is_some() {
761 self.wcp_stop_signal
763 .store(true, std::sync::atomic::Ordering::Relaxed);
764
765 self.wcp_server_thread = None;
766 self.wcp_server_address = None;
767 self.channels.wcp_s2c_sender = None;
768 self.channels.wcp_c2s_receiver = None;
769 info!("Stopped WCP server");
770 }
771 }
772}