This is a prelimininary discussion document. New displays will be written according to this spec and old ones will gradually be converted.
We're discussing the window handling, the data structures, their names and their memory handling.
The primary ideas about an ace-display are :
+--------------------------+ |O| AMAP:dj9812 |X| +--------------------------+ +-----------------------------------+ | = | | struct AceMapStruct | | =_ _ gene |------> | { | | = - = unc-1 | | <standard struct-members> | | dj9812 =_ = | | <specific struct-members> | | _- _ | | } | | | - | +-----------------------------------+ | - = | +--------------------------+
The way the window and the data-structure is created and destroyed needs standardization, some of that can hopefully be provided by a set of wrapper functions in the display-Package.
Let's assume that the name for this display is "AceMap", another display along the lines of FMAP or GMAP.
Every graph has an associator attached : an associator lets you register a void*
upon a unique identifier. This unique identifier is another pointer-type, and we will use
the address of a static string-constant. The unique address of that string is guaranted by the
compiler, which during link-time will fit the constant string within the address-space of the program.
When the window is created and it's data-structure filled with appropriate information, the
graphAssociator for the active graph will be registered to return the address of the data-structure
if it is presented with the string's magic address on that window again.
static magic_t GRAPH2AceMap_ASSOC = "GRAPH2AceMap";
It is because C has very poor type-checking and no generic
data-types, that the graphAss-package uses void*
as the
address to the window's data-structure.
The pointer returned by graphAssFind (upon the active graph) has to be
type-cast from void*
to the type of
AceMapStruct
. Pointer type-casts are always a
dangerous thing to do, because the dereferencing of structures is just
a very crude memory look-up in C.
Therefore, one of the <standard struct-members> is a
magic identifier, which is used to verify that the void*
pointer is what we expect. After that we know that the type-cast was
safe and we can derefence the void*
pointer based on the
struct definition of AceMapStruct
.
The magic identifier is again a unique pointer, generated by the compiler as the address to a static string constant. The magic id is declared in the following way :
static magic_t AceMap_MAGIC = "AceMap";One of the standard ace-display struct members is
->magic
which is assigned to &AceMap_MAGIC
when the data-structure
is created and filled with data.
Above we mentioned two goals about the memory handling of
ace-displays. The graph should be destroyable by the usual
graphDestroy()
menu-action or a window-managers "CLOSE"
signal and upon dying the memory of the data-strcuture needs to be
reclaimed cleanly. On the other hand we want to be able to create an
AceMapStruct and destroy it, which should clear up the graph.
The function creating the graph needs to create the data-structure and graphAssociate it with itself. The graph's destructor function needs to disassociate the data-structure, with the dying graph and clear its memory.
The function creating the data-structure needs to allocate the memory of the struct upon a given handle, register the finalisation function, assign it's magic identifier, assign the graph to be the one we've just opened and fill in all the data.
The window is created by the standard DisplayFunc
,
whose type is defined in acedb.h
(should move to
display.h) as
typedef BOOL (*DisplayFunc)(KEY key, KEY from, BOOL isOldDisplay) ;
A window that has a display-function of this type can be registered and configured using the wspec/displays.wrm configuration file.
The structure of the AceMap display module so far is :/*******************************************/ static magic_t GRAPH2AceMap_ASSOC = "GRAPH2AceMap"; static magic_t AceMap_MAGIC = "AceMap"; typedef struct AceMapStruct { /* standard display-struct members */ magic_t *magic; /* == &AceMap_MAGIC */ STORE_HANDLE handle; Graph graph; KEY originKey; /* AceMap display struct members */ Array exampleArray; char *exampleString; } *AceMap; /*******************************************/ static AceMap currentAceMap (char *caller) /* return AceMap structure associated with the active graph */ { AceMap amap; if (!graphAssFind (&GRAPH2AceMap_ASSOC,&amap)) messcrash("%s() could not find AceMap on graph", caller); if (!amap) messcrash("%s() received NULL AceMap pointer", caller); if (amap->magic != &AceMap_MAGIC) messcrash("%s() received non-magic AceMap pointer", caller); return amap; } /* currentAceMap */ /*******************************************/ static void aceMapFinalise (void *vp) /* Finalisation function for AceMap type */ { AceMap amap = (AceMap)vp; Graph old = graphActive (); if (graphActivate (amap->graph)) { graphDestroy (); /* let the graph-destructor do the rest */ graphActivate (old); } else handleDestroy (amap->handle); return; } /* aceMapFinalise */ static AceMap aceMapCreate (KEY key, KEY from) /* Constructor function for AceMap type */ { AceMap amap = NULL; if (aceMapPossibleFor(key)) { amap = (AceMap)messalloc(sizeof(struct AceMapStruct)); blockSetFinalise (amap, aceMapFinalise); amap->magic = &AceMap_MAGIC; amap->handle = handleCreate(); amap->graph = 0; amap->originKey = key; /* now the AceMap specifics */ amap->exampleArray = arrayHandleCreate(10, amap->handle); amap->exampleString = strnew ("example", amap->handle); } return amap; } /* aceMapCreate */ static void aceMapDestroy (void) /* graph-destructor for AceMap display */ { AceMap amap = currentAceMap ("aceMapDestroy"); amap->graph = 0; /* graph is dying already */ messfree (amap); } /* aceMapDestroy */ static void aceMapRefresh (void) { AceMap amap = currentAceMap("aceMapRefresh"); aceMapDraw (amap, 0); return; } /* aceMapRefresh */ static void aceMapDraw (AceMap amap, KEY from) { /* display object amap->originKey, but center on object 'from' */ return; } /* aceMapRefresh */ BOOL aceMapDisplay (KEY key, KEY from, BOOL isOldGraph) /* graph-constructor for AceMap display */ { AceMap amap; if (isOldGraph && /* there is a window of type "AceMap already" */ graphAssFind(&GRAPH2AceMap_ASSOC, &amap) && amap ->magic == &AceMap_MAGIC && amap->originKey == key) { /* That existing graph is already showing object we're asking for just redraw for new position based on the 'from'-object */ aceMapDraw (amap, from); return TRUE; } amap = aceMapCreate (key, from); if (!amap) { /* can't create an AceMap, no data or similar problem * so we show the object in the text display instead */ display (key, 0, TREE); return FALSE; } /* open new display of type "AceMap" */ if (!displayCreate ("AceMap")) return FALSE; graphRetitle (name(key)); graphRegister (RESIZE, aceMapRefresh) ; graphRegister (DESTROY, aceMapDestroy) ; graphAssociate (&GRAPH2AceMap_ASSOC, amap); amap->graph = graphActive(); aceMapDraw (amap, from); return TRUE; } /* aceMapDisplay */ /*******************************************/