Top

第4章 マウス入力を取得する

次はマウスを使ってみます。この章では、マウス入力を取得し、マウスの動きに合わせてマウスカーソルを描画してみます。

4.1 EFI_SIMPLE_POINTER_PROTOCOL

マウスの入力値を取得するには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関数の引数の意味は以下の通りです。

unsigned char ExtendedVerification
拡張の検査(ExtendedVerification)を行うか否かのフラグ。TRUEが設定されていると拡張検査が行われる。どのような拡張検査が行われるかはファームウェア依存。本書では特にそのような検査は不要であるためFALSEを指定。

また、GetState関数の引数については以下の通りです。

struct EFI_SIMPLE_POINTER_STATE *State
ポインティングデバイスの状態を指定されたポインタ変数へ格納。

なお、状態を表す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関数へ渡して使うことができます。

マウスの状態を見てみる(pstatコマンド)

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に示します。

pstatコマンド実行の様子

図4.1: pstatコマンド実行の様子

4.2 マウスカーソルを追加する

グラフィックを描画する方法が分かり、マウスの入力値の取得もできたので、アイコンもどきの矩形を表示するだけだった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)内の定常処理では以下の流れで処理を行っています。

  1. WaitForEventで待機し、GetState関数でマウス入力値を取得
  2. マウスカーソル座標更新
  3. マウスカーソル描画(put_cursor関数)
  4. ファイルアイコン処理

なお、put_cursor関数は引数で指定した座標へマウスカーソルを移動させる関数です。put_cursorでは以下の流れで処理を行います。

  1. 退避していたピクセルデータを復帰(load_cursor_area)
  2. 描画するカーソル位置のピクセルデータを退避(save_cursor_area)
  3. カーソル描画(draw_cursor)
  4. カーソル位置を退避(cursor_old_x,cursor_old_y)

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: マウスカーソルがアイコンにのるとハイライト表示


Top