キーボード、マウス、ウィンドウの
Event(出来事)の
Handling(操作)(問い合わせと処理)。
序文:
イベントとは、基本的には、キー押し、クリック、マウス移動などのユーザーの操作や、システムが生成した通知のような何らかの出来事を指します。
Event 型と
ScreenEvent 関数は、イベント(キーボード、マウス、ウィンドウ・イベント)にアクセスするために FreeBASIC が提供する組み込みインターフェースを構成します。
ScreenEvent は、ユーザーが独自のイベント・ハンドラを書けるように、イベントを通知します(ユーザー自身がデータを追跡することが必要です)。
ScreenEvent を使うことで、ユーザーは関数から返されたイベントを確認することができます。
この関数は、システム内部でキューに入れられたイベントを取得するために、定期的に呼び出されることが想定されています。
イベント型
Event 型は "fbgfx.bi" (
lang fb 方言専用の
FB 名前空間)で事前に定義された構造体です。
ユーザーが
Event 型のインスタンスを渡して
ScreenEvent を呼び出すと、
ScreenEvent はイベントデータでそれを埋めます(イベントフラグが返される場合)。
構文
#include once "fbgfx.bi"
Using FB
Dim variable As Event
"Event" structure
この構造は "fbgfx.bi" から抽出されます。
Type EVENT Field = 1
Type As Long
Union
Type
scancode As Long
ascii As Long
End Type
Type
x As Long
y As Long
dx As Long
dy As Long
End Type
button As Long
z As Long
w As Long
End Union
End Type
-
.type 項目には、"fbgfx.bi" で定義されている以下のシンボルのいずれかに対応するイベント型 ID 値を格納します:
- For key events, one among:
EVENT_KEY_PRESS
EVENT_KEY_RELEASE
EVENT_KEY_REPEAT
- For mouse event, one among:
EVENT_MOUSE_MOVE
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_DOUBLE_CLICK
EVENT_MOUSE_WHEEL
EVENT_MOUSE_HWHEEL
EVENT_MOUSE_ENTER
EVENT_MOUSE_EXIT
- For window event, one among:
EVENT_WINDOW_GOT_FOCUS
EVENT_WINDOW_LOST_FOCUS
EVENT_WINDOW_CLOSE
-
.scancode と .ascii 項目には、あるイベントの影響を受けたキーの scancode 値と ascii 表現(存在すれば0)がそれぞれ入ります:
EVENT_KEY_PRESS
EVENT_KEY_RELEASE
EVENT_KEY_REPEAT
-
.x, .y と .dx, .dy 項目には、それぞれ画面の左上隅を基準とした新しいマウスの位置(x, y)と、イベントによって引き起こされた動きの差分(dx, dy)が格納されます:
-
.button 項目には、1つのイベントで影響を受けるマウスボタンの識別情報(ビット0:左ボタン、ビット1:右ボタン、ビット2:中ボタン)が格納されています:
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_DOUBLE_CLICK
-
.z 項目には、イベントによって引き起こされる新しいホイールの位置が含まれます:
-
.w 項目には、イベントで新たに発生した水平方向のホイール位置が入ります:
注意:
- 最後の 5つの項目ブロック [
.scancode と
.ascii], [
.x,
.y と
.dx,
.dy], [
.button], [
.z] と [
.w] は、(ユニオン型の中で宣言されているため)同じメモリ位置に配置されており、1つの項目ブロックを入力すると、以前に入力された他の項目ブロックが上書きされます。
-
したがって、たとえば、
MOUSE_BUTTON_PRESS イベントが検出されたときに、マウスの位置 (mx, my) を利用できるようにするには、この位置は、
MOUSE_MOVE イベント毎に、独自のイベントデータ (
.x,
.y)から (mx, my) に、前もって格納されていなければなりません。なぜなら、(
.x,
.y) は
ScreenEvent によって返される他のイベントによって上書きされるからです。
ScreenEvent 関数
ScreenEvent 関数は、システムイベントを照会および取得することができます。
構文
Declare Function ScreenEvent ( ByVal event_instance_ptr As Any Ptr = 0 ) As Long
Usage
result = ScreenEvent( [ event_instance_ptr ] )
with:
event_instance_ptr: 関数が、上記で定義された
Event 型の構造体で、イベントデータを埋めるためのバッファ・アドレス。
result: 取得すべき保留中のイベントがある場合は -1、それ以外は 0。
記述
ScreenEvent 関数は、保留中のシステムイベントがあるかどうかをチェックします:
- イベントがない場合、この関数は 0(false)を返します。
- イベントがあれば -1(真)を返し、
event_instance_ptr ポインタが NULL でなければ、イベントデータをユーザに渡された構造体にコピーし、内部の
GfxLib イベントキューからイベント(使用可能な最新のもの)を削除します。
- つまり、保留中のイベントがあるかどうかを、(あれば)取得することなく、また、内部イベントキューから取り出すこともなく、単にチェックするには、この関数をパラメータなしで(あるいは、NULL パラメータを指定して)呼び出すだけで十分なのです。
ユーザーは、最初に
ScreenEvent を 1回呼び出して 1つのイベントを取得し、その 1つのイベントを関心のある各イベント型と照合する必要があります。
ユーザーがそのイベントを処理したら、すぐに
ScreenEvent を再度呼び出して、次のイベントを処理しなければなりません。そうしないと、ほぼ確実にイベントを見逃すことになります(例えば、
EVENT_MOUSE_MOVE のように)。
このため、連続するイベントをテストするプログラムループは、ユーザーが傍受したいイベントの速度(例えば、キーボード入力とマウスの動きの違い)と互換性のある周期を持たなければなりません。
逆に、イベント・テストループが 10ms より大幅に短い周期だと、不必要に CPU が占有されます。
イベント検索プログラムの骨格
ScreenEvent 関数が処理するイベントは一度に 1つだけなので、イベント問い合わせプログラムの骨格は、
[Select Case As Const...End Select] ブロックを中心に適切に構成できます。
別のケースは、ユーザーが処理したい別のイベントに対応します。
#include once "fbgfx.bi"
Using FB
' .....
Dim e As EVENT
' .....
Do
If (ScreenEvent(@e)) Then
Select Case As Const e.Type
Case EVENT_xxx
' handle the event xxx
Case EVENT_yyy
' handle the event yyy
' .....
' .....
Case EVENT_zzz
' handle the event zzz
Case Else
' event not handled
End Select
End If
Sleep 10, 1
Loop
' .....
例えば、キーを何秒か押したままにすると、これが誘発されます:
- a first EVENT_KEY_PRESS,
- followed by several EVENT_KEY_REPEAT (for as long as the key is held down),
- and EVENT_KEY_RELEASE when the key is released.
マウスの単純なクリック操作が 2回連続すると、明らかに 4回のマウスイベントが発生します:
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
マウスのダブルクリックだけの操作でも、4回の連続したマウスイベントが発生します。しかし:
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_DOUBLE_CLICK
EVENT_MOUSE_BUTTON_RELEASE
注意:
- ユーザー・プログラムが
KEY_... イベントを取得しても、キーボード・バッファはクリアされません。ユーザーがイベントを取得した後にキーボードバッファをクリアする必要がある場合は、手動でクリアする必要があります(
InKey 参照)。
- イベント・キューイングと InKey バッファリングは、キーボードの状態をテストするための 2つの完全に独立した方法です。これらは、競合する 2つのスレッドであっても、競合することなく共存できます。
- gfxlib でキーボードからの入力をテストするには、現在 3つの方法があります:
InKey を使う(ユーザーがテストできるのは、ascii 表現のあるキーだけです)。
MultiKey を使う (キーボードの任意のキーの押下/解放状態を確認できます。)
ScreenEvent を使う (
KEY_PRESS,
KEY_RELEASE,
KEY_REPEAT イベントを確認できます)。
例
キーが押されたイベント処理を実装するための単純な
ScreenEvent メカニズムの例:
(click on the window-close button [X] to exit)
' The main code tests events in a loop:
' - calls a user Sub each time a key-pressed event is retrieved
' - exits the loop if a window-close event is retrieved (by click on window-close button)
' The user Sub prints the character of the key pressed, the ascci code and the scancode.
#include once "fbgfx.bi"
Using FB
'' user callback Sub definition
Sub printInkeyData (ByVal ascii As Long, ByVal scancode As Long)
Print "'" & Chr(ascii) & "' (" & ascii & ")", scancode
End Sub
'' user main code
Screen 12
Dim e As EVENT
Do
If (ScreenEvent(@e)) Then
Select Case As Const e.Type
Case EVENT_KEY_PRESS '' test key-pressed event
printInkeyData(e.ascii, e.scancode)
Case EVENT_WINDOW_CLOSE '' test window-close event
Exit Do
End Select
End If
Sleep 10, 1
Loop
イベント・キューと InKey バッファリングは、競合する 2つのスレッドでも、衝突することなく共存できることを強調する、簡単な例です:
(ESC to quit)
' The main code (main thread) tests Inkey in a loop.
' The other thread tests ScreenEvent in a loop.
' The ESC character allows to exit the two loops.
#include "fbgfx.bi"
Using FB
Function getAscii (ByVal ascii As Long) As String
If ((ascii>0) And (ascii<255)) Then
Return "'" & Chr(ascii) & "'"
Else
Return "???"
End If
End Function
Sub Thread (ByVal p As Any Ptr)
Dim e As Event
Do
If (ScreenEvent(@e)) Then
Select Case As Const e.Type
Case EVENT_KEY_PRESS '' test key-pressed event
Print getAscii(e.ascii) &_
" is pressed (from ScreenEvent) (other thread)"
If (e.scancode=SC_ESCAPE) Then '' test ESC
Exit Sub
End If
Case EVENT_KEY_RELEASE '' test key-released event
Print getAscii(e.ascii) &_
" is released (from ScreenEvent) (other thread)"
Case EVENT_KEY_REPEAT '' test key-repeated event
Print getAscii(e.ascii) &_
" is repeated (from ScreenEvent) (other thread)"
End Select
End If
Sleep 10, 1
Loop
End Sub
Screen 12
Dim As String s
Dim As Any Ptr pt
pt = ThreadCreate(@Thread)
Do
s = Inkey
If s <> "" Then '' test inkey return
Print getAscii(s[0]) &_
" is viewed (from Inkey) (main thread)"
End If
Sleep 10, 1
Loop Until s = Chr(27) '' test ESC
ThreadWait(pt)
Print "main and other thread completed"
Sleep
マウスのイベント処理の例では、カーソルの位置やボタンの状態を EVENT の派生型に格納し、通常のイベント以外の時に利用できるようにしています:
(click on the window-close button [X] to exit)
' Memorization in (.mx, .my) of the cursor position of the mouse:
' - at MOUSE_MOVE event : e.mx = e.x, e.my = e.y
' Memorization in .mbutton of the buttons state of the mouse:
' - at MOUSE_BUTTON_PRESS event and MOUSE_DOUBLE_CLICK event : e.mbutton = e.mbutton Or e.button
' - at MOUSE_BUTTON_RELEASE event : e.mbutton = e.mbutton Xor e.button
#include once "fbgfx.bi"
Using FB
Type EVENTstore Extends EVENT
mx As Long
my As Long
mbutton As Long
End Type
Screen 19
Dim e As EVENTstore
Do
If ScreenEvent(@e) Then
Select Case As Const e.Type
Case EVENT_MOUSE_MOVE
e.mx = e.x
e.my = e.y
Print Using "Mouse move: x=#### / y=#### dx=#### / dy=#### button=##";_
e.x; e.y; e.dx; e.dy; e.mbutton
Case EVENT_MOUSE_BUTTON_PRESS
e.mbutton = e.mbutton Or e.button
Print Using "Mouse button press: button =## x=#### / y=####";_
e.button; e.mx; e.my
Case EVENT_MOUSE_BUTTON_RELEASE
e.mbutton = e.mbutton Xor e.button
Print Using "Mouse button release: button =## x=#### / y=####";_
e.button; e.mx; e.my
Case EVENT_MOUSE_DOUBLE_CLICK
e.mbutton = e.mbutton Or e.button
Print Using "Mouse button double click: button =## x=#### / y=####";_
e.button; e.mx; e.my
Case EVENT_MOUSE_WHEEL
Print Using "Mouse wheel: wheel= ########### x=#### / y=####";_
e.z; e.mx; e.my
Case EVENT_MOUSE_HWHEEL
Print Using "Mouse hwheel: hwheel= ########### x=#### / y=####";_
e.z; e.mx; e.my
Case EVENT_WINDOW_CLOSE
Exit Do
End Select
End If
Sleep 10, 1
Loop
参照: