The calls to build the interface occupy most of the main function. As it should be, the user input and output is the most indispensable part of the program. Without the ability to receive and handle events, the program would be limited to simple serial simulation. The first Xt call is in wrapped in an SoXt object method called init(); The call just binds Inventor with Xt so that they may work together. It calls SoDB::init() to initialize the Inventor database and calls XtAppInitialize to request an application context from X.
From main() there is one central call to build the application interface after the application initialization has taken place -- buildMainWindow(Widget parent). This call encapsulates all of the subsequent calls that build the interface widgets.
BuildmainWindow(Widget parent) first creates a form widget to manage all of the rest of the widgets that make up the Single Document Interface (or SDI). It is known as a manager widget and is meant to parent other widgets.
The current implementation adheres to the common Motif programming style as follows:
XtCreateWidget (as the name suggests) is a call to X and returns an unmanaged widget of the type XmWidgetClass, of parent, with the unique handle "uniqueNameString", and the attributes as set forth in the array args of length n. The call to Xt is avoided when there exists a fitting higher level Motif call that is specialized for the desired widget. An unmanaged widget differs from a managed one in that its existance is not initialized in the special tables and structures that Xt creates for event handling. Motif cannot know how yet to display an unmanaged widget. When the call XtManageChild(childName) or XtManageChildren(childrenArray, cardinality) is made, the Xt event handling structures learn about the widget, display it and begin to automate its reaction to predefined events.
XtAddCallback(widgetName, XmNcallBackListName, callbackProcedure, clientData) tells the widget widgetName to add the callback procedure callbackProcedure to the callback list XmNcallBackListName and pass the callback procedure the data specified by clientData. In effect, this call tells the widget to respond to a given event generated within its boundaries by calling the procedure callbackProcedure and passing clientData to it. Motif, and underlying Xt, use the entries in the callback lists to mask for events as they are passed from child to parent up through the heirarchy until either a widget responds to the event, or all widgets in the heirarchy up to the root have declined to react to it. At that point, normal execution resumes.
2.4.2 Callback Functions
A callback procedure is defined to look like the following:
Xt normally checks for events by invoking a call to XtAppMainLoop. XtAppMainLoop is simply an infinite loop that calls XtAppNextEvent to grab the next event from the event queue and then calls XtDispatchEvent to search the callback lists to dispatch the event to the appropriate procedure. The call to XtAppmainLoop must be the last call in the main body of the application because it is infinite and any subsequent calls will not be reached during normal program execution. Inventor simply wraps this call in an SoXt object method appropriately called SoXt:: XtAppMainLoop.
Normally the loop initiated by XtAppMainLoop is sufficient for any event handling. An exception is encountered when a significant ammount of processing is encountered in a callback procedure. When a callback procedure is computationally intensive and demands a large ammount of time to run to completion, the application will not respond to any events. The application appears frozen to user events. This is expected behavior when one considers what is actually going on behind the scenes. The event queue is normmally checked during the infinite loop initiated by XtAppMainLoop. When the application responds to an event, the processor state and program counter are pushed upon the stack as with any procedure call and the corresponding callback routine is initiated. During the execution of the callback routine, the application never checks the event queue again to process pending events. It is only when the callback routine returns that the event queue is considered by the application. There are two ways to correct for the seeming inappropriate behavior. Both have been used where they are appropriate.
The first way to avoid the application "freeze" during expensive callbacks is by explicitly embedding calls to check the event queue during callback execution. If events are found, they are processsed immediately and then normal execution resumes. A simple loop can inspect the event queue with XtAppPending to determine if there are events pending; and use XtAppNextEvent and XtDispatchEvent to process the event. Strategic placement of these calls in an expensive callback routine will correct for the "freezing" effect caused by the absence of event checking. The routine invoked to begin the numerical simulation is an example of a proceedure that benefits from this method. To speed execution of the callback procedures, the call can be made every nth iteration instead of every time step.
The second way to avoid the application "freeze" is supplied by Xt, and appears to be more elegant. Xt has anticipated this need by incorporating work procedures into its library. Work procedures use the same underlying methods as described above but they are implemented slightly differently. A call to: