When the edm widget set is extended, code is not statically linked with the application nor linked with any other library. Instead, new widgets are packaged into their own shareable library files. The new widgets are made available to edm through a registration process.
Registering a widget is simply making an entry in a file named edmObjects. An example of the contents of this file follows.
3 activeRectangleClass /edm/libEdmBase.so Graphics Active Rectangle activeMeterClass /edm/libEdmBase.so Monitors Meter activeSliderClass /edm/libEdmBase.so Controls Slider
The first line in the file holds the count of the number of widgets that follow. When you add or remove a widget, the count must be updated. The remainder of the file contains widget specifications, one per line, contained in the following fields:
Widget Class Name
Absolute Path to Shareable Library
Widget Type
Widget Nickname
For the first line of the previous example, widget class name is activeRectangleClass, path is /edm/libEdmBase.so, type is Graphics, and widget nickname is Active Rectangle.
One variation on the above is possible. A particular widget implementation may support both control and monitor identities. For such a case, optional information, which may not contain whitespace, is provided in the Widget Class Name field. The optional form is shown below.
<Widget Class Name>:<optional info> Example: activeXTextDspClass /edm/libEdmBase.so Controls Text Control activeXTextDspClass:noedit /edm/libEdmBase.so Monitors Text Monitor
The optional string (in this case noedit) may be obtained using the base class member function defined as
char *getCreateParam ( void )
One final note regarding the edmObjects file: Environment variable references are allowed. Therefore the first example could be modified as follows:
3 activeRectangleClass $(EDMLIBS)/libEdmBase.so Graphics Active Rectangle activeMeterClass $(EDMLIBS)/libEdmBase.so Monitors Meter activeSliderClass $(EDMLIBS)/libEdmBase.so Controls Slider
This assumes that the environment variable EDMLIBS exists and translates to /edm.
Widgets should be able to add and remove themselves to/from the edmObjects file. Indeed, part of packaging a widget involves supplying information that makes this possible. A user of a new package of widgets performs this registration/removal process as follows:
edm -add /edm/newWidgets.so edm -remove /edm/newWidgets.so
The abbriviated contents of a package can be examined with the command
edm -show /edm/newWidgets.so
The following shows an example output from this command
Edm component file is /edm/edmObjects
Component Menu Type Menu Text
activeRectangleClass Graphics Active Rectangle
activeMeterClass Monitors Meter
activeSliderClass Controls Slider
The name of the shareable library file and all component names must be unique. To avoid namespace collisions, UUID derived names may be employed. Most linux distributions include the uuidgen binary. Execution gives the following type of output:
uuidgen
738229ba-267d-4334-90c6-5c7170197bcf
uuidgen
f83e0c59-9e37-4e1f-a077-6c5b825c196d
uuidgen
332e77cd_c2dc_4dac_bfd0_bd6e146a9003
.
.
.The library file may be derived from the first of these using the name lib738229ba-267d-4334-90c6-5c7170197bcf.so.
The widget component names may be derived from the remaining by changing each hypen to an underscore. The first name, for example, would become f83e0c59_9e37_4e1f_a077_6c5b825c196d.
The name of a component is the string that must be returned by the class member function objName.
Once a widget component name is known, two non-member functions are added outside of the widget class. One creates a class object and the other clones an existing object. They are both "C" language compatible and are used for dynamic loading. The following gives an example for one component.
extern "C" {
void *create_f83e0c59_9e37_4e1f_a077_6c5b825c196dPtr ( void ) {
newWidgetClass *ptr;
ptr = new newWidgetClass;
return (void *) ptr;
}
void *clone_f83e0c59_9e37_4e1f_a077_6c5b825c196dPtr (
void *_srcPtr )
{
newWidgetClass *ptr, *srcPtr;
srcPtr = (newWidgetClass *) _srcPtr;
ptr = new newWidgetClass( srcPtr );
return (void *) ptr;
}
}Note that the name newWidgetClass is just part of the example. The actual name would be chosen by the widget developer. The function names, however, are important. They must be of the form
create_<component name>Ptr and clone_<component name>Ptr
In addtional to the above class factory functions, two more "C" language compatible functions, also declared outside of the widget class, are required to supply the widget self-registration information to the display engine. These functions are always named firstRegRecord and nextRegRecord. The following example shows the declaration and implementation of these two functions. The only variable is the contents of the libRec structure.
#include "environment.str"
typedef struct libRecTag {
char *className;
char *typeName;
char *text;
} libRecType, *libRecPtr;
static int libRecIndex = 0;
// Component name, type, Nickname
// For type, use constant from environment.str include file
// global_str2 = "Monitors"
// global_str3 = "Graphics"
// global_str5 = "Controls"
static libRecType libRec[] = {
{ "2ed7d2e8_f439_11d2_8fed_00104b8742df", global_str2, "New Monitor" },
{ "332e77cd_c2dc_4dac_bfd0_bd6e146a9003", global_str5, "New Control" }
};
#ifdef __cplusplus
extern "C" {
#endif
int firstRegRecord (
char **className,
char **typeName,
char **text )
{
libRecIndex = 0;
*className = libRec[libRecIndex].className;
*typeName = libRec[libRecIndex].typeName;
*text = libRec[libRecIndex].text;
return 0;
}
int nextRegRecord (
char **className,
char **typeName,
char **text )
{
int max = sizeof(libRec) / sizeof(libRecType) - 1;
if ( libRecIndex >= max ) return -1; //no more
libRecIndex++;
*className = libRec[libRecIndex].className;
*typeName = libRec[libRecIndex].typeName;
*text = libRec[libRecIndex].text;
return 0;
}
#ifdef __cplusplus
}
#endif