GUI下浏览文本文件

了解了如何读取文件内容之后,和之前的ls一样,接下来我们把这一功能扩展到GUI模式中。本节示例代码的目录为gui-cat (日文版为sample5_4_gui_cat)。这里我们将要实现这三个功能:

  1. 在文件图标(矩形)上单击进入文件浏览模式
  2. 在浏览模式中,首先进行清屏,再以Unicode文本显示文件内容
  3. 按Esc键退出浏览模式

这里主要修改的是gui.c文件,如代码6.15所示。

/* ...省略... */ /* 新增(此处开始) */ void cat_gui(unsigned short *file_name) { ST->ConOut->ClearScreen(ST->ConOut); cat(file_name); while (getc() != SC_ESC); } /* 新增(此处结束) */ void gui(void) { unsigned long long status; struct EFI_SIMPLE_POINTER_STATE s; int px = 0, py = 0; unsigned long long waitidx; int file_num; int idx; unsigned char prev_lb = FALSE; /* 新增 */ SPP->Reset(SPP, FALSE); file_num = ls_gui(); while (TRUE) { ST->BootServices->WaitForEvent(1, &(SPP->WaitForInput), &waitidx); status = SPP->GetState(SPP, &s); if (!status) { /* ...省略... */ /* 处理文件图标 */ for (idx = 0; idx < file_num; idx++) { if (is_in_rect(px, py, file_list[idx].rect)) { if (!file_list[idx].is_highlight) { draw_rect(file_list[idx].rect, yellow); file_list[idx].is_highlight = TRUE; } /* 新增(此处开始) */ if (prev_lb && !s.LeftButton) { cat_gui(file_list[idx].name); file_num = ls_gui(); } /* 新增(此处结束) */ } else { if (file_list[idx].is_highlight) { draw_rect(file_list[idx].rect, white); file_list[idx].is_highlight = FALSE; } } } /* 新增(此处开始) */ /* 保存鼠标左键状态 */ prev_lb = s.LeftButton; /* 新增(此处结束) */ } } }

代码6.15: gui-cat/gui.c

代码6.15的gui函数中加入了对于鼠标事件的处理。鼠标左键的上一次状态保存在变量prev_lb中,而松开鼠标左键的那一刻会被判定为发生了一次单击。在这一事件发生后,将会以点击的文件名作为参数调用cat_gui函数。这个过程是在处理文件图标的循环中进行的。cat_gui函数是对上一节cat函数的封装,也是实现GUI的浏览模式的函数。

这个例子中单击文件abc后进入的浏览模式如图6.4所示。

文件浏览模式

图6.4: 文件浏览模式

修正getc中非Unicode字符按键的返回值

代码6.15中的cat_gui函数中,通过循环等待getc函数返回SC_ESC(Esc键的扫描码)来实现按Esc键退出浏览模式的功能。事实上,在这个例子中,我们对common.c中的getc函数进行了一些修改来使它能够返回Unicode字符范围外的按键的扫描码(代码6.16)。

unsigned short getc(void) { struct EFI_INPUT_KEY key; unsigned long long waitidx; ST->BootServices->WaitForEvent(1, &(ST->ConIn->WaitForKey), &waitidx); while (ST->ConIn->ReadKeyStroke(ST->ConIn, &key)); /* 修改 */ return (key.UnicodeChar) ? key.UnicodeChar : (key.ScanCode + SC_OFS); }

代码6.16: sample_5_4_gui_cat/common_c:getc

代码6.16中,如果key.UnicodeChar为0时(该按键在Unicode范围外),将会返回key.ScanCode加上偏移量SC_OFS的值。这是因为,扫描码的范围(0x00〜0x17)与Unicode字符范围重叠(0x0000〜0xffff),且这一范围位于常用的ASCII子集中(0x00~0x7F)中,因此我们需要将扫描码放入一个Unicode不常用的范围中来避免冲突。

这里我们使用的是0x1680〜0x1697这个区间。在Unicode标准中,这一区间是欧甘字母的区间。这是一种在爱尔兰发现的中世纪前期使用的字母系统。12

因此,common.h中的SC_OFSSC_ESC被定义成代码6.17所示的形式。

#define SC_OFS 0x1680 #define SC_ESC (SC_OFS + 0x0017)

代码6.17: SC_OFSSC_ESC的定义

2

译者注:这里对于欧甘字母的解释与原书不同