Home page Home page Home page Home page
Pixel
Pixel Header R1 C1 Pixel
Pixel Header R2 C1 Pixel
Pixel Header R3 C1 Pixel
Pixel
By Sprezz | Thursday 21 June 2012 00:06 | 0 Comments
As ever a posting on the Rev forum sparked our interest when a user asked if it was possible to "look ahead" in the event queue - so that if for example a user clicked a "fast forward" button ten times, they could react to the button instantly instead of as ten separate events. Whilst this isn't a requirement we have had ourselves it did provide the ideal opportunity for a spot of "tinkering" to come up with a Sprezz style solution.

Before we continue however a disclaimer. Because the techniques discussed herein affect normal event flow within OpenInsight, neither Sprezzatura nor any of it employees will be liable for anything resulting from the application of the aforesaid methods.

The key to making implementing this is a core system routine call PEEK_EVENT. This literally looks into the event queue and returns any event that is waiting to be processed. If no event is waiting, it simply returns null. This behaviour differs from that of GET_EVENT which takes the same parameters but waits to receive an event before returning guaranteeing that it will always return something.

So the way in which we can achieve our stated objective is to go into a loop looking for events, collecting them until a certain time has elapsed of the user doing nothing and then exit returning the event count. Of course this assumes that the only events being generated are the events that we are looking at - if they're not we're going to have to find some way of processing them rather than just incrementing a counter. We could represent the problem graphically thus :-


This makes it clear that we're going to need to be able to


  • Check the event queue
  • Run events
  • Check elapsed time
We've already mentioned how we peek into the event queue but we haven't yet touched upon the syntax of the command. Running events is accomplished using the RUN_EVENT function which is another low level routine. Checking elapsed time can be accomplished in a variety of ways. The TIME() function is useless here as we really need to measure much smaller intervals than seconds. If a user is repeatedly clicking a key then they're going to want to see something happen when they stop clicking for a fraction of a second. Even DOSTIME isn't sufficiently granular so here in Sprezz Towers if we want to deal in milliseconds we tend to use a Windows API function called timeGetTime. To quote the MSDN documentation on this function - "The timeGetTime function retrieves the system time, in milliseconds. The system time is the time elapsed since Windows was started". So we can check for a difference of 200 units to represent a fifth of a second. As an added advantage timeGetTime is predeclared in OI so we can just use it. All we need to do is declare it as a function and then call it. 

PEEK_EVENT Function


object = peek_Event( identifier, ctrlEntId, class, event, p1, p2, p3, p4, p5, p6, p7, |
                     p8, p9)

The PEEK_EVENT Function takes 13 parameters - the first four are defined and the last nine are event specific. The first four that remain consistent are :-

  • identifier
  • ctrlEntId
  • class
  • event
None of these parameters are required as they are primarily designed to return information to the caller.

Identifier

In the context that we use this routine this seems to always be set to SYSPROG*OIWIN, This probably reflects an idea on the part of the original OI designers that there would be multiple classes of OI window such as popups and messages. This never came to fruition so the only identifier we normally see is this one.

Object

This is the ctrlEntId of the control generating the event. 

Class

This identifies the type of control generating the event - for example PUSHBUTTON.

Event

This is a string containing enough information for OI to ascertain how to deal with the event - for example CLICK*2*SYSPROG*CLICK*DELETEME.BUTTON_1. For our purposes it allows us to check that the event being generated IS a duplicate and can simply be incremented rather than executed.

RUN_EVENT Function



errors = run_event( identifier, ctrlEntId, class, event, p1, p2, p3, p4, p5, p6, p7, |
                    p8p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20)


All parameters as for PEEK_EVENT although RUN_EVENT hedges its bets as to how many parameters it may be passed :).

Putting all of this together we can code something that looks into the event queue and tallies up the amount of time the user has repeated an action before actually processing it - the code below is a simple implementation of clicking on a button multiple times.

declare function get_Current_Event, peek_Event, timeGetTime, run_Event

currentEvent = get_Current_Event()

startingTime = timeGetTime()
currentTime =  startingTime
eventIterations = 0

loop
   pendingEvent = peek_Event( identifier, ctrlEntId, class, event, |
                              p1, p2, p3, p4, p5, p6, p7, p8, p9)
   if len( pendingEvent ) then
      if event[1, "*"] = currentEvent and event[-1, "B*"] = ctrlEntId then
         eventIterations += 1
      end else
         call run_Event( identifier, ctrlEntId, class, event, |
                         p1, p2, p3, p4, p5, p6, p7, p8, p9)
      end
      startingTime = timeGetTime()
   end else
      currentTime = timeGetTime()
   end
   if currentTime < startingTime then
      startingTime = currentTime
   end
until  (currentTime - startingTime) > 200
repeat

call msg(@Window, "The click button was pressed " : |
                  eventIterations : " time" : str("s", (eventIterations > 1)) )

So we firstly get the current event so we can check that the events queued are actually the same ones, and if not run the event. We then check for a pending event. If there is one we check if it is "us". If it is we increment a counter, if it is not we run the event.  When we get an event we reset our starting time to allow the user to continue repeating their action. If there isn't an event we reset our current time to indicate that time has elapsed  without the user doing anything.

The timeGetTime is a useful routine but every forty days or so it resets to zero - if that happened we'd have to wait a long time to exit our loop - unless we reset our starting time which is why we perform that final check. Then finally we pedantically display a message that something was done X time or times.

If anyone finds practical uses for this we'd love to hear about it!
Pixel
Pixel Footer R1 C1 Pixel
Pixel
Pixel
Pixel