首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

如何改造 Linux 虚拟终端显示文字更新1

如何改造 Linux 虚拟终端显示文字更新1

为何图形模式控制台也不能显示汉字要解释图形模式控制台为何不能显示汉字,首先我们来了解一下虚拟终端是怎么管理屏幕上的文字显示的。虚拟终端的实现在 drivers/tty/vt/vt.c 。代表虚拟终端的数据是 struct vc。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct vc{
     struct vc_data;
     struct work_struct;
};
故而 struct vc_data 才是我們要的虛擬終端的定義。我們先來看看 struct vc_data 到底定義了什麼東西吧。
struct vc_data 的定義在 include/linux/console_struct.h, 定義摘錄如下,爲了不延長篇幅,有省略的部分:
struct vc_data {
     struct tty_port port;           /* Upper level data */

     unsigned short      vc_num;             /* Console number */
     unsigned int    vc_cols;        /* [#] Console size */
     unsigned int    vc_rows;
    省略 ...
     const struct consw *vc_sw;
     unsigned short *vc_screenbuf;  /* In-memory character/attribute buffer */
     unsigned int    vc_screenbuf_size;
    省略 ...
};




其中 vc_screenbuf 存储了虚拟终端要显示在屏幕上的文字。 const struct consw *vc_sw 指向控制台驱动提供的函数。虚拟终端利用里面的函数指针调用相应的操作,比如重绘屏幕,绘制一个字符等等。这些操作由 vgacon 和 fbcon 等控制台驱动实现。当你切换终端的时候,实际上就是把当前终端设置为你要切换过去的终端,并且重新绘制当前终端 vc_data->vc_screenbuf 存储的内容。当你从键盘输入命令或者程序运行过程中要输出内容的时候,虚拟终端首先将输出的字符进行编码转化,转化为对于字符的 GLYPH 代码,并且将 GLYPH 和当前字符属性结合,最后将合成结果写入当前光标所处的位置。内核中实际的算法要复杂的多,还牵涉到中断,但是为了简单快递的把我们关心的部分的核心表达出来,我使用一下伪代码表示不那么严谨的过程。希望了解全部的读者可以自行查看内核相关的代码,主要代码在 drivers/tty/vt.c 的 do_con_write() 中。
清单 1. 伪代码
1
2
3
4
5
6
7
8
9
10
11
12
vc_write(vc_data * vc, const char * string, int count){
           for( ; count ; ){
  /* 和当前编码有关,如果是 utf8 就以 utf8 方式解码不是 utf8 就按照 扩展的 ASCII 方式,也就是一个字节就是一个字母。*/
       int glyph = next_char(vc->utf,&string,&count);
       int c = vc_build_attribute(vc)|(vc_glyph_mask& glyph);
       // 把当前设置的前景色背景色等属性结合 ,glyph 不能超过描述它的位段 ;

       vc->vc_screenbuf[vc->vc_pos] = c; // 写入当前位置
        update_pos(); // 更新当前光标位置
    }
    notify_redraw(vc); // 调用 cosole 这个观察者重会屏幕
}




你也许想知道 vc_screenbuf 指向的缓冲区的格式到底是怎样的,现在也可以回答 前面提到过的为何图像控制台依然不能支持汉字显示的问题了:vc_screenbuf 的格式就是 VGA 文字模式时显卡所使用的文字缓冲区格式。上述伪代码中的  vc_glyph_mask 就是 0xFF, 也就是 glyph 被截断只能 8 比特长度。打从一开始,fbcon 就是按照 VGA 字符发生器设计的。因为当 vc_screenbuf 的格式和 VGA 字符缓冲区的格式一致的时候,切换终端就可以只需要 memcpy ——快速的拷贝到 VGA 字符缓冲区就能实现“重绘”当前终端。现在看来这种做法局限性非常大,但是单年 PC 性能还不够强大的时候,能做到快速的重绘是非常重要的。重绘例程中,notify_redraw(vc) 用伪代码表示为
清单 2. notify_redraw(vc) 伪代码
1
2
3
4
5
6
7
8
notify_redraw(vc_data * vc){
  for(int rows = 0 ; rows < vc->rows ; rows ++){
   unsigned short * current_line = &
       vc->vc_screen_buf [vc_size_row * rows + vc->vc_visible_origin ] ;
    // 在屏幕的第 row 行第 0 列绘制一行 current_line 指向的内容共 vc->cols 个字符。
    vc->vc_sw->con_puts(vc,current_line,vc->cols,0,rows);
  }
}




vc_sw 是个由控制台代码提供的指针,类型为 structconsw * , 控制台驱动的初始化部分会使用 vt_unbind() 将自己绑定为虚拟终端使用的控制台,传入的信息中就包括 vc_sw。在 vgacon 中  vc_sw->con_puts 事实上就是将要显示的内容简单的拷贝到 VGA 的字符缓冲区。对于 fbcon 而言则要复杂的多。 fbcon 提供的 con_puts 将 glyph 作为一个下标在 vc->vc_font 中找到对应的位图数据,然后拷贝到帧缓冲区。 TTY-> 虚拟终端 -> 控制台 -> 屏幕的路径用一幅图片表达为:
图 2. 路径图形模式控制台的改造
fbcon 将 glyph 作为下标到 vc_font 中获取位图数据,而 glyph 要么是一个 unicode (vc->utf=1 的时候,当然是被截断到 8 个比特)要么就是扩展的 ASCII 代码。由于扩展 ASCII 只有 8 个比特位表示一个字符,所以只能请出 unicode 作为中文的数字表示。要想控制台能支持汉字显示,需要解决 3 个问题:
  • 必须使用 UTF-8 模式 ( 默认 vc->utf=1 即可 )
  • 虚拟控制台的 vc_screenbuf 必须修改以为 glyph 提供至少 16bit 的空间。
  • 图形控制台需要 vc_font包含更多的字符,不只是 255 个,并提供代码绘制双倍宽度的中文字形,字体中的字符按照 UNICODE 排列,这样 glyph 就是字符的  UNICODE 编码。
返回列表