1 #include "textui.h"
2 
3 #include <driver/uart/uart.h>
4 #include "screen_manager.h"
5 #include <common/atomic.h>
6 #include <common/errno.h>
7 #include <common/printk.h>
8 #include <common/string.h>
9 
10 struct scm_ui_framework_t textui_framework;
11 static spinlock_t __window_id_lock = {1};
12 static uint32_t __window_max_id = 0;
13 
14 // 暂时初始化16080个初始字符对象以及67个虚拟行对象
15 #define INITIAL_CHARS 16080
16 #define INITIAL_VLINES (int)(1080 / 16)
17 static struct textui_char_chromatic_t __initial_chars[INITIAL_CHARS] = {0};
18 static struct textui_vline_chromatic_t __initial_vlines[INITIAL_VLINES] = {0};
19 static struct textui_window_t __initial_window = {0}; // 初始窗口
20 static struct textui_private_info_t __private_info = {0};
21 static struct List __windows_list;
22 static spinlock_t change_lock;
23 
24 // 用于标记是否允许输出到屏幕
25 static atomic_t __put_window_enable_flag = {1};
26 
27 /**
28  * @brief 初始化window对象
29  *
30  * @param window 窗口对象
31  * @param flags 标志位
32  * @param vlines_num 虚拟行的总数
33  * @param vlines_ptr 虚拟行数组指针
34  * @param cperline 每行最大的字符数
35  */
__textui_init_window(struct textui_window_t * window,uint8_t flags,uint16_t vlines_num,void * vlines_ptr,uint16_t cperline)36 static int __textui_init_window(struct textui_window_t *window, uint8_t flags, uint16_t vlines_num, void *vlines_ptr,
37                                 uint16_t cperline)
38 {
39     memset((window), 0, sizeof(struct textui_window_t));
40     list_init(&(window)->list);
41     window->lock.lock = 1;
42     spin_lock(&__window_id_lock);
43     window->id = __window_max_id++;
44     spin_unlock(&__window_id_lock);
45     window->flags = flags;
46     window->vlines_num = vlines_num;
47     window->vlines_used = 1;
48     window->top_vline = 0;
49     window->vline_operating = 0;
50     window->chars_per_line = cperline;
51     if (textui_is_chromatic(flags))
52         window->vlines.chromatic = vlines_ptr;
53     else
54         window->vlines.normal = vlines_ptr;
55     list_add(&__windows_list, &(window)->list);
56 }
57 
58 /**
59  * @brief 初始化虚拟行对象
60  *
61  * @param vline 虚拟行对象指针
62  * @param chars_ptr 字符对象数组指针
63  */
64 #define __textui_init_vline(vline, chars_ptr)                                                                          \
65     do                                                                                                                 \
66     {                                                                                                                  \
67         memset(vline, 0, sizeof(struct textui_vline_chromatic_t));                                                     \
68         (vline)->index = 0;                                                                                            \
69         (vline)->chars = chars_ptr;                                                                                    \
70     } while (0)
71 
textui_install_handler(struct scm_buffer_info_t * buf)72 int textui_install_handler(struct scm_buffer_info_t *buf)
73 {
74     // return printk_init(buf);
75     c_uart_send_str(COM1, "textui_install_handler\n");
76     return 0;
77 }
78 
textui_uninstall_handler(void * args)79 int textui_uninstall_handler(void *args)
80 {
81     return 0;
82 }
83 
textui_enable_handler(void * args)84 int textui_enable_handler(void *args)
85 {
86     c_uart_send_str(COM1, "textui_enable_handler\n");
87     atomic_cmpxchg(&__put_window_enable_flag, 0, 1);
88     return 0;
89 }
90 
textui_disable_handler(void * args)91 int textui_disable_handler(void *args)
92 {
93     c_uart_send_str(COM1, "textui_disable_handler\n");
94     atomic_set(&__put_window_enable_flag, 0);
95     return 0;
96 }
97 
textui_change_handler(struct scm_buffer_info_t * buf)98 int textui_change_handler(struct scm_buffer_info_t *buf)
99 {
100     memcpy((void *)buf->vaddr, (void *)(textui_framework.buf->vaddr), textui_framework.buf->size);
101     textui_framework.buf = buf;
102 
103     return 0;
104 }
105 
106 struct scm_ui_framework_operations_t textui_ops = {
107     .install = &textui_install_handler,
108     .uninstall = &textui_uninstall_handler,
109     .change = &textui_change_handler,
110     .enable = &textui_enable_handler,
111     .disable = &textui_disable_handler,
112 };
113 
114 /**
115  * @brief 获取textui的帧缓冲区能容纳的内容的行数
116  *
117  * @return uint16_t
118  */
__textui_get_actual_lines()119 uint16_t __textui_get_actual_lines()
120 {
121     return __private_info.actual_line;
122 }
123 
124 /**
125  * @brief 获取当前渲染的窗口的id
126  *
127  * @return uint16_t
128  */
__textui_get_current_window_id()129 uint32_t __textui_get_current_window_id()
130 {
131     return __private_info.current_window->id;
132 }
133 
134 /**
135  * @brief 插入换行
136  *
137  * @param window 窗口结构体
138  * @param vline_id 虚拟行号
139  * @return int
140  */
__textui_new_line(struct textui_window_t * window,uint16_t vline_id)141 static int __textui_new_line(struct textui_window_t *window, uint16_t vline_id)
142 {
143     // todo: 支持在两个虚拟行之间插入一个新行
144 
145     ++window->vline_operating;
146 
147     if (unlikely(window->vline_operating == window->vlines_num))
148         window->vline_operating = 0;
149     struct textui_vline_chromatic_t *vline = &window->vlines.chromatic[window->vline_operating];
150     memset(vline->chars, 0, sizeof(struct textui_char_chromatic_t) * window->chars_per_line);
151     vline->index = 0;
152 
153     if (likely(window->vlines_used == window->vlines_num)) // 需要滚动屏幕
154     {
155 
156         ++window->top_vline;
157 
158         if (unlikely(window->top_vline >= window->vlines_num))
159             window->top_vline = 0;
160 
161         // 刷新所有行
162         textui_refresh_vlines(window, window->top_vline, window->vlines_num);
163     }
164     else
165         ++window->vlines_used;
166 
167     return 0;
168 }
169 
170 /**
171  * @brief 真正向屏幕上输出字符的函数
172  *
173  * @param window
174  * @param character
175  * @return int
176  */
__textui_putchar_window(struct textui_window_t * window,uint16_t character,uint32_t FRcolor,uint32_t BKcolor)177 static int __textui_putchar_window(struct textui_window_t *window, uint16_t character, uint32_t FRcolor,
178                                    uint32_t BKcolor)
179 {
180     if (textui_is_chromatic(window->flags)) // 启用彩色字符
181     {
182         struct textui_vline_chromatic_t *vline = &window->vlines.chromatic[window->vline_operating];
183 
184         vline->chars[vline->index].c = character;
185         vline->chars[vline->index].FRcolor = FRcolor & 0xffffff;
186         vline->chars[vline->index].BKcolor = BKcolor & 0xffffff;
187         ++vline->index;
188         textui_refresh_characters(window, window->vline_operating, vline->index - 1, 1);
189         // 换行
190         // 加入光标后,因为会识别光标,所以需超过该行最大字符数才能创建新行
191         if (vline->index > window->chars_per_line)
192         {
193             __textui_new_line(window, window->vline_operating);
194         }
195     }
196     else
197     {
198         // todo: 支持纯文本字符
199         while (1)
200             pause();
201     }
202     return 0;
203 }
204 
205 /**
206  * @brief 在指定窗口上输出一个字符
207  *
208  * @param window 窗口
209  * @param character 字符
210  * @param FRcolor 前景色(RGB)
211  * @param BKcolor 背景色(RGB)
212  * @return int
213  */
textui_putchar_window(struct textui_window_t * window,uint16_t character,uint32_t FRcolor,uint32_t BKcolor)214 int textui_putchar_window(struct textui_window_t *window, uint16_t character, uint32_t FRcolor, uint32_t BKcolor)
215 {
216     if (unlikely(character == '\0'))
217         return 0;
218     if (!textui_is_chromatic(window->flags)) // 暂不支持纯文本窗口
219         return 0;
220 
221     // uint64_t rflags = 0; // 加锁后rflags存储到这里
222     spin_lock_no_preempt(&window->lock);
223     c_uart_send(COM1, character);
224     // 如果禁止输出,直接返回
225     if(atomic_read(&__put_window_enable_flag) == 0)
226     {
227         spin_unlock_no_preempt(&window->lock);
228         return 0;
229     }
230 
231     if (unlikely(character == '\n'))
232     {
233         // 换行时还需要输出\r
234         c_uart_send(COM1, '\r');
235         __textui_new_line(window, window->vline_operating);
236         // spin_unlock_irqrestore(&window->lock, rflags);
237         spin_unlock_no_preempt(&window->lock);
238         return 0;
239     }
240     else if (character == '\t') // 输出制表符
241     {
242         int space_to_print = 8 - window->vlines.chromatic[window->vline_operating].index % 8;
243 
244         while (space_to_print--)
245         {
246             __textui_putchar_window(window, ' ', FRcolor, BKcolor);
247         }
248     }
249     else if (character == '\b') // 退格
250     {
251         char bufff[128] = {0};
252         --(window->vlines.chromatic[window->vline_operating].index);
253         {
254             uint16_t tmp = window->vlines.chromatic[window->vline_operating].index;
255             if (tmp >= 0)
256             {
257                 window->vlines.chromatic[window->vline_operating].chars[tmp].c = ' ';
258                 window->vlines.chromatic[window->vline_operating].chars[tmp].BKcolor = BKcolor & 0xffffff;
259                 textui_refresh_characters(window, window->vline_operating, tmp, 1);
260             }
261         }
262         // 需要向上缩一行
263         if (window->vlines.chromatic[window->vline_operating].index <= 0)
264         {
265             window->vlines.chromatic[window->vline_operating].index = 0;
266             memset(window->vlines.chromatic[window->vline_operating].chars, 0,
267                    sizeof(struct textui_char_chromatic_t) * window->chars_per_line);
268             --(window->vline_operating);
269             if (unlikely(window->vline_operating < 0))
270                 window->vline_operating = window->vlines_num - 1;
271 
272             // 考虑是否向上滚动
273             if (likely(window->vlines_used > __private_info.actual_line))
274             {
275                 --window->top_vline;
276                 if (unlikely(window->top_vline < 0))
277                     window->top_vline = window->vlines_num - 1;
278             }
279             --window->vlines_used;
280             textui_refresh_vlines(window, window->top_vline, __private_info.actual_line);
281         }
282     }
283     else
284     {
285         if (window->vlines.chromatic[window->vline_operating].index == window->chars_per_line)
286             __textui_new_line(window, window->vline_operating);
287         __textui_putchar_window(window, character, FRcolor, BKcolor);
288     }
289 
290     // spin_unlock_irqrestore(&window->lock, rflags);
291     spin_unlock_no_preempt(&window->lock);
292     return 0;
293 }
294 
295 /**
296  * @brief 在默认窗口上输出一个字符
297  *
298  * @param character 字符
299  * @param FRcolor 前景色(RGB)
300  * @param BKcolor 背景色(RGB)
301  * @return int
302  */
textui_putchar(uint16_t character,uint32_t FRcolor,uint32_t BKcolor)303 int textui_putchar(uint16_t character, uint32_t FRcolor, uint32_t BKcolor)
304 {
305 
306     return textui_putchar_window(__private_info.default_window, character, FRcolor, BKcolor);
307 }
308 
309 /**
310  * @brief 初始化text ui框架
311  *
312  * @return int
313  */
textui_init()314 int textui_init()
315 {
316     spin_init(&change_lock);
317 
318     spin_init(&__window_id_lock);
319     __window_max_id = 0;
320     list_init(&__windows_list);
321     memset(&textui_framework, 0, sizeof(struct scm_ui_framework_t));
322     memset(&__private_info, 0, sizeof(struct textui_private_info_t));
323 
324     io_mfence();
325     char name[] = "textUI";
326     strcpy(textui_framework.name, name);
327 
328     textui_framework.ui_ops = &textui_ops;
329     textui_framework.type = 0;
330 
331     // 注册框架到屏幕管理器
332     int retval = scm_register(&textui_framework);
333     if (retval != 0)
334     {
335         c_uart_send_str(COM1, "text ui init failed\n");
336         while (1)
337             pause();
338     }
339 
340     uint16_t chars_per_vline = textui_framework.buf->width / TEXTUI_CHAR_WIDTH;
341     uint16_t total_vlines = textui_framework.buf->height / TEXTUI_CHAR_HEIGHT;
342     int cnt = chars_per_vline * total_vlines;
343 
344     struct textui_vline_chromatic_t *vl_ptr = __initial_vlines;
345     struct textui_char_chromatic_t *ch_ptr = __initial_chars;
346 
347     // 初始化虚拟行
348     for (int i = 0; i < total_vlines; ++i)
349     {
350         __textui_init_vline((vl_ptr + i), (ch_ptr + i * chars_per_vline));
351     }
352 
353     // 初始化窗口
354     __textui_init_window((&__initial_window), TEXTUI_WF_CHROMATIC, total_vlines, __initial_vlines, chars_per_vline);
355     __private_info.current_window = &__initial_window;
356     __private_info.default_window = &__initial_window;
357     __private_info.actual_line = textui_framework.buf->height / TEXTUI_CHAR_HEIGHT;
358 
359     c_uart_send_str(COM1, "text ui initialized\n");
360     return 0;
361 }
362 
363 
enable_textui()364 void enable_textui()
365 {
366     scm_framework_enable(&textui_framework);
367 }
368 
disable_textui()369 void disable_textui()
370 {
371     scm_framework_disable(&textui_framework);
372 }