Common Desktop Environment: Desktop KornShell User's Guide
3 Advanced Topics
Contents of Chapter:
- Using Context Variables
-
- Event Handler Context Variables
-
- Translation Context Variables
-
- Workspace Callback Context Variables
-
- Input Context Variables
-
- Accessing Event Subfields
-
- Responding to a Window Manager Close Notice
-
- Responding to a Session Manager Save State Notice
-
- Cooperating with Workspace Manager
-
- Creating Localized Shell Scripts
-
- Using dtksh to Access X Drawing Functions
-
- Setting Widget Translations
-
Now that you have the basic information about dtksh, this chapter introduces you to more advanced topics.
Using Context Variables
dtksh has a number of variables that provide context to certain aspects of an application.
An application registers event handlers with a widget to specify an action to occur when one of the specified events occurs. The action can be any arbitrary dtksh command line. For example:
XtAddEventHandler $W "Button2MotionMask" false "ActivateProc"
XtAddEventHandler $W "ButtonPressMask|ButtonReleaseMask" \
false "echo action"
Two environment variables are defined to provide context to the event handler:
- EH_WIDGET
- Set to the ID of the widget for which the event handler is registered.
- EH_EVENT
- Set to the address of the XEvent which triggered the event handler.
Access to the fields within the XEvent structure is shown in the following example:
if [ ${EH_EVENT.TYPE} = "ButtonPress" ]; then
echo "X = "${EH_EVENT.XBUTTON.X}
echo "Y = "${EH_EVENT.XBUTTON.Y}
elif [ ${EH_EVENT.TYPE} = "KeyPress" ]; then
echo "X = "${EH_EVENT.XKEY.X}
echo "Y = "${EH_EVENT.XKEY.Y}
fi
The Xt Intrinsics provides for event translations to be registered for a widget. Context for event translation is provided in the same way it is provided for event handlers. The two variables defined for translation commands are:
- TRANSLATION_WIDGET
- Set to the widget handle for the widget for which the translation is registered.
- TRANSLATION_EVENT
- Set to the address of the XEvent that triggered the translation.
Dot-notation provides access to the fields of the event:
echo "Event type = "${TRANSLATION_EVENT.TYPE}
echo "Display = "${TRANSLATION_EVENT.XANY.DISPLAY}
An application has the ability to register a callback function that is invoked whenever the user changes to a new workspace. When the callback is invoked, two special environment variables are set, and can be accessed by the shell callback code:
- CB_WIDGET
- Set to the ID for the widget that is invoking the callback.
- CB_CALL_DATA
- Set to the X atom that uniquely identifies the new workspace. This can be converted to its string representation, using the XmGetAtomName command.
The Xt Intrinsics provides the XtAddInput facility, which allows an application to register interest in any data available from a particular file descriptor. When programming in C, the application provides a handler function, which is invoked when input is available. It is up to the handler to read the data from the input source and to handle character escaping and line continuations.
dtksh also supports the XtAddInput facility, but takes it a step further and makes it easier for shell programmers to use. By default, when a shell script registers interest in a file descriptor, dtksh invokes the shell script's input handler only when a complete line of text has been received. A complete line of text is defined as a line terminated either by an unescaped newline character or by the end of the file. The input handler is also called if no data is available and the end of the file has been reached. The handler can then use XtRemoveInput to remove the input source and to close the file descriptor. The advantage of this default behavior is that input handlers need not be concerned with escape processing or with handling line continuations. The disadvantage is that it assumes that all of the input is line-oriented and contains no binary information.
dtksh also supports a "raw" input mode if the input source contains binary information or if the input handler wants to read the data from the input source directly. In raw mode, dtksh does not read any of the data from the input source. Whenever dtksh is notified that input is available on the input source, it invokes the shell script's input handler. It is then the handler's responsibility to read the incoming data, perform any required buffering and escape processing, and detect when the end of the file has been reached (so that the input source can be removed and the file descriptor closed). This mode seldom needs to be used by a dtksh script.
Whether the input handler has been configured to operate in the default mode or in raw mode, dtksh sets up several environment variables before calling the shell script's input handler. These environment variables provide the input handler with everything needed to handle the incoming data. The environment variables are:
- INPUT_LINE
- If operating in the default mode, this variable contains the next complete line of input available from the input source. If INPUT_EOF is true, then there is no data in this buffer. If operating in raw mode, then this variable always contains an empty string.
- INPUT_EOF
- If operating in the default mode, this variable is set to false anytime INPUT_LINE contains data, and it is set to true when the end of file is reached. When the end of file is reached, the shell script's input handler should unregister the input source and close the file descriptor. If operating in raw mode, this variable is always set to false.
- INPUT_SOURCE
- This indicates the file descriptor for which input is available. If operating in raw mode, this file descriptor is used to obtain the pending input. The file descriptor is also used to close the input source, when no longer needed.
- INPUT_ID
- This indicates the ID returned by XtAddInput, when the input source was originally registered. This information is needed to remove the input source with XtRemoveInput.
The XEvent structure has many different configurations, based on the event's type. dtksh provides access only to the most frequently used XEvents. Any of the other standard XEvents can be accessed using the event type XANY, followed by any of the subfields defined by the XANY event structure, which includes the following subfields:
- ${TRANSLATION_EVENT.XANY.TYPE}
- ${TRANSLATION_EVENT.XANY.SERIAL}
- ${TRANSLATION_EVENT.XANY.SEND_EVENT}
- ${TRANSLATION_EVENT.XANY.DISPLAY}
- ${TRANSLATION_EVENT.XANY.WINDOW}
dtksh supports full access to all of the event fields for the following event types:
- XANY
- XBUTTON
- XEXPOSE
- XNOEXPOSE
- XGRAPHICSEXPOSE
- XKEY
- XMOTION
The following examples show how the subfields for the preceding event types can be accessed:
${TRANSLATION_EVENT.XBUTTON.X}
$(CB_CALL_DATA.EVENT.XKEY.STATE}
${EH_EVENT.XGRAPHICSEXPOSE.WIDTH}
Responding to a Window Manager Close Notice
When the user selects Close from the Window Manager menu for an application, the application is terminated unless it has arranged to "catch" the Close notification. If the application does not catch the notification, then multiple windows managed by the application all disappear and application data may be left in an undesirable state. To avoid this, dtksh provides for catching and handling the Close notification. The application must:
- Define a procedure to handle the Close notification
- Request notification when Close is selected
- Override the response, so the application is not shut down
The following code illustrates this processing.
# This is the `callback' invoked when the user selects
# the `Close' menu item
WMCallback()
{
echo "User has selected the Close menu item"
}
# Create the toplevel application shell
XtInitialize TOPLEVEL test Dtksh $0 "$@"
XtDisplay DISPLAY $TOPLEVEL
# Request notification when the user selects the `Close'
# menu item
XmInternAtom DELETE_ATOM $DISPLAY "WM_DELETE_WINDOW" false
XmAddWMProtocolCallback $TOPLEVEL $DELETE_ATOM "WMCallback"
# Ask Motif to not automatically close down your
# application window
XtSetValues $TOPLEVEL deleteResponse:DO_NOTHING
Session Manager allows applications to save their current state when the user terminates the current session, so that when the user later restarts the session, an application can return to the state it was in. In dtksh, this is accomplished by setting up a handler in a similar way of handling a Close notification. If a handler is not set up, the application has to be restarted manually in the new session, and the application does not retain any state.
To set up a handler to save the current state, the application must:
- Define functions to save the state at the end of the session and to restore it on startup
- Register interest in the Session Manager notification
- Register the function to save the state
- At startup, determine whether the saved state should be restored
The following code illustrates this process.
#! /usr/dt/bin/dtksh
# Function invoked when the session is being ended by the user
SessionCallback()
{
# Get the name of the file into which we should save our
# session information
if DtSessionSavePath $TOPLEVEL PATH SAVEFILE; then
exec 9>$PATH
# Save off whether we are currently in an iconified state
if DtShellIsIconified $TOPLEVEL ; then
print -u9 `Iconified'
else
print -u9 `Deiconified'
fi
# Save off the list of workspaces we currently reside in
if DtWsmGetWorkspacesOccupied $(XtDisplay "-" $TOPLEVEL) \
$(XtWindow "-" $TOPLEVEL) \
CURRENT_WS_LIST ;
then
# Map the comma-separated list of atoms into
# their string representation
oldIFS=$IFS
IFS=","
for item in $CURRENT_WS_LIST;
do
XmGetAtomName NAME $(XtDisplay "-" $TOPLEVEL) \
$item
print -u9 $NAME
done
IFS=$oldIFS
fi
exec 9<&-
# Let the session manager know how to invoke us when
# the session is restored
DtSetStartupCommand $TOPLEVEL \
"/usr/dt/contrib/dtksh/SessionTest $SAVEFILE"
else
echo "DtSessionSavePath FAILED!!"
exit -3
fi
}
# Function invoked during a restore session; restores the
# application to its previous state
RestoreSession()
{
# Retrieve the path where our session file resides
if DtSessionRestorePath $TOPLEVEL PATH $1; then
exec 9<$PATH
read -u9 ICONIFY
# Extract and restore our iconified state
case $ICONIFY in
Iconified) DtSetIconifyHint $TOPLEVEL True;;
*) DtSetIconifyHint $TOPLEVEL False;
esac
# Extract the list of workspaces we belong in, convert
# them to atoms, and ask the Workspace Manager to relocate
# us to those workspaces
WS_LIST=""
while read -u9 NAME
do
XmInternAtom ATOM $(XtDisplay "-" $TOPLEVEL) \
$NAME False
if [ ${#WS_LIST} -gt 0 ]; then
WS_LIST=$WS_LIST,$ATOM
else
WS_LIST=$ATOM
fi
done
DtWsmSetWorkspacesOccupied $(XtDisplay "-" $TOPLEVEL) \
$(XtWindow "-" $TOPLEVEL) $WS_LIST
exec 9<&-
else
echo "DtSessionRestorePath FAILED!!"
exit -3
fi
}
################## Create the Main UI #######################
XtInitialize TOPLEVEL wmProtTest Dtksh $0 "$@"
XtCreateManagedWidget DA da XmDrawingArea $TOPLEVEL \
height:200 width:200
XmInternAtom SAVE_SESSION_ATOM $(XtDisplay "-" $TOPLEVEL) \
"WM_SAVE_YOURSELF" False
# If a command-line argument was supplied, then treat it as the
# name of the session file
if (( $# > 0))
then
# Restore to the state specified in the passed-in session file
XtSetValues $TOPLEVEL mappedWhenManaged:False
XtRealizeWidget $TOPLEVEL
XSync $(XtDisplay "-" $TOPLEVEL) False
RestoreSession $1
XtSetValues $TOPLEVEL mappedWhenManaged:True
XtPopup $TOPLEVEL GrabNone
else
# This is not a session restore, so come up in the default state
XtRealizeWidget $TOPLEVEL
XSync $(XtDisplay "-" $TOPLEVEL) False
fi
# Register the fact that we are interested in participating in
# session management
XmAddWMProtocols $TOPLEVEL $SAVE_SESSION_ATOM
XmAddWMProtocolCallback $TOPLEVEL $SAVE_SESSION_ATOM \
SessionCallback
XtMainLoop
dtksh provides access to all of the major Workspace Manager functions of the Dt libraries, including functions for querying and setting the set of workspaces with which an application is associated; for querying the list of all workspaces; for querying and setting the current workspace; and for requesting that an application be notified any time the user changes to a different workspace.
From a user's perspective, workspaces are identified by a set of names, but from the Workspace Manager's standpoint, workspaces are identified by X atoms. Whenever the shell script asks for a list of workspace identifiers, a string of X atoms is returned. If more than one X atom is present, then the list is comma-separated. The Workspace Manager expects that the shell script uses the same format when passing workspace identifiers back to it. During a given session, it is safe for the shell script to work with the X atoms, since they remain constant over the lifetime of the session. However, as was shown in the Session Manager shell script example in the previous section, if the shell script is going to save and restore workspace identifiers, the identifiers must be converted from their X atom representation to a string before they are saved. Then, when the session is restored, the shell script needs to remap the names into X atoms before passing the information on to the Workspace Manager. Mapping between X atoms and strings, and between strings and X atoms, is accomplished using the following two commands:
- XmInternAtom ATOM $DISPLAY $WORKSPACE_NAME false
- XmGetAtomName NAME $DISPLAY $ATOM
Specific dtksh commands for dealing with workspace management are documented in "Built-in libDt Session Management Commands" in Appendix A.
dtksh scripts are internationalized and then localized in a process similar to C applications. All strings that may be presented to the user are identified in the script. A post-processor extracts the strings from the script and, from them, builds a catalogue, which can then be translated to any desired locale. When the script executes, the current locale determines which message catalog is searched for strings to display. When a string is to be presented, it is identified by a message-set ID (corresponding to the catalog) and a message number within the set. These values determine what text the user sees. The following code illustrates the process:
# Attempt to open our message catalog
catopen MSG_CAT_ID "myCatalog.cat"
# The localized button label is in set 1, and is message # 2
XtCreatePushButton OK $PARENT ok \
labelString:$(catgets $MSG_CAT_ID 1 2 "OK")
# The localized button label is in set 1, and is message #3
XtCreatePushButton CANCEL $PARENT cancel \
labelString:$(catgets $MSG_CAT_ID 1 3 "Cancel")
# Close the message catalog, when no longer needed
catclose $MSG_CAT_ID
It is important to note that the file descriptor returned by catopen must be closed using catclose and not by using the kshell exec command.
dtksh commands include standard Xlib drawing functions to draw lines, points, segments, rectangles, arcs, and polygons. In the standard C programming environment, these functions take a graphics context (GC) as an argument, in addition to the drawing data. In dtksh drawing functions, a collection of GC options are specified in the parameter list to the command.
By default, the drawing commands create a GC that is used for that specific command and then discarded. If the script specifies the -gc option, the name of a graphics context object can be passed to the command. This GC is used in interpreting the command, and the variable is updated with any modifications to the GC performed by the command.
- -gc <GC>
- <GC> is the name of an environment variable which has not yet been initialized or which has been left holding a graphic context by a previous drawing command. If this option is specified, then it must be the first GC option specified.
- -foreground <color>
- The foreground color, which may be either the name of a color or a pixel number.
- -background <color>
- The background color, which may be either the name of a color or a pixel number.
- -font <font name>
- The name of the font to be used.
- -line_width <number>
- The line width to be used during drawing.
- -function <drawing function>
- The drawing function, which can be xor, or, clear, and, copy, noop, nor, nand, set, invert, equiv, andReverse, orReverse, or copyInverted.
- -line_style <style>
- The line style, which can be any of the following: LineSolid, LineDoubleDash, or LineOnOffDash.
dtksh provides mechanisms for augmenting, overriding, and removing widget translations, much as in the C programming environment. In C, an application installs a set of translation action procedures, which can then be attached to specific sequences of events (translations are composed of an event sequence and the associated action procedure). Translations within dtksh are handled in a similar fashion, except only a single action procedure is available. This action procedure, named ksh_eval, interprets any parameters passed to it as dtksh commands and evaluates them when the translation is triggered. The following shell script segment gives an example of how translations can be used:
BtnDownProcedure()
{
echo "Button Down event occurred in button "$1
}
XtCreateManagedWidget BUTTON1 button1 XmPushButton $PARENT \
labelString:"Button 1" \
translations:'#augment
<EnterNotify>:ksh_eval("echo Button1 entered")
<Btn1Down>:ksh_eval("BtnDownProcedure 1")'
XtCreateManagedWidget BUTTON2 button2 XmPushButton $PARENT \
labelString:"Button 2"
XtOverrideTranslations $BUTTON2 \
'#override
<Btn1Down>:ksh_eval("BtnDownProcedure 2")'
Generated with CERN WebMaker