#include <tcl.h>
void Tcl_CreateEventSource(setupProc,
checkProc, clientData)
void Tcl_DeleteEventSource(setupProc,
checkProc, clientData)
void Tcl_SetMaxBlockTime(timePtr)
void Tcl_QueueEvent(evPtr,
position)
void Tcl_ThreadQueueEvent(threadId, evPtr,
position)
void Tcl_ThreadAlert(threadId,
clientData)
Tcl_ThreadId Tcl_GetCurrentThread()
void Tcl_DeleteEvents(deleteProc,
clientData)
ClientData Tcl_InitNotifier()
void Tcl_FinalizeNotifier(clientData)
int Tcl_WaitForEvent(timePtr)
void Tcl_AlertNotifier(clientData)
void Tcl_SetTimer(timePtr)
int Tcl_ServiceAll()
int Tcl_ServiceEvent(flags)
int Tcl_GetServiceMode()
int Tcl_SetServiceMode(mode)
- Tcl_EventSetupProc *setupProc (in)
-
Procedure to invoke to prepare for event wait in
Tcl_DoOneEvent().
- Tcl_EventCheckProc *checkProc (in)
-
Procedure for Tcl_DoOneEvent() to invoke after waiting for
events. Checks to see if any events have occurred and, if so,
queues them.
- ClientData clientData (in)
-
Arbitrary one-word value to pass to setupProc,
checkProc, or
deleteProc.
- Tcl_Time *timePtr (in)
-
Indicates the maximum amount of time to wait for an event. This
is specified as an interval (how long to wait), not an absolute
time (when to wakeup). If the pointer passed to
Tcl_WaitForEvent()
is NULL, it means there is no maximum wait time: wait forever if
necessary.
- Tcl_Event *evPtr (in)
-
An event to add to the event queue. The storage for the event must
have been allocated by the caller using Tcl_Alloc() or
ckalloc.
- Tcl_QueuePosition position (in)
-
Where to add the new event in the queue: TCL_QUEUE_TAIL,
TCL_QUEUE_HEAD, or TCL_QUEUE_MARK.
- Tcl_ThreadId threadId (in)
-
A unique identifier for a thread.
- Tcl_EventDeleteProc *deleteProc
(in)
-
Procedure to invoke for each queued event in
Tcl_DeleteEvents().
- int flags (in)
-
What types of events to service. These flags are the same as those
passed to Tcl_DoOneEvent().
- int mode (in)
-
Inidicates whether events should be serviced by
Tcl_ServiceAll().
Must be one of TCL_SERVICE_NONE or TCL_SERVICE_ALL.
The interfaces described here are used to customize the Tcl event
loop. The two most common customizations are to add new sources of
events and to merge Tcl's event loop with some other event loop, such
as one provided by an application in which Tcl is embedded. Each of
these tasks is described in a separate section below.
The procedures in this reference page are the building blocks out of which
the Tcl event notifier is constructed. The event notifier is the lowest
layer in the Tcl event mechanism. It consists of three things:
-
Event sources: these represent the ways in which events can be
generated. For example, there is a timer event source that implements
the Tcl_CreateTimerHandler() procedure and the
after
command, and there is a file event source that implements the
Tcl_CreateFileHandler() procedure on UNIX systems. An
event
source must work with the notifier to detect events at the right
times, record them on the event queue, and eventually notify
higher-level software that they have occurred. The procedures
Tcl_CreateEventSource(),
Tcl_DeleteEventSource(),
and Tcl_SetMaxBlockTime(),
Tcl_QueueEvent(), and
Tcl_DeleteEvents() are used primarily by event sources.
-
The event queue: for non-threaded applications,
there is a single queue for the whole application,
containing events that have been detected but not yet serviced. Event
sources place events onto the queue so that they may be processed in
order at appropriate times during the event loop. The event queue
guarantees a fair discipline of event handling, so that no event
source can starve the others. It also allows events to be saved for
servicing at a future time.
Threaded applications work in a
similar manner, except that there is a separate event queue for
each thread containing a Tcl interpreter.
Tcl_QueueEvent() is used (primarily
by event sources) to add events to the event queue and
Tcl_DeleteEvents() is used to remove events from the queue
without
processing them. In a threaded application,
Tcl_QueueEvent() adds
an event to the current thread's queue, and
Tcl_ThreadQueueEvent()
adds an event to a queue in a specific thread.
-
The event loop: in order to detect and process events, the application
enters a loop that waits for events to occur, places them on the event
queue, and then processes them. Most applications will do this by
calling the procedure Tcl_DoOneEvent(), which is described
in a separate reference page.
Most Tcl applications need not worry about any of the internals of
the Tcl notifier. However, the notifier now has enough flexibility
to be retargeted either for a new platform or to use an external event
loop (such as a Motif event loop, when Tcl is embedded in a Motif
application). The procedures Tcl_WaitForEvent() and
Tcl_SetTimer() are normally implemented by Tcl, but may be
replaced with new versions to retarget the notifier (the
Tcl_InitNotifier(), Tcl_AlertNotifier(),
Tcl_FinalizeNotifier(), Tcl_Sleep(),
Tcl_CreateFileHandler(), and
Tcl_DeleteFileHandler() must
also be replaced; see CREATING A NEW NOTIFIER below for details).
The procedures Tcl_ServiceAll(),
Tcl_ServiceEvent(),
Tcl_GetServiceMode(), and
Tcl_SetServiceMode() are provided
to help connect Tcl's event loop to an external event loop such as
Motif's.
The easiest way to understand how the notifier works is to consider
what happens when Tcl_DoOneEvent() is called.
Tcl_DoOneEvent() is passed a flags argument
that indicates
what sort of events it is OK to process and also whether or not to
block if no events are ready. Tcl_DoOneEvent() does the
following
things:
-
Check the event queue to see if it contains any events that can
be serviced. If so, service the first possible event, remove it
from the queue, and return. It does this by calling
Tcl_ServiceEvent() and passing in the flags
argument.
-
Prepare to block for an event. To do this,
Tcl_DoOneEvent()
invokes a setup procedure in each event source.
The event source will perform event-source specific initialization and
possibly call Tcl_SetMaxBlockTime() to limit how long
Tcl_WaitForEvent() will block if no new events occur.
-
Call Tcl_WaitForEvent(). This procedure is implemented
differently on different platforms; it waits for an event to occur, based
on the information provided by the event sources.
It may cause the application to block if timePtr specifies
an interval other than 0.
Tcl_WaitForEvent() returns when something has happened,
such as a file becoming readable or the interval given by
timePtr
expiring. If there are no events for Tcl_WaitForEvent() to
wait for, so that it would block forever, then it returns immediately
and Tcl_DoOneEvent() returns 0.
-
Call a check procedure in each event source. The check
procedure determines whether any events of interest to this source
occurred. If so, the events are added to the event queue.
-
Check the event queue to see if it contains any events that can
be serviced. If so, service the first possible event, remove it
from the queue, and return.
-
See if there are idle callbacks pending. If so, invoke all of them and
return.
-
Either return 0 to indicate that no events were ready, or go back to
step [2] if blocking was requested by the caller.
An event source consists of three procedures invoked by the notifier,
plus additional C procedures that are invoked by higher-level code
to arrange for event-driven callbacks. The three procedures called
by the notifier consist of the setup and check procedures described
above, plus an additional procedure that is invoked when an event
is removed from the event queue for servicing.
The procedure Tcl_CreateEventSource() creates a new event
source.
Its arguments specify the setup procedure and check procedure for
the event source.
SetupProc should match the following prototype:
typedef void Tcl_EventSetupProc(
ClientData clientData,
int flags);
The clientData argument will be the same as the
clientData
argument to Tcl_CreateEventSource(); it is typically used
to
point to private information managed by the event source.
The flags argument will be the same as the flags
argument passed to Tcl_DoOneEvent() except that it will
never
be 0 (Tcl_DoOneEvent() replaces 0 with
TCL_ALL_EVENTS).
Flags indicates what kinds of events should be considered;
if the bit corresponding to this event source isn't set, the event
source should return immediately without doing anything. For
example, the file event source checks for the TCL_FILE_EVENTS
bit.
SetupProc's job is to make sure that the application wakes up
when events of the desired type occur. This is typically done in a
platform-dependent fashion. For example, under UNIX an event source
might call Tcl_CreateFileHandler(); under Windows it might
request notification with a Windows event. For timer-driven event
sources such as timer events or any polled event, the event source
can call Tcl_SetMaxBlockTime() to force the application to
wake
up after a specified time even if no events have occurred.
If no event source calls Tcl_SetMaxBlockTime()
then Tcl_WaitForEvent() will wait as long as necessary for
an
event to occur; otherwise, it will only wait as long as the shortest
interval passed to Tcl_SetMaxBlockTime() by one of the
event
sources. If an event source knows that it already has events ready to
report, it can request a zero maximum block time. For example, the
setup procedure for the X event source looks to see if there are
events already queued. If there are, it calls
Tcl_SetMaxBlockTime() with a 0 block time so that
Tcl_WaitForEvent() does not block if there is no new data
on the X
connection.
The timePtr argument to Tcl_WaitForEvent()
points to
a structure that describes a time interval in seconds and
microseconds:
typedef struct Tcl_Time {
long sec;
long usec;
} Tcl_Time;
The usec field should be less than 1000000.
Information provided to Tcl_SetMaxBlockTime()
is only used for the next call to Tcl_WaitForEvent(); it is
discarded after Tcl_WaitForEvent() returns.
The next time an event wait is done each of the event sources'
setup procedures will be called again, and they can specify new
information for that event wait.
If the application uses an external event loop rather than
Tcl_DoOneEvent(), the event sources may need to call
Tcl_SetMaxBlockTime() at other times. For example, if a
new event
handler is registered that needs to poll for events, the event source
may call Tcl_SetMaxBlockTime() to set the block time to
zero to
force the external event loop to call Tcl. In this case,
Tcl_SetMaxBlockTime() invokes
Tcl_SetTimer() with the shortest
interval seen since the last call to Tcl_DoOneEvent() or
Tcl_ServiceAll().
In addition to the generic procedure Tcl_SetMaxBlockTime(),
other
platform-specific procedures may also be available for
setupProc, if there is additional information needed by
Tcl_WaitForEvent() on that platform. For example, on UNIX
systems
the Tcl_CreateFileHandler() interface can be used to wait
for file events.
The second procedure provided by each event source is its check
procedure, indicated by the checkProc argument to
Tcl_CreateEventSource(). CheckProc must
match the
following prototype:
typedef void Tcl_EventCheckProc(
ClientData clientData,
int flags);
The arguments to this procedure are the same as those for
setupProc.
CheckProc is invoked by Tcl_DoOneEvent() after
it has waited
for events. Presumably at least one event source is now prepared to
queue an event. Tcl_DoOneEvent() calls each of the event
sources
in turn, so they all have a chance to queue any events that are ready.
The check procedure does two things. First, it must see if any events
have triggered. Different event sources do this in different ways.
If an event source's check procedure detects an interesting event, it
must add the event to Tcl's event queue. To do this, the event source
calls Tcl_QueueEvent(). The evPtr argument
is a pointer to
a dynamically allocated structure containing the event (see below for
more information on memory management issues). Each event source can
define its own event structure with whatever information is relevant
to that event source. However, the first element of the structure
must be a structure of type Tcl_Event, and the address of this
structure is used when communicating between the event source and the
rest of the notifier. A Tcl_Event has the following definition:
typedef struct {
Tcl_EventProc *proc;
struct Tcl_Event *nextPtr;
} Tcl_Event;
The event source must fill in the proc field of
the event before calling Tcl_QueueEvent().
The nextPtr is used to link together the events in the queue
and should not be modified by the event source.
An event may be added to the queue at any of three positions, depending
on the position argument to
Tcl_QueueEvent():
- TCL_QUEUE_TAIL
-
Add the event at the back of the queue, so that all other pending
events will be serviced first. This is almost always the right
place for new events.
- TCL_QUEUE_HEAD
-
Add the event at the front of the queue, so that it will be serviced
before all other queued events.
- TCL_QUEUE_MARK
-
Add the event at the front of the queue, unless there are other
events at the front whose position is TCL_QUEUE_MARK; if so,
add the new event just after all other TCL_QUEUE_MARK events.
This value of position is used to insert an ordered sequence of
events at the front of the queue, such as a series of
Enter and Leave events synthesized during a grab or ungrab operation
in Tk.
When it is time to handle an event from the queue (steps 1 and 4
above) Tcl_ServiceEvent() will invoke the
proc specified
in the first queued Tcl_Event structure.
Proc must match the following prototype:
typedef int Tcl_EventProc(
Tcl_Event *evPtr,
int flags);
The first argument to proc is a pointer to the event, which
will
be the same as the first argument to the Tcl_QueueEvent()
call that
added the event to the queue.
The second argument to proc is the flags
argument for the
current call to Tcl_ServiceEvent(); this is used by the
event source
to return immediately if its events are not relevant.
It is up to proc to handle the event, typically by invoking
one or more Tcl commands or C-level callbacks.
Once the event source has finished handling the event it returns 1
to indicate that the event can be removed from the queue.
If for some reason the event source decides that the event cannot
be handled at this time, it may return 0 to indicate that the event
should be deferred for processing later; in this case
Tcl_ServiceEvent()
will go on to the next event in the queue and attempt to service it.
There are several reasons why an event source might defer an event.
One possibility is that events of this type are excluded by the
flags argument.
For example, the file event source will always return 0 if the
TCL_FILE_EVENTS bit isn't set in flags.
Another example of deferring events happens in Tk if
Tk_RestrictEvents has been invoked to defer certain kinds
of window events.
When proc returns 1, Tcl_ServiceEvent() will
remove the
event from the event queue and free its storage.
Note that the storage for an event must be allocated by
the event source (using Tcl_Alloc() or the Tcl macro
ckalloc)
before calling Tcl_QueueEvent(), but it
will be freed by Tcl_ServiceEvent(), not by the event
source.
Threaded applications work in a
similar manner, except that there is a separate event queue for
each thread containing a Tcl interpreter.
Calling Tcl_QueueEvent() in a multithreaded application
adds
an event to the current thread's queue.
To add an event to another thread's queue, use
Tcl_ThreadQueueEvent().
Tcl_ThreadQueueEvent() accepts as an argument a
Tcl_ThreadId argument,
which uniquely identifies a thread in a Tcl application. To obtain the
Tcl_ThreadID for the current thread, use the
Tcl_GetCurrentThread()
procedure. (A thread would then need to pass this identifier to other
threads for those threads to be able to add events to its queue.)
After adding an event to another thread's queue, you then typically
need to call Tcl_ThreadAlert() to "wake up" that thread's
notifier to
alert it to the new event.
Tcl_DeleteEvents() can be used to explicitly remove one or
more
events from the event queue. Tcl_DeleteEvents() calls
proc
for each event in the queue, deleting those for with the procedure
returns 1. Events for which the procedure returns 0 are left in the
queue. Proc should match the following prototype:
typedef int Tcl_EventDeleteProc(
Tcl_Event *evPtr,
ClientData clientData);
The clientData argument will be the same as the
clientData
argument to Tcl_DeleteEvents(); it is typically used to
point to
private information managed by the event source. The evPtr
will
point to the next event in the queue.
Tcl_DeleteEventSource() deletes an event source. The
setupProc,
checkProc, and clientData arguments must exactly
match those
provided to the Tcl_CreateEventSource() for the event
source to be deleted.
If no such source exists, Tcl_DeleteEventSource() has no
effect.
The notifier consists of all the procedures described in this reference
page, plus Tcl_DoOneEvent() and
Tcl_Sleep(), which are
available on all platforms, and Tcl_CreateFileHandler() and
Tcl_DeleteFileHandler(), which are UNIX-specific. Most of
these
procedures are generic, in that they are the same for all notifiers.
However, eight of the procedures are notifier-dependent:
Tcl_InitNotifier(), Tcl_AlertNotifier(),
Tcl_FinalizeNotifier(),
Tcl_SetTimer(), Tcl_Sleep(),
Tcl_WaitForEvent(),
Tcl_CreateFileHandler() and
Tcl_DeleteFileHandler(). To
support a new platform or to integrate Tcl with an
application-specific event loop, you must write new versions of these
procedures.
Tcl_InitNotifier() initializes the notifier state and
returns
a handle to the notifier state. Tcl calls this
procedure when initializing a Tcl interpreter. Similarly,
Tcl_FinalizeNotifier() shuts down the notifier, and is
called by Tcl_Finalize() when shutting down a Tcl
interpreter.
Tcl_WaitForEvent() is the lowest-level procedure in the
notifier;
it is responsible for waiting for an interesting event to occur
or
for a given time to elapse. Before Tcl_WaitForEvent() is
invoked,
each of the event sources' setup procedure will have been invoked.
The timePtr argument to
Tcl_WaitForEvent() gives the maximum time to block for an
event,
based on calls to Tcl_SetMaxBlockTime() made by setup
procedures
and on other information (such as the TCL_DONT_WAIT bit in
flags).
Ideally, Tcl_WaitForEvent() should only wait for an event
to occur; it should not actually process the event in any way.
Later on, the
event sources will process the raw events and create Tcl_Events on
the event queue in their checkProc procedures.
However, on some platforms (such as Windows) this isn't possible;
events may be processed in Tcl_WaitForEvent(), including
queuing
Tcl_Events and more (for example, callbacks for native widgets may be
invoked). The return value from Tcl_WaitForEvent() must be
either
0, 1, or -1. On platforms such as Windows where events get processed in
Tcl_WaitForEvent(), a return value of 1 means that there
may be more
events still pending that haven't been processed. This is a sign to the
caller that it must call Tcl_WaitForEvent() again if it
wants all
pending events to be processed. A 0 return value means that calling
Tcl_WaitForEvent() again will not have any effect: either
this is a
platform where Tcl_WaitForEvent() only waits without doing
any event
processing, or Tcl_WaitForEvent() knows for sure that there
are no
additional events to process (for example, it returned because the time
elapsed). Finally, a return value of -1 means that the event loop is
no longer operational and the application should probably unwind and
terminate. Under Windows this happens when a WM_QUIT message is received;
under UNIX it happens when Tcl_WaitForEvent() would have
waited
forever because there were no active event sources and the timeout was
infinite.
Tcl_AlertNotifier() is used in multithreaded applications
to allow
any thread to "wake up" the notifier to alert it to new events on its
queue. Tcl_AlertNotifier() requires as an argument the
notifier
handle returned by Tcl_InitNotifier().
If the notifier will be used with an external event loop, then it must
also support the Tcl_SetTimer() interface.
Tcl_SetTimer() is
invoked by Tcl_SetMaxBlockTime() whenever the maximum
blocking
time has been reduced. Tcl_SetTimer() should arrange for
the
external event loop to invoke Tcl_ServiceAll() after the
specified
interval even if no events have occurred. This interface is needed
because Tcl_WaitForEvent() isn't invoked when there is an
external
event loop. If the
notifier will only be used from Tcl_DoOneEvent(), then
Tcl_SetTimer() need not do anything.
On UNIX systems, the file event source also needs support from the
notifier. The file event source consists of the
Tcl_CreateFileHandler() and
Tcl_DeleteFileHandler()
procedures, which are described in the
Tcl_CreateFileHandler()
reference page.
The Tcl_Sleep() and Tcl_DoOneEvent()
interfaces are described
in their respective reference pages.
The easiest way to create a new notifier is to look at the code
for an existing notifier, such as the files unix/tclUNIXNotfy.c
or win/tclWinNotify.c in the Tcl source distribution.
The notifier interfaces are designed so that Tcl can be embedded into
applications that have their own private event loops. In this case,
the application does not call Tcl_DoOneEvent() except in
the case
of recursive event loops such as calls to the Tcl commands
update
or vwait. Most of the time is spent in the external event
loop
of the application. In this case the notifier must arrange for the
external event loop to call back into Tcl when something
happens on the various Tcl event sources. These callbacks should
arrange for appropriate Tcl events to be placed on the Tcl event queue.
Because the external event loop is not calling
Tcl_DoOneEvent() on
a regular basis, it is up to the notifier to arrange for
Tcl_ServiceEvent() to be called whenever events are pending
on the
Tcl event queue. The easiest way to do this is to invoke
Tcl_ServiceAll() at the end of each callback from the
external
event loop. This will ensure that all of the event sources are
polled, any queued events are serviced, and any pending idle handlers
are processed before returning control to the application. In
addition, event sources that need to poll for events can call
Tcl_SetMaxBlockTime() to force the external event loop to
call
Tcl even if no events are available on the system event queue.
As a side effect of processing events detected in the main external
event loop, Tcl may invoke Tcl_DoOneEvent() to start a
recursive event
loop in commands like vwait.
Tcl_DoOneEvent() will invoke
the external event loop, which will result in callbacks as described
in the preceding paragraph, which will result in calls to
Tcl_ServiceAll(). However, in these cases it is
undesirable to
service events in Tcl_ServiceAll(). Servicing events there
is
unnecessary because control will immediately return to the
external event loop and hence to Tcl_DoOneEvent(), which
can
service the events itself. Furthermore, Tcl_DoOneEvent()
is
supposed to service only a single event, whereas
Tcl_ServiceAll()
normally services all pending events. To handle this situation,
Tcl_DoOneEvent() sets a flag for
Tcl_ServiceAll()
that causes it to return without servicing any events.
This flag is called the service mode;
Tcl_DoOneEvent() restores it to its previous value before
it returns.
In some cases, however, it may be necessary for
Tcl_ServiceAll()
to service events
even when it has been invoked from Tcl_DoOneEvent(). This
happens
when there is yet another recursive event loop invoked via an
event handler called by Tcl_DoOneEvent() (such as one that
is
part of a native widget). In this case, Tcl_DoOneEvent()
may not
have a chance to service events so Tcl_ServiceAll() must
service
them all. Any recursive event loop that calls an external event
loop rather than Tcl_DoOneEvent() must reset the service
mode so
that all events get processed in Tcl_ServiceAll(). This is
done
by invoking the Tcl_SetServiceMode() procedure. If
Tcl_SetServiceMode() is passed
TCL_SERVICE_NONE, then calls
to Tcl_ServiceAll() will return immediately without
processing any
events. If Tcl_SetServiceMode() is passed
TCL_SERVICE_ALL,
then calls to Tcl_ServiceAll() will behave normally.
Tcl_SetServiceMode() returns the previous value of the
service
mode, which should be restored when the recursive loop exits.
Tcl_GetServiceMode() returns the current value of the
service
mode.
Windows 8.1. Windows Server 2012 R2. Windows 10. Windows Server 2016. Windows Server 2019. Windows 11. Windows Server 2022.
PTC MKS Toolkit for Professional Developers
PTC MKS Toolkit for Enterprise Developers
PTC MKS Toolkit for Enterprise Developers 64-Bit Edition
- Functions:
- Tcl_CreateFileHandler(), Tcl_DeleteFileHandler(), Tcl_DoOneEvent(), Tcl_Sleep(), Thread()
PTC MKS Toolkit 10.4 Documentation Build 39.