xref: /DragonOS/kernel/src/libs/lib_ui/textui.rs (revision 9993c0fc61e9603f631bd6748ff0b4fecb7bd483)
1 use crate::{
2     driver::{
3         tty::serial::serial8250::send_to_default_serial8250_port, video::video_refresh_manager,
4     },
5     kdebug, kinfo,
6     libs::{
7         lib_ui::font::FONT_8x16,
8         rwlock::RwLock,
9         spinlock::{SpinLock, SpinLockGuard},
10     },
11 };
12 use alloc::{boxed::Box, collections::LinkedList, string::ToString};
13 use alloc::{sync::Arc, vec::Vec};
14 use core::{
15     fmt::Debug,
16     intrinsics::unlikely,
17     ops::{Add, AddAssign, Sub},
18     sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering},
19 };
20 use system_error::SystemError;
21 
22 use super::{
23     screen_manager::{
24         scm_register, ScmBuffer, ScmBufferInfo, ScmFramworkType, ScmUiFramework,
25         ScmUiFrameworkMetadata,
26     },
27     textui_no_alloc::no_init_textui_putchar_window,
28 };
29 
30 /// 声明全局的TEXTUI_FRAMEWORK
31 static mut __TEXTUI_FRAMEWORK: Option<Arc<TextUiFramework>> = None;
32 
33 /// 每个字符的宽度和高度(像素)
34 pub const TEXTUI_CHAR_WIDTH: u32 = 8;
35 
36 pub const TEXTUI_CHAR_HEIGHT: u32 = 16;
37 
38 pub static mut TEXTUI_IS_INIT: bool = false;
39 
40 static ENABLE_PUT_TO_WINDOW: AtomicBool = AtomicBool::new(false);
41 
42 /// 启用将文本输出到窗口的功能。
43 pub fn textui_enable_put_to_window() {
44     ENABLE_PUT_TO_WINDOW.store(true, Ordering::SeqCst);
45 }
46 
47 /// 禁用将文本输出到窗口的功能。
48 pub fn textui_disable_put_to_window() {
49     ENABLE_PUT_TO_WINDOW.store(false, Ordering::SeqCst);
50 }
51 
52 /// 检查是否启用了将文本输出到窗口的功能。
53 ///
54 /// # 返回
55 /// 如果启用了将文本输出到窗口的功能,则返回 `true`,否则返回 `false`。
56 pub fn textui_is_enable_put_to_window() -> bool {
57     ENABLE_PUT_TO_WINDOW.load(Ordering::SeqCst)
58 }
59 
60 /// 获取TEXTUI_FRAMEWORK的可变实例
61 pub fn textui_framework() -> Arc<TextUiFramework> {
62     unsafe {
63         return __TEXTUI_FRAMEWORK
64             .as_ref()
65             .expect("Textui framework has not been initialized yet!")
66             .clone();
67     }
68 }
69 
70 /// 初始化TEXTUI_FRAMEWORK
71 fn textui_framwork_init() {
72     if unsafe { __TEXTUI_FRAMEWORK.is_none() } {
73         kinfo!("textuiframework init");
74         let metadata = ScmUiFrameworkMetadata::new("TextUI".to_string(), ScmFramworkType::Text);
75         kdebug!("textui metadata: {:?}", metadata);
76         // 为textui框架生成第一个窗口
77         let vlines_num = (metadata.buf_info().height() / TEXTUI_CHAR_HEIGHT) as usize;
78 
79         let chars_num = (metadata.buf_info().width() / TEXTUI_CHAR_WIDTH) as usize;
80 
81         let initial_window = TextuiWindow::new(
82             WindowFlag::TEXTUI_CHROMATIC,
83             vlines_num as i32,
84             chars_num as i32,
85         );
86 
87         let current_window: Arc<SpinLock<TextuiWindow>> = Arc::new(SpinLock::new(initial_window));
88 
89         let default_window = current_window.clone();
90 
91         // 生成窗口链表,并把上面窗口添加进textui框架的窗口链表中
92         let window_list: Arc<SpinLock<LinkedList<Arc<SpinLock<TextuiWindow>>>>> =
93             Arc::new(SpinLock::new(LinkedList::new()));
94         window_list.lock().push_back(current_window.clone());
95 
96         unsafe {
97             __TEXTUI_FRAMEWORK = Some(Arc::new(TextUiFramework::new(
98                 metadata,
99                 window_list,
100                 current_window,
101                 default_window,
102             )))
103         };
104 
105         scm_register(textui_framework()).expect("register textui framework failed");
106         kdebug!("textui framework init success");
107 
108         send_to_default_serial8250_port("\ntext ui initialized\n\0".as_bytes());
109         unsafe { TEXTUI_IS_INIT = true };
110     } else {
111         panic!("Try to init TEXTUI_FRAMEWORK twice!");
112     }
113 }
114 // window标志位
115 bitflags! {
116     pub struct WindowFlag: u8 {
117         // 采用彩色字符
118         const TEXTUI_CHROMATIC = 1 << 0;
119     }
120 }
121 
122 /**
123  * @brief 黑白字符对象
124  *
125  */
126 #[derive(Clone, Debug)]
127 struct TextuiCharNormal {
128     _data: u8,
129 }
130 
131 #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Default)]
132 pub struct LineId(i32);
133 impl LineId {
134     pub fn new(num: i32) -> Self {
135         LineId(num)
136     }
137 
138     pub fn check(&self, max: i32) -> bool {
139         self.0 < max && self.0 >= 0
140     }
141 
142     pub fn data(&self) -> i32 {
143         self.0
144     }
145 }
146 impl Add<i32> for LineId {
147     type Output = LineId;
148     fn add(self, rhs: i32) -> Self::Output {
149         LineId::new(self.0 + rhs)
150     }
151 }
152 impl Sub<i32> for LineId {
153     type Output = LineId;
154 
155     fn sub(self, rhs: i32) -> Self::Output {
156         LineId::new(self.0 - rhs)
157     }
158 }
159 
160 impl Into<i32> for LineId {
161     fn into(self) -> i32 {
162         self.0.clone()
163     }
164 }
165 impl Into<u32> for LineId {
166     fn into(self) -> u32 {
167         self.0.clone() as u32
168     }
169 }
170 impl Into<usize> for LineId {
171     fn into(self) -> usize {
172         self.0.clone() as usize
173     }
174 }
175 impl Sub<LineId> for LineId {
176     type Output = LineId;
177 
178     fn sub(mut self, rhs: LineId) -> Self::Output {
179         self.0 -= rhs.0;
180         return self;
181     }
182 }
183 impl AddAssign<LineId> for LineId {
184     fn add_assign(&mut self, rhs: LineId) {
185         self.0 += rhs.0;
186     }
187 }
188 #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Default)]
189 pub struct LineIndex(i32);
190 impl LineIndex {
191     pub fn new(num: i32) -> Self {
192         LineIndex(num)
193     }
194     pub fn check(&self, chars_per_line: i32) -> bool {
195         self.0 < chars_per_line && self.0 >= 0
196     }
197 }
198 impl Add<LineIndex> for LineIndex {
199     type Output = LineIndex;
200 
201     fn add(self, rhs: LineIndex) -> Self::Output {
202         LineIndex::new(self.0 + rhs.0)
203     }
204 }
205 impl Add<i32> for LineIndex {
206     // type Output = Self;
207     type Output = LineIndex;
208 
209     fn add(self, rhs: i32) -> Self::Output {
210         LineIndex::new(self.0 + rhs)
211     }
212 }
213 impl Sub<i32> for LineIndex {
214     type Output = LineIndex;
215 
216     fn sub(self, rhs: i32) -> Self::Output {
217         LineIndex::new(self.0 - rhs)
218     }
219 }
220 
221 impl Into<i32> for LineIndex {
222     fn into(self) -> i32 {
223         self.0.clone()
224     }
225 }
226 impl Into<u32> for LineIndex {
227     fn into(self) -> u32 {
228         self.0.clone() as u32
229     }
230 }
231 impl Into<usize> for LineIndex {
232     fn into(self) -> usize {
233         self.0.clone() as usize
234     }
235 }
236 #[derive(Copy, Clone, Debug)]
237 pub struct FontColor(u32);
238 #[allow(dead_code)]
239 impl FontColor {
240     pub const BLUE: FontColor = FontColor::new(0, 0, 0xff);
241     pub const RED: FontColor = FontColor::new(0xff, 0, 0);
242     pub const GREEN: FontColor = FontColor::new(0, 0xff, 0);
243     pub const WHITE: FontColor = FontColor::new(0xff, 0xff, 0xff);
244     pub const BLACK: FontColor = FontColor::new(0, 0, 0);
245     pub const YELLOW: FontColor = FontColor::new(0xff, 0xff, 0);
246     pub const ORANGE: FontColor = FontColor::new(0xff, 0x80, 0);
247     pub const INDIGO: FontColor = FontColor::new(0x00, 0xff, 0xff);
248     pub const PURPLE: FontColor = FontColor::new(0x80, 0x00, 0xff);
249 
250     pub const fn new(r: u8, g: u8, b: u8) -> Self {
251         let val = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
252         return FontColor(val & 0x00ffffff);
253     }
254 }
255 
256 impl From<u32> for FontColor {
257     fn from(value: u32) -> Self {
258         return Self(value & 0x00ffffff);
259     }
260 }
261 impl Into<usize> for FontColor {
262     fn into(self) -> usize {
263         self.0.clone() as usize
264     }
265 }
266 impl Into<u32> for FontColor {
267     fn into(self) -> u32 {
268         self.0.clone()
269     }
270 }
271 impl Into<u16> for FontColor {
272     fn into(self) -> u16 {
273         self.0.clone() as u16
274     }
275 }
276 impl Into<u64> for FontColor {
277     fn into(self) -> u64 {
278         self.0.clone() as u64
279     }
280 }
281 
282 /// 彩色字符对象
283 
284 #[derive(Clone, Debug, Copy)]
285 pub struct TextuiCharChromatic {
286     c: Option<char>,
287 
288     // 前景色
289     frcolor: FontColor, // rgb
290 
291     // 背景色
292     bkcolor: FontColor, // rgb
293 }
294 
295 #[derive(Debug)]
296 pub struct TextuiBuf<'a> {
297     buf: Option<&'a mut [u32]>,
298     guard: Option<SpinLockGuard<'a, Box<[u32]>>>,
299 }
300 
301 impl TextuiBuf<'_> {
302     pub fn new(buf: &mut ScmBufferInfo) -> TextuiBuf {
303         let len = buf.buf_size() / 4;
304 
305         match &buf.buf {
306             ScmBuffer::DeviceBuffer(vaddr) => {
307                 return TextuiBuf {
308                     buf: Some(unsafe {
309                         core::slice::from_raw_parts_mut(vaddr.data() as *mut u32, len)
310                     }),
311                     guard: None,
312                 };
313             }
314 
315             ScmBuffer::DoubleBuffer(double_buffer) => {
316                 let guard: SpinLockGuard<'_, Box<[u32]>> = double_buffer.lock();
317 
318                 return TextuiBuf {
319                     buf: None,
320                     guard: Some(guard),
321                 };
322             }
323         }
324     }
325 
326     pub fn buf_mut(&mut self) -> &mut [u32] {
327         if let Some(buf) = &mut self.buf {
328             return buf;
329         } else {
330             return self.guard.as_mut().unwrap().as_mut();
331         }
332     }
333     pub fn put_color_in_pixel(&mut self, color: u32, index: usize) {
334         let buf: &mut [u32] = self.buf_mut();
335         buf[index] = color;
336     }
337     pub fn get_index_of_next_line(now_index: usize) -> usize {
338         textui_framework().metadata.read().buf_info().width() as usize + now_index
339     }
340     pub fn get_index_by_x_y(x: usize, y: usize) -> usize {
341         textui_framework().metadata.read().buf_info().width() as usize * y + x
342     }
343     pub fn get_start_index_by_lineid_lineindex(lineid: LineId, lineindex: LineIndex) -> usize {
344         //   x 左上角列像素点位置
345         //   y 左上角行像素点位置
346         let index_x: u32 = lineindex.into();
347         let x: u32 = index_x * TEXTUI_CHAR_WIDTH;
348 
349         let id_y: u32 = lineid.into();
350         let y: u32 = id_y * TEXTUI_CHAR_HEIGHT;
351 
352         TextuiBuf::get_index_by_x_y(x as usize, y as usize)
353     }
354 }
355 
356 #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
357 pub struct Font([u8; 16]);
358 impl Font {
359     #[inline]
360     pub fn get_font(character: char) -> Font {
361         let x = FONT_8x16.char_map(character);
362 
363         let mut data = [0u8; 16];
364         data.copy_from_slice(x);
365         return Font(data);
366     }
367     pub fn is_frcolor(&self, height: usize, width: usize) -> bool {
368         let w = self.0[height];
369         let testbit = 1 << (8 - width);
370         w & testbit != 0
371     }
372 }
373 
374 impl TextuiCharChromatic {
375     pub fn new(c: Option<char>, frcolor: FontColor, bkcolor: FontColor) -> Self {
376         TextuiCharChromatic {
377             c,
378             frcolor,
379             bkcolor,
380         }
381     }
382 
383     /// 将该字符对象输出到缓冲区
384     /// ## 参数
385     /// -line_id 要放入的真实行号
386     /// -index 要放入的真实列号
387     pub fn textui_refresh_character(
388         &self,
389         lineid: LineId,
390         lineindex: LineIndex,
391     ) -> Result<i32, SystemError> {
392         // 找到要渲染的字符的像素点数据
393 
394         let font: Font = Font::get_font(self.c.unwrap_or(' '));
395 
396         let mut count = TextuiBuf::get_start_index_by_lineid_lineindex(lineid, lineindex);
397 
398         let mut _binding = textui_framework().metadata.read().buf_info();
399 
400         let mut buf = TextuiBuf::new(&mut _binding);
401 
402         // 在缓冲区画出一个字体,每个字体有TEXTUI_CHAR_HEIGHT行,TEXTUI_CHAR_WIDTH列个像素点
403         for i in 0..TEXTUI_CHAR_HEIGHT {
404             let start = count;
405             for j in 0..TEXTUI_CHAR_WIDTH {
406                 if font.is_frcolor(i as usize, j as usize) {
407                     // 字,显示前景色
408                     buf.put_color_in_pixel(self.frcolor.into(), count);
409                 } else {
410                     // 背景色
411                     buf.put_color_in_pixel(self.bkcolor.into(), count);
412                 }
413                 count += 1;
414             }
415             count = TextuiBuf::get_index_of_next_line(start);
416         }
417 
418         return Ok(0);
419     }
420 
421     pub fn no_init_textui_render_chromatic(&self, lineid: LineId, lineindex: LineIndex) {
422         // 找到要渲染的字符的像素点数据
423         let font = Font::get_font(self.c.unwrap_or(' '));
424 
425         //   x 左上角列像素点位置
426         //   y 左上角行像素点位置
427         let index_x: u32 = lineindex.into();
428         let x: u32 = index_x * TEXTUI_CHAR_WIDTH;
429 
430         let id_y: u32 = lineid.into();
431         let y: u32 = id_y * TEXTUI_CHAR_HEIGHT;
432 
433         let buf_width = video_refresh_manager().device_buffer().width();
434         // 找到输入缓冲区的起始地址位置
435         let buf_start =
436             if let ScmBuffer::DeviceBuffer(vaddr) = video_refresh_manager().device_buffer().buf {
437                 vaddr
438             } else {
439                 panic!("device buffer is not init");
440             };
441 
442         let mut testbit: u32; // 用来测试特定行的某列是背景还是字体本身
443 
444         // 在缓冲区画出一个字体,每个字体有TEXTUI_CHAR_HEIGHT行,TEXTUI_CHAR_WIDTH列个像素点
445         for i in 0..TEXTUI_CHAR_HEIGHT {
446             // 计算出帧缓冲区每一行打印的起始位置的地址(起始位置+(y+i)*缓冲区的宽度+x)
447 
448             let mut addr: *mut u32 =
449                 (buf_start + buf_width as usize * 4 * (y as usize + i as usize) + 4 * x as usize)
450                     .data() as *mut u32;
451 
452             testbit = 1 << (TEXTUI_CHAR_WIDTH + 1);
453 
454             for _j in 0..TEXTUI_CHAR_WIDTH {
455                 //从左往右逐个测试相应位
456                 testbit >>= 1;
457                 if (font.0[i as usize] & testbit as u8) != 0 {
458                     unsafe { *addr = self.frcolor.into() }; // 字,显示前景色
459                 } else {
460                     unsafe { *addr = self.bkcolor.into() }; // 背景色
461                 }
462 
463                 unsafe {
464                     addr = (addr.offset(1)) as *mut u32;
465                 }
466             }
467         }
468     }
469 }
470 
471 /// 单色显示的虚拟行结构体
472 
473 #[derive(Clone, Debug, Default)]
474 pub struct TextuiVlineNormal {
475     _characters: Vec<TextuiCharNormal>, // 字符对象数组
476     _index: i16,                        // 当前操作的位置
477 }
478 /// 彩色显示的虚拟行结构体
479 
480 #[derive(Clone, Debug, Default)]
481 pub struct TextuiVlineChromatic {
482     chars: Vec<TextuiCharChromatic>, // 字符对象数组
483     index: LineIndex,                // 当前操作的位置
484 }
485 impl TextuiVlineChromatic {
486     pub fn new(char_num: usize) -> Self {
487         let mut r = TextuiVlineChromatic {
488             chars: Vec::with_capacity(char_num),
489             index: LineIndex::new(0),
490         };
491 
492         for _ in 0..char_num {
493             r.chars.push(TextuiCharChromatic::new(
494                 None,
495                 FontColor::BLACK,
496                 FontColor::BLACK,
497             ));
498         }
499 
500         return r;
501     }
502 }
503 
504 #[derive(Clone, Debug)]
505 pub enum TextuiVline {
506     Chromatic(TextuiVlineChromatic),
507     _Normal(TextuiVlineNormal),
508 }
509 
510 #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
511 pub struct WindowId(u32);
512 
513 impl WindowId {
514     pub fn new() -> Self {
515         static MAX_ID: AtomicU32 = AtomicU32::new(0);
516         return WindowId(MAX_ID.fetch_add(1, Ordering::SeqCst));
517     }
518 }
519 #[allow(dead_code)]
520 #[derive(Clone, Debug)]
521 pub struct TextuiWindow {
522     // 虚拟行是个循环表,头和尾相接
523     id: WindowId,
524     // 虚拟行总数
525     vline_sum: i32,
526     // 当前已经使用了的虚拟行总数(即在已经输入到缓冲区(之后显示在屏幕上)的虚拟行数量)
527     vlines_used: i32,
528     // 位于最顶上的那一个虚拟行的行号
529     top_vline: LineId,
530     // 储存虚拟行的数组
531     vlines: Vec<TextuiVline>,
532     // 正在操作的vline
533     vline_operating: LineId,
534     // 每行最大容纳的字符数
535     chars_per_line: i32,
536     // 窗口flag
537     flags: WindowFlag,
538 }
539 
540 impl TextuiWindow {
541     /// 使用参数初始化window对象
542     /// ## 参数
543     ///
544     /// -flags 标志位
545     /// -vlines_num 虚拟行的总数
546     /// -chars_num 每行最大的字符数
547 
548     pub fn new(flags: WindowFlag, vlines_num: i32, chars_num: i32) -> Self {
549         let mut initial_vlines = Vec::new();
550 
551         for _ in 0..vlines_num {
552             let vline = TextuiVlineChromatic::new(chars_num as usize);
553 
554             initial_vlines.push(TextuiVline::Chromatic(vline));
555         }
556         TextuiWindow {
557             id: WindowId::new(),
558             flags,
559             vline_sum: vlines_num,
560             vlines_used: 1,
561             top_vline: LineId::new(0),
562             vlines: initial_vlines,
563             vline_operating: LineId::new(0),
564             chars_per_line: chars_num,
565         }
566     }
567 
568     /// 刷新某个窗口的缓冲区的某个虚拟行的连续n个字符对象
569     /// ## 参数
570     /// - window 窗口结构体
571     /// - vline_id 要刷新的虚拟行号
572     /// - start 起始字符号
573     /// - count 要刷新的字符数量
574 
575     fn textui_refresh_characters(
576         &mut self,
577         vline_id: LineId,
578         start: LineIndex,
579         count: i32,
580     ) -> Result<(), SystemError> {
581         let actual_line_sum = textui_framework().actual_line.load(Ordering::SeqCst);
582 
583         // 判断虚拟行参数是否合法
584         if unlikely(
585             !vline_id.check(self.vline_sum)
586                 || (<LineIndex as Into<i32>>::into(start) + count) > self.chars_per_line,
587         ) {
588             return Err(SystemError::EINVAL);
589         }
590         // 计算虚拟行对应的真实行(即要渲染的行)
591         let mut actual_line_id = vline_id - self.top_vline; //为正说明虚拟行不在真实行显示的区域上面
592 
593         if <LineId as Into<i32>>::into(actual_line_id) < 0 {
594             //真实行数小于虚拟行数,则需要加上真实行数的位置,以便正确计算真实行
595             actual_line_id = actual_line_id + actual_line_sum;
596         }
597 
598         // 将此窗口的某个虚拟行的连续n个字符对象往缓存区写入
599         if self.flags.contains(WindowFlag::TEXTUI_CHROMATIC) {
600             let vline = &mut self.vlines[<LineId as Into<usize>>::into(vline_id)];
601             let mut i = 0;
602             let mut index = start;
603 
604             while i < count {
605                 if let TextuiVline::Chromatic(vline) = vline {
606                     vline.chars[<LineIndex as Into<usize>>::into(index)]
607                         .textui_refresh_character(actual_line_id, index)?;
608 
609                     index = index + 1;
610                 }
611                 i += 1;
612             }
613         }
614 
615         return Ok(());
616     }
617 
618     /// 重新渲染某个窗口的某个虚拟行
619     /// ## 参数
620 
621     /// - window 窗口结构体
622     /// - vline_id 虚拟行号
623 
624     fn textui_refresh_vline(&mut self, vline_id: LineId) -> Result<(), SystemError> {
625         if self.flags.contains(WindowFlag::TEXTUI_CHROMATIC) {
626             return self.textui_refresh_characters(
627                 vline_id,
628                 LineIndex::new(0),
629                 self.chars_per_line,
630             );
631         } else {
632             //todo支持纯文本字符()
633             todo!();
634         }
635     }
636 
637     // 刷新某个窗口的start 到start + count行(即将这些行输入到缓冲区)
638     fn textui_refresh_vlines(&mut self, start: LineId, count: i32) -> Result<i32, SystemError> {
639         let mut refresh_count = count;
640         for i in <LineId as Into<i32>>::into(start)
641             ..(self.vline_sum).min(<LineId as Into<i32>>::into(start) + count)
642         {
643             self.textui_refresh_vline(LineId::new(i))?;
644             refresh_count -= 1;
645         }
646         //因为虚拟行是循环表
647         let mut refresh_start = 0;
648         while refresh_count > 0 {
649             self.textui_refresh_vline(LineId::new(refresh_start))?;
650             refresh_start += 1;
651             refresh_count -= 1;
652         }
653         return Ok(0);
654     }
655 
656     /// 往某个窗口的缓冲区的某个虚拟行插入换行
657     /// ## 参数
658     /// - window 窗口结构体
659     /// - vline_id 虚拟行号
660 
661     fn textui_new_line(&mut self) -> Result<i32, SystemError> {
662         // todo: 支持在两个虚拟行之间插入一个新行
663         let actual_line_sum = textui_framework().actual_line.load(Ordering::SeqCst);
664         self.vline_operating = self.vline_operating + 1;
665         //如果已经到了最大行数,则重新从0开始
666         if !self.vline_operating.check(self.vline_sum) {
667             self.vline_operating = LineId::new(0);
668         }
669 
670         if let TextuiVline::Chromatic(vline) =
671             &mut (self.vlines[<LineId as Into<usize>>::into(self.vline_operating)])
672         {
673             for i in 0..self.chars_per_line {
674                 if let Some(v_char) = vline.chars.get_mut(i as usize) {
675                     v_char.c = None;
676                     v_char.frcolor = FontColor::BLACK;
677                     v_char.bkcolor = FontColor::BLACK;
678                 }
679             }
680             vline.index = LineIndex::new(0);
681         }
682         // 当已经使用的虚拟行总数等于真实行总数时,说明窗口中已经显示的文本行数已经达到了窗口的最大容量。这时,如果继续在窗口中添加新的文本,就会导致文本溢出窗口而无法显示。因此,需要往下滚动屏幕来显示更多的文本。
683 
684         if self.vlines_used == actual_line_sum {
685             self.top_vline = self.top_vline + 1;
686 
687             if !self.top_vline.check(self.vline_sum) {
688                 self.top_vline = LineId::new(0);
689             }
690 
691             // 刷新所有行
692             self.textui_refresh_vlines(self.top_vline, actual_line_sum)?;
693         } else {
694             //换行说明上一行已经在缓冲区中,所以已经使用的虚拟行总数+1
695             self.vlines_used += 1;
696         }
697 
698         return Ok(0);
699     }
700 
701     /// 真正向窗口的缓冲区上输入字符的函数(位置为window.vline_operating,window.vline_operating.index)
702     /// ## 参数
703     /// - window
704     /// - character
705 
706     fn true_textui_putchar_window(
707         &mut self,
708         character: char,
709         frcolor: FontColor,
710         bkcolor: FontColor,
711     ) -> Result<(), SystemError> {
712         // 启用彩色字符
713         if self.flags.contains(WindowFlag::TEXTUI_CHROMATIC) {
714             let mut line_index = LineIndex::new(0); //操作的列号
715             if let TextuiVline::Chromatic(vline) =
716                 &mut (self.vlines[<LineId as Into<usize>>::into(self.vline_operating)])
717             {
718                 let index = <LineIndex as Into<usize>>::into(vline.index);
719 
720                 if let Some(v_char) = vline.chars.get_mut(index) {
721                     v_char.c = Some(character);
722                     v_char.frcolor = frcolor;
723                     v_char.bkcolor = bkcolor;
724                 }
725                 line_index = vline.index;
726                 vline.index = vline.index + 1;
727             }
728 
729             self.textui_refresh_characters(self.vline_operating, line_index, 1)?;
730 
731             // 加入光标后,因为会识别光标,所以需超过该行最大字符数才能创建新行
732             if !line_index.check(self.chars_per_line - 1) {
733                 self.textui_new_line()?;
734             }
735         } else {
736             // todo: 支持纯文本字符
737             todo!();
738         }
739         return Ok(());
740     }
741     /// 根据输入的一个字符在窗口上输出
742     /// ## 参数
743 
744     /// - window 窗口
745     /// - character 字符
746     /// - FRcolor 前景色(RGB)
747     /// - BKcolor 背景色(RGB)
748 
749     fn textui_putchar_window(
750         &mut self,
751         character: char,
752         frcolor: FontColor,
753         bkcolor: FontColor,
754         is_enable_window: bool,
755     ) -> Result<(), SystemError> {
756         let actual_line_sum = textui_framework().actual_line.load(Ordering::SeqCst);
757 
758         //字符'\0'代表ASCII码表中的空字符,表示字符串的结尾
759         if unlikely(character == '\0') {
760             return Ok(());
761         }
762 
763         if unlikely(character == '\r') {
764             return Ok(());
765         }
766 
767         // 暂不支持纯文本窗口
768         if !self.flags.contains(WindowFlag::TEXTUI_CHROMATIC) {
769             return Ok(());
770         }
771         send_to_default_serial8250_port(&[character as u8]);
772 
773         //进行换行操作
774         if character == '\n' {
775             // 换行时还需要输出\r
776             send_to_default_serial8250_port(&[b'\r']);
777             if is_enable_window == true {
778                 self.textui_new_line()?;
779             }
780             return Ok(());
781         }
782         // 输出制表符
783         else if character == '\t' {
784             if is_enable_window == true {
785                 if let TextuiVline::Chromatic(vline) =
786                     &self.vlines[<LineId as Into<usize>>::into(self.vline_operating)]
787                 {
788                     //打印的空格数(注意将每行分成一个个表格,每个表格为8个字符)
789                     let mut space_to_print = 8 - <LineIndex as Into<usize>>::into(vline.index) % 8;
790                     while space_to_print > 0 {
791                         self.true_textui_putchar_window(' ', frcolor, bkcolor)?;
792                         space_to_print -= 1;
793                     }
794                 }
795             }
796         }
797         // 字符 '\x08' 代表 ASCII 码中的退格字符。它在输出中的作用是将光标向左移动一个位置,并在该位置上输出后续的字符,从而实现字符的删除或替换。
798         else if character == '\x08' {
799             if is_enable_window == true {
800                 let mut tmp = LineIndex(0);
801                 if let TextuiVline::Chromatic(vline) =
802                     &mut self.vlines[<LineId as Into<usize>>::into(self.vline_operating)]
803                 {
804                     vline.index = vline.index - 1;
805                     tmp = vline.index;
806                 }
807                 if <LineIndex as Into<i32>>::into(tmp) >= 0 {
808                     if let TextuiVline::Chromatic(vline) =
809                         &mut self.vlines[<LineId as Into<usize>>::into(self.vline_operating)]
810                     {
811                         if let Some(v_char) =
812                             vline.chars.get_mut(<LineIndex as Into<usize>>::into(tmp))
813                         {
814                             v_char.c = Some(' ');
815 
816                             v_char.bkcolor = bkcolor;
817                         }
818                     }
819                     return self.textui_refresh_characters(self.vline_operating, tmp, 1);
820                 }
821                 // 需要向上缩一行
822                 if <LineIndex as Into<i32>>::into(tmp) < 0 {
823                     // 当前行为空,需要重新刷新
824                     if let TextuiVline::Chromatic(vline) =
825                         &mut self.vlines[<LineId as Into<usize>>::into(self.vline_operating)]
826                     {
827                         vline.index = LineIndex::new(0);
828                         for i in 0..self.chars_per_line {
829                             if let Some(v_char) = vline.chars.get_mut(i as usize) {
830                                 v_char.c = None;
831                                 v_char.frcolor = FontColor::BLACK;
832                                 v_char.bkcolor = FontColor::BLACK;
833                             }
834                         }
835                     }
836                     // 上缩一行
837                     self.vline_operating = self.vline_operating - 1;
838                     if self.vline_operating.data() < 0 {
839                         self.vline_operating = LineId(self.vline_sum - 1);
840                     }
841 
842                     // 考虑是否向上滚动(在top_vline上退格)
843                     if self.vlines_used > actual_line_sum {
844                         self.top_vline = self.top_vline - 1;
845                         if <LineId as Into<i32>>::into(self.top_vline) < 0 {
846                             self.top_vline = LineId(self.vline_sum - 1);
847                         }
848                     }
849                     //因为上缩一行所以显示在屏幕中的虚拟行少一
850                     self.vlines_used -= 1;
851                     self.textui_refresh_vlines(self.top_vline, actual_line_sum)?;
852                 }
853             }
854         } else {
855             if is_enable_window == true {
856                 if let TextuiVline::Chromatic(vline) =
857                     &self.vlines[<LineId as Into<usize>>::into(self.vline_operating)]
858                 {
859                     if !vline.index.check(self.chars_per_line) {
860                         self.textui_new_line()?;
861                     }
862 
863                     return self.true_textui_putchar_window(character, frcolor, bkcolor);
864                 }
865             }
866         }
867 
868         return Ok(());
869     }
870 }
871 impl Default for TextuiWindow {
872     fn default() -> Self {
873         TextuiWindow {
874             id: WindowId(0),
875             flags: WindowFlag::TEXTUI_CHROMATIC,
876             vline_sum: 0,
877             vlines_used: 1,
878             top_vline: LineId::new(0),
879             vlines: Vec::new(),
880             vline_operating: LineId::new(0),
881             chars_per_line: 0,
882         }
883     }
884 }
885 #[allow(dead_code)]
886 #[derive(Debug)]
887 pub struct TextUiFramework {
888     metadata: RwLock<ScmUiFrameworkMetadata>,
889     window_list: Arc<SpinLock<LinkedList<Arc<SpinLock<TextuiWindow>>>>>,
890     actual_line: AtomicI32, // 真实行的数量(textui的帧缓冲区能容纳的内容的行数)
891     current_window: Arc<SpinLock<TextuiWindow>>, // 当前的主窗口
892     default_window: Arc<SpinLock<TextuiWindow>>, // 默认print到的窗口
893 }
894 
895 impl TextUiFramework {
896     pub fn new(
897         metadata: ScmUiFrameworkMetadata,
898         window_list: Arc<SpinLock<LinkedList<Arc<SpinLock<TextuiWindow>>>>>,
899         current_window: Arc<SpinLock<TextuiWindow>>,
900         default_window: Arc<SpinLock<TextuiWindow>>,
901     ) -> Self {
902         let actual_line =
903             AtomicI32::new((&metadata.buf_info().height() / TEXTUI_CHAR_HEIGHT) as i32);
904         let inner = TextUiFramework {
905             metadata: RwLock::new(metadata),
906             window_list,
907             actual_line,
908             current_window,
909             default_window,
910         };
911         return inner;
912     }
913 }
914 
915 impl ScmUiFramework for TextUiFramework {
916     // 安装ui框架的回调函数
917     fn install(&self) -> Result<i32, SystemError> {
918         send_to_default_serial8250_port("\ntextui_install_handler\n\0".as_bytes());
919         return Ok(0);
920     }
921     // 卸载ui框架的回调函数
922     fn uninstall(&self) -> Result<i32, SystemError> {
923         return Ok(0);
924     }
925     // 启用ui框架的回调函数
926     fn enable(&self) -> Result<i32, SystemError> {
927         textui_enable_put_to_window();
928         return Ok(0);
929     }
930     // 禁用ui框架的回调函数
931     fn disable(&self) -> Result<i32, SystemError> {
932         textui_disable_put_to_window();
933 
934         return Ok(0);
935     }
936     // 改变ui框架的帧缓冲区的回调函数
937     fn change(&self, buf_info: ScmBufferInfo) -> Result<i32, SystemError> {
938         let old_buf = textui_framework().metadata.read().buf_info();
939 
940         textui_framework().metadata.write().set_buf_info(buf_info);
941 
942         let mut new_buf = textui_framework().metadata.read().buf_info();
943 
944         new_buf.copy_from_nonoverlapping(&old_buf);
945         kdebug!("textui change buf_info: old: {:?}", old_buf);
946         kdebug!("textui change buf_info: new: {:?}", new_buf);
947 
948         return Ok(0);
949     }
950     ///  获取ScmUiFramework的元数据
951     ///  ## 返回值
952     ///
953     ///  -成功:Ok(ScmUiFramework的元数据)
954     ///  -失败:Err(错误码)
955     fn metadata(&self) -> Result<ScmUiFrameworkMetadata, SystemError> {
956         let metadata = self.metadata.read().clone();
957 
958         return Ok(metadata);
959     }
960 }
961 
962 /// Mapping from characters to glyph indices.
963 pub trait GlyphMapping: Sync {
964     /// Maps a character to a glyph index.
965     ///
966     /// If `c` isn't included in the font the index of a suitable replacement glyph is returned.
967     fn index(&self, c: char) -> usize;
968 }
969 
970 impl<F> GlyphMapping for F
971 where
972     F: Sync + Fn(char) -> usize,
973 {
974     fn index(&self, c: char) -> usize {
975         self(c)
976     }
977 }
978 
979 /// 在默认窗口上输出一个字符
980 /// ## 参数
981 /// - character 字符
982 /// - FRcolor 前景色(RGB)
983 /// - BKcolor 背景色(RGB)
984 
985 #[no_mangle]
986 pub extern "C" fn rs_textui_putchar(character: u8, fr_color: u32, bk_color: u32) -> i32 {
987     return textui_putchar(
988         character as char,
989         FontColor::from(fr_color),
990         FontColor::from(bk_color),
991     )
992     .map(|_| 0)
993     .unwrap_or_else(|e| e.to_posix_errno());
994 }
995 
996 pub fn textui_putchar(
997     character: char,
998     fr_color: FontColor,
999     bk_color: FontColor,
1000 ) -> Result<(), SystemError> {
1001     if unsafe { TEXTUI_IS_INIT } {
1002         return textui_framework()
1003             .current_window
1004             .lock()
1005             .textui_putchar_window(
1006                 character,
1007                 fr_color,
1008                 bk_color,
1009                 textui_is_enable_put_to_window(),
1010             );
1011     } else {
1012         //未初始化暴力输出
1013         return no_init_textui_putchar_window(
1014             character,
1015             fr_color,
1016             bk_color,
1017             textui_is_enable_put_to_window(),
1018         );
1019     }
1020 }
1021 
1022 /// 向默认窗口输出一个字符串
1023 pub fn textui_putstr(
1024     string: &str,
1025     fr_color: FontColor,
1026     bk_color: FontColor,
1027 ) -> Result<(), SystemError> {
1028     let window = if unsafe { TEXTUI_IS_INIT } {
1029         let fw = textui_framework();
1030         let w = fw.current_window.clone();
1031         Some(w)
1032     } else {
1033         None
1034     };
1035 
1036     let mut guard = window.as_ref().map(|w| w.lock());
1037 
1038     for character in string.chars() {
1039         if unsafe { TEXTUI_IS_INIT } {
1040             guard.as_mut().unwrap().textui_putchar_window(
1041                 character,
1042                 fr_color,
1043                 bk_color,
1044                 textui_is_enable_put_to_window(),
1045             )?;
1046         } else {
1047             no_init_textui_putchar_window(
1048                 character,
1049                 fr_color,
1050                 bk_color,
1051                 textui_is_enable_put_to_window(),
1052             )?;
1053         }
1054     }
1055 
1056     return Ok(());
1057 }
1058 
1059 /// 初始化text ui框架
1060 #[inline(never)]
1061 pub fn textui_init() -> Result<i32, SystemError> {
1062     textui_framwork_init();
1063 
1064     return Ok(0);
1065 }
1066