这篇文章主要记录一些setup界面的实例,这些实例都是EDK上的,我们可以看到如下图:
上面三个为banner,下面的都是通过LABLE动态加载的,代码如下:
我们可以看到 UiListThirdPartyDrivers (HiiHandle, &gEfiIfrFrontPageGuid, NULL, StartOpCodeHandle);这个函数,就是发现第三方的HII驱动然后给它放到FrontPage也就是主页当中,怎么发现,就是通过VFR中的classguid是不是gEfiIfrFrontPageGuid,这是重要依据,自己创建一个第三方驱动AnthonyPage,其中VFR:
, C文件中保留重要的函数就行,仿照其他第三方驱动,这些驱动以Library形式在DSC文件中被包入,这样就新增了一个页面,在DeviceManager页面,还有一种驱动,这种是独立的驱动
在Devices List下有六个子项,这六个子项就是六个独立的驱动,以Browser Testcase Engine为例子,查看vfr:
可以看到重点是EFI_HII_PLATFORM_SETUP_FORMSET_GUID,这个classguid和device manager这个页面是绑定的,在仿照这种驱动的时候,这个classguid必须是EFI_HII_PLATFORM_SETUP_FORMSET_GUID。
打开Browser Testcase Engine页面,一个一个分析:
第一个:My subtitle text:
title就是My First Setup Page,下面的就是My subtitle text和它的help,一个vfr只有一个formset,其他的就是formid,formid和LABLE同级。
第二个My CPU Speed is :
第一个text就是左边的文字,第二个就是它的值,这种形式可以看到,它是可以选中但是不能改动的,因为没有key,不能执行action
Exit now!
text
help = STRING_TOKEN(STR_EXIT_TEXT),
text = STRING_TOKEN(STR_EXIT_TEXT),
flags = INTERACTIVE, // VfrCompiler will generate opcode EFI_IFR_ACTION for Text marked as INTERACTIVE
key = 0x1237;
INTERACTIVE,意味人机交互,也就是可以动作的,通过这个key去进行操作,这种形式选中后按Enter就是执行操作,先理解三个函数,ExtractConfig,导出配置,RouteConfig,存储配置,CallBack,操作Hii数据。执行操作在callback中:
case EFI_BROWSER_ACTION_CHANGED:
switch (QuestionId) {
case 0x1237:
//
// User press "Exit now", request Browser to exit
//
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
break;
case 0x1238:
//
// User press "Save now", request Browser to save the uncommitted data.
//
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
break;
直接跳过到My one-of prompt #1,前面几个类似,不介绍了:
//
// Define oneof (EFI_IFR_ONE_OF)
//
oneof name = MyOneOf, // Define reference name for Question
varid = MyIfrNVData.SuppressGrayOutSomething, // Use "DataStructure.Member" to reference Buffer Storage
prompt = STRING_TOKEN(STR_ONE_OF_PROMPT),
help = STRING_TOKEN(STR_ONE_OF_HELP),
//
// Define an option (EFI_IFR_ONE_OF_OPTION)
//
option text = STRING_TOKEN(STR_ONE_OF_TEXT4), value = 0x0, flags = 0;
option text = STRING_TOKEN(STR_ONE_OF_TEXT5), value = 0x1, flags = 0;
//
// DEFAULT indicate this option will be marked with EFI_IFR_OPTION_DEFAULT
//
option text = STRING_TOKEN(STR_ONE_OF_TEXT6), value = 0x2, flags = DEFAULT;
endoneof;
看代码,one-of也就是其中之一,这里有三个选项,分别是0:让checkbox隐藏,1:让checkbox不可选,2:让checkbox可选,我们尝试更改为Suppress the Checkbox,可以看到:
下方的checkbox没有了,反而出现了一个Pick 1。这里主要说明不可选和隐藏的使用,
grayoutif ideqval MyIfrNVData.SuppressGrayOutSomething == 0x1;
suppressif questionref(MyOneOf) == 0x0;
checkbox varid = MyIfrNVData.ChooseToActivateNuclearWeaponry,
prompt = STRING_TOKEN(STR_CHECK_BOX_PROMPT),
help = STRING_TOKEN(STR_CHECK_BOX_HELP),
//
// CHECKBOX_DEFAULT indicate this checkbox is marked with EFI_IFR_CHECKBOX_DEFAULT
// CHECKBOX_DEFAULT_MFG indicate EFI_IFR_CHECKBOX_DEFAULT_MFG.
//
flags = CHECKBOX_DEFAULT | CHECKBOX_DEFAULT_MFG,
default = TRUE,
endcheckbox;
endif;
endif;
第一句就是可选与不可选,ideqval只是判断,这句话可以这样理解,grayout if true,如果是true就不可选,什么条件,也就是SuppressGrayOutSomething 的值为0x1;如果不加判断,直接不可选,那么可以这么写 grayoutif TRUE;,别 grayoutif ideqval TRUE,不然会报错。隐藏也是一样,MyOneof只是引用,给该变量取个别名一样,使用是一样的,只是需要加一个questionref。
标准的checkbox如上,简化的如下,需要先初始化变量:
checkbox varid = MyIfrNVData.SerialPortStatus,
prompt = STRING_TOKEN(STR_SERIAL_PORT_STATUS),
help = STRING_TOKEN(STR_CHECK_BOX_HELP),
endcheckbox;
上图选项更改成LargeBootList,Boot Order会多显示USB选项,这个就是在最后一个USB选项加了个隐藏选项,也就是说隐藏和不可选也可以放在一个选项的内部。
然后再看一下Boot Order,它能显示多个选项,是因为使用了orderedlist,这里可以看到suppressif嵌入在中间:
orderedlist
varid = MyIfrNVData.BootOrder,
prompt = STRING_TOKEN(STR_BOOT_OPTIONS),
help = STRING_TOKEN(STR_NULL_STRING),
flags = RESET_REQUIRED,
option text = STRING_TOKEN(STR_BOOT_OPTION2), value = 2, flags = 0;
option text = STRING_TOKEN(STR_BOOT_OPTION1), value = 1, flags = 0;
option text = STRING_TOKEN(STR_BOOT_OPTION3), value = 3, flags = 0;
suppressif ideqval MyIfrNVData.BootOrderLarge == 0;
option text = STRING_TOKEN(STR_BOOT_OPTION4), value = 4, flags = 0;
endif;
endlist;
Goto Dynamic Page+:
说明这个页面是动态创建的,首先Goto Dynamic Page+这个选项需要显示:
goto 0x1234, // Destination Form ID
prompt = STRING_TOKEN(STR_GOTO_DYNAMIC), // Prompt string
help = STRING_TOKEN(STR_GOTO_HELP), // Help string
flags = INTERACTIVE, // INTERACTIVE indicate it's marked with EFI_IFR_FLAG_CALLBACK
key = 0x1234; // Question ID which will be passed-in in COnfigAccess.Callback()
进入这个goto,就到了另一个页面:
form formid = 0x1234, // Dynamically created page,
title = STRING_TOKEN(STR_DYNAMIC_TITLE); // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code
label LABEL_UPDATE1;
//
// This is where we will insert dynamic created opcodes
//
label LABEL_END;
endform;
case EFI_BROWSER_ACTION_CHANGING:
{
switch (QuestionId) {
case 0x1249:
{
if (Type != EFI_IFR_TYPE_REF) {
return EFI_INVALID_PARAMETER;
}
Value->ref.FormId = 0x1234;
}
break;
case 0x1234:
//
// Initialize the container for dynamic opcodes
//
StartOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (StartOpCodeHandle != NULL);
EndOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (EndOpCodeHandle != NULL);
//
// Create Hii Extend Label OpCode as the start opcode
//
StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
StartLabel->Number = LABEL_UPDATE1;
//
// Create Hii Extend Label OpCode as the end opcode
//
EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
EndLabel->Number = LABEL_END;
HiiCreateActionOpCode (
StartOpCodeHandle, // Container for dynamic created opcodes
0x1237, // Question ID
STRING_TOKEN(STR_EXIT_TEXT), // Prompt text
STRING_TOKEN(STR_EXIT_TEXT), // Help text
EFI_IFR_FLAG_CALLBACK, // Question flag
0 // Action String ID
);
//
// Create Option OpCode
//
OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
ASSERT (OptionsOpCodeHandle != NULL);
HiiCreateOneOfOptionOpCode (
OptionsOpCodeHandle,
STRING_TOKEN (STR_BOOT_OPTION1),
0,
EFI_IFR_NUMERIC_SIZE_1,
1
);
HiiCreateOneOfOptionOpCode (
OptionsOpCodeHandle,
STRING_TOKEN (STR_BOOT_OPTION2),
0,
EFI_IFR_NUMERIC_SIZE_1,
2
);
//
// Prepare initial value for the dynamic created oneof Question
//
PrivateData->Configuration.DynamicOneof = 2;
Status = gRT->SetVariable(
VariableName,
&gDriverSampleFormSetGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (DRIVER_SAMPLE_CONFIGURATION),
&PrivateData->Configuration
);
//
// Set initial vlaue of dynamic created oneof Question in Form Browser
//
Configuration = AllocateZeroPool (sizeof (DRIVER_SAMPLE_CONFIGURATION));
ASSERT (Configuration != NULL);
if (HiiGetBrowserData (&gDriverSampleFormSetGuid, VariableName, sizeof (DRIVER_SAMPLE_CONFIGURATION), (UINT8 *) Configuration)) {
Configuration->DynamicOneof = 2;
//
// Update uncommitted data of Browser
//
HiiSetBrowserData (
&gDriverSampleFormSetGuid,
VariableName,
sizeof (DRIVER_SAMPLE_CONFIGURATION),
(UINT8 *) Configuration,
NULL
);
}
FreePool (Configuration);
HiiCreateOneOfOpCode (
StartOpCodeHandle, // Container for dynamic created opcodes
0x8001, // Question ID (or call it "key")
CONFIGURATION_VARSTORE_ID, // VarStore ID
(UINT16) DYNAMIC_ONE_OF_VAR_OFFSET, // Offset in Buffer Storage
STRING_TOKEN (STR_ONE_OF_PROMPT), // Question prompt text
STRING_TOKEN (STR_ONE_OF_HELP), // Question help text
EFI_IFR_FLAG_CALLBACK, // Question flag
EFI_IFR_NUMERIC_SIZE_1, // Data type of Question Value
OptionsOpCodeHandle, // Option Opcode list
NULL // Default Opcode is NULl
);
这里只简单贴下代码,主要说下几个重要函数:
HiiCreateOneOfOptionOpCode用于创建一种选项(Option)。它用于定义在系统配置界面中的单选或多选菜单中的一个选项。
HiiCreateActionOpCode该操作码用于创建一个与用户界面交互的动作。动作可以是按下按钮、选择复选框、拖动滑块等用户交互事件。
HiiCreateDateOpCode该操作码用于创建一个与日期相关的控件或选项。
HiiCreateTimeOpCode操作码用于创建一个与时间相关的控件或选项。
HiiCreateGotoOpCode用于创建跳转操作。
numeric varid = MyIfrNVData.QuestionNonXUefiKeywordRestStyle,
prompt = STRING_TOKEN(STR_ONE_OF_PROMPT_NON_X_UEFI),
help = STRING_TOKEN(STR_ONE_OF_PROMPT_NON_X_UEFI_HELP),
flags = RESET_REQUIRED | REST_STYLE,
minimum = 0,
maximum = 0xf0,
step = 0, // Stepping of 0 equates to a manual entering
// of a value, otherwise it will be adjusted by "+"/"-"
default = 0, // defaultstore could be used to specify the default type
// If no defaultstore is specified, it implies Standard Default
endnumeric;
这里flags也可以是flags = READ_ONLY, 如果是这种表明可见但不可选。
//
// Define a string (EFI_IFR_STRING)
//
string varid = MyIfrNVData.MyStringData,
prompt = STRING_TOKEN(STR_MY_STRING_PROMPT2),
help = STRING_TOKEN(STR_MY_STRING_HELP2),
flags = INTERACTIVE,
key = 0x1236,
minsize = 6,
maxsize = 40,
inconsistentif prompt = STRING_TOKEN(STR_STRING_CHECK_ERROR_POPUP),
pushthis != stringref(STRING_TOKEN(STR_STRING_CHECK))
endif;
endstring;
作用不明,在vfr中maxsize下有两句,第一句的意思是没输入对会弹窗报错,第二行也就是右侧的值是否等于STR_STRING_CHECK。
最后分析一下,怎么跳转到另一个页面:
这个页面在DeviceManager中,是和该同级页面在同一个驱动中生成的,因此可以直接通过GUID去指向:
goto
formsetguid = DRIVER_SAMPLE_INVENTORY_GUID,
formid = 0x1,
question = 0x1,
prompt = STRING_TOKEN(STR_GOTO_ANOTHER_FORMSET),
help = STRING_TOKEN(STR_GOTO_ANOTHER_FORMSET_HELP);