Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 4 additions & 32 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions console/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ gui_common = { path = "../gui_common" }
eframe = "0.33"
anyhow = "1"
log = "0.4"
simplelog = "0.12"
rmp-serde = { workspace = true }
sha2 = "0.10"
zero_configure = { path = "../zero_configure" }
hostname = "0.4"

[target.'cfg(target_os = "macos")'.dependencies]
oslog = "0.2"
objc2 = "0.5"
objc2-app-kit = "0.2"
objc2-foundation = "0.2"
Expand Down
21 changes: 19 additions & 2 deletions console/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use eframe::egui;
use admin_panel::{AdminPanelState, AdminService};
use audio_panel::AudioPanelState;
use gui_common::envelope_viewer::EnvelopeViewerState;
use gui_common::log_status::{self, LogStatusPanel, LogStatusState};
use gui_common::tracked::TrackedBool;
use gui_common::{CloseHandler, MessageModal, clock_panel};
use midi_panel::{MidiPanel, MidiPanelState};
Expand All @@ -31,6 +32,7 @@ enum Tab {
Audio,
Animation,
Clients,
Status,
}

pub struct ConfigApp {
Expand All @@ -55,6 +57,7 @@ pub struct ConfigApp {
active_tab: Tab,
visualizer_active: TrackedBool,
gui_state: SharedGuiState,
log_status: LogStatusState,
}

impl eframe::App for ConfigApp {
Expand All @@ -67,9 +70,16 @@ impl eframe::App for ConfigApp {
ui.selectable_value(&mut self.active_tab, Tab::Audio, "Audio");
ui.selectable_value(&mut self.active_tab, Tab::Animation, "Animation");
ui.selectable_value(&mut self.active_tab, Tab::Clients, "Clients");
if log_status::status_tab(ui, self.active_tab == Tab::Status, &self.log_status) {
self.active_tab = Tab::Status;
}
});
});

// Let the drain thread know whether the live log view is in front, so
// it wakes the GUI on plain records only while the Status tab is open.
self.log_status.set_viewing(self.active_tab == Tab::Status);

// Notify the show when the visualizer is visible (either tab or detached window).
let detached = self.visualizer_detached.load(Ordering::Relaxed);
self.visualizer_active
Expand Down Expand Up @@ -111,8 +121,7 @@ impl eframe::App for ConfigApp {

ui.add_space(16.0);
ui.separator();
let clock_running =
self.gui_state.clock_service_running.load(Ordering::Relaxed);
let clock_running = self.gui_state.clock_service_running.load();
if let Some(action) = clock_panel::clock_service_ui(ui, clock_running) {
let cmd = match action {
clock_panel::ClockServiceAction::Start => {
Expand Down Expand Up @@ -163,6 +172,12 @@ impl eframe::App for ConfigApp {
&self.visualizer_detached,
);
}
Tab::Status => {
LogStatusPanel {
state: &mut self.log_status,
}
.ui(ui);
}
Tab::Clients => {} // handled above
});
}
Expand All @@ -181,6 +196,7 @@ impl ConfigApp {
hostname: String,
repaint: RepaintSignal,
envelope_streams_rx: Receiver<EnvelopeStreams>,
log_status: LogStatusState,
) -> Self {
let audio_state = gui_state.audio_state.load();
let devices = tunnels::audio::AudioInput::devices().unwrap_or_default();
Expand All @@ -205,6 +221,7 @@ impl ConfigApp {
active_tab: Tab::default(),
visualizer_active: TrackedBool::new(false),
gui_state,
log_status,
}
}
}
Expand Down
49 changes: 33 additions & 16 deletions console/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ use tunnels_lib::repaint::RepaintSignal;
/// Approximately 240 fps.
const RENDER_INTERVAL: Duration = Duration::from_nanos(16666667 / 4);

/// Backlog of in-flight log records between the producer and the drain thread.
/// Records are dropped (and counted) when this fills, so logging never blocks
/// a real-time thread.
const LOG_CHANNEL_CAPACITY: usize = 1024;

/// Per-severity scrollback retained for the in-GUI log view.
const LOG_SCROLLBACK_PER_SEVERITY: usize = 500;

/// Override NSApplication's terminate: to send performClose: to the key
/// window instead of killing the process. This converts Cmd+Q into the
/// same close event as clicking the red window button, which our
Expand Down Expand Up @@ -50,22 +58,13 @@ fn install_terminate_override() {
}
}

#[cfg(target_os = "macos")]
fn init_logger() {
oslog::OsLogger::new("com.generalelectrix.tunnels")
.level_filter(log::LevelFilter::Info)
.init()
.expect("failed to initialize os_log");
}

#[cfg(not(target_os = "macos"))]
fn init_logger() {
simplelog::SimpleLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
.expect("failed to initialize logger");
}

fn main() -> Result<()> {
init_logger();
// The in-GUI Status view is the only log destination — no stderr/terminal output.
// The sink captures whatever passes the global gate; the GUI "Capture" dropdown owns
// that gate via `log::set_max_level`. Start at Warn.
let (capture, log_rx) = gui_common::log_status::channel(LOG_CHANNEL_CAPACITY);
log::set_boxed_logger(Box::new(capture)).expect("failed to initialize logger");
log::set_max_level(log::LevelFilter::Warn);

#[cfg(target_os = "macos")]
install_terminate_override();
Expand All @@ -90,6 +89,7 @@ fn main() -> Result<()> {
client,
admin,
hostname,
log_rx,
));

eframe::run_native(
Expand All @@ -98,14 +98,30 @@ fn main() -> Result<()> {
Box::new(move |cc| {
stage_theme::apply(&cc.egui_ctx);

let (send, recv, client, admin, hostname) =
let (send, recv, client, admin, hostname, log_rx) =
startup.take().expect("creator closure called once");

let repaint: RepaintSignal = {
let ctx = cc.egui_ctx.clone();
Arc::new(move || ctx.request_repaint())
};

// Build the in-GUI log surfaces and spawn the drain thread that
// moves captured records into scrollback and fires the repaint.
let log_alert = Arc::new(gui_common::log_status::LogAlert::new(repaint.clone()));
let scrollback = Arc::new(std::sync::Mutex::new(
gui_common::log_status::Scrollback::new(LOG_SCROLLBACK_PER_SEVERITY),
));
let viewing = Arc::new(std::sync::atomic::AtomicBool::new(false));
gui_common::log_status::spawn_drain_thread(
log_rx,
scrollback.clone(),
log_alert.clone(),
viewing.clone(),
);
let log_status =
gui_common::log_status::LogStatusState::new(log_alert, scrollback, viewing);

let gui_state = Arc::new(GuiState::new(repaint.clone()));
let show_gui_state = gui_state.clone();

Expand All @@ -128,6 +144,7 @@ fn main() -> Result<()> {
hostname,
repaint,
envelope_rx,
log_status,
)))
}),
)
Expand Down
4 changes: 4 additions & 0 deletions gui_common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ version = "0.1.0"
edition.workspace = true

[dependencies]
chrono = "0.4"
eframe = "0.33"
egui_plot = "0.34"
itertools = "0.12"
log = "0.4"
midi_harness = { path = "../midi_harness" }
tunnels_audio = { path = "../tunnels_audio" }
tunnels_lib = { path = "../tunnels_lib" }

[dev-dependencies]
egui_kittest = { version = "0.33", features = ["wgpu", "snapshot"] }
1 change: 1 addition & 0 deletions gui_common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod audio_panel;
pub mod clock_panel;
pub mod envelope_viewer;
pub mod log_status;
pub mod midi_panel;
pub mod scrolling_plot;
pub mod tracked;
Expand Down
Loading