wedi_core/
terminal.rs

1use anyhow::Result;
2use crossterm::{
3    cursor,
4    event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
5    execute,
6    terminal::{self, ClearType},
7};
8use std::io::{self, Write};
9
10pub struct Terminal {
11    size: (u16, u16),
12}
13
14impl Terminal {
15    pub fn new() -> Result<Self> {
16        let size = terminal::size()?;
17        Ok(Self { size })
18    }
19
20    pub fn enter_raw_mode() -> Result<()> {
21        terminal::enable_raw_mode()?;
22        execute!(io::stdout(), terminal::EnterAlternateScreen)?;
23        Ok(())
24    }
25
26    pub fn exit_raw_mode() -> Result<()> {
27        execute!(io::stdout(), terminal::LeaveAlternateScreen)?;
28        terminal::disable_raw_mode()?;
29        Ok(())
30    }
31
32    pub fn clear_screen() -> Result<()> {
33        execute!(io::stdout(), terminal::Clear(ClearType::All))?;
34        Ok(())
35    }
36
37    pub fn size(&self) -> (u16, u16) {
38        self.size
39    }
40
41    #[allow(dead_code)]
42    pub fn update_size(&mut self) -> Result<()> {
43        self.size = terminal::size()?;
44        Ok(())
45    }
46
47    #[allow(dead_code)]
48    pub fn flush() -> Result<()> {
49        io::stdout().flush()?;
50        Ok(())
51    }
52
53    pub fn read_key() -> Result<KeyEvent> {
54        loop {
55            let event = event::read()?;
56
57            match event {
58                Event::Key(key_event) => {
59                    // 處理正常的 Press 和 Repeat 事件
60                    if key_event.kind == KeyEventKind::Press
61                        || key_event.kind == KeyEventKind::Repeat
62                    {
63                        return Ok(key_event);
64                    }
65                }
66                Event::Resize(_cols, _rows) => {
67                    // 視窗大小改變,返回特殊標記
68                    return Ok(KeyEvent::new(KeyCode::F(21), KeyModifiers::NONE));
69                }
70                Event::Paste(_text) => {
71                    // Windows Terminal 的 Ctrl+V 觸發 Paste 事件
72                    // 返回一個特殊按鍵標記,攜帶文本長度信息
73                    // 實際文本需要從剪貼簿讀取
74                    return Ok(KeyEvent::new(KeyCode::F(20), KeyModifiers::NONE));
75                }
76                _ => {
77                    // 忽略其他事件(鼠標、調整大小等)
78                }
79            }
80        }
81    }
82
83    #[allow(dead_code)]
84    pub fn set_cursor_position(x: u16, y: u16) -> Result<()> {
85        execute!(io::stdout(), cursor::MoveTo(x, y))?;
86        Ok(())
87    }
88
89    #[allow(dead_code)]
90    pub fn hide_cursor() -> Result<()> {
91        execute!(io::stdout(), cursor::Hide)?;
92        Ok(())
93    }
94
95    pub fn show_cursor() -> Result<()> {
96        execute!(io::stdout(), cursor::Show)?;
97        Ok(())
98    }
99}
100
101impl Drop for Terminal {
102    fn drop(&mut self) {
103        let _ = Self::exit_raw_mode();
104        let _ = Self::show_cursor();
105    }
106}