#include "textui.h" #include #include "screen_manager.h" #include #include #include #include struct scm_ui_framework_t textui_framework; static spinlock_t __window_id_lock = {1}; static uint32_t __window_max_id = 0; // 暂时初始化16080个初始字符对象以及67个虚拟行对象 #define INITIAL_CHARS 16080 #define INITIAL_VLINES (int)(1080 / 16) static struct textui_char_chromatic_t __initial_chars[INITIAL_CHARS] = {0}; static struct textui_vline_chromatic_t __initial_vlines[INITIAL_VLINES] = {0}; static struct textui_window_t __initial_window = {0}; // 初始窗口 static struct textui_private_info_t __private_info = {0}; static struct List __windows_list; static spinlock_t change_lock; /** * @brief 初始化window对象 * * @param window 窗口对象 * @param flags 标志位 * @param vlines_num 虚拟行的总数 * @param vlines_ptr 虚拟行数组指针 * @param cperline 每行最大的字符数 */ static int __textui_init_window(struct textui_window_t *window, uint8_t flags, uint16_t vlines_num, void *vlines_ptr, uint16_t cperline) { memset((window), 0, sizeof(struct textui_window_t)); list_init(&(window)->list); window->lock.lock = 1; spin_lock(&__window_id_lock); window->id = __window_max_id++; spin_unlock(&__window_id_lock); window->flags = flags; window->vlines_num = vlines_num; window->vlines_used = 1; window->top_vline = 0; window->vline_operating = 0; window->chars_per_line = cperline; if (textui_is_chromatic(flags)) window->vlines.chromatic = vlines_ptr; else window->vlines.normal = vlines_ptr; list_add(&__windows_list, &(window)->list); } /** * @brief 初始化虚拟行对象 * * @param vline 虚拟行对象指针 * @param chars_ptr 字符对象数组指针 */ #define __textui_init_vline(vline, chars_ptr) \ do \ { \ memset(vline, 0, sizeof(struct textui_vline_chromatic_t)); \ (vline)->index = 0; \ (vline)->chars = chars_ptr; \ } while (0) int textui_install_handler(struct scm_buffer_info_t *buf) { // return printk_init(buf); c_uart_send_str(COM1, "textui_install_handler"); return 0; } int textui_uninstall_handler(void *args) { return 0; } int textui_enable_handler(void *args) { c_uart_send_str(COM1, "textui_enable_handler\n"); return 0; } int textui_disable_handler(void *args) { return 0; } int textui_change_handler(struct scm_buffer_info_t *buf) { memcpy((void *)buf->vaddr, (void *)(textui_framework.buf->vaddr), textui_framework.buf->size); textui_framework.buf = buf; return 0; } struct scm_ui_framework_operations_t textui_ops = { .install = &textui_install_handler, .uninstall = &textui_uninstall_handler, .change = &textui_change_handler, .enable = &textui_enable_handler, .disable = &textui_disable_handler, }; /** * @brief 获取textui的帧缓冲区能容纳的内容的行数 * * @return uint16_t */ uint16_t __textui_get_actual_lines() { return __private_info.actual_line; } /** * @brief 获取当前渲染的窗口的id * * @return uint16_t */ uint32_t __textui_get_current_window_id() { return __private_info.current_window->id; } /** * @brief 插入换行 * * @param window 窗口结构体 * @param vline_id 虚拟行号 * @return int */ static int __textui_new_line(struct textui_window_t *window, uint16_t vline_id) { // todo: 支持在两个虚拟行之间插入一个新行 ++window->vline_operating; if (unlikely(window->vline_operating == window->vlines_num)) window->vline_operating = 0; struct textui_vline_chromatic_t *vline = &window->vlines.chromatic[window->vline_operating]; memset(vline->chars, 0, sizeof(struct textui_char_chromatic_t) * window->chars_per_line); vline->index = 0; if (likely(window->vlines_used == window->vlines_num)) // 需要滚动屏幕 { ++window->top_vline; if (unlikely(window->top_vline >= window->vlines_num)) window->top_vline = 0; // 刷新所有行 textui_refresh_vlines(window, window->top_vline, window->vlines_num); } else ++window->vlines_used; return 0; } /** * @brief 真正向屏幕上输出字符的函数 * * @param window * @param character * @return int */ static int __textui_putchar_window(struct textui_window_t *window, uint16_t character, uint32_t FRcolor, uint32_t BKcolor) { if (textui_is_chromatic(window->flags)) // 启用彩色字符 { struct textui_vline_chromatic_t *vline = &window->vlines.chromatic[window->vline_operating]; vline->chars[vline->index].c = character; vline->chars[vline->index].FRcolor = FRcolor & 0xffffff; vline->chars[vline->index].BKcolor = BKcolor & 0xffffff; ++vline->index; textui_refresh_characters(window, window->vline_operating, vline->index - 1, 1); // 换行 // 加入光标后,因为会识别光标,所以需超过该行最大字符数才能创建新行 if (vline->index > window->chars_per_line) { __textui_new_line(window, window->vline_operating); } } else { // todo: 支持纯文本字符 while (1) pause(); } return 0; } /** * @brief 在指定窗口上输出一个字符 * * @param window 窗口 * @param character 字符 * @param FRcolor 前景色(RGB) * @param BKcolor 背景色(RGB) * @return int */ int textui_putchar_window(struct textui_window_t *window, uint16_t character, uint32_t FRcolor, uint32_t BKcolor) { if (unlikely(character == '\0')) return 0; if (!textui_is_chromatic(window->flags)) // 暂不支持纯文本窗口 return 0; // uint64_t rflags = 0; // 加锁后rflags存储到这里 spin_lock_no_preempt(&window->lock); c_uart_send(COM1, character); if (unlikely(character == '\n')) { // 换行时还需要输出\r c_uart_send(COM1, '\r'); __textui_new_line(window, window->vline_operating); // spin_unlock_irqrestore(&window->lock, rflags); spin_unlock_no_preempt(&window->lock); return 0; } else if (character == '\t') // 输出制表符 { int space_to_print = 8 - window->vlines.chromatic[window->vline_operating].index % 8; while (space_to_print--) { __textui_putchar_window(window, ' ', FRcolor, BKcolor); } } else if (character == '\b') // 退格 { char bufff[128] = {0}; --(window->vlines.chromatic[window->vline_operating].index); { uint16_t tmp = window->vlines.chromatic[window->vline_operating].index; if (tmp >= 0) { window->vlines.chromatic[window->vline_operating].chars[tmp].c = ' '; window->vlines.chromatic[window->vline_operating].chars[tmp].BKcolor = BKcolor & 0xffffff; textui_refresh_characters(window, window->vline_operating, tmp, 1); } } // 需要向上缩一行 if (window->vlines.chromatic[window->vline_operating].index <= 0) { window->vlines.chromatic[window->vline_operating].index = 0; memset(window->vlines.chromatic[window->vline_operating].chars, 0, sizeof(struct textui_char_chromatic_t) * window->chars_per_line); --(window->vline_operating); if (unlikely(window->vline_operating < 0)) window->vline_operating = window->vlines_num - 1; // 考虑是否向上滚动 if (likely(window->vlines_used > __private_info.actual_line)) { --window->top_vline; if (unlikely(window->top_vline < 0)) window->top_vline = window->vlines_num - 1; } --window->vlines_used; textui_refresh_vlines(window, window->top_vline, __private_info.actual_line); } } else { if (window->vlines.chromatic[window->vline_operating].index == window->chars_per_line) __textui_new_line(window, window->vline_operating); __textui_putchar_window(window, character, FRcolor, BKcolor); } // spin_unlock_irqrestore(&window->lock, rflags); spin_unlock_no_preempt(&window->lock); return 0; } /** * @brief 在默认窗口上输出一个字符 * * @param character 字符 * @param FRcolor 前景色(RGB) * @param BKcolor 背景色(RGB) * @return int */ int textui_putchar(uint16_t character, uint32_t FRcolor, uint32_t BKcolor) { return textui_putchar_window(__private_info.default_window, character, FRcolor, BKcolor); } /** * @brief 初始化text ui框架 * * @return int */ int textui_init() { spin_init(&change_lock); spin_init(&__window_id_lock); __window_max_id = 0; list_init(&__windows_list); memset(&textui_framework, 0, sizeof(struct scm_ui_framework_t)); memset(&__private_info, 0, sizeof(struct textui_private_info_t)); io_mfence(); char name[] = "textUI"; strcpy(textui_framework.name, name); textui_framework.ui_ops = &textui_ops; textui_framework.type = 0; // 注册框架到屏幕管理器 int retval = scm_register(&textui_framework); if (retval != 0) { c_uart_send_str(COM1, "text ui init failed\n"); while (1) pause(); } uint16_t chars_per_vline = textui_framework.buf->width / TEXTUI_CHAR_WIDTH; uint16_t total_vlines = textui_framework.buf->height / TEXTUI_CHAR_HEIGHT; int cnt = chars_per_vline * total_vlines; struct textui_vline_chromatic_t *vl_ptr = __initial_vlines; struct textui_char_chromatic_t *ch_ptr = __initial_chars; // 初始化虚拟行 for (int i = 0; i < total_vlines; ++i) { __textui_init_vline((vl_ptr + i), (ch_ptr + i * chars_per_vline)); } // 初始化窗口 __textui_init_window((&__initial_window), TEXTUI_WF_CHROMATIC, total_vlines, __initial_vlines, chars_per_vline); __private_info.current_window = &__initial_window; __private_info.default_window = &__initial_window; __private_info.actual_line = textui_framework.buf->height / TEXTUI_CHAR_HEIGHT; c_uart_send_str(COM1, "text ui initialized\n"); return 0; }