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