次はマウスを使ってみます。この章では、マウス入力を取得し、マウスの動きに合わせてマウスカーソルを描画してみます。
マウスの入力値を取得するにはEFI_SIMPLE_POINTER_PROTOCOLを使用します(仕様書"11.5 Simple Pointer Protocol(P.439)")。本書で使用する箇所の定義はリスト4.1の通りです。
リスト4.1: EFI_SIMPLE_POINTER_PROTOCOLの定義
struct EFI_SIMPLE_POINTER_PROTOCOL {
unsigned long long (*Reset)(
struct EFI_SIMPLE_POINTER_PROTOCOL *This,
unsigned char ExtendedVerification);
unsigned long long (*GetState)(
struct EFI_SIMPLE_POINTER_PROTOCOL *This,
struct EFI_SIMPLE_POINTER_STATE *State);
void *WaitForInput;
};
EFI_SIMPLE_POINTER_PROTOCOLでは、Reset関数でポインティングデバイス(マウス)をリセットし、GetState関数で状態を取得します。
Reset関数の引数の意味は以下の通りです。
また、GetState関数の引数については以下の通りです。
なお、状態を表すEFI_SIMPLE_POINTER_STATE構造体の定義はリスト4.2の通りです。
リスト4.2: EFI_SIMPLE_POINTER_STATEの定義
struct EFI_SIMPLE_POINTER_STATE {
int RelativeMovementX; /* X軸方向の相対移動量 */
int RelativeMovementY; /* Y軸方向の相対移動量 */
int RelativeMovementZ; /* Z軸方向の相対移動量 */
unsigned char LeftButton; /* 左ボタン押下状態(TRUE=1,FALSE=0) */
unsigned char RightButton; /* 右ボタン押下状態(TRUE=1,FALSE=0) */
};
リスト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)。
リスト4.3: sample4_1_get_pointer_state/efi.c
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の通りです。
リスト4.4: sample4_1_get_pointer_state/shell.c
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に示します。
図4.1: pstatコマンド実行の様子
グラフィックを描画する方法が分かり、マウスの入力値の取得もできたので、アイコンもどきの矩形を表示するだけだったGUIモードへマウスカーソルを表示する機能を追加してみます。サンプルのディレクトリは"sample4_2_add_cursor"です。
実装の仕方としては、ここではとにかく簡単にするため、「マウスカーソルは1ドット」で作ってみます。マウスカーソルの機能を追加したgui.cをリスト4.5に示します。
リスト4.5: sample4_2_add_cursor/gui.c
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] 白黒の印刷なので印刷上は分からないかも知れませんが。。
図4.2: マウスカーソルが表示される様子
図4.3: マウスカーソルがアイコンにのるとハイライト表示