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