本文目录!

  • 界面设计概论
  • BDS引导到UIAPP
  • HII驱动
    • UNI文件
    • VFR文件
      • FORMSET
      • FORM
      • VARSTORE
      • TEXT
      • LABEL
      • 其他

最近
我从显示驱动搞到显示LOGO和显示界面、再到做界面、改框架,一路走来很是坎坷,但或许会有意义所在吧?
一直想写点东西,然而真没空懒 ,三月末疫情哐的一下给我锁家里了,好了,可算是有空了。
废话说的太多,直接上笔记好了,为了不违规用的都是EDK2官方的代码,可以直接找对应的文件对应着看一下!

界面设计概论

最新的EDK2代码,构造界面框架大概如下


稍微解释一下
HiiDriver:最上层的界面实现,由.uni、.vfr和.c组成,这里我只画了一个,其实可以有很多个,以lib的形式被UiApp集成。
SetupBrowser: 控件实现,简单的说,这个模块做了很多工具,在HiiDriver中你可以使用某些工具、根据需求去完成这个界面。界面嘛,不同厂商可能想做不同的,但又有些逻辑可以复用,为了更好的完成以上需求,这个模块把自己分成了三部分。除自己外的另两个都可以由厂商自行实现——它甚至给自行实现的部分也分了级,CustomizedDisplayLib基本都要自己实现,Text Display Engine可以根据情况看看,它真的好温柔,我哭死(
Text Display Engine:对于EDK2界面框架,我对它的理解类似于一种界面编码,通过编码传递信息和解释界面,这个模块就主要针对这部分,后面细写的时候再说吧。
CustomizedDisplayLib:这是各个厂商基本都要重新实现的,通过它可以改变颜色和修改控件布局。
UiApp:这个模块是页面的集成,BDS通过找到它进入界面,这个详细流程后面会讲。

看完上面这些,相信你已经胸(一)有(脸)成(懵)竹(B)了,所以我们从最上层开始说。

BDS引导到UIAPP

EDK2的BDS模块从IntelFrameworkModulePkg升级到MdePkg中现有的BdsEntry,其实在启动项方面变化不大,更多的在界面这里(个人认为)。
打开UiApp的INF文件,你会发现这个文件的类型是UEFI_APPLICATION

[Defines]INF_VERSION                    = 0x00010005BASE_NAME                      = UiAppMODULE_UNI_FILE                = UiApp.uniFILE_GUID                      = 462CAA21-7614-4503-836E-8AB6F4662331MODULE_TYPE                    = UEFI_APPLICATIONVERSION_STRING                 = 1.0ENTRY_POINT                    = InitializeUserInterface

对于UEFI_APPLICATION类型的模块,我们无法像驱动一样通过FDF中的INF去引导。正确的方法是首先,把它放在DSC中,让它被编译成二进制。通过句柄在FV中找到对应文件,然后通过StartImage去执行。
从底层来看,BDS也是这么做的,但它用了比较巧妙的方式——要知道,BIOS的主要功能是引导进GRUB(或者其他的),GRUB也是一个二进制文件。既然都是引导二进制文件,那它们能不能复用逻辑呢?巧了,EDK2就是这么想的。它调用BmRegisterBootManagerMenu函数把界面二进制文件当成启动项来注册,打开这个函数,你可以看到寻找二进制文件的两种方式

gBS->LocateHandleBuffer (ByProtocol,&gEfiLoadFileProtocolGuid,NULL,&HandleCount,&Handles);for (Index = 0; Index < HandleCount; Index++) {if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {DevicePath  = DuplicateDevicePath (DevicePathFromHandle (Handles[Index]));Description = BmGetBootDescription (Handles[Index]);break;}}

这是第一种,寻找现有句柄中所有打开gEfiLoadFileProtocolGuid这一协议的,遍历对应句柄的设备路径,如果是BootMenu类型的就返回找到了
那么怎么判断BootMenu类型呢?答案是

CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile));

这个Pcd的内容可以在DEC文件里面查看

OvmfPkg/AmdSev/AmdSevX64.dsc:510:
gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|
{ 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }

眼熟吗,这一串拼出来其实是上面那个INF文件中的FILE_GUID,怎么样,对于INF文件是不是有了更深刻的认识。

第二种方式和上面的大同小异

if (DevicePath == NULL) {Status = GetFileDevicePathFromAnyFv (PcdGetPtr (PcdBootManagerMenuFile),EFI_SECTION_PE32,0,&DevicePath);if (EFI_ERROR (Status)) {DEBUG ((DEBUG_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n"));return EFI_NOT_FOUND;}

理解到这里,引导界面的方法呼之欲出,

EFI_STATUS                    Status;
EFI_BOOT_MANAGER_LOAD_OPTION  BootManagerMenu;Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
EfiBootManagerBoot (&BootManagerMenu);

两个函数都是官方LIB提供的,其中,EfiBootManagerBoot就是启动项启动的函数,也就是说,我们可以把界面当成一个特殊的启动项来理解。

那么,现在我们从BDS找到了UiApp,加载了里面的内容。现在压力来到了后者。

HII驱动

UiApp其实就是Hii驱动的集合,所以我们先来说HII驱动好啦。
HII,中文译名人机交互接口(我翻译的……),我们能看见、操作界面、能更换语言、能使用之前预存的字符,都是拜这个模块所赐。
之前已经说过了,比较健全的HII驱动包括.uni、.vfr、.c和.h四种文件。先说比较简单的.uni吧。

UNI文件

很多文件都有.uni,用来保存模块名称和模块作用。这里引用的是官方代码BootManager的uni文件,直观一些。

#langdef   en-US "English"
#langdef   fr-FR "Français"#string STR_BM_BANNER                  #language en-US  "Boot Manager"#language fr-FR  "Boot Manager"
#string STR_BOOT_MANAGER_HELP          #language en-US  "This selection will take you to the Boot Manager"#language fr-FR  "This selection will take you to the Boot Manager"
#string STR_HELP_FOOTER                #language en-US  "Use the <↑> and <↓> keys to choose a boot option, the <Enter> key to select a boot option, and the <Esc> key to exit the Boot Manager Menu."#language fr-FR  "<↑> pour <↓> changer l'option, <ENTRER> choisir une option, <ESC> pour sortir"
#string STR_AND                        #language en-US  " and "#language fr-FR  " et "
#string STR_BOOT_OPTION_BANNER         #language en-US  "Boot Manager Menu"#language fr-FR  "le Menu d'Option de Botte"
#string STR_ANY_KEY_CONTINUE           #language en-US  "Press any key to continue..."#language fr-FR  "Appuie n'importe quelle pour continuer..."
#string STR_LAST_STRING                #language en-US  ""#language fr-FR  ""

简单的说,这个就是字符串资源文件,左边是字符串的TOKEN,右边是对应TOKEN的、不同语言下的字符串。
这个文件是在预处理前被autogen工具处理的,所以,它不支持宏、ifdef之类的修饰符。还有一点比较有趣的是并不是所有字符串都会被编译,只有TOKEN在其他文件中被引用时才会被编译打包,这是一种减少不必要数据的机制。
引用它的方式也很简单

STRING_TOKEN(STR_BM_BANNER)

这样就可以了。
顺便一提,界面的文字转换主要就是运用这个文件,通过指定对应全局变量修改所有TOKEN引用的语言。

VFR文件

如果说.uni是字符串的资源文件,vfr就是窗体的资源文件,里面记录着各个控件的位置和使用方式。
刚接到做界面任务时,我是非常有信心的,我好歹也是QT选手欸!设计过很复杂的界面的!然后就被这玩意制裁了,真的,写界面时没有一刻不怀念QT上万页的说明文档。
这里记一下我用过的控件们

FORMSET

formsetguid      = FORMSET_GUID,title     = STRING_TOKEN(STR_BM_BANNER),help      = STRING_TOKEN(STR_BOOT_MANAGER_HELP),classguid = gEfiIfrFrontPageGuid,……
endformset;

主窗体,这个应该都见过哦。
guid:不提了,不要重复就行。
title:显示在屏幕最上方(这个可以在Browser里面改)
help:显示在下面(也可以改)
classguid:窗体的GUID,可以用来判断窗体是否是同一级的,比如说这个函数

UiListThirdPartyDrivers (HiiHandle, &gEfiIfrFrontPageGuid, NULL, StartOpCodeHandle);

就是通过classguid判断系统里有没有这个级的HII驱动,如果有,就创建一个摁纽作为跳转到这个驱动的入口——官方代码中的菜单界面,就是这么构成的。这玩意是不能写死的,总不能你这个项目不想要启动管理界面,那个写死的界面就空着吧。

主窗体是界面设计里面的最大单位,一切内容都要在窗体内部,即formset与endformset中间。

FORM

普通窗体,一个主窗体可以有很多普通窗体,至少一个。

form formid = BOOT_MANAGER_FORM_ID,title  = STRING_TOKEN(STR_BM_BANNER);……
endform;

formid:顾名思义,可以用于标识不同FORM
title:显示在Formset下的第一行
窗体内部就是界面的内容了

VARSTORE

  varstore LEGACY_BOOT_NV_DATA,varid = VARSTORE_ID_LEGACY_BOOT,name = LegacyBootData,guid = LEGACY_BOOT_OPTION_FORMSET_GUID;

非常重要的一个控件,基本需要用户交互的都有
从本质上讲,资源文件与代码相互传递信息用的是全局变量。这个结构就是存储这个窗体需要的所有全局变量的地方。其中,varstore后面跟着的是全局变量的成员,自己定义的,例如上文的这个

typedef struct {UINT16    LegacyFD[MAX_MENU_NUMBER];UINT16    LegacyHD[MAX_MENU_NUMBER];UINT16    LegacyCD[MAX_MENU_NUMBER];UINT16    LegacyNET[MAX_MENU_NUMBER];UINT16    LegacyBEV[MAX_MENU_NUMBER];
} LEGACY_BOOT_NV_DATA;

值得注意的是,vfr只能识别UINTN/8/16/32/64五种类型,所以结构的成员只能是这些。还有一点是一定要记得,vfr文件需要包含拥有这个类型声明的头文件,不然会给你报一个很诡异的编译错。我查这个编译错查了整整一天,呵呵!
对了,头文件的本质是链接,所以包含这个声明的头文件里,除了它自己,其他声明里也不能有其他数据结构,你最好为了它单建一个头文件……
name:这个全局变量的名字,在后面的控件中有应用。
guid:身份证号,没有什么好说的。

与这个控件功能类似的是efivarstore,写法也类似,会多一个和全局变量一样的attribute

TEXT

虽然它叫TEXT,但从某种程度上来说,可以分成文字和摁纽两种控件。

subtitle text = STRING_TOKEN(STR_DEVICES_LIST);

这是第一种用法,就是普通的一行一列文字,后面通过TOKEN提取UNI中的字符串

suppressif TRUE;texthelp  = STRING_TOKEN(STR_LAST_STRING ),text  = STRING_TOKEN(STR_LAST_STRING ),text = STRING_TOKEN(STR_LAST_STRING ),endif;

这是第二种用法,一行两列文字,可以用来显示固件信息之类的。光标不会出现在上面,我一般叫它“死文字”。
当然,它的内容其实是可以修改的,不过不是用户来改。通过HII封装好的SetString可以修改UNI中对应TOKEN保存的内容,这样可以改变界面上显示的文字——所以我会用它显示固件信息,可以在初始化时调用函数获取,然后SetString,这样在引导进界面时,信息就会出现在屏幕上。
哦,顺便讲一下suppressif,其实就是VFR中的ifdef,后面可以接判断条件,如果满足就加载包含内容,不满足就跳过。比如,后面的条件是VARSTORE的某一成员是否为1,这样,可以在代码中修改这个值来修改界面元素。

texthelp  = STRING_TOKEN(STR_LAST_STRING ),text  = STRING_TOKEN(STR_LAST_STRING ),flags = INTERACTIVE,key   = 0x1212;

这种写法更像摁纽,选中后点击会回调代码中的函数
text:摁纽上面的内容
help:光标移到摁纽时右侧(可以改)的内容
flag:性质,这里这个表示这个摁纽可以触发回调函数
key:用于在回调函数中判断是哪个摁纽被触发了——因为回调函数是公用的,该界面所有有回调FLAG的控件都会调回去,得有标志区分它们。

LABEL

标签,用于在代码中生成控件

label LABEL_FRANTPAGE_INFORMATION;
label LABEL_END;

有些时候,界面的内容需要根据现状更改。比如启动项界面,在设计时我们并不知道现在有几个启动项,这就需要这个控件去占位,然后在代码里替换掉这些占位符。
使用方法可以参照官方代码的UpdateFrontPageForm函数,注意两点,一是写控件的参数,二是写完控件后要调用HiiUpdateForm更新界面信息。

其他

STRING:可以在界面显示一个可选中的文本信息,点击后出现一个框可以让用户输入,输入后点击回车可以保存
ONEOF:可以在界面显示一个可选中的文本信息,点击后出现一个框,里面是写好的选择,用户可以选中任一,类似于主界面选择语言的
NUMERIC:可以在界面显示一个可修改的数字信息
……
这些都是交互类控件,大同小异。举STRING为例

oneof varid = BootDiscoveryPolicy.BootDiscoveryPolicy,prompt      = STRING_TOKEN(STR_FORM_BDP_MAIN_TITLE),help        = STRING_TOKEN(STR_FORM_BDP_MAIN_TITLE),flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,option text = STRING_TOKEN(STR_FORM_BDP_CONN_MIN), value = BDP_CONNECT_MINIMAL, flags = DEFAULT;option text = STRING_TOKEN(STR_FORM_BDP_CONN_NET), value = BDP_CONNECT_NET, flags = 0;option text = STRING_TOKEN(STR_FORM_BDP_CONN_ALL), value = BDP_CONNECT_ALL, flags = 0;endoneof;

varid:后面跟着varstore里存储的数据名称.对应成员
prompt:显示在左边的文字
help:选中后出现在右侧提示栏的文字
flag:属性
这些都是这些交互类控件公用的,其他的根据自己的性质有不同,比如这里后面是oneof里面的选项,default说明这是默认选项。STRING中有最短输入字符和最长之类的。
可以在代码中调用HiiGetBrowserData获取当前界面中用户输入的内容

嗯……控件就差不多这样?后面有心得我再更新

UEFI中的界面设计(一)相关推荐

  1. APP开发流程实例讲解-儒释道网络电台八天开发全程-在Android Studio中完成界面设计

    APP开发流程实例讲解-儒释道网络电台八天开发全程 功能和界面初步设定 APP开发流程实例讲解-儒释道网络电台八天开发全程 项目发起 功能和界面初步设定 在Android Studio中完成界面设计 ...

  2. VB中的界面设计原则和编程技巧

    Windows的通用图形界面的出现,使计算机用户不必通过专门的学习就可以得心应手地使用各种Windows的软件.不仅如此,它还是程序设计者在设计Windows程序界面时所必须遵循的标准,这在很大程度上 ...

  3. html中一些界面设计

    1.使用表格整行作为可点击跳转 HTML: <tbody><tr class="tr-link" onclick="window.document.lo ...

  4. python制作软件界面_python界面设计工具Qtdesigner

    QtDesigner Qtdesigner是python中的界面设计工具.创建的界面保存为ui类型的文件,再用Pyuic工具把UI界面文件转化为py代码. 安装PyQt5-tools 先要安装pyqt ...

  5. 一个Web系统OA界面设计和开发

    早在中国IT业方兴未艾之时,计算机应用系统主要以功能实现为主,几乎没有界面设计这个概念.时至今日,随着计算机和网络的不断普及,社会信息化程度日益加深,用户和市场的不断成熟,人们已经不仅仅满足于&quo ...

  6. 移动端界面中的版式设计原理

    "我总觉得页面不太好看但是我又说不出来","我不懂设计,但是我就是觉得不协调","你觉得这好看?你的审美要加强啊"这些听着熟悉的话往往是产品 ...

  7. 中剪取一种颜色的板块_不知道UI设计中APP界面版式如何排版?来看这个!

    UI设计中APP的界面看似只有几个简单的元素组合起来, 所有元素的绘制可以说比较简单: 然而,当一个产品原型出来后,设计师如果单纯按原型来进行设计而不考虑信息化规则, 那么很多时候就会出现不协调的效果 ...

  8. UI实用案例|黄金分割在界面设计中的应用

    那黄金分割线到底是个什么东西呢.它在什么位置?它在画面中的哪个地方呢? "有一条线条,如果我们从中切一段,如果左边是0.618这么一个比列,右边则是1这么一个比例."如果符合这样的 ...

  9. UI设计实例|界面设计中,版式实战运用以及设计思路

    什么是用户体验界面版式设计版式设计是现代设计艺术的重要组成部分,是视觉传达的重要手段.表面上看,它是一种关于编排的学问:实际上,它不仅是一种技能,更实现了技术与艺术的高度统一,版式设计是现代设计者所必 ...

最新文章

  1. html-body标签中相关标签 02
  2. VMware Workstation 网络连接配置
  3. 国开专科计算机应用基础,2021年国开专科《计算机应用基础》形考任务题库大全.docx...
  4. 秒杀多线程第六篇 经典线程同步 事件Event
  5. python查找最长公共前缀_Python实现查找字符串数组最长公共前缀示例
  6. UVA4671 K-neighbor substrings FFT+字符串hash
  7. js 数字递增递减_js验证连续两位数字递增或递减和连续三位数字相同
  8. 矩形变弧度角_懒惰使人类进步:不想刷马桶,那就让马桶自己变干净丨种草机...
  9. MVC @RenderBody、@RenderSection、@RenderPage、@Html.RenderPartial、@Html.RenderAction
  10. mac 安装mysql
  11. c#的IList,IEnumerable和IEnumerator
  12. 终于搞懂python通过twain模块控制扫描仪了
  13. 老徐和阿珍的故事:强引用、软引用、弱引用、虚引用,傻傻分不清楚
  14. 计算机硬盘解密,如何解除电脑硬盘密码 解除电脑硬盘密码方法【详解】
  15. mac强制关机后悲剧了
  16. 谷歌浏览器插件开发之 manifest.json 配置说明
  17. Diffusion Model
  18. LTE Phich 分析
  19. JDK1.8之Lambada表达式一
  20. Ubuntu中Kdevelop的安装和使用

热门文章

  1. command_execution
  2. 财务内部收益率用计算机怎么算,财务内部收益率EXCEL怎么计算
  3. VS2015 编译开源的基于Opencascade的3D查看器Mayo
  4. Word字体的字号与像素对应关系
  5. 信道容量的数值解法(非对称信道)
  6. 公司要求实时监控服务器,写个Web的监控系统
  7. css中的*代表什么
  8. 干货 | 科研人的KPI怎么算,H指数和G指数是什么
  9. 274. H 指数----中等
  10. 两栈共享存储空间(线性结构栈)