Top

第2章 キーボード入力

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()を紹介します。

2.1 特定のキー入力で呼び出される関数を登録する

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:                                       &notify_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図2.2の通りです。

RegisterKeyNotify()実行例1('q'キー入力待ち)

図2.1: RegisterKeyNotify()実行例1('q'キー入力待ち)

RegisterKeyNotify()実行例2('q'キー入力後)

図2.2: RegisterKeyNotify()実行例2('q'キー入力後)

補足: Unocode外のキー入力を検出するには

Unicode外のキー入力を検出するには、struct EFI_KEY_DATAのScanCodeを使用します。仕様書"12.1.2 ConsoleIn Definition"からの引用ですが、指定できるスキャンコードは表2.1の通りです。

表2.1: スキャンコード

スキャンコード説明スキャンコード説明
0x00Nullスキャンコード0x15F11
0x010x16F12
0x020x68F13
0x030x69F14
0x040x6AF15
0x05Home0x6BF16
0x06End0x6CF17
0x07Insert0x6DF18
0x08Delete0x6EF19
0x09Page Up0x6FF20
0x0aPage Down0x70F21
0x0bF10x71F22
0x0cF20x72F23
0x0dF30x73F24
0x0eF40x7Fミュート
0x0fF50x80音量を上げる
0x10F60x81音量を下げる
0x11F70x100画面の明るさを上げる
0x12F80x101画面の明るさを下げる
0x13F90x102サスペンド
0x14F100x103ハイバネート
0x17Escape0x104ディスプレイトグル
  0x105リカバリー
  0x106イジェクト
  0x8000-0xFFFFOEM予約領域

表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}};

Top