How do I arrange for a Text widget to display the system time?
|
8-Aug-01 16:00 GMT
|
Question:
We have to display system time on a TextField widget.
How can i solve this problem?
This can be done using an Xt TimerCallbackProc, registered with the toolkit by XtAppAddTimeOut().
Firstly, some notes on timers, so-called synthetic events, and event handling in general.
Associated with the XtAppContext structure are a set of queues for handling events of various types.
There is a queue for handling Work procedures (registered by XtAppAddWorkProc()), a queue for handling
Input procedures (registered through XtAppAddInput()), a queue for handling TimeOut procedures (registered
through XtAppAddTimeOut()), a queue for handling Signals (registered by XtAppAddSignal()), and so forth.
If you are unfamiliar with these routines, a Work procedure is an application-defined function which you can
arrange to have called whenever there are no user-events on the X queue. It is used for idle-time background
processing. An Input procedure is an application routine which processes input on a pipe or other file descriptor
in such a way that the handling of data reception does not interfere with other events going on in the X system.
Similarly, a Signal procedure is a way of handling the processing of asynchronous signals so that the X queue
mechanisms are not interrupted at critical moments. Lastly, a TimeOut procedure, the one we will consider
in detail below, is the means whereby an application can set up handlers designed to go off at specific
intervals in the future, such that the handling of the event again does not interfere with critical X queue
processing.
The XtAppContext event handling mechanisms check the state of the synthetic event queues, as well as the normal
X event queue, whenever a request is made to process the current state of the event queues. In other words, when
the application issues the request to process the next event, the toolkit looks not just to see if there are
any user X events, but also to see if any timer has gone off, if any signal has arrived, or if there is data
pending on an Input queue. This is where XtAppNextEvent()/XtAppProcessEvent() differ from the lower
level Xlib routines (XNextEvent(), and so forth). If you hand write your X event handling loop such that you
avoid the XtApp* routines, then any timer/work/ signal or input routines you have registered with the toolkit will
simply fail to be processed.
If you want to use XtAppAddTimeOut() or any of the other related synthetic event mechanisms, you are therefore
forced to use the XtAppContext-parametered Xt event handling routines (or the Xt routines which use
a default XtAppContext internally if you do not create your own specific XtAppContext structure). This means
that your event mechanisms are constrained to use XtAppMainLoop(), or XtAppNextEvent(),
XtAppProcessEvent() at certain specific points in the application event handling. This does
not mean to say that you cannot use XNextEvent() and so forth at specific points in your application if there
are certain effects you require, just that if you want your timers handled, they are only handled correctly
if you call the right event handling routines at the right time, and not by the lower level Xlib routines.
A TimeOut procedure is, as already specified, an application-defined routine which is called after
a specific time interval. TimeOut procedures are registered with the toolkit using the routine
XtAppAddTimeOut(), formally specified as follows:
XtIntervalId XtAppAddTimeOut(XtAppContext app_context,
unsigned long timeout_period,
XtTimerCallbackProc timeout_handler,
XtPointer call_data)
The app_context parameter is an XtAppContext, an opaque handle onto
a structure created by XtCreateApplicationContext(), or returned from XtAppInitialize()
and related routines. Basically, it is a structure originally designed to coordinate the memory of multiple X
applications in platform environments which do not fully support independent multiple address spaces;
the structure had its points, so became a general repository of X application control
information.
The timeout_period parameter specifies a time interval, measured in milliseconds,
after which the application-defined routine timeout_handler is to be invoked.
The timeout_handler parameter is the application-defined routine: it is what you want called
when the timeout period expires. What it does is entirely application specific; we will present a
simple handler below which updates a text widget with the current system time.
The XtIntervalId returned value is an opaque type: it is a handle onto
the structure which is registered with the toolkit. Its only use as far as the application is
concerned is so that the request to call an application routine at some point in the future can be
cancelled. To cancel the timeout, simply call XtRemoveTimeOut(), passing as parameter the
XtIntervalId returned from XtAppAddTimeOut():
XtIntervalId id = XtAppAddTimeOut(app_context, ....) ;
...
/* Changed our minds: timer no longer needed */
XtRemoveTimeOut(id) ;
A TimeOut procedure, or more formally an XtTimerCallbackProc, has the following signature:
void (*XtTimerCallbackProc)(XtPointer client_data, XtIntervalId *id)
The first parameter is the client_data, which is anything application-specific you want passed to the routine.
This is the same as whatever you pass as the fourth parameter to the XtAppAddTimeOut() call. For example:
extern XtAppContext app_context ;
extern Widget text ;
void my_timer(XtPointer client_data, XtIntervalId *id)
{
...
}
...
XtIntervalId *id = XtAppAddTimeOut(app_context,
(unsigned long) 1000, /* milliseconds */
my_timer,
(XtPointer) text) ;
The first thing to understand is that a call to XtAppAddTimeOut() does not arrange for
our application routine to be called every n milliseconds; it only arranges for the
application function to be called once after the period expires. If you want regular or
repeated polling, you have to arrange to call the routine again. This means a subsequent call
to XtAppAddTimeOut(), generally insides the TimeOut routine, so that it sets itself
up for the subsequent call. A function which was to be called every second would therefore
have the following architecture:
extern XtAppContext app_context ;
void my_looping_timer(XtPointer client_data, XtIntervalId *id)
{
/* Do the application-specific functionality */
...
/* Go round again in 1 second, same parameters */
XtAppAddTimeOut(app_context, (unsigned long) 1000, my_looping_timer, \
client_data) ;
}
Given a Motif Text component (XmText or XmTextField), we can update the component to display
the system time as follows:
-
We define an XtTimerCallbackProc which fetches the current clock tick, converts it
to a string, and inserts the result in the Text.
-
We arrange that this routine is called every second.
-
We call this routine as soon as the Text is created anyway, so that it is seeded
with the system time first up.
Firstly, the XtTimerCallbackProc. Assume that we pass the Text component concerned as client
data to the routine; time() and ctime() are standard C library routines:
#include <time.h>
extern XtAppContext app_context ; /* Or whatever you call yours */
extern Widget text ; /* Where you want to display the system time */
void show_system_time(XtPointer client_data, XtIntervalId *id)
{
Widget text = (Widget) client_data ; /* Where we write the system time */
long system_tick = time((long *) 0) ; /* Get the system clock tick */
char *cptr = ctime(&system_tick) ; /* Convert to string */
char buffer[32] ;
/* Convert the system time to whatever format you require */
/* Here, I just blat it out without conversion. Beware that */
/* ctime() returns a pointer to static storage. */
(void) sprintf(buffer, "%s", cptr) ;
/* Write the result to the Text widget */
/* This works for either XmText or XmTextField */
/* NB: XmTextFieldSetString() doesnt work for XmText */
XmTextSetString(text, buffer) ;
/* Now arrange to call ourselves again in 1 second */
XtAppAddTimeOut(app_context,
(unsigned long) 1000, /* milliseconds */
show_system_time,
client_data) ;
}
This sets up the looping and displaying part. We need to initialize the loop
with a first call when the text widget is created. There are two ways of doing this.
We can either set up a Timer which goes off straight away:
Widget text = XmCreateTextField(...) ;
XtAppAddTimeOut(app_context,
(unsigned long) 0,
show_system_time,
(XtPointer) text) ;
Or we can simply call the handler directly ourselves:
Widget text = XmCreateTextField(...) ;
show_system_time((XtPointer) text, (XtIntervalId *) 0) ;
Timers in Xt cannot be used as an atomic clock. You can expect some interference with
normal X queue event processing, with whatever network delays this may involve, such that
you cannot guarantee an exact concurrence between the dispatch of your handler and the
requested timeout interval. This does not mean to say that TimeOut procedures are hopelessly
inaccurate - all things being equal, expect the error to be measured in a few milliseconds;
what you might need to do, however, is to check the system clock periodically and adjust the
next timeout period accordingly to avoid creeping slippage if you need something which is
regularly accurate. Also to be borne in mind is the fact that the XtAppNextEvent()
routine processes Input procedures before TimeOut handlers; if you have an Input procedure
registered, and its operation is expensive, then a pending TimeOut handler will slip by the
duration of the Input handler.
[We avoided this issue in the example, by simply calling time() inside the TimeOut handler:
the system tick we display is as current as possible, even though it may happen to be somewhat
over a second since the previous call of the handler, depending on circumstances.]
Attempting to remove a TimeOut procedure (XtRemoveTimeOut()), when the TimeOut handler
to which the XtIntervalId pertains has already been called, is unstable. You do not
have to unregister if your handler has already fired - it is removed from the queue automatically;
just make sure that you do not remove a timer that has already gone off or a core dump is the likely
result. If you are caching the XtIntervalId globally for cases where you do need to
remove a timer, make sure that the timer internally resets the XtIntervalId so that
XtRemoveTimeOut() has no nasty side effects:
XtIntervalId myId = XtAppAddTimeOut(..., my_timer, ...)
...
void my_timer(XtPointer client_data, XtIntervalId *id)
{
myId = (XtIntervalId) 0 ;
...
}
...
if (myId != (XtIntervalId) 0) {
XtRemoveTimeOut(myId) ;
}
For a good general-purpose introduction to Events, Event Processing,
the following is recommended: in particular, Chapter 12 covers the ground given
in the text above:
Advanced Motif Programming Techniques,
Alistair George and Mark Riches,
Prentice Hall,
ISBN: 0-13-219965-3
Unfortunately this book is out of print,
but you may be able to buy it from Amazon.com
For reference materials on the routines mentioned in the text, the following are
recommended:
X Toolkit Intrinsics Programming Manual,
Adrian Nye and Tim O'Reilly,
O'Reilly and Associates,
ISBN: 0-937175-34-X
Buy it from Amazon.com or Amazon.co.uk
X Toolkit Intrinsics Reference Manual,
Edited by David Flanaghan,
O'Reilly and Associates,
ISBN: 1-56592-007-4
Buy it from Amazon.com or Amazon.co.uk
Xlib Reference Manual,
Edited by Adrian Nye,
O'Reilly and Associates,
ISBN: 1-56592-006-6
Buy it from Amazon.com or Amazon.co.uk
Sponsored
by X-Designer - The Leading X/Motif GUI Builder
- Click to download a FREE evaluation
Goto top of page
|