Skip to content

Commit 3d2a97b

Browse files
Merge pull request #684 from braedonsaunders/claude/remove-unused-import-KfbWA
Claude/remove unused import kfb wa
2 parents 5c92f73 + 6e87a8c commit 3d2a97b

3 files changed

Lines changed: 64 additions & 57 deletions

File tree

native-bridge/src/main.rs

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -114,50 +114,58 @@ async fn main() -> Result<()> {
114114
}));
115115

116116
if enable_tui {
117-
// Run TUI mode with LocalSet to allow non-Send futures
118-
// AppState contains cpal::Stream which has RefCell closures (not Send)
119-
// LocalSet allows spawning non-Send futures on a single-threaded executor
117+
// Run TUI mode
118+
// The WebSocket server runs in a native thread with its own runtime
119+
// This avoids Send requirements and allows the TUI to use blocking I/O
120120
info!("Starting TUI interface...");
121121

122122
let tui_rx = tui_rx.unwrap();
123123

124124
// Create TUI app
125125
let app = tui::App::new();
126126

127-
// Use LocalSet to run both WebSocket server and TUI on same thread
128-
let local = tokio::task::LocalSet::new();
129-
local.run_until(async move {
130-
// Send initial device info
131-
if let Some(tx) = &tui_tx {
132-
let _ = tx.send(tui::AppEvent::DeviceInfo {
133-
input_device,
134-
output_device,
135-
sample_rate,
136-
buffer_size,
137-
}).await;
138-
139-
let _ = tx.send(tui::AppEvent::Log {
140-
level: tui::LogLevel::Info,
141-
message: format!("OpenStudio Bridge v{} started", env!("CARGO_PKG_VERSION")),
142-
}).await;
143-
}
144-
145-
// Spawn WebSocket server in background local task (non-Send safe)
146-
let server_state = state.clone();
147-
tokio::task::spawn_local(async move {
148-
if let Err(e) = protocol::run_server("127.0.0.1:9999", server_state).await {
149-
tracing::error!("WebSocket server error: {}", e);
150-
}
151-
});
152-
153-
// Small delay to ensure server is listening before TUI takes over terminal
154-
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
155-
156-
// Run TUI on main thread
157-
if let Err(e) = tui::run(app, tui_rx).await {
158-
tracing::error!("TUI error: {}", e);
159-
}
160-
}).await;
127+
// Send initial device info
128+
if let Some(tx) = &tui_tx {
129+
let _ = tx.send(tui::AppEvent::DeviceInfo {
130+
input_device,
131+
output_device,
132+
sample_rate,
133+
buffer_size,
134+
}).await;
135+
136+
let _ = tx.send(tui::AppEvent::Log {
137+
level: tui::LogLevel::Info,
138+
message: format!("OpenStudio Bridge v{} started", env!("CARGO_PKG_VERSION")),
139+
}).await;
140+
}
141+
142+
// Spawn WebSocket server in a native thread with its own tokio runtime
143+
// This avoids Send requirements (AppState contains non-Send cpal::Stream)
144+
// and allows the TUI's blocking I/O to not starve the server
145+
let server_state = state.clone();
146+
std::thread::Builder::new()
147+
.name("websocket-server".to_string())
148+
.spawn(move || {
149+
let rt = tokio::runtime::Builder::new_current_thread()
150+
.enable_all()
151+
.build()
152+
.expect("Failed to create WebSocket server runtime");
153+
154+
rt.block_on(async move {
155+
if let Err(e) = protocol::run_server("127.0.0.1:9999", server_state).await {
156+
tracing::error!("WebSocket server error: {}", e);
157+
}
158+
});
159+
})
160+
.expect("Failed to spawn WebSocket server thread");
161+
162+
// Small delay to ensure server is listening before TUI takes over terminal
163+
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
164+
165+
// Run TUI on main thread (uses blocking terminal I/O)
166+
if let Err(e) = tui::run(app, tui_rx).await {
167+
tracing::error!("TUI error: {}", e);
168+
}
161169
} else {
162170
// Run headless mode
163171
info!("Bridge running on ws://localhost:9999 (headless mode)");

native-bridge/src/tui/app.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -419,20 +419,8 @@ impl App {
419419
self.should_quit = true;
420420
}
421421

422-
pub fn toggle_effects_panel(&mut self) {
423-
self.active_panel = if self.active_panel == ActivePanel::Effects {
424-
ActivePanel::Audio
425-
} else {
426-
ActivePanel::Effects
427-
};
428-
}
429-
430-
pub fn toggle_network_panel(&mut self) {
431-
self.active_panel = if self.active_panel == ActivePanel::Network {
432-
ActivePanel::Audio
433-
} else {
434-
ActivePanel::Network
435-
};
422+
pub fn set_panel(&mut self, panel: ActivePanel) {
423+
self.active_panel = panel;
436424
}
437425

438426
pub fn toggle_help(&mut self) {

native-bridge/src/tui/mod.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ mod app;
55
mod ui;
66
mod widgets;
77

8-
pub use app::{App, AppEvent, LogLevel};
8+
pub use app::{ActivePanel, App, AppEvent, LogLevel};
99
pub use ui::draw;
1010

1111
use crossterm::{
12-
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyModifiers},
12+
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, KeyModifiers},
1313
execute,
1414
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
1515
};
@@ -48,18 +48,29 @@ pub async fn run(
4848
// Check for crossterm events (keyboard, etc.)
4949
if crossterm::event::poll(timeout)? {
5050
if let Event::Key(key) = event::read()? {
51+
// Only handle key press events, not repeats or releases
52+
// This prevents flickering when holding a key
53+
if key.kind != KeyEventKind::Press {
54+
continue;
55+
}
56+
5157
match (key.code, key.modifiers) {
5258
// Quit on Ctrl+C or Q
5359
(KeyCode::Char('c'), KeyModifiers::CONTROL) | (KeyCode::Char('q'), _) => {
5460
break;
5561
}
56-
// Toggle effects panel
62+
// Direct panel switching (no toggling - prevents flicker)
63+
(KeyCode::Char('a'), _) => {
64+
app.set_panel(ActivePanel::Audio);
65+
}
5766
(KeyCode::Char('e'), _) => {
58-
app.toggle_effects_panel();
67+
app.set_panel(ActivePanel::Effects);
5968
}
60-
// Toggle network panel
6169
(KeyCode::Char('n'), _) => {
62-
app.toggle_network_panel();
70+
app.set_panel(ActivePanel::Network);
71+
}
72+
(KeyCode::Char('l'), _) => {
73+
app.set_panel(ActivePanel::Logs);
6374
}
6475
// Toggle help
6576
(KeyCode::Char('?'), _) | (KeyCode::F(1), _) => {

0 commit comments

Comments
 (0)