fix: replace ncurses with crossterm to fix CI builds on both arches
ncurses-rs is broken on Arch Linux ARM (missing TRUE constant in generated bindings) and the git HEAD version breaks Ubuntu too. crossterm is pure Rust with no C dependency, eliminating the issue on all platforms. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+88
-68
@@ -1,9 +1,17 @@
|
||||
use clap::Parser;
|
||||
use ncurses::*;
|
||||
use ncurses::{LcCategory, setlocale};
|
||||
use crossterm::{
|
||||
cursor::{Hide, MoveTo},
|
||||
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||
execute, queue,
|
||||
style::Print,
|
||||
terminal::{
|
||||
self, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen,
|
||||
disable_raw_mode, enable_raw_mode,
|
||||
},
|
||||
};
|
||||
use rustfft::{algorithm::Radix4, num_complex::Complex, Fft, FftDirection};
|
||||
use std::collections::VecDeque;
|
||||
use std::io::{stdin, Read};
|
||||
use std::io::{stdout, Read, Write};
|
||||
use std::fs::File;
|
||||
use std::time::{Duration, Instant};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
@@ -20,7 +28,7 @@ struct Cell {
|
||||
}
|
||||
|
||||
struct ViewDiff {
|
||||
changes: Vec<(i32, i32, char)>,
|
||||
changes: Vec<(u16, u16, char)>,
|
||||
}
|
||||
|
||||
impl ViewDiff {
|
||||
@@ -33,13 +41,13 @@ impl ViewDiff {
|
||||
}
|
||||
|
||||
fn add_change(&mut self, y: usize, x: usize, character: char) {
|
||||
self.changes.push((y as i32, x as i32, character));
|
||||
self.changes.push((y as u16, x as u16, character));
|
||||
}
|
||||
|
||||
fn apply(&self) {
|
||||
let mut buf = [0u8; 4];
|
||||
let mut out = stdout();
|
||||
for &(y, x, ch) in &self.changes {
|
||||
let _ = mvprintw(y, x, ch.encode_utf8(&mut buf));
|
||||
let _ = queue!(out, MoveTo(x, y), Print(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,14 +64,14 @@ fn render_bar(buf: &mut Vec<Vec<Cell>>, col: usize, sr: usize, bar_height_f: f32
|
||||
}
|
||||
|
||||
fn handle_terminal_resize(
|
||||
rows: &mut i32,
|
||||
cols: &mut i32,
|
||||
new_rows: i32,
|
||||
new_cols: i32,
|
||||
max_width: i32,
|
||||
max_height: i32,
|
||||
safe_rows: &mut i32,
|
||||
safe_cols: &mut i32,
|
||||
rows: &mut u16,
|
||||
cols: &mut u16,
|
||||
new_rows: u16,
|
||||
new_cols: u16,
|
||||
max_width: u16,
|
||||
max_height: u16,
|
||||
safe_rows: &mut u16,
|
||||
safe_cols: &mut u16,
|
||||
current_buffer: &mut Vec<Vec<Cell>>,
|
||||
next_buffer: &mut Vec<Vec<Cell>>,
|
||||
last_bars: &mut Vec<f32>,
|
||||
@@ -95,8 +103,9 @@ fn handle_terminal_resize(
|
||||
freq_mapping.push(index.min(fft_size / 2 - 1));
|
||||
}
|
||||
|
||||
clear();
|
||||
refresh();
|
||||
let mut out = stdout();
|
||||
let _ = queue!(out, Clear(ClearType::All));
|
||||
let _ = out.flush();
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -129,16 +138,18 @@ struct Cli {
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
let _ = setlocale(LcCategory::all, "");
|
||||
initscr();
|
||||
noecho();
|
||||
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||
keypad(stdscr(), true);
|
||||
timeout(0);
|
||||
enable_raw_mode().expect("failed to enable raw mode");
|
||||
let mut out = stdout();
|
||||
execute!(out, EnterAlternateScreen, Hide).expect("failed to initialize terminal");
|
||||
|
||||
let mut rows: i32 = 0;
|
||||
let mut cols: i32 = 0;
|
||||
getmaxyx(stdscr(), &mut rows, &mut cols);
|
||||
let default_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
let _ = disable_raw_mode();
|
||||
let _ = execute!(stdout(), LeaveAlternateScreen);
|
||||
default_hook(info);
|
||||
}));
|
||||
|
||||
let (mut cols, mut rows) = terminal::size().expect("failed to get terminal size");
|
||||
|
||||
let sample_rate: usize = 48000;
|
||||
let bit_depth: usize = 16;
|
||||
@@ -161,26 +172,26 @@ fn main() {
|
||||
PipeWire(std::process::ChildStdout),
|
||||
}
|
||||
|
||||
refresh();
|
||||
let _ = out.flush();
|
||||
|
||||
let mut audio_source = if cli.pipewire {
|
||||
let process = Command::new("pw-record")
|
||||
.args(["--format=s16", "--rate=48000", "--channels=2", "--raw", "-"])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Failed to start pw-record");
|
||||
let stdout = process.stdout.expect("Failed to capture pw-record stdout");
|
||||
refresh();
|
||||
AudioSource::PipeWire(stdout)
|
||||
.expect("failed to start pw-record");
|
||||
let pipe = process.stdout.expect("failed to capture pw-record stdout");
|
||||
let _ = out.flush();
|
||||
AudioSource::PipeWire(pipe)
|
||||
} else if let Some(filename) = &cli.input {
|
||||
let file = File::open(filename).expect("Failed to open input file");
|
||||
let file = File::open(filename).expect("failed to open input file");
|
||||
AudioSource::File(file)
|
||||
} else {
|
||||
AudioSource::StdIn(stdin().lock())
|
||||
AudioSource::StdIn(std::io::stdin().lock())
|
||||
};
|
||||
|
||||
let max_width = 1000;
|
||||
let max_height = 1000;
|
||||
let max_width: u16 = 1000;
|
||||
let max_height: u16 = 1000;
|
||||
|
||||
let mut safe_cols = cols.min(max_width);
|
||||
let mut safe_rows = rows.min(max_height);
|
||||
@@ -215,38 +226,44 @@ fn main() {
|
||||
let mut max_magnitude: f32 = 1.0;
|
||||
|
||||
loop {
|
||||
if last_terminal_resize.elapsed() >= resize_check_interval {
|
||||
let mut new_rows = 0;
|
||||
let mut new_cols = 0;
|
||||
getmaxyx(stdscr(), &mut new_rows, &mut new_cols);
|
||||
if new_rows != rows || new_cols != cols {
|
||||
handle_terminal_resize(
|
||||
&mut rows, &mut cols, new_rows, new_cols,
|
||||
max_width, max_height, &mut safe_rows, &mut safe_cols,
|
||||
&mut current_buffer, &mut next_buffer,
|
||||
&mut last_bars, &mut freq_bands, &mut freq_mapping,
|
||||
cli.log_power, fft_size,
|
||||
);
|
||||
sc = safe_cols as usize;
|
||||
sr = safe_rows as usize;
|
||||
if event::poll(Duration::ZERO).unwrap_or(false) {
|
||||
match event::read() {
|
||||
Ok(Event::Key(KeyEvent { code: KeyCode::Char('q'), .. })) => break,
|
||||
Ok(Event::Key(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
})) => break,
|
||||
Ok(Event::Resize(new_cols, new_rows)) => {
|
||||
handle_terminal_resize(
|
||||
&mut rows, &mut cols, new_rows, new_cols,
|
||||
max_width, max_height, &mut safe_rows, &mut safe_cols,
|
||||
&mut current_buffer, &mut next_buffer,
|
||||
&mut last_bars, &mut freq_bands, &mut freq_mapping,
|
||||
cli.log_power, fft_size,
|
||||
);
|
||||
sc = safe_cols as usize;
|
||||
sr = safe_rows as usize;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
last_terminal_resize = Instant::now();
|
||||
}
|
||||
|
||||
let ch = getch();
|
||||
if ch == KEY_RESIZE {
|
||||
let mut new_rows = 0;
|
||||
let mut new_cols = 0;
|
||||
getmaxyx(stdscr(), &mut new_rows, &mut new_cols);
|
||||
handle_terminal_resize(
|
||||
&mut rows, &mut cols, new_rows, new_cols,
|
||||
max_width, max_height, &mut safe_rows, &mut safe_cols,
|
||||
&mut current_buffer, &mut next_buffer,
|
||||
&mut last_bars, &mut freq_bands, &mut freq_mapping,
|
||||
cli.log_power, fft_size,
|
||||
);
|
||||
sc = safe_cols as usize;
|
||||
sr = safe_rows as usize;
|
||||
if last_terminal_resize.elapsed() >= resize_check_interval {
|
||||
if let Ok((new_cols, new_rows)) = terminal::size() {
|
||||
if new_rows != rows || new_cols != cols {
|
||||
handle_terminal_resize(
|
||||
&mut rows, &mut cols, new_rows, new_cols,
|
||||
max_width, max_height, &mut safe_rows, &mut safe_cols,
|
||||
&mut current_buffer, &mut next_buffer,
|
||||
&mut last_bars, &mut freq_bands, &mut freq_mapping,
|
||||
cli.log_power, fft_size,
|
||||
);
|
||||
sc = safe_cols as usize;
|
||||
sr = safe_rows as usize;
|
||||
}
|
||||
}
|
||||
last_terminal_resize = Instant::now();
|
||||
}
|
||||
|
||||
if last_frame_time.elapsed() >= interval {
|
||||
@@ -261,7 +278,7 @@ fn main() {
|
||||
let read_result = match &mut audio_source {
|
||||
AudioSource::StdIn(input) => input.read(&mut input_buffer),
|
||||
AudioSource::File(file) => file.read(&mut input_buffer),
|
||||
AudioSource::PipeWire(stdout) => stdout.read(&mut input_buffer),
|
||||
AudioSource::PipeWire(pipe) => pipe.read(&mut input_buffer),
|
||||
};
|
||||
|
||||
match read_result {
|
||||
@@ -313,7 +330,7 @@ fn main() {
|
||||
}
|
||||
view_diff.apply();
|
||||
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
||||
refresh();
|
||||
let _ = out.flush();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -379,7 +396,7 @@ fn main() {
|
||||
view_diff.apply();
|
||||
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
||||
last_frame_time = Instant::now();
|
||||
refresh();
|
||||
let _ = out.flush();
|
||||
} else if buffer_is_empty {
|
||||
for i in 0..sc {
|
||||
last_bars[i] *= drop_factor;
|
||||
@@ -397,8 +414,11 @@ fn main() {
|
||||
view_diff.apply();
|
||||
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
||||
last_frame_time = Instant::now();
|
||||
refresh();
|
||||
let _ = out.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = execute!(out, LeaveAlternateScreen);
|
||||
let _ = disable_raw_mode();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user