URI: 
       Fixed cancel transfer & limit beep rate. - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
   DIR commit 60e1ec836e50d0d9be9e60f2b2e25aa26b1ea6bb
   DIR parent cb69b11a334def21561eb8755933fb7584b02870
  HTML Author: Mike Krüger <mkrueger@posteo.de>
       Date:   Wed,  1 May 2024 11:36:51 +0200
       
       Fixed cancel transfer & limit beep rate.
       
       Diffstat:
         M crates/icy_engine/src/parsers/ansi… |      19 +++++++++++++++++--
         M crates/icy_engine/src/parsers/ansi… |       4 ++--
         M crates/icy_engine/src/parsers/pars… |       8 +++++---
         M crates/icy_term/src/ui/app.rs       |      11 +++--------
         M crates/icy_term/src/ui/dialogs/cap… |       4 ++--
         M crates/icy_term/src/ui/mod.rs       |       1 -
         M crates/icy_term/src/ui/terminal_th… |     132 ++++++++++---------------------
         M crates/icy_term/src/util/music.rs   |      21 +++++++++++++--------
       
       8 files changed, 82 insertions(+), 118 deletions(-)
       ---
   DIR diff --git a/crates/icy_engine/src/parsers/ansi/mod.rs b/crates/icy_engine/src/parsers/ansi/mod.rs
       @@ -263,7 +263,10 @@ impl BufferParser for Parser {
                                    buf.print_char(current_layer, caret, ch);
                                    Ok(CallbackAction::Update)
                                }
       -                        _ => Err(ParserError::UnsupportedEscapeSequence(self.current_escape_sequence.clone()).into()),
       +                        _ => {
       +                            self.state = EngineState::Default;
       +                            Err(ParserError::UnsupportedEscapeSequence(self.current_escape_sequence.clone()).into())
       +                        }
                            }
                        };
                    }
       @@ -602,6 +605,7 @@ impl BufferParser for Parser {
                            }
                            'r' => return self.reset_margins(buf),
                            'm' => {
       +                        self.state = EngineState::Default;
                                if self.parsed_numbers.len() != 2 {
                                    return Err(ParserError::UnsupportedEscapeSequence(self.current_escape_sequence.clone()).into());
                                }
       @@ -1105,6 +1109,7 @@ impl BufferParser for Parser {
        
                            '?' => {
                                if !is_start {
       +                            self.state = EngineState::Default;
                                    return Err(ParserError::UnsupportedEscapeSequence(
                                        self.current_escape_sequence.clone(),
                                    ).into());
       @@ -1115,6 +1120,7 @@ impl BufferParser for Parser {
                            }
                            '=' => {
                                if !is_start {
       +                            self.state = EngineState::Default;
                                    return Err(ParserError::UnsupportedEscapeSequence(
                                        self.current_escape_sequence.clone(),
                                    ).into());
       @@ -1125,6 +1131,7 @@ impl BufferParser for Parser {
                            }
                            '!' => {
                                if !is_start {
       +                            self.state = EngineState::Default;
                                    return Err(ParserError::UnsupportedEscapeSequence(
                                        self.current_escape_sequence.clone(),
                                    ).into());
       @@ -1135,6 +1142,7 @@ impl BufferParser for Parser {
                            }
                            '<' => {
                                if !is_start {
       +                            self.state = EngineState::Default;
                                    return Err(ParserError::UnsupportedEscapeSequence(
                                        self.current_escape_sequence.clone(),
                                    ).into());
       @@ -1445,13 +1453,14 @@ impl Parser {
                };
                for ch in m.chars() {
                    if let Err(err) = self.print_char(buf, current_layer, caret, ch) {
       +                self.state = EngineState::Default;
                        log::error!("Error during macro invocation: {}", err);
                    }
                }
            }
        
            fn execute_aps_command(&self, _buf: &mut Buffer, _caret: &mut Caret) {
       -        log::warn!("TODO execute APS command: {}", self.parse_string);
       +        log::warn!("TODO execute APS command: {}", fmt_error_string(&self.parse_string));
            }
        }
        
       @@ -1473,3 +1482,8 @@ fn set_font_selection_success(buf: &mut Buffer, caret: &mut Caret, slot: usize) 
        pub fn parse_next_number(x: i32, ch: u8) -> i32 {
            x.saturating_mul(10).saturating_add(ch as i32).saturating_sub(b'0' as i32)
        }
       +
       +
       +pub fn fmt_error_string(input: &str) -> String {
       +    input.chars().take(40).collect::<String>()
       +}
       +\ No newline at end of file
   DIR diff --git a/crates/icy_engine/src/parsers/ansi/osc.rs b/crates/icy_engine/src/parsers/ansi/osc.rs
       @@ -2,7 +2,7 @@ use regex::Regex;
        
        use crate::{Buffer, CallbackAction, Caret, EngineResult, ParserError};
        
       -use super::{parse_next_number, Parser};
       +use super::{fmt_error_string, parse_next_number, Parser};
        lazy_static::lazy_static! {
            static ref OSC_PALETTE: Regex = Regex::new(r"(\d+)?;[rR][gG][bB]:([0-9a-fA-F]{2})/([0-9a-fA-F]{2})/([0-9a-fA-F]{2})").unwrap();
        }
       @@ -48,7 +48,7 @@ impl Parser {
                    return Ok(CallbackAction::NoUpdate);
                }
        
       -        Err(ParserError::UnsupportedOSCSequence(self.parse_string.clone()).into())
       +        Err(ParserError::UnsupportedOSCSequence(fmt_error_string(&self.parse_string)).into())
            }
        
            fn handle_osc_hyperlinks(&mut self, parse_string: impl Into<String>, buf: &mut Buffer, caret: &mut Caret) {
   DIR diff --git a/crates/icy_engine/src/parsers/parser_errors.rs b/crates/icy_engine/src/parsers/parser_errors.rs
       @@ -1,5 +1,7 @@
        use std::error::Error;
        
       +use crate::ansi::fmt_error_string;
       +
        #[derive(Debug, Clone)]
        pub enum ParserError {
            InvalidChar(char),
       @@ -30,13 +32,13 @@ impl std::fmt::Display for ParserError {
                match self {
                    ParserError::InvalidChar(ch) => write!(f, "invalid character {ch}"),
                    ParserError::UnsupportedEscapeSequence(seq) => {
       -                write!(f, "unsupported escape sequence {seq}")
       +                write!(f, "unsupported escape sequence {}", fmt_error_string(&seq))
                    }
                    ParserError::UnsupportedDCSSequence(seq) => {
       -                write!(f, "unsupported DCS sequence {seq}")
       +                write!(f, "unsupported DCS sequence {}", fmt_error_string(&seq))
                    }
                    ParserError::UnsupportedOSCSequence(seq) => {
       -                write!(f, "unsupported OSC sequence {seq}")
       +                write!(f, "unsupported OSC sequence {}", fmt_error_string(&seq))
                    }
                    ParserError::Description(str) => write!(f, "{str}"),
                    ParserError::UnsupportedControlCode(code) => {
   DIR diff --git a/crates/icy_term/src/ui/app.rs b/crates/icy_term/src/ui/app.rs
       @@ -78,7 +78,6 @@ impl MainWindow {
                    auto_transfer: None,
                    auto_login: None,
                    sound_thread: Arc::new(eframe::epaint::mutex::Mutex::new(SoundThread::new())),
       -            enabled: true,
                    terminal_type: None,
                    mouse_field: Vec::new(),
                    cache_directory: PathBuf::new(),
       @@ -191,28 +190,24 @@ impl eframe::App for MainWindow {
        
                    MainWindowMode::FileTransfer(download) => {
                        self.update_terminal_window(ctx, frame, false);
       -
                        let state = self.buffer_update_thread.lock().current_transfer.clone();
       -
                        // auto close uploads.
                        if !download && state.is_finished {
                            self.set_mode(MainWindowMode::ShowTerminal);
                        }
                        match dialogs::up_download_dialog::FileTransferDialog::new().show_dialog(ctx, frame, &state, download) {
                            dialogs::up_download_dialog::FileTransferDialogAction::Run => {}
       +                    dialogs::up_download_dialog::FileTransferDialogAction::Close |
                            dialogs::up_download_dialog::FileTransferDialogAction::CancelTransfer => {
                                if state.is_finished {
                                    self.set_mode(MainWindowMode::ShowTerminal);
                                } else {
       -                            println!("send cancel transfer") ;
                                    self.send_data(super::connect::SendData::CancelTransfer);
       +                            self.set_mode(MainWindowMode::ShowTerminal);
       +                            ctx.request_repaint_after(Duration::from_millis(150));
                                }
                            }
       -                    dialogs::up_download_dialog::FileTransferDialogAction::Close => {
       -                        self.set_mode(MainWindowMode::ShowTerminal);
       -                    }
                        }
       -                ctx.request_repaint_after(Duration::from_millis(150));
                    }
                    MainWindowMode::ShowCaptureDialog => {
                        self.update_terminal_window(ctx, frame, false);
   DIR diff --git a/crates/icy_term/src/ui/dialogs/capture_dialog.rs b/crates/icy_term/src/ui/dialogs/capture_dialog.rs
       @@ -26,10 +26,10 @@ pub enum Message {
        }
        
        impl DialogState {
       -    pub(crate) fn append_data(&mut self, data: u8) {
       +    pub(crate) fn append_data(&mut self, data: &[u8]) {
                if self.capture_session {
                    if let Ok(mut data_file) = std::fs::OpenOptions::new().create(true).append(true).open(&self.capture_filename) {
       -                if let Err(err) = data_file.write_all(&[data]) {
       +                if let Err(err) = data_file.write_all(data) {
                            if !self.show_capture_error {
                                self.show_capture_error = true;
                                log::error!("{err}");
   DIR diff --git a/crates/icy_term/src/ui/mod.rs b/crates/icy_term/src/ui/mod.rs
       @@ -139,7 +139,6 @@ impl MainWindow {
            }
        
            pub fn set_mode(&mut self, mode: MainWindowMode) {
       -        println!("Setting mode: {:?}", mode);
                self.state.mode = mode;
            }
        
   DIR diff --git a/crates/icy_term/src/ui/terminal_thread.rs b/crates/icy_term/src/ui/terminal_thread.rs
       @@ -33,7 +33,6 @@ pub struct TerminalThread {
            pub sound_thread: Arc<Mutex<SoundThread>>,
        
            pub auto_transfer: Option<(TransferProtocolType, bool)>,
       -    pub enabled: bool,
        
            pub terminal_type: Option<(TerminalEmulation, MusicOption)>,
        
       @@ -51,10 +50,9 @@ impl TerminalThread {
                connection: &mut ConnectionThreadData,
                buffer_parser: &mut dyn BufferParser,
                data: &[u8],
       -    ) -> TerminalResult<(u64, usize)> {
       +    )  -> Res<()> {
                self.sound_thread.lock().update_state()?;
       -        let res = self.update_buffer( connection, buffer_parser, data).await;
       -        Ok(res)
       +        self.update_buffer( connection, buffer_parser, data).await
            }
        
            async fn update_buffer(
       @@ -62,36 +60,13 @@ impl TerminalThread {
                connection: &mut ConnectionThreadData,
                buffer_parser: &mut dyn BufferParser,
                data: &[u8],
       -    ) -> (u64, usize) {
       -        let has_data = !data.is_empty();
       -        if !self.enabled {
       -            return (10, 0);
       -        }
       -
       -        {
       -            let mut caret: Caret = Caret::default();
       -            mem::swap(&mut caret, self.buffer_view.lock().get_caret_mut());
       -            let mut update = false;
       -            let mut ms = 0;
       -            loop {
       -                let Some(act) = buffer_parser.get_next_action(self.buffer_view.lock().get_buffer_mut(), &mut caret, 0) else {
       -                    break;
       -                };
       -                let (p, _ms) = self.handle_action(act, connection).await;
       -                update |= p;
       -                ms = _ms.max(ms);
       -            }
       -
       -            if update {
       -                self.buffer_view.lock().get_edit_state_mut().set_is_buffer_dirty();
       -                mem::swap(&mut caret, self.buffer_view.lock().get_caret_mut());
       -                return (ms as u64, 0);
       -            }
       -
       -            mem::swap(&mut caret, self.buffer_view.lock().get_caret_mut());
       -        }
       -
       -        let mut idx = 0;
       +    ) -> Res<()> {
       +        let mut caret: Caret = Caret::default();
       +        mem::swap(&mut caret, self.buffer_view.lock().get_caret_mut());
       +        let mut lock = self.buffer_view.lock();
       +        let buffer = lock.get_buffer_mut();
       +        self.capture_dialog.append_data(&data);
       +        
                for ch in data {
                    let ch = *ch;
                    if let Some((protocol_type, download)) = self.auto_file_transfer.try_transfer(ch) {
       @@ -99,44 +74,26 @@ impl TerminalThread {
                    }
                    if let Some(autologin) = &mut self.auto_login {
                        if let Ok(Some(data)) = autologin.try_login(ch) {
       -                    connection.com.send(&data).await.unwrap();
       +                    connection.com.send(&data).await?;
                            autologin.logged_in = true;
                        }
                    }
       -            self.capture_dialog.append_data(ch);
       -            let (p, ms) = self.print_char(connection, buffer_parser, ch).await;
       -            idx += 1;
       -
       -            if p {
       -                self.buffer_view.lock().get_edit_state_mut().set_is_buffer_dirty();
       -                return (ms as u64, idx);
       +            let result = buffer_parser.print_char(buffer, 0, &mut caret, ch as char);
       +            match result {
       +                Ok(action) => {
       +                    let res =  self.handle_action(action, connection).await;
       +                }
       +                Err(err) => {
       +                    log::error!("print_char: {err}");
       +                }
                    }
                }
       -
       -        if has_data {
       -            (0, data.len())
       -        } else {
       -            (10, data.len())
       -        }
       +        mem::swap(&mut caret, lock.get_caret_mut());
       +        lock.get_buffer_mut().update_hyperlinks();
       +        lock.redraw_view();
       +        Ok(())
            }
        
       -    pub async fn print_char(&self, connection: &mut ConnectionThreadData, buffer_parser: &mut dyn BufferParser, c: u8) -> (bool, u32) {
       -        let mut caret: Caret = Caret::default();
       -        mem::swap(&mut caret, self.buffer_view.lock().get_caret_mut());
       -        let result = buffer_parser.print_char(self.buffer_view.lock().get_buffer_mut(), 0, &mut caret, c as char);
       -        mem::swap(&mut caret, self.buffer_view.lock().get_caret_mut());
       -
       -        match result {
       -            Ok(action) => {
       -                return self.handle_action(action, connection).await;
       -            }
       -
       -            Err(err) => {
       -                log::error!("print_char: {err}");
       -            }
       -        }
       -        (false, 0)
       -    }
        
            async fn handle_action(&self, result: icy_engine::CallbackAction, connection: &mut ConnectionThreadData) -> (bool, u32) {
                match result {
       @@ -238,30 +195,21 @@ pub fn start_update_thread(
                                            }
                                            Ok(size) => {
                                                if size > 0 {
       -                                            let mut idx = 0;
       -                                            while idx < size {
       -                                                let update_state = update_thread.lock().update_state(&mut connection, &mut *buffer_parser, &data[idx..size]).await;
       -                                                match &update_state {
       -                                                    Err(err) => {
       -                                                        println(&update_thread, &mut buffer_parser, &format!("\n{err}\n"));
       -                                                        log::error!("run_update_thread::update_state: {err}");
       -                                                        idx = size;
       -                                                    }
       -                                                    Ok((sleep_ms, parsed_data)) => {
       -                                                        let data = buffer_parser.get_picture_data();
       -                                                        if data.is_some() {
       -                                                            update_thread.lock().mouse_field = buffer_parser.get_mouse_fields();
       -                                                            update_thread.lock().buffer_view.lock().set_reference_image(data);
       -                                                        }
       -                                                        if *sleep_ms > 0 {
       -                                                            thread::sleep(Duration::from_millis(*sleep_ms));
       -                                                        }
       -                                                        idx += *parsed_data;
       +                                            let update_state = update_thread.lock().update_state(&mut connection, &mut *buffer_parser, &data[0..size]).await;
       +                                            match &update_state {
       +                                                Err(err) => {
       +                                                    println(&update_thread, &mut buffer_parser, &format!("\n{err}\n"));
       +                                                    log::error!("run_update_thread::update_state: {err}");
       +                                                }
       +                                                Ok(()) => {
       +                                                    let data = buffer_parser.get_picture_data();
       +                                                    if data.is_some() {
       +                                                        update_thread.lock().mouse_field = buffer_parser.get_mouse_fields();
       +                                                        update_thread.lock().buffer_view.lock().set_reference_image(data);
                                                            }
                                                        }
                                                    }
                                                    ctx.request_repaint();
       -                                            update_thread.lock().buffer_view.lock().get_buffer_mut().update_hyperlinks();
                                                }
                                            }
                                        }
       @@ -295,7 +243,6 @@ async fn open_connection(connection_data: &OpenConnectionData) -> Res<Box<dyn Co
            if connection_data.term_caps.window_size.0 == 0 {
                return Ok(Box::new(NullConnection {}));
            }
       -    println!("Opening connection: {:?}", connection_data.connection_type);
        
            match connection_data.connection_type {
                icy_net::ConnectionType::Raw => Ok(Box::new(RawConnection::open(&connection_data.address, connection_data.timeout.clone()).await?)),
       @@ -351,19 +298,15 @@ async fn handle_receive(ctx: &egui::Context, c: &mut ConnectionThreadData, data:
                }
        
                SendData::Upload(protocol, files) => {
       -            println!("upload !");
                    if let Err(err) = upload(ctx, c, protocol, files, update_thread).await {
                        log::error!("Failed to upload files: {err}");
                    }
       -            println!("upload done !");
                }
        
                SendData::Download(protocol) => {
       -            println!("download !");
                    if let Err(err) = download(ctx, c, protocol, update_thread).await {
                        log::error!("Failed to download files: {err}");
                    }
       -            println!("download done !");
                }
        
                _ => {}
       @@ -387,15 +330,19 @@ async fn upload(ctx: &egui::Context, c: &mut ConnectionThreadData, protocol: Tra
        
        async fn file_transfer(ctx: &egui::Context, mut transfer_state: TransferState, prot: &mut dyn Protocol, c: &mut ConnectionThreadData, update_thread: &Arc<Mutex<TerminalThread>>) -> Res<()> {
            ctx.request_repaint();
       +    let instant = Instant::now();
            while !transfer_state.is_finished {
                tokio::select! {
                    Ok(()) = prot.update_transfer(&mut *c.com, &mut transfer_state) => {
       -                update_thread.lock().current_transfer = transfer_state.clone();
       +                if instant.elapsed() > Duration::from_millis(500) {
       +                    update_thread.lock().current_transfer = transfer_state.clone();
       +                    ctx.request_repaint();
       +                }
                    }
                    data = c.rx.recv() => {
                        match data {
                            Some(SendData::CancelTransfer) => {
       -                        ctx.request_repaint();
       +                        transfer_state.is_finished = true;
                                prot.cancel_transfer(&mut *c.com).await?;
                                break;
                            }
       @@ -406,6 +353,7 @@ async fn file_transfer(ctx: &egui::Context, mut transfer_state: TransferState, p
            }
            copy_downloaded_files(&mut transfer_state)?;
            update_thread.lock().current_transfer = transfer_state.clone();
       +    ctx.request_repaint();
            Ok(())
        }
        
   DIR diff --git a/crates/icy_term/src/util/music.rs b/crates/icy_term/src/util/music.rs
       @@ -128,6 +128,7 @@ impl SoundThread {
                    tx: tx2,
                    music: VecDeque::new(),
                    thread_is_running: true,
       +            last_beep: Instant::now(),
                };
        
                if let Err(err) = std::thread::Builder::new().name("music_thread".to_string()).spawn(move || {
       @@ -228,6 +229,7 @@ pub struct SoundBackgroundThreadData {
            thread_is_running: bool,
        
            music: VecDeque<SoundData>,
       +    last_beep: Instant
        }
        
        impl SoundBackgroundThreadData {
       @@ -262,20 +264,23 @@ impl SoundBackgroundThreadData {
                };
                match data {
                    SoundData::PlayMusic(music) => self.play_music(&music),
       -            SoundData::Beep => SoundBackgroundThreadData::beep(),
       +            SoundData::Beep => self.beep(),
                    _ => {}
                }
            }
        
       -    fn beep() {
       -        let (_stream, stream_handle) = OutputStream::try_default().unwrap();
       -        let sink = rodio::Sink::try_new(&stream_handle).unwrap();
       -        sink.set_volume(0.1);
       +    fn beep(&mut self) {
       +        if self.last_beep.elapsed().as_millis() > 500 {
       +            let (_stream, stream_handle) = OutputStream::try_default().unwrap();
       +            let sink = rodio::Sink::try_new(&stream_handle).unwrap();
       +            sink.set_volume(0.1);
        
       -        let source = rodio::source::SineWave::new(880.);
       -        sink.append(source);
       +            let source = rodio::source::SineWave::new(880.);
       +            sink.append(source);
        
       -        thread::sleep(std::time::Duration::from_millis(200));
       +            thread::sleep(std::time::Duration::from_millis(200));
       +        }
       +        self.last_beep = Instant::now();
            }
        
            fn play_music(&mut self, music: &AnsiMusic) {