26 March 2010

Sleep, DoEvents, PeekEvent, or GetEvent?

In contrast with VB a GB32 application needs a global message loop to retrieve pending messages from the application’s message queue. In GFA-BASIC 32 the message loop is most often placed in the ‘main’ section of the program and often looks like this:
Do
Sleep
Loop Until Me Is Nothing

Me is a Form object variable holding the current active window of the GFA-BASIC 32 program. The Me variable always represents the current active window should a program open multiple windows. Me always represents a window (Form) unless the last window is closed, then Me is equal to Nothing. That’s the moment the Do loop is ends and the program stops.

GFA-BASIC 32 offers 4 different commands to read the message loop and dispatch the message to the appropriate window procedure. Essentially, all these commands utilize PeekMessage(), TranslateMessage(), and DispatchMessage() - like any other message loop under Windows. They only slightly differ which I will discuss here. Let’s start with PeekEvent because it is the heart of all four commands.

PeekEvent invokes the PeekMessage(V:msg,0,0,0,PM_REMOVE) function, which checks a thread message queue for a message and places the message (if any) in the specified structure. If no messages are available, the return value is zero. The PM_REMOVE flag indicates that messages are removed from the queue after processing by PeekMessage. The following GB32 listing shows pseudo-code for the PeekEvent command.

Procedure PeekEvent()
Dim msg As MESSAGE
// Clear MENU() event array
MemZero(MENU(0), 17)
// Obtain message if available
If PeekMessage(V:msg, 0, 0, 0, PM_REMOVE)
// Copy msg members to MENU() array
MENU(2) = msg.pt.x
MENU(3) = msg.pt.y
MENU(16)= msg.time
// Handle key input messages
If msg.message >= WM_KEYFIRST && _
msg.message <= WM_KEYLAST
// Handle keyboard events at
// the application level.
Exit Sub If Screen_KeyPreview(V:msg)
// If Name starts with 'Dlg_' use
// special IsDialogMessage().
If Me.IsDialog Then
IsDlg_XX_Message(Me.hWnd, V:msg)
Else
~TranslateMessage(V:msg)
EndIf
EndIf
// Send to appropriate window proc
~DispatchMessage(V:msg)
EndIf    // PeekMessage()
EndProc

This pseudo code is rather accurate. I omitted the check for reentrance while executing PeekEvent. Only this, once PeekEvent is executing and is dispatching messages, it cannot be reentered again.

- GetEvent is a simple variation on PeekEvent. Like PeekEvent it only retrieves one message a time and then returns to the application, in fact GetEvent invokes PeekEvent as follows:

Procedure GetEvent()
If Not GetQueueStatus(QS_ALLEVENTS) Then _
~WaitMessage()
@PeekEvent()
EndProc

GetEvent waits for a message when - on entrance - no messages are current in the queue. When a message is available it calls PeekEvent to handle the message and then returns to the application.

-DoEvents is a VB compatible command (used seldom in VB because the message loop is hidden and executed automatically in VB programs). In GB32 it is implement as a loop calling PeekEvent until the message queue is empty.

Function DoEvents%()
Dim Count% = 0
While @PeekEvent()
Count% ++
Wend
~Sleep(Count%)
Return Count%
EndFunc

DoEvents yields execution for a small amount of time after retrieving all messages form the queue. It returns the number of messages obtained. The yielding time depends on the number of handled messages.

- Sleep starts with calling DoEvents. When DoEvents returns 0 then Sleep goes in a wait state and waits for new messages and when they arrive it executes DoEvents again.

Procedure Sleep()
If @DoEvents%() = 0 Then
~WaitMessage()
@DoEvents%()
EndIf
EndProc

Sleep is the preferred command for the main message loop of course.

No comments:

Post a Comment