![]() |
Documentation: Timers, Signals and Input Messages
![]() |
Other Messages
![]() |
There are four types of such messages: Timers, Chores, Signals, and Inputs.
Timer Messages
![]() |
Like all messages, timer messages are handled by specifying a target object which is to handle the message. When the specified time has elapsed, the object will receive a message of the type SEL_TIMEOUT, with the message ID being the one which was registered at the beginning of the time interval.
The length of the time interval is expressed in milliseconds, and the interval starts at the time the callback message was registered. The message callback to the target object will be when the interval has expired.
Here's how you would program a message map
entry to catch a timer message in a target object of type MyObject:
// Message map entry of "object"
FXDEFMAP(MyObject) MyObjectMap[]={ FXMAPFUNC(SEL_TIMEOUT,MyObject::ID_ANIMATIONSTEP,MyObject::onAnimationStep), ... }; |
To register a timer message, you would call
the function FXApp::addTimeout(), as follows:
// Register Timer callback message
FXTimer* timerhandle; MyObject* object; timerhandle=app->addTimeout(1000,object,ID_ANIMATIONSTEP); |
Timers can be unregistered at any time prior to being
fired, by calling FXApp::removeTimeout() with the handle which was
previously returned from FXApp::addTimeout():
// Unregister Timer callback message
app->removeTimeout(timerhandle); |
Note that it is an error
to unregister the timer callback message after it has already been fired,
as timers are automatically unregistered already when they're fired. A
common practice is therefore for the target of a timer to reset the handle
to NULL when the timer callback has been received, as demonstrated by the
following code segment:
// Receive a Timer callback
long MyObject::onAnimationStep(FXObject*,FXSelector,void*){ if(continueToAnimate){ timerhandle=app->addTimeout(1000,object,ID_ANIMATIONSTEP); // Restart timer for another interval } else{ timerhandle=NULL; // We're done, zero-out the handle to we won't remove the timer twice } return 1; } |
It is OK to call FXApp::removeTimeout() with a NULL handle, so
in the destructor of MyObject, you could simply:
// Clean up to prevent sending a timer message
to a deleted target
MyObject::~MyObject(){ app->removeTimeout(timerhandle); } |
Which makes for much cleaner code.
Timers are fired when the application returns to the event loop. In other words, handlers may be invoked a bit later than specified by the timer interval. To maintain smooth animation sequences, you might want to call gettimeofday() or some equivalent function so as to decrease the next timer interval in the sequence a little bit to correct for this effect, particularly if some of your applications are CPU intensive.
Chore Messages
![]() |
When the chore message is fired, your object will
receive a message of the type SEL_CHORE, with the message ID
being the one which was registered. To intercept this message, here's
how you would program your message map:
// Message map entry of "object"
FXDEFMAP(MyObject) MyObjectMap[]={ FXMAPFUNC(SEL_CHORE,MyObject::ID_IDLETASK,MyObject::onIdleTask), ... }; |
As you see, it is very similar to timer callback
processing. Setting or registering a chore callback message is similar
as well, and is done by calling FXApp::addChore() as shown below:
// Register Chore callback message
FXChore* chorehandle; MyObject* object; chorehandle=app->addChore(object,ID_ANIMATIONSTEP); |
Chores can be unregistered at any time prior to being
fired, by calling FXApp::removeChore() with the handle which was
previously returned from FXApp::addChore():
// Unregister Chore callback message
app->removeChore(chorehandle); |
Since chores, like timers, are automatically unregistered when they are fired, it is an error to remove a chore after it has been fired. Thus, your program will likely deal with chore handle's in a similar way as with timer handles, as described above.
Some notes:
Signal Messages
![]() |
You can use Signal messages to allow FOX objects to receive signals and process them. For example, you could register a signal handler for SIGINT, so that an application may be closed down properly when the user hits ^C on the controlling terminal. Another use might be to register a handler to catch the SIGFPE during a computation, so a warning panel can be popped for a divide by zero, and perhaps gracefully save the user's data rather than core dumping.
When a Signal message is sent, your target object
will receive a message of the type SEL_SIGNAL with the ID
being the one specified when the callback message was registered:
// Message map entry of "object"
FXDEFMAP(MyObject) MyObjectMap[]={ FXMAPFUNC(SEL_SIGNAL,MyObject::ID_INTERRUPT,MyObject::onCleanUpAndQuit), ... }; |
A signal handler can be added by calling FXApp::addSignal(). There are two methods to deliver a signal to the application: synchronously, and asynchronously (immediately).
Synchronous or non-immediate signals are held until the application returns to the event loop, and then dispatched to the application. Thus, in most cases, the normal flow of computation in the application will not be interrupted, and your signal callback message handler can assume that all data structures are in a consistent state. Relatively harmless signals such as SIGINT are best handled synchronously.
Asynchronous or immediate signals are dispatched to the target object immediately. Since the regular processing of your application may have been interrupted by the signal, you will have to exercise extreme caution in the handler, as data structures may be partically complete. The immediate signal handlers are best reserved for last-ditch efforts, such as cleaning up after a SIGSEGV or SIGBUS, when a grave error has occured but there may be a chance to perhaps recover some of the user's data.
You can set a signal as follows:
// Register Signal callback message
app->addSignal(SIGINT,myobject,ID_INTERRUPT,FALSE,flags); |
The flags are set as for POSIX signal handling facilities, pleace confer
your man pages for sigaction(2).
To remove the signal handler callback message and restore the default
signal handling action, you can call FXApp::removeSignal() as follows:
// Unregister Signal callback message
app->removeSignal(SIGINT); |
Input Messages
![]() |
Writing networked applications, such as e.g. a chat program, involves watching inputs from a number of different sources. You could have your program continuously check all these inputs for activity in a timer callback, but it is far more efficient to register an input source and yield the CPU until there is something going on.
Fortunately, most operating systems provide such
a facility, and FOX can take advantage of this:
To register a callback message for an input source, you can call FXApp::addInput(). The callback message will remain registered even even after it has fired, unlike for Timers and Chores which are automatically removed after being fired once.
When a synchronization object becomes signaled, a message of the type
SEL_IO_READ, SEL_IO_WRITE, or SEL_IO_EXCEPT will be sent to the target
object, with the ID being the one specified in addInput(). You can
intercept these messages as follows:
// Message map entry of "object"
FXDEFMAP(MyObject) MyObjectMap[]={ FXMAPFUNC(SEL_IO_READ,MyObject::ID_ACCEPT,MyObject::onAcceptConnectionFromTheNet), FXMAPFUNC(SEL_IO_READ,MyObject::ID_SOCKET,MyObject::onReceivedInputFromTheNet), FXMAPFUNC(SEL_IO_WRITE,MyObject::ID_SOCKET,MyObject::onSendOutputToTheNet), FXMAPFUNC(SEL_IO_EXCEPT,MyObject::ID_SOCKET,MyObject::onDealWithExcept), ... }; |
In this example, a server type application may be creating a socket (socket(2)), and listen for incoming connections. When an incoming connection is received the callback handler onAcceptConnectionFromTheNet() presumably verifies the request and calls accept (accept(2)) and registers another handler to deal with incoming or outgoing data, and exceptional conditions.
You can register a input handler by calling FXApp::addInput().
// Accept the connection
socket=accept(...); // Register input callback message
|
Passing INPUT_READ|INPUT_WRITE|INPUT_EXCEPT will register the
same callback message handler ID for all three types of I/O activities.
To remove a callback message handler, you can call FXApp::removeInput()
as follows:
// Unregister input callback message
app->removeInput(socket,INPUT_WRITE); |
This will remove the callback message ID for I/O output.
It is usually a good idea for output, because the file descriptor will
remain signaled as long as there is buffering to accept more outgoing data.
You would add the INPUT_WRITE back only when buffers get full
[when the other party is tardy processing the data you're sending, lets
say].