EFI_SIMPLE_TEXT_INPUT_PROTOCOLよりも少し高機能なEFI_SIMPLE_TEXT_INPUT_EX_PROTOCOLがあります。この章ではEFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL関係のTIPSを紹介します。
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOLのGUIDと定義はリスト2.1の通りです。
リスト2.1: EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOLのGUIDと定義
struct EFI_GUID stiep_guid = {0xdd9e7534, 0x7762, 0x4698, \
{0x8c, 0x14, 0xf5, 0x85, \
0x17, 0xa6, 0x25, 0xaa}};
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
/* テキスト入力デバイスをリセットする */
unsigned long long (*Reset)(
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
unsigned char ExtendedVerification);
/* キー入力データを取得 */
unsigned long long (*ReadKeyStrokeEx)(
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
struct EFI_KEY_DATA *KeyData);
/* キー入力を待つEFI_EVENT */
void *WaitForKeyEx;
/* キーのトグル状態を設定(ScrollLock,NumLock,CapsLock) */
unsigned long long (*SetState)(
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
unsigned char *KeyToggleState);
/* 特定のキー入力を通知する関数を設定 */
unsigned long long (*RegisterKeyNotify)(
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
struct EFI_KEY_DATA *KeyData,
unsigned long long (*KeyNotificationFunction)(
struct EFI_KEY_DATA *KeyData),
void **NotifyHandle);
/* 特定のキー入力を通知する関数を解除 */
unsigned long long (*UnregisterKeyNotify)(
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
void *NotificationHandle);
};
この章ではRegisterKeyNotify()を紹介します。
RegisterKeyNotify()を使用すると、特定のキー入力で呼び出される関数を登録できます(リスト2.2)。
サンプルのディレクトリは"020_simple_text_input_ex_register_key_notify"です。
リスト2.2: RegisterKeyNotify()の定義(efi.hより)
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
・・・
unsigned long long (*RegisterKeyNotify)(
struct EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
struct EFI_KEY_DATA *KeyData,
/* イベントとして扱うキー入力を指定 */
unsigned long long (*KeyNotificationFunction)(
struct EFI_KEY_DATA *KeyData),
/* 通知関数 */
void **NotifyHandle
/* ユニークなハンドルを返す
* 登録解除時に使用 */
);
・・・
};
RegisterKeyNotify()は、第2引数へイベントとして扱うキー入力を指定し、第3引数へイベント発生を通知する関数を設定します。第2引数で指定するstruct EFI_KEY_DATAの定義はリスト2.3の通りです。
リスト2.3: struct EFI_KEY_DATAの定義(efi.hより)
struct EFI_KEY_DATA {
/* 入力されたキーに関する指定 */
struct EFI_INPUT_KEY {
unsigned short ScanCode;
/* Unicode外のキー入力時に使用。スキャンコード */
unsigned short UnicodeChar;
/* Unicode内のキー入力時に使用。ユニコード値 */
} Key;
/* キー入力時の状態に関する指定 */
struct EFI_KEY_STATE {
unsigned int KeyShiftState;
/* Shiftキーの押下状態 */
unsigned char KeyToggleState;
/* キーボードのトグル状態
* (ScrollLock,NumLock,CapsLock)を指定 */
} KeyState;
};
「どういう状態で何のキーが押されたか」を指定するためにEFI_KEY_DATAにはキーボードのトグル状態なども含まれますが、今回はEFI_INPUT_KEYのみ使用し、その他は0を設定しておきます。EFI_KEY_STATEの定義について詳しくは、仕様書の"Protocols - Console Support"の"Simple Text Input Ex Protocol"の"EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.ReadKeyStrokeEx()"を見てみてください。
RegisterKeyNotify()の使用例はリスト2.4の通りです。
リスト2.4: RegisterKeyNotify()の使用例(main.cより)
1: #include "efi.h"
2: #include "common.h"
3:
4: unsigned char is_exit = FALSE;
5:
6: unsigned long long key_notice(
7: struct EFI_KEY_DATA *KeyData __attribute__ ((unused)))
8: {
9: is_exit = TRUE;
10:
11: return EFI_SUCCESS;
12: }
13:
14: void efi_main(void *ImageHandle __attribute__ ((unused)),
15: struct EFI_SYSTEM_TABLE *SystemTable)
16: {
17: unsigned long long status;
18: struct EFI_KEY_DATA key_data = {{0, L'q'}, {0, 0}};
19: void *notify_handle;
20:
21: efi_init(SystemTable);
22: ST->ConOut->ClearScreen(ST->ConOut);
23:
24: puts(L"Waiting for the 'q' key input...\r\n");
25:
26: status = STIEP->RegisterKeyNotify(STIEP, &key_data, key_notice,
27: ¬ify_handle);
28: assert(status, L"RegisterKeyNotify");
29:
30: while (!is_exit);
31:
32: puts(L"exit.\r\n");
33:
34: while (TRUE);
35: }
リスト2.4は、'q'キーの入力でefi_main()内の特定の処理を抜けるサンプルになっています。
まず、'q'キーをイベント通知対象とするため、struct EFI_KEY_DATA key_dataへはstruct EFI_INPUT_KEYのUnicodeCharへ'q'を登録し、その他は0を設定しています。
そして、RegisterKeyNotify()を使用し、'q'キー入力でkey_notice()が呼び出される様に登録しています。
key_notice()では、グローバル変数is_exitへTRUEを設定するため、'q'キー入力でefi_main()内の"while (!is_exit);"を抜けます。
図2.1: RegisterKeyNotify()実行例1('q'キー入力待ち)
図2.2: RegisterKeyNotify()実行例2('q'キー入力後)
Unicode外のキー入力を検出するには、struct EFI_KEY_DATAのScanCodeを使用します。仕様書"12.1.2 ConsoleIn Definition"からの引用ですが、指定できるスキャンコードは表2.1の通りです。
表2.1: スキャンコード
| スキャンコード | 説明 | スキャンコード | 説明 |
|---|---|---|---|
| 0x00 | Nullスキャンコード | 0x15 | F11 |
| 0x01 | 上 | 0x16 | F12 |
| 0x02 | 下 | 0x68 | F13 |
| 0x03 | 右 | 0x69 | F14 |
| 0x04 | 左 | 0x6A | F15 |
| 0x05 | Home | 0x6B | F16 |
| 0x06 | End | 0x6C | F17 |
| 0x07 | Insert | 0x6D | F18 |
| 0x08 | Delete | 0x6E | F19 |
| 0x09 | Page Up | 0x6F | F20 |
| 0x0a | Page Down | 0x70 | F21 |
| 0x0b | F1 | 0x71 | F22 |
| 0x0c | F2 | 0x72 | F23 |
| 0x0d | F3 | 0x73 | F24 |
| 0x0e | F4 | 0x7F | ミュート |
| 0x0f | F5 | 0x80 | 音量を上げる |
| 0x10 | F6 | 0x81 | 音量を下げる |
| 0x11 | F7 | 0x100 | 画面の明るさを上げる |
| 0x12 | F8 | 0x101 | 画面の明るさを下げる |
| 0x13 | F9 | 0x102 | サスペンド |
| 0x14 | F10 | 0x103 | ハイバネート |
| 0x17 | Escape | 0x104 | ディスプレイトグル |
| 0x105 | リカバリー | ||
| 0x106 | イジェクト | ||
| 0x8000-0xFFFF | OEM予約領域 |
表2.1から、Escapeキーを検出したい場合は、struct EFI_KEY_DATAのScanCodeへ0x17を設定しておけば良いことが分かります。
そのため、リスト2.4を、「Escapeキーでwhileループを抜ける」へ変更する場合、18行目の変数"key_data"への代入処理をリスト2.5へ変更すれば良いです。
リスト2.5: "ESC"キーでwhileループを抜けるよう変更
struct EFI_KEY_DATA esc_key_data = {{0x17, 0}, {0, 0}};