xref: /DragonOS/kernel/src/driver/tty/virtual_terminal/mod.rs (revision 418ad41fd84c15ed7e132e56970150ac38fc24a9)
1*418ad41fSLoGin use core::{fmt::Formatter, sync::atomic::Ordering};
252da9a59SGnoCiYeH 
3dfe53cf0SGnoCiYeH use alloc::{
4dfe53cf0SGnoCiYeH     string::{String, ToString},
5dfe53cf0SGnoCiYeH     sync::Arc,
6dfe53cf0SGnoCiYeH     vec::Vec,
7dfe53cf0SGnoCiYeH };
852da9a59SGnoCiYeH use system_error::SystemError;
952da9a59SGnoCiYeH 
1052da9a59SGnoCiYeH use crate::{
11*418ad41fSLoGin     driver::base::device::{
1252da9a59SGnoCiYeH         device_number::{DeviceNumber, Major},
1352da9a59SGnoCiYeH         device_register, IdTable,
1452da9a59SGnoCiYeH     },
1552da9a59SGnoCiYeH     filesystem::devfs::devfs_register,
1652da9a59SGnoCiYeH     libs::spinlock::SpinLock,
1752da9a59SGnoCiYeH };
1852da9a59SGnoCiYeH 
1952da9a59SGnoCiYeH use self::virtual_console::{VirtualConsoleData, CURRENT_VCNUM};
2052da9a59SGnoCiYeH 
2152da9a59SGnoCiYeH use super::{
2252da9a59SGnoCiYeH     console::ConsoleSwitch,
2352da9a59SGnoCiYeH     termios::{InputMode, TTY_STD_TERMIOS},
2452da9a59SGnoCiYeH     tty_core::{TtyCore, TtyCoreData},
25dfe53cf0SGnoCiYeH     tty_device::{TtyDevice, TtyType},
2652da9a59SGnoCiYeH     tty_driver::{TtyDriver, TtyDriverManager, TtyDriverType, TtyOperation},
2752da9a59SGnoCiYeH };
2852da9a59SGnoCiYeH 
2952da9a59SGnoCiYeH pub mod console_map;
3052da9a59SGnoCiYeH pub mod virtual_console;
3152da9a59SGnoCiYeH 
3252da9a59SGnoCiYeH pub const MAX_NR_CONSOLES: u32 = 63;
3352da9a59SGnoCiYeH pub const VC_MAXCOL: usize = 32767;
3452da9a59SGnoCiYeH pub const VC_MAXROW: usize = 32767;
3552da9a59SGnoCiYeH 
3652da9a59SGnoCiYeH pub const DEFAULT_RED: [u16; 16] = [
3752da9a59SGnoCiYeH     0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff,
3852da9a59SGnoCiYeH ];
3952da9a59SGnoCiYeH 
4052da9a59SGnoCiYeH pub const DEFAULT_GREEN: [u16; 16] = [
4152da9a59SGnoCiYeH     0x00, 0x00, 0xaa, 0x55, 0x00, 0x00, 0xaa, 0xaa, 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xff, 0xff,
4252da9a59SGnoCiYeH ];
4352da9a59SGnoCiYeH 
4452da9a59SGnoCiYeH pub const DEFAULT_BLUE: [u16; 16] = [
4552da9a59SGnoCiYeH     0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff,
4652da9a59SGnoCiYeH ];
4752da9a59SGnoCiYeH 
48b5b571e0SLoGin pub const COLOR_TABLE: &[u8] = &[0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15];
4952da9a59SGnoCiYeH 
5052da9a59SGnoCiYeH lazy_static! {
5152da9a59SGnoCiYeH     pub static ref VIRT_CONSOLES: Vec<Arc<SpinLock<VirtualConsoleData>>> = {
5252da9a59SGnoCiYeH         let mut v = Vec::with_capacity(MAX_NR_CONSOLES as usize);
5352da9a59SGnoCiYeH         for i in 0..MAX_NR_CONSOLES as usize {
5452da9a59SGnoCiYeH             v.push(Arc::new(SpinLock::new(VirtualConsoleData::new(i))));
5552da9a59SGnoCiYeH         }
5652da9a59SGnoCiYeH 
5752da9a59SGnoCiYeH         v
5852da9a59SGnoCiYeH     };
5952da9a59SGnoCiYeH }
6052da9a59SGnoCiYeH 
6152da9a59SGnoCiYeH #[derive(Debug, Clone, Copy, Default)]
6252da9a59SGnoCiYeH pub struct Color {
6352da9a59SGnoCiYeH     pub red: u16,
6452da9a59SGnoCiYeH     pub green: u16,
6552da9a59SGnoCiYeH     pub blue: u16,
6652da9a59SGnoCiYeH     pub transp: u16,
6752da9a59SGnoCiYeH }
6852da9a59SGnoCiYeH 
6952da9a59SGnoCiYeH impl Color {
from_256(col: u32) -> Self7052da9a59SGnoCiYeH     pub fn from_256(col: u32) -> Self {
7152da9a59SGnoCiYeH         let mut color = Self::default();
7252da9a59SGnoCiYeH         if col < 8 {
7352da9a59SGnoCiYeH             color.red = if col & 1 != 0 { 0xaa } else { 0x00 };
7452da9a59SGnoCiYeH             color.green = if col & 2 != 0 { 0xaa } else { 0x00 };
7552da9a59SGnoCiYeH             color.blue = if col & 4 != 0 { 0xaa } else { 0x00 };
7652da9a59SGnoCiYeH         } else if col < 16 {
7752da9a59SGnoCiYeH             color.red = if col & 1 != 0 { 0xff } else { 0x55 };
7852da9a59SGnoCiYeH             color.green = if col & 2 != 0 { 0xff } else { 0x55 };
7952da9a59SGnoCiYeH             color.blue = if col & 4 != 0 { 0xff } else { 0x55 };
8052da9a59SGnoCiYeH         } else if col < 232 {
8152da9a59SGnoCiYeH             color.red = ((col - 16) / 36 * 85 / 2) as u16;
8252da9a59SGnoCiYeH             color.green = ((col - 16) / 6 % 6 * 85 / 2) as u16;
8352da9a59SGnoCiYeH             color.blue = ((col - 16) % 6 * 85 / 2) as u16;
8452da9a59SGnoCiYeH         } else {
8552da9a59SGnoCiYeH             let col = (col * 10 - 2312) as u16;
8652da9a59SGnoCiYeH             color.red = col;
8752da9a59SGnoCiYeH             color.green = col;
8852da9a59SGnoCiYeH             color.blue = col;
8952da9a59SGnoCiYeH         }
9052da9a59SGnoCiYeH 
9152da9a59SGnoCiYeH         color
9252da9a59SGnoCiYeH     }
9352da9a59SGnoCiYeH }
9452da9a59SGnoCiYeH 
9552da9a59SGnoCiYeH pub struct TtyConsoleDriverInner {
96*418ad41fSLoGin     console: Arc<dyn ConsoleSwitch>,
97*418ad41fSLoGin }
98*418ad41fSLoGin 
99*418ad41fSLoGin impl core::fmt::Debug for TtyConsoleDriverInner {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result100*418ad41fSLoGin     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
101*418ad41fSLoGin         write!(f, "TtyConsoleDriverInner")
102*418ad41fSLoGin     }
10352da9a59SGnoCiYeH }
10452da9a59SGnoCiYeH 
10552da9a59SGnoCiYeH impl TtyConsoleDriverInner {
new() -> Result<Self, SystemError>10652da9a59SGnoCiYeH     pub fn new() -> Result<Self, SystemError> {
107*418ad41fSLoGin         let console = {
108*418ad41fSLoGin             #[cfg(not(target_arch = "riscv64"))]
109*418ad41fSLoGin             {
110*418ad41fSLoGin                 Arc::new(crate::driver::video::fbdev::base::fbcon::framebuffer_console::BlittingFbConsole::new()?)
111*418ad41fSLoGin             }
112*418ad41fSLoGin 
113*418ad41fSLoGin             #[cfg(target_arch = "riscv64")]
114*418ad41fSLoGin             crate::driver::video::console::dummycon::dummy_console()
115*418ad41fSLoGin         };
116*418ad41fSLoGin 
117*418ad41fSLoGin         Ok(Self { console })
11852da9a59SGnoCiYeH     }
11952da9a59SGnoCiYeH 
do_write(&self, tty: &TtyCoreData, buf: &[u8], mut nr: usize) -> Result<usize, SystemError>12052bcb59eSGnoCiYeH     fn do_write(&self, tty: &TtyCoreData, buf: &[u8], mut nr: usize) -> Result<usize, SystemError> {
12152da9a59SGnoCiYeH         // 关闭中断
12252da9a59SGnoCiYeH         let mut vc_data = tty.vc_data_irqsave();
12352da9a59SGnoCiYeH 
12452da9a59SGnoCiYeH         let mut offset = 0;
12552da9a59SGnoCiYeH 
12652da9a59SGnoCiYeH         // 这个参数是用来扫描unicode字符的,但是这部分目前未完成,先写着
12752da9a59SGnoCiYeH         let mut rescan = false;
12852da9a59SGnoCiYeH         let mut ch: u32 = 0;
12952da9a59SGnoCiYeH 
13052da9a59SGnoCiYeH         let mut draw = DrawRegion::default();
13152da9a59SGnoCiYeH 
13252da9a59SGnoCiYeH         // 首先隐藏光标再写
13352da9a59SGnoCiYeH         vc_data.hide_cursor();
13452da9a59SGnoCiYeH 
13552da9a59SGnoCiYeH         while nr != 0 {
13652da9a59SGnoCiYeH             if !rescan {
13752da9a59SGnoCiYeH                 ch = buf[offset] as u32;
13852da9a59SGnoCiYeH                 offset += 1;
13952da9a59SGnoCiYeH                 nr -= 1;
14052da9a59SGnoCiYeH             }
14152da9a59SGnoCiYeH 
14252da9a59SGnoCiYeH             let (tc, rescan_last) = vc_data.translate(&mut ch);
14352da9a59SGnoCiYeH             if tc.is_none() {
14452da9a59SGnoCiYeH                 // 表示未转换完成
14552da9a59SGnoCiYeH                 continue;
14652da9a59SGnoCiYeH             }
14752da9a59SGnoCiYeH 
14852da9a59SGnoCiYeH             let tc = tc.unwrap();
14952da9a59SGnoCiYeH             rescan = rescan_last;
15052da9a59SGnoCiYeH 
15152da9a59SGnoCiYeH             if vc_data.is_control(tc, ch) {
15252da9a59SGnoCiYeH                 vc_data.flush(&mut draw);
15352da9a59SGnoCiYeH                 vc_data.do_control(ch);
15452da9a59SGnoCiYeH                 continue;
15552da9a59SGnoCiYeH             }
15652da9a59SGnoCiYeH 
15752da9a59SGnoCiYeH             if !vc_data.console_write_normal(tc, ch, &mut draw) {
15852da9a59SGnoCiYeH                 continue;
15952da9a59SGnoCiYeH             }
16052da9a59SGnoCiYeH         }
16152da9a59SGnoCiYeH 
16252da9a59SGnoCiYeH         vc_data.flush(&mut draw);
16352da9a59SGnoCiYeH 
16452da9a59SGnoCiYeH         // TODO: notify update
16552da9a59SGnoCiYeH         return Ok(offset);
16652da9a59SGnoCiYeH     }
16752bcb59eSGnoCiYeH }
16852da9a59SGnoCiYeH 
16952bcb59eSGnoCiYeH impl TtyOperation for TtyConsoleDriverInner {
install(&self, _driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError>17052bcb59eSGnoCiYeH     fn install(&self, _driver: Arc<TtyDriver>, tty: Arc<TtyCore>) -> Result<(), SystemError> {
17152bcb59eSGnoCiYeH         let tty_core = tty.core();
17252bcb59eSGnoCiYeH         let mut vc_data = VIRT_CONSOLES[tty_core.index()].lock();
17352bcb59eSGnoCiYeH 
17452bcb59eSGnoCiYeH         self.console.con_init(&mut vc_data, true)?;
17552bcb59eSGnoCiYeH         if vc_data.complement_mask == 0 {
17652bcb59eSGnoCiYeH             vc_data.complement_mask = if vc_data.color_mode { 0x7700 } else { 0x0800 };
17752bcb59eSGnoCiYeH         }
17852bcb59eSGnoCiYeH         vc_data.s_complement_mask = vc_data.complement_mask;
17952bcb59eSGnoCiYeH         // vc_data.bytes_per_row = vc_data.cols << 1;
18052bcb59eSGnoCiYeH         vc_data.index = tty_core.index();
18152bcb59eSGnoCiYeH         vc_data.bottom = vc_data.rows;
18252bcb59eSGnoCiYeH         vc_data.set_driver_funcs(Arc::downgrade(
18352bcb59eSGnoCiYeH             &(self.console.clone() as Arc<dyn ConsoleSwitch>),
18452bcb59eSGnoCiYeH         ));
18552bcb59eSGnoCiYeH 
18652bcb59eSGnoCiYeH         // todo: unicode字符集处理?
18752bcb59eSGnoCiYeH 
18852bcb59eSGnoCiYeH         if vc_data.cols > VC_MAXCOL || vc_data.rows > VC_MAXROW {
18952bcb59eSGnoCiYeH             return Err(SystemError::EINVAL);
19052bcb59eSGnoCiYeH         }
19152bcb59eSGnoCiYeH 
19252bcb59eSGnoCiYeH         vc_data.init(None, None, true);
19352bcb59eSGnoCiYeH         vc_data.update_attr();
19452bcb59eSGnoCiYeH 
19552bcb59eSGnoCiYeH         let window_size = tty_core.window_size_upgradeable();
19652bcb59eSGnoCiYeH         if window_size.col == 0 && window_size.row == 0 {
19752bcb59eSGnoCiYeH             let mut window_size = window_size.upgrade();
19852bcb59eSGnoCiYeH             window_size.col = vc_data.cols as u16;
19952bcb59eSGnoCiYeH             window_size.row = vc_data.rows as u16;
20052bcb59eSGnoCiYeH         }
20152bcb59eSGnoCiYeH 
20252bcb59eSGnoCiYeH         if vc_data.utf {
20352bcb59eSGnoCiYeH             tty_core.termios_write().input_mode.insert(InputMode::IUTF8);
20452bcb59eSGnoCiYeH         } else {
20552bcb59eSGnoCiYeH             tty_core.termios_write().input_mode.remove(InputMode::IUTF8);
20652bcb59eSGnoCiYeH         }
20752bcb59eSGnoCiYeH 
208dfe53cf0SGnoCiYeH         // 设置tty的端口为vc端口
209dfe53cf0SGnoCiYeH         vc_data.port().setup_internal_tty(Arc::downgrade(&tty));
210dfe53cf0SGnoCiYeH         tty.set_port(vc_data.port());
21152bcb59eSGnoCiYeH         // 加入sysfs?
21252bcb59eSGnoCiYeH 
213dfe53cf0SGnoCiYeH         CURRENT_VCNUM.store(tty_core.index() as isize, Ordering::SeqCst);
21452bcb59eSGnoCiYeH         Ok(())
21552bcb59eSGnoCiYeH     }
21652bcb59eSGnoCiYeH 
open(&self, _tty: &TtyCoreData) -> Result<(), SystemError>21752bcb59eSGnoCiYeH     fn open(&self, _tty: &TtyCoreData) -> Result<(), SystemError> {
21852bcb59eSGnoCiYeH         Ok(())
21952bcb59eSGnoCiYeH     }
22052bcb59eSGnoCiYeH 
write_room(&self, _tty: &TtyCoreData) -> usize22152bcb59eSGnoCiYeH     fn write_room(&self, _tty: &TtyCoreData) -> usize {
22252bcb59eSGnoCiYeH         32768
22352bcb59eSGnoCiYeH     }
22452bcb59eSGnoCiYeH 
22552bcb59eSGnoCiYeH     /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/tty/vt/vt.c#2894
22652bcb59eSGnoCiYeH     #[inline(never)]
write(&self, tty: &TtyCoreData, buf: &[u8], nr: usize) -> Result<usize, SystemError>22752bcb59eSGnoCiYeH     fn write(&self, tty: &TtyCoreData, buf: &[u8], nr: usize) -> Result<usize, SystemError> {
228dfe53cf0SGnoCiYeH         // if String::from_utf8_lossy(buf) == "Hello world!\n" {
229dfe53cf0SGnoCiYeH         //     loop {}
230dfe53cf0SGnoCiYeH         // }
23152bcb59eSGnoCiYeH         let ret = self.do_write(tty, buf, nr);
23252bcb59eSGnoCiYeH         self.flush_chars(tty);
23352bcb59eSGnoCiYeH         ret
23452bcb59eSGnoCiYeH     }
23552bcb59eSGnoCiYeH 
23652bcb59eSGnoCiYeH     #[inline(never)]
flush_chars(&self, tty: &TtyCoreData)23752da9a59SGnoCiYeH     fn flush_chars(&self, tty: &TtyCoreData) {
23852da9a59SGnoCiYeH         let mut vc_data = tty.vc_data_irqsave();
23952da9a59SGnoCiYeH         vc_data.set_cursor();
24052da9a59SGnoCiYeH     }
24152da9a59SGnoCiYeH 
put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError>24252da9a59SGnoCiYeH     fn put_char(&self, tty: &TtyCoreData, ch: u8) -> Result<(), SystemError> {
24352da9a59SGnoCiYeH         self.write(tty, &[ch], 1)?;
24452da9a59SGnoCiYeH         Ok(())
24552da9a59SGnoCiYeH     }
24652da9a59SGnoCiYeH 
ioctl(&self, _tty: Arc<TtyCore>, _cmd: u32, _arg: usize) -> Result<(), SystemError>24752da9a59SGnoCiYeH     fn ioctl(&self, _tty: Arc<TtyCore>, _cmd: u32, _arg: usize) -> Result<(), SystemError> {
24852da9a59SGnoCiYeH         // TODO
24952da9a59SGnoCiYeH         Err(SystemError::ENOIOCTLCMD)
25052da9a59SGnoCiYeH     }
251dfe53cf0SGnoCiYeH 
close(&self, _tty: Arc<TtyCore>) -> Result<(), SystemError>252dfe53cf0SGnoCiYeH     fn close(&self, _tty: Arc<TtyCore>) -> Result<(), SystemError> {
253dfe53cf0SGnoCiYeH         Ok(())
254dfe53cf0SGnoCiYeH     }
2559365e801SGnoCiYeH 
resize( &self, _tty: Arc<TtyCore>, _winsize: super::termios::WindowSize, ) -> Result<(), SystemError>2569365e801SGnoCiYeH     fn resize(
2579365e801SGnoCiYeH         &self,
2589365e801SGnoCiYeH         _tty: Arc<TtyCore>,
2599365e801SGnoCiYeH         _winsize: super::termios::WindowSize,
2609365e801SGnoCiYeH     ) -> Result<(), SystemError> {
2619365e801SGnoCiYeH         todo!()
2629365e801SGnoCiYeH     }
26352da9a59SGnoCiYeH }
26452da9a59SGnoCiYeH 
26552da9a59SGnoCiYeH #[derive(Debug, Clone)]
26652da9a59SGnoCiYeH pub struct VtModeData {
26752da9a59SGnoCiYeH     mode: VtMode,
26852da9a59SGnoCiYeH     /// 释放请求时触发的信号
26952da9a59SGnoCiYeH     relsig: u16,
27052da9a59SGnoCiYeH     /// 获取请求时触发的信号
27152da9a59SGnoCiYeH     acqsig: u16,
27252da9a59SGnoCiYeH }
27352da9a59SGnoCiYeH 
27452da9a59SGnoCiYeH #[allow(dead_code)]
27552da9a59SGnoCiYeH #[derive(Debug, Clone)]
27652da9a59SGnoCiYeH pub enum VtMode {
27752da9a59SGnoCiYeH     /// 自动切换模式,即在请求输入时自动切换到终端
27852da9a59SGnoCiYeH     Auto,
27952da9a59SGnoCiYeH     /// 手动切换模式,需要通过 ioctl 请求切换到终端
28052da9a59SGnoCiYeH     Process,
28152da9a59SGnoCiYeH     /// 等待终端确认,即在切换到终端时等待终端的确认信号
28252da9a59SGnoCiYeH     Ackacq,
28352da9a59SGnoCiYeH }
28452da9a59SGnoCiYeH 
28552da9a59SGnoCiYeH /// 用于给vc确定要写入的buf位置
28652da9a59SGnoCiYeH #[derive(Debug, Default)]
28752da9a59SGnoCiYeH pub struct DrawRegion {
28852da9a59SGnoCiYeH     /// 偏移量
28952da9a59SGnoCiYeH     pub offset: usize,
29052da9a59SGnoCiYeH     /// 写入数量
29152da9a59SGnoCiYeH     pub size: usize,
29252da9a59SGnoCiYeH     pub x: Option<u32>,
29352da9a59SGnoCiYeH }
29452da9a59SGnoCiYeH 
29552da9a59SGnoCiYeH // 初始化虚拟终端
29652da9a59SGnoCiYeH #[inline(never)]
vty_init() -> Result<(), SystemError>29752da9a59SGnoCiYeH pub fn vty_init() -> Result<(), SystemError> {
29852da9a59SGnoCiYeH     // 注册虚拟终端设备并将虚拟终端设备加入到文件系统
29952da9a59SGnoCiYeH     let vc0 = TtyDevice::new(
300dfe53cf0SGnoCiYeH         "vc0".to_string(),
30152da9a59SGnoCiYeH         IdTable::new(
30252da9a59SGnoCiYeH             String::from("vc0"),
30352da9a59SGnoCiYeH             Some(DeviceNumber::new(Major::TTY_MAJOR, 0)),
30452da9a59SGnoCiYeH         ),
305dfe53cf0SGnoCiYeH         TtyType::Tty,
30652da9a59SGnoCiYeH     );
30752da9a59SGnoCiYeH     // 注册tty设备
30852da9a59SGnoCiYeH     // CharDevOps::cdev_add(
30952da9a59SGnoCiYeH     //     vc0.clone() as Arc<dyn CharDevice>,
31052da9a59SGnoCiYeH     //     IdTable::new(
31152da9a59SGnoCiYeH     //         String::from("vc0"),
31252da9a59SGnoCiYeH     //         Some(DeviceNumber::new(Major::TTY_MAJOR, 0)),
31352da9a59SGnoCiYeH     //     ),
31452da9a59SGnoCiYeH     //     1,
31552da9a59SGnoCiYeH     // )?;
31652da9a59SGnoCiYeH 
31752da9a59SGnoCiYeH     // CharDevOps::register_chardev_region(DeviceNumber::new(Major::TTY_MAJOR, 0), 1, "/dev/vc/0")?;
31852da9a59SGnoCiYeH     device_register(vc0.clone())?;
31952da9a59SGnoCiYeH     devfs_register("vc0", vc0)?;
32052da9a59SGnoCiYeH 
32152da9a59SGnoCiYeH     // vcs_init?
32252da9a59SGnoCiYeH 
32352da9a59SGnoCiYeH     let console_driver = TtyDriver::new(
32452da9a59SGnoCiYeH         MAX_NR_CONSOLES,
32552da9a59SGnoCiYeH         "tty",
32652da9a59SGnoCiYeH         1,
32752da9a59SGnoCiYeH         Major::TTY_MAJOR,
32852da9a59SGnoCiYeH         0,
32952da9a59SGnoCiYeH         TtyDriverType::Console,
330b5b571e0SLoGin         *TTY_STD_TERMIOS,
33152da9a59SGnoCiYeH         Arc::new(TtyConsoleDriverInner::new()?),
33252da9a59SGnoCiYeH     );
33352da9a59SGnoCiYeH 
33452da9a59SGnoCiYeH     TtyDriverManager::tty_register_driver(console_driver)?;
33552da9a59SGnoCiYeH 
33652da9a59SGnoCiYeH     CURRENT_VCNUM.store(0, Ordering::SeqCst);
33752da9a59SGnoCiYeH 
33852da9a59SGnoCiYeH     // 初始化键盘?
33952da9a59SGnoCiYeH 
34052da9a59SGnoCiYeH     // TODO: 为vc
34152da9a59SGnoCiYeH 
34252da9a59SGnoCiYeH     Ok(())
34352da9a59SGnoCiYeH }
344