次はマウスを使ってみます。この章では、マウス入力を取得し、マウスの動きに合わせてマウスカーソルを描画してみます。
マウスの入力値を取得するにはEFI_SIMPLE_POINTER_PROTOCOLを使用します(仕様書"11.5 Simple Pointer Protocol(P.439)")。本書で使用する箇所の定義はリスト4.1の通りです。
EFI_SIMPLE_POINTER_PROTOCOLでは、Reset関数でポインティングデバイス(マウス)をリセットし、GetState関数で状態を取得します。
Reset関数の引数の意味は以下の通りです。
また、GetState関数の引数については以下の通りです。
なお、状態を表すEFI_SIMPLE_POINTER_STATE構造体の定義はリスト4.2の通りです。
リスト4.2に関し、各メンバの意味はコメントの通りです。なお、"RelativeMovementZ"は普通のマウスを使う限り常に0でした(特殊なマウスを持っていないので、筆者が試す限り0以外見たことがありません)。
EFI_SIMPLE_POINTER_PROTOCOL(リスト4.1)の"WaitForInput"は、マウス入力があるまで待機するためのイベントです。EFI_SIMPLE_TEXT_INPUT_PROTOCOLのWaitForKeyと同様に、SystemTable->BootServices->WaitForEvent関数へ渡して使うことができます。
EFI_SIMPLE_POINTER_PROTOCOLを使うことでマウスの状態を取得できることが分かったので、試しにマウスの状態をコンソール上へダンプしてみます。ここではpstatというコマンドとしてマウス状態ダンプの機能を追加してみます。サンプルのディレクトリは"sample4_1_get_pointer_state"です。
まず、LocateProtocol関数でEFI_SIMPLE_POINTER_PROTOCOLの構造体の先頭アドレスを取得します。そのため、efi.cのefi_init関数へLocateProtocolの処理を追加し、グローバル変数としてアクセスできるようにします(リスト4.3)。
1: #include "efi.h" 2: #include "common.h" 3: 4: struct EFI_SYSTEM_TABLE *ST; 5: struct EFI_GRAPHICS_OUTPUT_PROTOCOL *GOP; 6: struct EFI_SIMPLE_POINTER_PROTOCOL *SPP; /* 追加 */ 7: 8: void efi_init(struct EFI_SYSTEM_TABLE *SystemTable) 9: { 10: struct EFI_GUID gop_guid = {0x9042a9de, 0x23dc, 0x4a38, \ 11: {0x96, 0xfb, 0x7a, 0xde, \ 12: 0xd0, 0x80, 0x51, 0x6a}}; 13: /* 追加(ここから) */ 14: struct EFI_GUID spp_guid = {0x31878c87, 0xb75, 0x11d5, \ 15: {0x9a, 0x4f, 0x0, 0x90, \ 16: 0x27, 0x3f, 0xc1, 0x4d}}; 17: /* 追加(ここまで) */ 18: 19: ST = SystemTable; 20: ST->BootServices->SetWatchdogTimer(0, 0, 0, NULL); 21: ST->BootServices->LocateProtocol(&gop_guid, NULL, (void **)&GOP); 22: /* 追加 */ 23: ST->BootServices->LocateProtocol(&spp_guid, NULL, (void **)&SPP); 24: }
pstatコマンドを追加したshell.cはリスト4.4の通りです。
1: #include "efi.h" /* 追加 */ 2: #include "common.h" 3: #include "graphics.h" 4: #include "shell.h" 5: #include "gui.h" 6: 7: #define MAX_COMMAND_LEN 100 8: 9: /* 追加(ここから) */ 10: void pstat(void) 11: { 12: unsigned long long status; 13: struct EFI_SIMPLE_POINTER_STATE s; 14: unsigned long long waitidx; 15: 16: SPP->Reset(SPP, FALSE); 17: 18: while (1) { 19: ST->BootServices->WaitForEvent(1, &(SPP->WaitForInput), 20: &waitidx); 21: status = SPP->GetState(SPP, &s); 22: if (!status) { 23: puth(s.RelativeMovementX, 8); 24: puts(L" "); 25: puth(s.RelativeMovementY, 8); 26: puts(L" "); 27: puth(s.RelativeMovementZ, 8); 28: puts(L" "); 29: puth(s.LeftButton, 1); 30: puts(L" "); 31: puth(s.RightButton, 1); 32: puts(L"\r\n"); 33: } 34: } 35: } 36: /* 追加(ここまで) */ 37: 38: void shell(void) 39: { 40: unsigned short com[MAX_COMMAND_LEN]; 41: struct RECT r = {10, 10, 100, 200}; 42: 43: while (TRUE) { 44: puts(L"poiOS> "); 45: if (gets(com, MAX_COMMAND_LEN) <= 0) 46: continue; 47: 48: if (!strcmp(L"hello", com)) 49: puts(L"Hello UEFI!\r\n"); 50: /* ・・・省略・・・ */ 51: else if (!strcmp(L"pstat", com)) /* 追加 */ 52: pstat(); /* 追加 */ 53: else 54: puts(L"Command not found.\r\n"); 55: } 56: }
リスト4.4に関して、pstat関数ではputh関数を使用して画面へ数値を表示しています。puth関数はこのサンプルからcommon.cへ追加した関数で、引数で指定した数値を16進数で表示します。第1引数が表示する数値で、第2引数が表示する際の桁数です。
最後にリスト4.4のサンプルの実行の様子を図4.1に示します。
グラフィックを描画する方法が分かり、マウスの入力値の取得もできたので、アイコンもどきの矩形を表示するだけだったGUIモードへマウスカーソルを表示する機能を追加してみます。サンプルのディレクトリは"sample4_2_add_cursor"です。
実装の仕方としては、ここではとにかく簡単にするため、「マウスカーソルは1ドット」で作ってみます。マウスカーソルの機能を追加したgui.cをリスト4.5に示します。
1: #include "efi.h" 2: #include "common.h" 3: #include "graphics.h" 4: #include "shell.h" 5: #include "gui.h" 6: 7: /* 追加(ここから) */ 8: struct EFI_GRAPHICS_OUTPUT_BLT_PIXEL cursor_tmp = {0, 0, 0, 0}; 9: int cursor_old_x; 10: int cursor_old_y; 11: 12: void draw_cursor(int x, int y) 13: { 14: draw_pixel(x, y, white); 15: } 16: 17: void save_cursor_area(int x, int y) 18: { 19: cursor_tmp = get_pixel(x, y); 20: cursor_tmp.Reserved = 0xff; 21: } 22: 23: void load_cursor_area(int x, int y) 24: { 25: draw_pixel(x, y, cursor_tmp); 26: } 27: 28: void put_cursor(int x, int y) 29: { 30: if (cursor_tmp.Reserved) 31: load_cursor_area(cursor_old_x, cursor_old_y); 32: 33: save_cursor_area(x, y); 34: 35: draw_cursor(x, y); 36: 37: cursor_old_x = x; 38: cursor_old_y = y; 39: } 40: /* 追加(ここまで) */ 41: 42: void gui(void) 43: { 44: struct RECT r = {10, 10, 20, 20}; 45: /* 追加・変更(ここから) */ 46: unsigned long long status; 47: struct EFI_SIMPLE_POINTER_STATE s; 48: int px = 0, py = 0; 49: unsigned long long waitidx; 50: unsigned char is_highlight = FALSE; 51: 52: ST->ConOut->ClearScreen(ST->ConOut); 53: SPP->Reset(SPP, FALSE); 54: 55: /* ファイルアイコンに見立てた矩形を描画 */ 56: draw_rect(r, white); 57: 58: while (TRUE) { 59: ST->BootServices->WaitForEvent(1, &(SPP->WaitForInput), &waitidx); 60: status = SPP->GetState(SPP, &s); 61: if (!status) { 62: /* マウスカーソル座標更新 */ 63: px += s.RelativeMovementX >> 13; 64: if (px < 0) 65: px = 0; 66: else if (GOP->Mode->Info->HorizontalResolution <= 67: (unsigned int)px) 68: px = GOP->Mode->Info->HorizontalResolution - 1; 69: py += s.RelativeMovementY >> 13; 70: if (py < 0) 71: py = 0; 72: else if (GOP->Mode->Info->VerticalResolution <= 73: (unsigned int)py) 74: py = GOP->Mode->Info->VerticalResolution - 1; 75: 76: /* マウスカーソル描画 */ 77: put_cursor(px, py); 78: 79: /* ファイルアイコン処理 */ 80: if (is_in_rect(px, py, r)) { 81: if (is_highlight) { 82: draw_rect(r, yellow); 83: is_highlight = TRUE; 84: } 85: } else { 86: if (is_highlight) { 87: draw_rect(r, white); 88: is_highlight = FALSE; 89: } 90: } 91: } 92: } 93: /* 追加・変更(ここまで) */ 94: }
gui関数へ追加した処理の流れとしては、ClearScreen関数からdraw_rect関数までで、画面とマウスの初期化を行い、アイコン(矩形)の描画を行っています。while(TRUE)
内の定常処理では以下の流れで処理を行っています。
なお、put_cursor関数は引数で指定した座標へマウスカーソルを移動させる関数です。put_cursorでは以下の流れで処理を行います。
save_cursor_area関数内に関して、get_pixel関数はgraphics.cへ新たに追加した関数で、フレームバッファからピクセルデータを取得します。ピクセルデータはEFI_GRAPHICS_OUTPUT_BLT_PIXEL構造体の形式です。フレームバッファから読み出した際のReservedメンバの値は0なのですが、フレームバッファへ書き込むときに0を指定していると何も表示されないので、save_cursor_area関数内でReservedは0xffで上書きしています。これを利用して、put_cursor関数内では、退避済みのピクセルデータがあるか否かをReservedメンバの値で確認しています。
gui関数内のwhile(TRUE)
内の処理に戻り、"2. マウスカーソル座標更新"のマウスの移動量計算は、pstatコマンドで確認した結果からおおよそ算出したものです。下位12ビットが全て0であり、12ビットシフトするだけでは移動量としては大きすぎたため、13ビット右シフトしています。もしpstatコマンドで確認した結果が異なるようであれば、適宜修正してください。
"4. ファイルアイコン処理"のアイコン更新処理では、現在のマウスカーソル座標がアイコン(矩形)の範囲内であればアイコンをハイライト色で上書きし、そうでなければ元の色で上書きしています。
サンプルを実行し、シェル上でguiコマンドを実行することでGUIモードへ遷移すると、図4.2の画面になります。アイコン(矩形)の右側にある点がマウスカーソルです*1。マウスカーソルがアイコンの上にのると、枠が黄色にハイライト表示されます(図4.3)*2。
[*1] ゴミのように見えますがマウスカーソルなのです。。。
[*2] 白黒の印刷なので印刷上は分からないかも知れませんが。。