18. ReadKeyStroke 함수로 사용자 입력 처리

이번 장에서는 사용자의 입력을 처리할 수 있는 애플리케이션을 만든다.

사용자의 입력을 처리하기 위해서는 ReadKeyStroke 가 필요하다.

EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke()

Summary:
Reads the next keystroke from the input device.

Prototype:
typedef
EFI_STATUS
(EFIAPI *EFI_INPUT_READ_KEY) (
 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
 OUT EFI_INPUT_KEY *Key
 );

Parameters:
This 		A pointer to the EFI_SIMPLE_TEXT_INPUT_PROTOCOL instance.
Key 		A pointer to a buffer that is filled in with the keystroke information
		for the key that was pressed.

Description:
The ReadKeyStroke() function reads the next keystroke from the input device.

Status Codes Returned:
EFI_SUCCESS 		The keystroke information was returned.
EFI_NOT_READY 		There was no keystroke data available.
EFI_DEVICE_ERROR 	The keystroke information was not returned due to hardware errors.

EFI_INPUT_KEY 함수는 아래와 같이 정의된다.

typedef struct {
 UINT16 ScanCode;
 CHAR16 UnicodeChar;
} EFI_INPUT_KEY;

간단한 애플리케이션을 만들어 본다. 아래 코드는 ReadKeyStroke를 사용해 사용자의 키 입력을 받는 간단한 프로그램이다. 애플리케이션은 무한 반복으로 사용자가 k 를 입력하면 Correct! 라는 문자열을 출력하고 종료되고 다른 키를 입력할 경우 Wrong! 을 출력하며 프로그램 종료는 q 를 입력하여 종료할 수 있다.

애플리케이션을 만드는 방법은 설명하지 않고 아래 코드에서는 UefiMain을 사용하기 때문에 HelloWorld 애플리케이션의 *.inf 파일을 수정하여 사용한다.

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINTN Index;
  EFI_INPUT_KEY Key;

  Print(L"Try to guess the secret symbol!\n");
  Print(L"To quit press 'q'\n");

  while(TRUE) {
    gBS->WaitForEvent(1, &(gST->ConIn->WaitForKey), &Index);
    gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
    Print(L"ScanCode = %04x, UnicodeChar = %04x (%c)\n", Key.ScanCode, Key.UnicodeChar, Key.UnicodeChar);

    if (Key.UnicodeChar == 'k') {
      Print(L"Correct!\n");
      break;
    } else if (Key.UnicodeChar == 'q') {
      Print(L"Bye!\n");
      break;
    } else {
      Print(L"Wrong!\n");
    }
  }
  gST->ConIn->Reset(gST->ConIn, FALSE);
  return EFI_SUCCESS;
}

QEMU에서 그래픽 옵션이 있는 경우와 없는 경우의 키 입력 차이

QEMU를 -nographic 옵션을 사용해 실행한다.

qemu-system-x86_64 \
  -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -nographic

InteractiveApp.efi 을 실행해서 PageUp 키를 입력하면 아래와 같이 출력이 나타난다.

ScanCode = 0017, UnicodeChar = 0000 ( )
Wrong!
ScanCode = 0000, UnicodeChar = 005B ([)
Wrong!
ScanCode = 0000, UnicodeChar = 0035 (5)
Wrong!
ScanCode = 0000, UnicodeChar = 007E (~)
Wrong!

SimpleTextIn 프로토콜의 UEFI 스펙과 EDKII 헤더에 따르면 PageUp 키는 이스케이프 스캔 코드로 시작하는 4개의 기호가 아닌 다른 스캔 코드로 시작되어야 한다. (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/SimpleTextIn.h)

#define SCAN_PAGE_UP    0x0009
...
#define SCAN_ESC        0x0017

이 문제는 QEMU의 -nographic 옵션으로 인해 발생한다. QEMU를 사용하면 모든 특수 키를 변환하여 콘솔 터미널에서 발생하는 것처럼 시퀀스를 이스케이프한다.

정상적인 키 입력을 보기 위해서는 QEMU를 그래픽 모드로 실행한다.

qemu-system-x86_64 \
  -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk

시스템에서 기본 그래픽을 지원하지 않는 경우 VNC 옵션으로 QEMU를 실행할 수 있다. QEMU는 제공한 포트에 VNC 서버를 생성하며, VNC 클라이언트 앱을 통해 해당 포트에 연결할 수 있다. ex) VNC 뷰어(https://www.realvnc.com/en/connect/download/viewer/)

아래 명령은 127.0.0.1:1 주소로 VNC 서버를 생성한다.

qemu-system-x86_64 \
  -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -vnc :1

그래픽 모드로 실행된 QEMU에서 Interactive.efi 를 실행해서 PageUp키를 입력하면 정상적으로 입력받는 것을 볼 수 있다.

Last updated