2022-08-15 01:42:34 +08:00

336 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "textui.h"
#include "screen_manager.h"
#include "driver/uart/uart.h"
#include <common/string.h>
#include <common/printk.h>
#include <common/atomic.h>
#include <common/errno.h>
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;
/**
* @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);
uart_send_str(COM1, "textui_install_handler");
return 0;
}
int textui_uninstall_handler(void *args)
{
return 0;
}
int textui_enable_handler(void *args)
{
uart_send_str(COM1, "textui_enable_handler");
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_irqsave(&window->lock, rflags);
uart_send(COM1, character);
if (unlikely(character == '\n'))
{
__textui_new_line(window, window->vline_operating);
spin_unlock_irqrestore(&window->lock, rflags);
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 = ' ';
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);
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(&__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)
{
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;
uart_send_str(COM1, "text ui initialized\n");
return 0;
}