# gTimer

Definition: | include xpGUI.e gdx id = gTimer(rtn action, integer msecs=40, boolean active=true, object user_data=NULL)
Description: | Creates a timer which periodically invokes a handler when the time is up. action: a procedure to be invoked periodically, see ACTION below. msecs: an interval in miliseconds, you can change the TIME (etc) attribute later. active: true or false, you can change the RUN attribute later (and from anywhere). user_data: anything which might be needed in action, or elsewhere, see USER_DATA.
pwa/p2js: | Supported.
Example: |
    -- simplified version of demo\xpGUI\gTimer.exw
    include xpGUI.e
    constant title = "Timer"
    gdx timer, dlg

    procedure action(gdx /*id*/)
        gSetAttribute(dlg,"TITLE","%s:%.1fs",{title,time()})
    end procedure

    timer = gTimer(action,100,true)
    dlg = gDialog(NULL,title,`SIZE=260x80`)
    gShow(dlg)
    gMainLoop()
Note that timers are highly system dependent, and not generally noted for their pin-point accuracy. It is almost inevitable that some timer events will trigger a few milliseconds late, and worse, that effect is often cumulative, at least it is so on Windows. For instance were you to run the above with a 1000ms (1s) interval, you would likely see 14.0, 15.1, 16.1, ... 40.1, 41.2, etc. In some cases you may want to periodically restart the timer with a fractionally shorter interval, to counteract any such "drift", and quite probably restart it again on the very next iteration with the full-length interval. The full demo\xpGUI\gTimer.exw has a bCorrectDrift constant and the code needed for at least one way of doing just that, and demo\rosetta\virtunome.exw another way. A generally useful example of using a timer to allow any currently active drawing processes to abandon before starting a new one can be found in demo\rosetta\VoronoiDiagram.exw. While relatively simple, it can make a huge difference to responsiveness (and in that particular case, not crashing). DEV re-test: In one case (Conway’s game of life) setting a timer before the IupShow() prevented the latter from returning for quite some time, and perhaps not until Escape had been keyed, so from that experience I strongly recommend not creating a timer until after an initial gShow() has returned, and probably after setting any ?RASTER?SIZE to NULL, otherwise it may misbehave - in the program just mentioned two things went wrong: Escape had to be keyed twice to close the window, and the canvas did not resize properly, and both were fixed simply by moving the [Iup/]gTimer() creation down three lines. Using a timer should keep the CPU usage in the low single digits, as opposed to the 100% of a busy loop (which would not be compatible with JavaScript anyway), which is obviously a good thing and especially so should it prolong the battery life of a portable device.
Attributes: |
MSECS | The time interval in milliseconds. In Windows the minimum value is 10ms. The aliases "MSEC" and "TIME" can also be used for this.
RUN | Starts and stops the timer. Possible values: "YES"/true or "NO"/false. Returns the current timer state. If you have multiple threads start the timer in the main thread. The aliases "RUNNING" and "ACTIVE" can also be used for this.
Handlers: |
ACTION | procedure action(gdx id) id: identifier of the timer that activated the event. Called every time the defined time interval is reached. To stop the handler being called again simply set RUN to false.