LHEATcl Plugin API

The LHEATcl Plugin is an enhanced version of the TCL Plugin version 2.0 source release. The version number of the current LHEATcl release is 2.0 despite the fact that it is the first official release in order to match the version number of the TCL plugin that it "descends" from. The LHEATcl Plugin adds the following extra features to the plugin:

  • The dash patch : This adds several missing things to the Tk canvas widget. These include dashed and stippled outlines, the "visitor" patch to allow easier extension of canvas capabilities, and postscript output for images. See the dash patch home page for details.
  • FitsTcl: This adds the ability to read and write FITS files. It is essentially a Tcl interface to cfitsio
  • POW: A scientific curve and image plotting and interface widget which uses the Tk canvas with several extensions to produced curve plots, images, contours, histograms, etc. Since y using the Tk canvas, user input can be done almost any way you wish.
  • The LHEA orbit library. This is currently under development and in release 2.0, only three functions are present which are built into the POW library, but in future releases, this will be a separate package adding spacecraft orbit calculation utility routines.
Follow the links above to get in depth information on any of the LHEATcl components. Documentation for the original Tcl plugin is here. What follows is a brief description of how the plugin works followed by some sample tclets.

What It is and How It Works

The "guts" of the LHEATcl plugin is a dynamically loadable library (DLL) containing the compiled object files from Tcl/Tk, FitsTcl, POW, etc. Currently, there is just one, monolithic LHEATcl DLL, unlike the LHEA standalone Tcl scripts such as fv where there one DLL for each package (Tcl, Tk, fitsTcl, POW, etc) . This library is placed in your browser's plugins directory. Currently Netscape4.x and Internet Explorer 3.x, 4.x, and 5.0 are supported. Netscape3.x is not supported because the Javascript we use to setup a window to run the tclet in doesn't work in Netscape 3.x. When your browser starts up it scans the plugins directory. If it finds the LHEATcl plugin library, it adds the LHEATcl plugin to its list of plugins and registers the plugin to process files of mime type "application/x-tcl". For details of how this information is stored in the library file, see the documentation for the Netscape plugin SDK.

Now when your browser encounters:

  • a URL that ends in ".tcl" or
  • a page that has mime type "application/x-tcl" or
  • a statement like:
     
    <embed src=http://weefle.com/myapp.tcl type=application/x-tcl>
    
    or
     
    <embed script='label .l -text "Hello World"; pack .l' type=application/x-tcl>
    
    it loads the DLL (".so" file in the Unix world). We had hoped to use a unique extension and mime type for LHEATclets rather than coopting the original "tcl" type, but bugs in the Windows version of Netscape prevented it. Luckily, the LHEATcl plugin should run any generic tclets without problems.

    Loading the DLL creates a Tcl "interpreter" either as a thread or a separate process, depending on your configuration. The interpreter executes several initialization scripts (written in Tcl) and reads configuration parameters. The scripts are located in the user's .netscape/lheatclplug/2.0/* directories for UNIX and in the lheaplug/2.0/* directory for Windows. On the Mac, these scripts are located in the resource fork of the DLL. The configuration parameters are in files in the config/ directory and are used to setup security policies and a few other adjustable settings (on the Mac, these config files are in the extensions folder in the "Tool Command Language" folder).

    Finally, the browser fetches the Tcl code (tclet) that started the whole process from the web server and the interpreter executes it.

    Security "Policies" and Configuration Files

    To expand slightly on the above description, when your browser loads the LHEATcl DLL and finishes running startup scripts, it spawns a slave interpreter and runs the tclet in the slave interpreter. The reason for this is that the slave interpreter is restricted from doing certain insecure things. These restrictions are implemented through security policies.

    For a great deal more information about security policies, see the Tcl plugin manual pages, particularily the policy and plugin architecture pages. All of the files dealing with policy and configuration are in the config subdirectory of the plugin's runtime library directory. Most of the files in the config directory are responsible for implementing various named security policy. The code in each of these files specifies what specific features of Tcl are turned on or off for that policy. So when a tclet wants to use a feature such as opening a socket to another computer save files on the browser's machine it will need to ask for a security policy that allows the use of the desired feature. For instance, a tclet wanting to send Javascript commands to the browser would need to include the line: policy javascript If the origin URL of the tclet is allowed to invoke the "javascript" policy, tclet execution will proceed, if not, the process will abort.

    The file that actually sets up the behavior of your plugin config/plugin.cfg. Most of this file sets up which origin URLs can use which policies. For instance, by default, the original Tcl plugin allowed the "home" policy for any tclet, whereas the fairly dangerous features of the "javascript" policy are only allowed if the origin URL is located at SUN Microsystems. There is a "lhea" policy which allows several necessary features for our programs. Unfortunately, there is a bug in MAKI (our first major production tclet) that prevents it from running under the "lhea" policy and we haven't had time to figure out how to fix this; therefore, the LHEATcl plugin currently installs configured so that any URL matching *.gsfc.nasa.gov for it's domain name is allowed to use the dangerous "trusted" policy.

    The last few lines of "plugin.cfg" can be uncommented to set variables which allow some useful plugin features, primarily for debugging tclets:

    TCL_PLUGIN_CONSOLE
    If this is set to 1, a tk console window will pop up when a tclet loads. Typing Tcl commands into the console will execute them in the unrestricted *master* Tcl interpreter, not the restricted slave interpreter. If you want to send commands to the slave use the interp eval command. The interp slaves command can be used to find the name of the slave interpreter(s).
    TCL_PLUGIN_LOGWINDOW
    If set to 1, a window containing a fairly detailed log of what the plugin is doing pops up when a given tclet loads
    TCL_PLUGIN_LOGFILE
    Set this to the name of a file if you want to capture the log output from the plugin.
    TCL_PLUGIN_WISH
    Setting this to 1 causes the plugin to launch as a separate process instead of as a thread of your browser's process. On most platforms, this doesn't make much difference, but setting TCL_PLUGIN_WISH to 1 under Solaris is currently necessary because recent Solaris versions of Netscape were built under Solaris2.5 which had broken signal handling in threaded processes.

    File IO in the Plugin

    File IO in the LHEATcl plugin is somewhat complicated. By default, the plugin's security features disable writing files to the local machine entirely. However, fitsTcl's file writing does not implement any security features. The trusted policy allows unrestricted file writing on the local machine. The lhea policy allows the more limited persistent file writing. FITS file writing through fitsTcl doesn't work under Windows for the current version of the plugin. We will attempt to summarize the various possibilities here:

    1. Writing non-FITS files can be done with the standard Tcl commands (open, puts, etc.). If you use a 'policy trusted' statement, the files will appear in the current working directory of the browser process.
    2. If you use 'policy lhea' (or any policy which allows the "persist" feature) the files will appear in /tmp/username/persist/lhea/dir0. In Windows: '/tmp/username' will instead be the Windows temp directory; frequently this is C:/WINDOWS/temp. When using the persist feature, the total ammount of disk space used by persistent files is limited. See Tcl plugin documentation for details.
    3. You cannot read files from the browser's computer unless they are present in the persistent file area or you are using the 'trusted' policy.
    4. Using fitsTcl calls, you can read and write FITS files anywhere that the user is allowed to read or write files on the client machine. The default location is the current working directory of the browser process. There is no size limit.
    5. You can use the Tcl built-in http package or standard extension packages that provide ftp, etc. (as long as they're written in straight Tcl) to fetch files from URLs.
    6. For FITS files, you can pass a URL rather than a filename to the fitsTcl 'fits open' command and it will fetch the file using cfitsio's internal net drivers. Note: cfitsio net drivers currently only work on UNIX.

    Known Problems and Considerations

    There are a few known quirks in the plugin or its build that need to be allowed for.

    On most Unix machines, if you resize the browser window containing a tclet, the browser will crash and core dump. This is a problem with the original Tcl plugin. We prevent this for MAKI by launching the tclet in it's own, non-resizable, browser window. See the example below for details on how to do this.

    It would be safer, not so much to prevent malicious intent as to guard against catastrophic bugs, if we used the 'lhea' security policy rather than the 'trusted' security policy for MAKI. We are using some forbidden feature which we have not had the time to track down. "Your mileage may vary", so try to use the 'lhea' policy if you can. This does impose some strict limits on your code, the most obvious is probably the lack of the 'menu' widget. See Tcl plugin documents for details.

    As mentioned above, if you want to use cfitsio net drivers, you are limited to Unix and for Solaris you must set TCL_PLUGIN_WISH equal to 1 in the plugin.cfg file. This is done by default in our prebuilt copies of the plugin.

    If you need to rebuild and/or extend the LHEATcl plugin yourself, you are welcome to do so (read the SUN license agreement for full details on the legal status of the plugin, the terms are pretty generous). You may want to contact us to coordinate efforts if you're planning extensive improvements (send email to wasabi@oheasarc.gsfc.nasa.gov). The UNIX build is fairly straightforward, we use proprietary compilers where possible since that is how Netscape is usually built, but gcc should work fine. The Windows build is fairly torturous at present and requires both Borland C++ and Microsoft Visual C++ and is not for the faint of heart, or even weak of stomach, I suspect. The MacIntosh build uses CodeWarrior.

    Examples

    This example is a simple function plotter. It shows how to create powCurve objects and graph them.

    #This is a simple example to plot any TCL expression
    #ask for the trusted security policy (hopefully, next release, the lhea policy
    #will be an option, but it's currently broken)
    policy trusted
    
    #load the POW features (it's built into the plugin as a "static package")
    load "" pow
    
    #make a place to put the POW window
    frame .powholder 
    grid .powholder -column 0 -row 0 -columnspan 4
    
    #open up a POW window to plot stuff in
    #arguments mean:
    # "safe" - tell POW we're running in a browser so don't try to manage the X
    #          color resources
    # ".powholder"    - where to put the POW window
    # "1"             - include the standard POW GUI (use 0 to suppress the POW
    #                   GUI for more specialized applications)
    powInit safe .powholder 1
    
    #tell the POW window how big to be
    .pow.pow configure -width 380 -height 380
    
    #Get the function to plot
    button .plotbutton -text "Plot Function" -command \
        {plotFunc $exprString $rMin $rMax}
    grid .plotbutton -column 0 -row 1 -sticky ew -columnspan 2
    
    set exprString {sin($x)}
    entry .exprentry -textvariable exprString -width 40
    grid .exprentry -column 2 -row 1 -sticky ew -columnspan 2
    
    #Get the range they want
    set rMin 0
    label .minlab -text XMin
    entry .minentry -textvariable rMin -width 10
    grid .minlab  -column 0 -row 2 -sticky ew
    grid .minentry -column 1 -row 2 -sticky ew
    
    set rMax 6.2831853
    label .maxlab -text XMax
    entry .maxentry -textvariable rMax -width 10
    grid .maxlab  -column 2 -row 2 -sticky ew
    grid .maxentry -column 3 -row 2 -sticky ew
    
    set ext 0
    
    
    
    proc plotFunc {exprstring min max} {
        global ext    
    #create a curve
        makeCurve $exprstring $min $max
    #Check to see if a graph exists
        if {[powListGraphs] != "" } {
    #add new curve to graph
    	powPlotCurves MyFunctions "curve_$ext" 
    #call function to rescale the graph to show all curves 
    	powEndROI 1
        } else {
    #no graph, make one
    	powCreateGraph MyFunctions "curve_$ext" NULL NULL NULL NULL NULL 300 300
        }
    
    }
    
    proc makeCurve {exprstring min max} {
    global ext
    #make a new curve name
        incr ext
        set curve "curve_${ext}"
    #calculate 100 curve points
        set deltx [expr double($max - $min)/100.0]
        set i 0
        catch {unset xvect}
        catch {unset yvect}
        for {set x $min} {$x < $max} {set x [expr $x + $deltx]} {
    	lappend xvect $x
    	lappend yvect [expr $exprstring]
    	incr i
        }
    #Make powData objects from TCL lists
        powCreateDataFromList xdata $xvect
        powCreateDataFromList ydata $yvect
        
    #Make powVector objects from powData objects
        powCreateVector xvector xdata 0 $i NULL
        powCreateVector yvector ydata 0 $i NULL
    
    #Make powCurve object from powVector objects
        powCreateCurve "curve_$ext" xvector NULL yvector NULL
        
    }    
    

    Now let's try a tclet with some fitsTcl calls. This will be a bit more complicated. Here's an annotated FITS file image display tclet:

    #ask for the trusted security policy (hopefully, next release, the lhea policy
    #will be an option, but it's currently broken)
    policy trusted
    
    #load the POW and fitsTcl features (they're built into the plugin as "static
    #packages")
    load "" pow
    load "" fits
    
    #make a place to put the POW window
    frame .powholder 
    grid .powholder -column 0 -row 0 -columnspan 2
    
    #open up a POW window to plot stuff in
    #arguments mean:
    # "safe" - tell POW we're running in a browser so don't try to manage the X
    #          color resources
    # ".powholder"    - where to put the POW window
    # "0"             - don't include the standard POW GUI (You might try 
    #                   setting this to "1" for a more versatile tclet
    powInit safe .powholder 1
    
    #tell the POW window how big to be
    .pow.pow configure -width 380 -height 380
    
    button .imagebutton -text "Plot Image" -command {loadImage $imageURL}
    grid .imagebutton -column 0 -row 1 -sticky ew
    
    set imageURL "http://heasarc.gsfc.nasa.gov/wasabi/algol.fits"
    entry .imageentry -textvariable imageURL -width 40
    grid .imageentry -column 1 -row 1 -sticky ew
    
    
    proc loadImage {url} {
        
    #make a name for the graph:
    set gname "MyGraph"
    #make sure we don't reuse an existing name
    set ext  0
    while {[lsearch [powListGraphs] $gname] != -1} {
        incr ext
        set gname "${gname}_${ext}"
    }
    
    
    #for simplicity, in this example, we'll let cfitsio read the file.
    #if you're running under Windows or MacOS, this means you can't use
    #a URL, just a local file name. We could be more elaborate, but this
    #is meant to be a *simple* example
    #open the fits file (readonly)    
    set infilehandle [fits open $url 0]
    
    #load the image data into memory
    set imghandle [$infilehandle load image]
        
    #get the dimensions of the image
    set dims [$infilehandle info imgdim]
    set n1 [lindex $dims 0]
    set n2 [lindex $dims 1]
    
    #get the data type of the image 
    set data_type [lindex [lindex [$infilehandle get keyword BITPIX] 0] 1]
    
    #Now we work around a bug in the 2.0 version of the plugin, future versions
    #will be able to use the BITPIX value for data type without translation
        set ppFitsDataType(8) 0
        set ppFitsDataType(16) 1
        set ppFitsDataType(32) 2
        set ppFitsDataType(-32) 3
        set ppFitsDataType(-64) 4
        set data_type $ppFitsDataType($data_type)
    
    
    #Now a bit of Voodoo to deal with possible wierd file types:
    
    #If the image has BZERO or BSCALE keywords in the header, fitsTcl will
    #do the appropriate thing with them automatically, but the datatype returned
    #will be floating point doubles (isn't FITS fun:)
    if { ([catch {$infilehandle get keyword BZERO}] == 0) ||
         ([catch {$infilehandle get keyword BSCALE}] == 0) } {
        set data_type 4
    }
    
    #make a POW DATA object
    
    powCreateData ${gname}_data $imghandle $data_type [expr $n1 * $n2] 0
    
    #make a POW IMAGE object; the units (pixels, intensity) are arbitrary; since
    #this is a general application, we don't know what they are
    
    powCreateImage ${gname}_img ${gname}_data 0 0 $n1 $n2 0 1 0 1 pixels pixels intensity
    
    #This will setup POW to use the Astronomical coordinate information
    #in the file (if there is any) 
    global powWCS
    if { ! [catch  {$infilehandle get imgwcs} wcsString] } {
        set powWCS(${gname}_img) $wcsString
    }
    
    #we're done reading the file now
    $infilehandle close
    
    #make a POW Graph object which contains only our image
    
    powCreateGraph $gname NULL ${gname}_img \
        pixels pixels NULL NULL 300 300
    
    }
    
    

    While it's not strictly a LHEATcl question, it is handy to know how to launch a tclet in a new console window using Javascript. Here's the code to make a button that launches MAKI (a LHEATcl applet) on a the test file algol.fits:

    <FORM>
    <INPUT TYPE="button" VALUE="Launch MAKI" onClick='goober()'>
    <SCRIPT LANGUAGE="JavaScript">
    function goober() {
    var newWindow
    var winwidth
    var content
    
    if (screen.availWidth > 850 ) {
        winwidth = "850"
    } else {	
        winwidth = "800"
    }
    
    newWindow = window.open("","MAKI","scrollbars,menubar,height=" +
    screen.availHeight + ",width=" + winwidth)
    
    content="<embed src=http://heasarc.gsfc.nasa.gov/Tools/maki/MAKI.tcl "+
    "type=application/x-tcl height=2000 width=" + winwidth + 
    " url=http://heasarc.gsfc.nasa.gov/Tools/maki/algol.fits graphname=Algol>"
    newWindow.document.write(content)
    newWindow.document.close()
    }
    </SCRIPT> 
    </FORM>
    

    Tako/MAKI/LHEATcl Plugin help line
    Last modified: Mon Dec 6 15:49:01 EST 1999