FreeBASIC マニュアルのトップに戻る

FreeBASIC ProPgEventHandling

目次→教本→プログラマーのための案内Event Handling←オリジナル・サイト

イベント操作 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

キーボード、マウス、ウィンドウの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
注意:
- 最後の 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


参照:
プログラマーのための案内に戻る
目次に戻る
←リンク元に戻る プログラム開発関連に戻る

ページ歴史:2022-06-07 12:44:28
日本語翻訳:WATANABE Makoto、原文著作者:fxm

ホームページのトップに戻る

表示-非営利-継承