next up previous contents index Karma Home Page
Next: Graphics without Widgets Up: Karma Programming Manual Previous: Image Display Tool

Dynamic Extensions

  

Dynamic extensions are a mechanism whereby you can have your own code automatically loaded and executed by parts of the Karma library. This requires that your operating system supports building shared objects (often called shared libraries) and that loading of shared objects is supported by the dlopen() function.

Rationale

An alternative to dynamic extensions is to modify the source code for a Karma module or some part of the library. Modifying the code is appropriate for a generic change (if you submit the code I can include it in the mainstream Karma release), but if it's something specific to your own work environment it might not be appropriate to include it in the mainstream Karma. Maintaining your own private versions of pieces of Karma can quickly become a maintenance nightmare, since newer versions of Karma will contain bugfixes and improvements but don't include your private changes. Simply replacing the Karma version of some module or part of the library with your own version may not work, since other parts of Karma may depend on new features which your code does not have. Merging in your changes with each new source release of Karma involves work, and does not solve the problem if you want to install a new beta (binary-only) release of Karma, since you won't have access to the source code changes until the next source release.

Other changes you may wish to make to Karma may require linking with extra libraries. This is something that is unlikely to be included in the mainstream Karma release, since it introduces dependencies on libraries not commonly available.

To solve these problems, dynamic extensions are supported. These provide a way to have your own changes integrated into the library or a module, maintaining binary compatibility between different releases of Karma. The aim is to allow you to include your own code without having to worry about maintenance when you install a newer version of Karma.

Architecture

Karma defines many ``services'' which can be extended in defined ways. A service may be a module (i.e. the entire application), a widget or some other part of the library. Each service will call certain known functions within the relevant extension at defined points in the execution of the service. There are three main classes of services:

Package Services

The service name for a package is the name of the package itself. For example, the foreign package defines the foreign service. Each package will call known extension functions specific to that package. You will need to read the package-specific documentation to determine which extension functions are defined for that package.

Widget Services

The service name for a widget is the class name of the widget. For example, the Filepopup widget (section 13.17.2) defines the service Filepopup. Widgets may call the following known functions:

Each of these functions has the same interface definition as the generic Xt class member functions. Note that not all widgets currently call these extension functions. The widgets will be changed as required (most widgets will probably never have extensions written for them by anybody). See the chapter on widgets (section 13) where each widget documents any extension functions it calls.

Module Services

The service name for a module is the name of the module itself. Modules may call the following known functions:

Creating an Extension

You will need to compile your C source code with whatever option your compiler uses for generating position-independent code. You may wish to include $KARMAINCLUDEPATH/gmakedefs/general.h1 in your GNUmakefile because it defines make variables with the correct compiler flags that you require for your platform. Once you have compiled your C code, you will need to use the linker to generate a shared object with extension .so which may then be dynamically loaded. An example GNUmakefile is shown below:

include $(KARMAINCLUDEPATH)/gmakedefs/general.h1

myobject.so: mysource.o
        $(LDso) -o myobject.so mysource.o

This uses the implicit rule in <gmake> to compile the source file into an object. If necessary, you may define the CFLAGS variable to provide extra compile flags (the implicit rule uses this variable).

Configuration

A single file is used to configure the extensions available. Your personal configuration file is scanned first, and is called tex2html_wrap_inline5797/.karma/extensions which specifies which shared objects are available for the various services. After that the $KARMABASE/site/share/extensions file is scanned to load any extensions provided by your local Karma installer. Finally the $KARMABASE/share/extensions file is scanned to load any extensions provided with the Karma distribution. The format of this file is as follows:

SO_DIRS  directories
SERVICE  servicename objects

where:

It is legal to list the same service multiple times: this simply concatenates the list of objects for that service. Functions will be called in the same order in which the configuration files were scanned.

Example

The following is an example of an extension for the Filepopup widget (section 13.17.2) which defines the service Filepopup. It adds a few accelerator buttons to the file browser. Note how there is no state defined in the extension: using static variables must be done with caution, since the same WidgetInitialise function is called several times, once for each widget that is created.

To use this, my tex2html_wrap_inline5797/.karma/extensions file contains the following:

SO_DIRS  ~/.karma/lib/$MACHINE_OS
SERVICE  Filepopup myobject

And the code to implement this follows:

/*  This extension adds some accelerator buttons to a Filepopup widget
    Copyright (C) 1997-1998  Richard Gooch
    This code is distributed under the GNU General Public License
*/
#include <stdio.h>
#include <math.h>
#include <errno.h>
#include <X11/Intrinsic.h>
#include <X11/Xos.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Command.h>
#include <karma.h>
#include <karma_a.h>
#include <Xkw/Filepopup.h>
#include <Xkw/Filewin.h>


/*  Private functions  */
STATIC_FUNCTION (Widget add_button,
                 (Widget box, CONST char *label, CONST char *directory) );
STATIC_FUNCTION (void chdir_cbk, (Widget w, XtPointer client_data,
                                  XtPointer call_data) );


/*  Public functions follow  */

flag WidgetInitialise (Widget Request, Widget New)
{
    Widget box;

    XtVaGetValues (New, XkwNboxWidget, &box, NULL);
    /*  Add the various personalised buttons  */
    add_button (box, "/", "/");
    add_button (box, "/data", "/data");
    add_button (box, "/cdrom", "/cdrom");
    return (TRUE);
}   /*  End Function WidgetInitialise  */


/*  Private functions follow */

static Widget add_button (Widget box, CONST char *label, CONST char *directory)
/*  [SUMMARY] Add a button to a box.
    <box> The enclosing box widget.
    <upper> The widget under which the new widget should be placed.
    <left> The widget to the left of where the widget should be placed.
    <label> The label to appear in the button. The pointer must remain valid.
    <directory> The directory to change to when the button is pressed. The
    pointer must remain valid.
    [RETURNS] The new button widget.
*/
{
    Widget w;

    w = XtVaCreateManagedWidget ("button", commandWidgetClass, box,
                                 XtNlabel, label,
                                 NULL);
    XtAddCallback (w, XtNcallback, chdir_cbk, (XtPointer) directory);
    return (w);
}   /*  End Function add_button  */

static void chdir_cbk (Widget w, XtPointer client_data, XtPointer call_data)
{
    Widget filewin, parent, filepopup;
    CONST char *dirname = (CONST char *) client_data;
    static char function_name[] = "Filepopup-ext_chdir_cbk";

    for ( parent = XtParent (w), filepopup = NULL;
          (filepopup == NULL) && (parent != NULL);
          parent = XtParent (parent) )
    {
        if ( XtIsFilepopup (parent) ) filepopup = parent;
    }
    if (filepopup == NULL)
    {
        fprintf (stderr, "Could not find a Filepopup ancestor\n");
        a_prog_bug (function_name);
    }
    XtVaGetValues (filepopup, XkwNfilewinWidget, &filewin, NULL);
    XkwFilewinChangeDirectory (filewin, dirname);
}   /*  End Function chdir_cbk  */

Note how the exported function WidgetInitialise returns a boolean value. All extension functions must return either TRUE or FALSE.


next up previous contents index Karma Home Page
Next: Graphics without Widgets Up: Karma Programming Manual Previous: Image Display Tool

Richard Gooch
Mon Aug 14 22:12:47 PDT 2006