11. Pages

In Monolith, a "Page" is special program for controlling things in Monolith. Multiple pages exist simultaneously, but are controlled one at at time. When a page is "selected", the page is granted access to all the peripherals (which right now consists of the griffin and the monome).

11.1. Active Page Selection

At any given time, only one page can be selected. This active page is sored in a variable in the top-level struct called curpage.

<<struct_contents>>=
monolith_page *curpage;

By default, it is set to point to NULL.

<<init>>=
m->curpage = NULL;

The current page is set with the function monolith_curpage_set.

<<function_declarations>>=
void monolith_curpage_set(monolith_d *m, monolith_page *pg);
<<functions>>=
void monolith_curpage_set(monolith_d *m, monolith_page *pg)
{
    if(m->curpage != NULL) monolith_page_close(pg);
    m->curpage = pg;
    monolith_page_open(pg);
}

11.1.1. Find and set a page

A page can be selected by name with the function monolith_curpage_find_and_select. It will attempt to look up the name in the dictionary, and then set that to the current page.

On success, TRUE (1). Failure, FALSE (0).

<<function_declarations>>=
int monolith_curpage_find_and_select(monolith_d *m,
                                     const char *name,
                                     unsigned int len);
<<functions>>=
int monolith_curpage_find_and_select(monolith_d *m,
                                     const char *name,
                                     unsigned int len)
{
    monolith_dict *dict;
    monolith_page *pg;
    int rc;

    dict = monolith_dict_get(m);
    rc = monolith_dict_lookup(dict, &pg, name, len);
    if(!rc) return 0;
    monolith_curpage_set(m, pg);
    return 1;
}

11.1.2. Scheme Function

A page can be selected by name using the scheme function monolith:page-select.

<<primitive_entries>>=
{"monolith:page-select", pp_page_select, 1, 1, {STR,___,___}},
<<scheme_functions>>=
static cell pp_page_select(cell x)
{
    const char *str;
    monolith_d *m;
    int rc;

    m = monolith_data_get();

    str = string(car(x));

    rc = monolith_curpage_find_and_select(m,
                                          str,
                                          strlen(str));

    if(!rc) {
        fprintf(stderr, "Could not find page '%s'\n", str);
        return UNSPECIFIC;
    }
    return UNSPECIFIC;
}

11.1.3. Janet Function

<<core_janet_entries>>=
{
"monolith/page-select",
j_page_select,
"Selects a page."
},
<<janet_functions>>=
static Janet j_page_select(int32_t argc, Janet *argv)
{
    monolith_d *m;
    const char *str;
    int rc;

    m = monolith_data_get();
    janet_fixarity(argc, 1);
    str = (const char *)janet_unwrap_string(argv[0]);
    rc = monolith_curpage_find_and_select(m,
                                          str,
                                          strlen(str));

    if(!rc) {
        fprintf(stderr, "Could not find page '%s'\n", str);
    }

    return janet_wrap_nil();
}

11.2. Checking if a Page is Selected

A page can be checked if it is selected or not with the function monolith_page_selected. It will compare the pointer address of the page with the currently selected page.

<<function_declarations>>=
int monolith_page_selected(monolith_page *pg);
<<functions>>=
int monolith_page_selected(monolith_page *pg)
{
    return pg == pg->m->curpage;
}

11.3. Page Dictionary (included in top-level struct)

11.3.1. Entry Page Lookup

11.3.1.1. Dictionary Lookup

A monolith page is retrieved from the dictionary using the function monolith_page_lookup. It has arguments which mirror monolith_dict_newpage. A found monolith page will store itself in the supplied page pointer.

The function will return TRUE (1) on success and FALSE (0) on failure.

<<function_declarations>>=
int monolith_dict_lookup(monolith_dict *d,
                         monolith_page **pg,
                         const char *name,
                         unsigned int len);
<<functions>>=
int monolith_dict_lookup(monolith_dict *d,
                         monolith_page **pg,
                         const char *name,
                         unsigned int len)
{
    unsigned int listpos;
    monolith_dict_entrylist *el;
    monolith_dict_entry *ent;
    unsigned int i;

    listpos = dict_hash(name, len, MONOLITH_DICT_SIZE);
    el = &d->lists[listpos];
    ent = el->head;
    for(i = 0; i < el->size; i++) {
        if(len == ent->len) {
            if(!strncmp(name, ent->key, len)) {
                if (ent->type != MONOLITH_ENTRY_PAGE) {
                    return 0;
                }
                if (pg != NULL) *pg = ent->ud;
                return 1;
            }
        }
        ent = ent->nxt;
    }
    return 0;
}
11.3.1.2. Page Lookup

This task is done often enough by pages that a convience function was created called monolith_lookup_page, created so that that pages didn't have to access the dictionary from the opaque monolith pointer.

<<function_declarations>>=
int monolith_lookup_page(monolith_d *m,
                         monolith_page **pg,
                         const char *key,
                         unsigned int len);
<<functions>>=
int monolith_lookup_page(monolith_d *m,
                         monolith_page **pg,
                         const char *key,
                         unsigned int len)
{
    return monolith_dict_lookup(&m->dict, pg, key, len);
}

11.3.2. Entry Find

This is a more generalized version of monolith_dict_lookup. Instead of looking for a page, it just stores the entry. Initially created to look up images stored in the dictionary.

<<function_declarations>>=
int monolith_dict_find(monolith_dict *d,
                       monolith_dict_entry **pent,
                       const char *name,
                       unsigned int len);
<<functions>>=
int monolith_dict_find(monolith_dict *d,
                       monolith_dict_entry **pent,
                       const char *name,
                       unsigned int len)
{
    unsigned int listpos;
    monolith_dict_entrylist *el;
    monolith_dict_entry *ent;
    unsigned int i;

    listpos = dict_hash(name, len, MONOLITH_DICT_SIZE);
    el = &d->lists[listpos];
    ent = el->head;
    for(i = 0; i < el->size; i++) {
        if(len == ent->len) {
            if(!strncmp(name, ent->key, len)) {
                if (pent != NULL) *pent = ent;
                return MONOLITH_OK;
            }
        }
        ent = ent->nxt;
    }

    return MONOLITH_NOTFOUND;
}

11.3.3. Entry Struct

The atomic element of a dictionary is an entry. A page entry is a wrapper around a monolith page, which makes it ready for a linked list.

In addition, an entry has a corresponding key value and length, which is stored in a char pointer.

Page entries are managed inside of a list known as an entry list, known as the monolith_dict_entry_list.

<<monolith_dict_typedefs>>=
typedef struct monolith_dict_entry monolith_dict_entry;
<<monolith_dict_entry_struct>>=
struct monolith_dict_entry {
    void *ud;
    int type;
    char *key;
    unsigned int len;
    monolith_dict_entry *nxt;
    monolith_dict_entry *prv;
    void (*clean)(monolith_dict_entry *);
};
<<macros>>=
enum {
    MONOLITH_ENTRY_NONE,
    MONOLITH_ENTRY_PAGE,
    MONOLITH_ENTRY_CHAN,
    MONOLITH_ENTRY_IMAGE,
    MONOLITH_ENTRY_FTBL
};
<<function_declarations>>=
const char *monolith_dict_entry_key(monolith_dict_entry *ent);
<<functions>>=
const char *monolith_dict_entry_key(monolith_dict_entry *ent)
{
    return ent->key;
}
<<function_declarations>>=
monolith_page * monolith_dict_entry_page(monolith_dict_entry *ent);
<<functions>>=
monolith_page * monolith_dict_entry_page(monolith_dict_entry *ent)
{
    /* TODO: type checking */
    return (monolith_page *)ent->ud;
}

The generic version of monolith_dict_entry_page.

<<function_declarations>>=
void * monolith_dict_entry_data(monolith_dict_entry *ent);
<<functions>>=
void * monolith_dict_entry_data(monolith_dict_entry *ent)
{
    return ent->ud;
}
<<function_declarations>>=
void monolith_dict_entry_data_set(
    monolith_dict_entry *ent,
    void *ud);
<<functions>>=
void monolith_dict_entry_data_set(
    monolith_dict_entry *ent,
    void *ud)
{
    ent->ud = ud;
}

A custom clean-up allocator can be used for custom types. Set it with monolith_dict_entry_clean.

<<function_declarations>>=
void monolith_dict_entry_clean(
    monolith_dict_entry *ent,
    void (*cleanup)(monolith_dict_entry *)
);
<<functions>>=
void monolith_dict_entry_clean(
    monolith_dict_entry *ent,
    void (*clean)(monolith_dict_entry *))
{
    ent->clean = clean;
}

11.3.4. Entry List

A list of entries is known as an entry list. This is a linked list structure. These lists are used to build a monolith structure. This is known as a monome_dict_entrylist.

11.3.4.1. Entry List Struct

<<monolith_dict_typedefs>>=
typedef struct monolith_dict_entrylist monolith_dict_entrylist;
<<monolith_dict_entrylist_struct>>=
struct monolith_dict_entrylist {
    monolith_dict_entry *head;
    monolith_dict_entry *tail;
    unsigned int size;
};
11.3.4.2. Entry List Initialization

An entry list is initialized with the function monolith_dict_entrylist_init.

<<function_declarations>>=
void monolith_dict_entrylist_init(monolith_dict_entrylist *e);
<<functions>>=
void monolith_dict_entrylist_init(monolith_dict_entrylist *e)
{
    e->head = NULL;
    e->tail = NULL;
    e->size = 0;
}
11.3.4.3. Entry List Cleanup

An entry list is freed with the function monolith_dict_entrylist_free.

<<function_declarations>>=
void monolith_dict_entrylist_free(monolith_dict_entrylist *el);
<<functions>>=
void monolith_dict_entrylist_free(monolith_dict_entrylist *el)
{
    unsigned int i;
    monolith_dict_entry *ent;
    monolith_dict_entry *nxt;
    monolith_page *pg;

    ent = el->head;

    for(i = 0; i < el->size; i++) {
        nxt = ent->nxt;
        if (ent->type == MONOLITH_ENTRY_PAGE) {
            pg = ent->ud;
            monolith_page_free(pg);
            free(ent->ud);
        } else if (ent->type == MONOLITH_ENTRY_IMAGE) {
            monolith_gfx_img *img;
            img = ent->ud;
            monolith_gfx_img_free(&img);
        } else if (ent->type == MONOLITH_ENTRY_FTBL) {
            sp_ftbl *ft;
            ft = ent->ud;
            sp_ftbl_destroy(&ft);
        } else if (ent->clean != NULL) {
            ent->clean(ent);
        }
        free(ent->key);
        free(ent);
        ent = nxt;
    }
}
11.3.4.4. Appending an Entry to an Entry-List

An entry can be appended to an entry-list with the function monolith_dict_entrylist_append.

<<function_declarations>>=
void monolith_dict_entrylist_append(monolith_dict_entrylist *el,
                                    monolith_dict_entry *ent);
<<functions>>=
void monolith_dict_entrylist_append(monolith_dict_entrylist *el,
                                    monolith_dict_entry *ent)
{
    if(el->size == 0) {
        el->head = ent;
    }
    if(el->tail != NULL) {
        el->tail->nxt = ent;
    }
    ent->prv = el->tail;
    el->tail = ent;
    el->size++;
}

11.3.5. Dictionary Struct Initialization and Cleanup

A page dictionary is declared as the C struct monolith_dict.

<<monolith_dict_typedefs>>=
typedef struct monolith_dict monolith_dict;
<<monolith_dict_struct>>=
<<monolith_dict_entry_struct>>
<<monolith_dict_entrylist_struct>>
struct monolith_dict {
<<monolith_dict_contents>>
};

The monolith dictionary data is initialized with the function monolith_dict_init. This function willallocate memory, so it should only be called once before runtime.

<<function_declarations>>=
void monolith_dict_init(monolith_d *m, monolith_dict *d);
<<functions>>=
void monolith_dict_init(monolith_d *m, monolith_dict *d)
{
    int i;
<<monolith_dict_init>>
}

All memory allocated by monolith_dict_init is freed with the function monolith_dict_free. This should be called once during cleanup.

<<function_declarations>>=
void monolith_dict_cleanup(monolith_dict *d);
<<functions>>=
void monolith_dict_cleanup(monolith_dict *d)
{
    int i;
<<monolith_dict_cleanup>>
}

11.3.6. Dictionary in Main Struct

A single instance of a dictionary is declared in the main struct.

<<struct_contents>>=
monolith_dict dict;

It can be accessed from the opaque pointer using the function monolith_dict_get.

<<function_declarations>>=
monolith_dict * monolith_dict_get(monolith_d *m);
<<functions>>=
monolith_dict * monolith_dict_get(monolith_d *m)
{
    return &m->dict;
}
<<init>>=
monolith_dict_init(m, &m->dict);

It is allocated initialized in the top-level init function, and freed in the top level free function.

<<cleanup>>=
monolith_dict_cleanup(&m->dict);

11.3.7. Dictionary Hash

The dictionary uses the static function dict_hash to hash a string, and figure out which entry list to place it in.

<<static_function_declarations>>=
static unsigned int dict_hash(const char *str,
                              unsigned int len,
                              unsigned int nlists);
<<functions>>=
static unsigned int dict_hash(const char *str,
                              unsigned int len,
                              unsigned int nlists)
{
    unsigned long h;
    unsigned int i;

    h = 5381;
    for(i = 0; i < len; i++) {
        h = ((h << 5) + h) ^ str[i];
        h %= 0x7FFFFFFF;
    }

    return h % nlists;
}

11.3.8. Dictionary Contents

11.3.8.1. Monolith Data Reference

A reference to the top-level monolith data is stored inside the dictionary.

<<monolith_dict_contents>>=
monolith_d *m;
<<monolith_dict_init>>=
d->m = m;
11.3.8.2. Number of Types

The ntypes variable keeps track of how many page types there are.

<<monolith_dict_contents>>=
unsigned int ntypes;

There are of course zero types to begin with because there are no pages.

<<monolith_dict_init>>=
d->ntypes = 0;
11.3.8.3. Number of Words

The total number of words in the dictionary is stored in a variable called nwords.

<<monolith_dict_contents>>=
unsigned int nwords;
<<monolith_dict_init>>=
d->nwords = 0;
11.3.8.4. Entry Lists

<<macros>>=
#ifndef MONOLITH_DICT_SIZE
#define MONOLITH_DICT_SIZE 32
#endif
<<monolith_dict_contents>>=
monolith_dict_entrylist lists[MONOLITH_DICT_SIZE];
<<monolith_dict_init>>=
for(i = 0; i < MONOLITH_DICT_SIZE; i++) {
    monolith_dict_entrylist_init(&d->lists[i]);
}
<<monolith_dict_cleanup>>=
for(i = 0; i < MONOLITH_DICT_SIZE; i++) {
    monolith_dict_entrylist_free(&d->lists[i]);
}

11.3.9. Type Handling

A new type is acquired with the function monolith_dict_newtype. This will return an integer containing a unique numeric ID.

<<function_declarations>>=
unsigned int monolith_dict_newtype(monolith_dict *d);

All this function does is increment the type counter and return that value. This is all the logic that is needed here. Pages themselves must figure out a way to keep track of their type ID and get assigned a type ID at runtime.

<<functions>>=
unsigned int monolith_dict_newtype(monolith_dict *d)
{
    d->ntypes++;
    return d->ntypes;
}

11.3.10. Page Entry Creation

The function monolith_dict_newpage is used to create a page with a particular key. This page can then be passed into a particular page function to be created. The page will be stored in the page pointer provided. If the key already exists, an error will be returned. Every page will have an associated keyword associated with it, supplied by name and the corresponding key length (excluding NULL terminator) len.

The function will return TRUE (1) on success and FALSE (0) on failure.

<<function_declarations>>=
int monolith_dict_newpage(monolith_dict *d,
                          monolith_page **ppg,
                          const char *name,
                          unsigned int len);

A note that two memory allocations here: one for the entry itself, and another for key inside the entry.

<<functions>>=
int monolith_dict_newpage(monolith_dict *d,
                          monolith_page **ppg,
                          const char *name,
                          unsigned int len)
{
    int rc;
    monolith_page *pg;
    monolith_dict_entry *ent;

    rc = monolith_dict_newentry(d, &ent, name, len);

    if (rc != MONOLITH_OK) return 0;

    pg = calloc(1, sizeof(monolith_page));
    monolith_page_init(d->m, pg);
    (*ppg) = pg;
    ent->ud = pg;
    ent->type = MONOLITH_ENTRY_PAGE;

    return 1;
}

11.3.11. Generic Entry Creation

Like monolith_dict_newpage, only for generic entries. Eventually will be refactored into that function. For now, it is needed for creating an image entry.

<<function_declarations>>=
int monolith_dict_newentry(monolith_dict *d,
                           monolith_dict_entry **pent,
                           const char *name,
                           unsigned int len);
<<functions>>=
int monolith_dict_newentry(monolith_dict *d,
                           monolith_dict_entry **pent,
                           const char *name,
                           unsigned int len)
{
    monolith_dict_entry *ent;
    unsigned int i;
    unsigned int listpos;
    monolith_dict_entrylist *el;

    if(monolith_dict_lookup(d, NULL, name, len)) {
        *pent = NULL;
        return MONOLITH_NOTOK;
    }

    ent = calloc(1, sizeof(monolith_dict_entry));
    if(ent == NULL) return 0;
    ent->key = calloc(1, len + 1);
    ent->len = len;
    ent->type = MONOLITH_ENTRY_NONE;
    ent->clean = NULL;

    for(i = 0; i < len; i++) {
        ent->key[i] = name[i];
    }

    *pent = ent;
    ent->ud = NULL;
    ent->nxt = NULL;
    ent->prv = NULL;
    listpos = dict_hash(name, len, MONOLITH_DICT_SIZE);
    el = &d->lists[listpos];
    monolith_dict_entrylist_append(el, ent);
    return MONOLITH_OK;
}

11.3.12. TODO Erasing an Entry

The mechanics of removing a page are a ways away, but they will be implemented...

Eventually, this function will remove an from the dictionary and free the page associated with it.

11.4. Page Struct Declaration

A monolith page is declared as type monolith_page.

<<typedefs>>=
typedef struct monolith_page monolith_page;
<<monolith_dict_typedefs>>
<<structs>>=
struct monolith_page {
<<monolith_page_contents>>
};
<<monolith_dict_struct>>

The monolith page struct is initialized with the function monolith_page_init.

<<function_declarations>>=
void monolith_page_init(monolith_d *m, monolith_page *p);
<<functions>>=
void monolith_page_init(monolith_d *m, monolith_page *p)
{
<<monolith_page_init>>
}

11.5. User Data

All state information is handled inside of a void pointer.

<<monolith_page_contents>>=
void *ud;
<<monolith_page_init>>=
p->ud = NULL;

Data can be set using the function monolith_page_data_set, and retrieved using monolith_page_data_get.

<<function_declarations>>=
void* monolith_page_data_get(monolith_page *pg);
void monolith_page_data_set(monolith_page *pg, void *ud);
<<functions>>=
void* monolith_page_data_get(monolith_page *pg)
{
    return pg->ud;
}

void monolith_page_data_set(monolith_page *pg, void *ud)
{
    pg->ud = ud;
}

11.6. Monolith Top-level data

A copy of the monolith data is stored in page struct. While top-heavy, this is awfully convenient for now.

<<monolith_page_contents>>=
monolith_d *m;
<<monolith_page_init>>=
p->m = m;

This can be retrieved using the function monolith_page_monolith.

<<function_declarations>>=
monolith_d * monolith_page_monolith(monolith_page *pg);
<<functions>>=
monolith_d * monolith_page_monolith(monolith_page *pg)
{
    return pg->m;
}

11.7. Type Information

The page type is stored inside of an integer called type. Nodes requiring particular page types will use this flag to validate that yes indeed that page is being given.

<<monolith_page_contents>>=
unsigned int type;
<<monolith_page_init>>=
p->type = 0;

This type variable can be set/get with the functions monolith_page_type_set and monolith_page_type_get, respectively.

<<function_declarations>>=
void monolith_page_type_set(monolith_page *pg, unsigned int type);
unsigned int monolith_page_type_get(monolith_page *pg);
<<functions>>=
void monolith_page_type_set(monolith_page *pg, unsigned int type)
{
    pg->type = type;
}
unsigned int monolith_page_type_get(monolith_page *pg)
{
    return pg->type;
}

In previous things I've done, I've managed types using an enum. While convenient and easy, this makes things more centralized. I'd like to try to try a different approach this time around, that would make it so pages could be more "standalone".

11.8. Page Callbacks

A page is can be thought of as an interface made up of little callbacks which touch a various peripherals. By default, they are all set to be NULL. All parts of the program which call page callbacks must check for NULL first!

11.8.1. Page Open

This callback gets called anytime a page is opened. This is where you'd recall a monome state, for instance.

<<monolith_page_contents>>=
void (*open)(monolith_page *);
<<monolith_page_init>>=
p->open = NULL;

The monolith open callback is set with the function monolith_page_open_set.

<<function_declarations>>=
void monolith_page_open_set(monolith_page *p, void (*open)(monolith_page *));
<<functions>>=
void monolith_page_open_set(monolith_page *p, void (*open)(monolith_page *))
{
    p->open = open;
}

It is called with the function monolith_page_open.

<<function_declarations>>=
void monolith_page_open(monolith_page *pg);
<<functions>>=
void monolith_page_open(monolith_page *pg)
{
    if(pg != NULL) {
        if(pg->open != NULL) pg->open(pg);
    }
}

11.8.2. Page Close

This callback gets called anytime a page is closed. Not sure what you'd be doing here, but it seems useful.

<<monolith_page_contents>>=
void (*close)(monolith_page *);
<<monolith_page_init>>=
p->close = NULL;

The monolith close callback is set with the function monolith_page_close_set.

<<function_declarations>>=
void monolith_page_close_set(monolith_page *p, void (*close)(monolith_page *));
<<functions>>=
void monolith_page_close_set(monolith_page *p, void (*close)(monolith_page *))
{
    p->close = close;
}

The is function can be called with the function monolith_page_close.

<<function_declarations>>=
void monolith_page_close(monolith_page *pg);
<<functions>>=
void monolith_page_close(monolith_page *pg)
{
    if(pg != NULL) {
        if(pg->close != NULL) pg->close(pg);
    }
}

11.8.3. Page Monome key

Anytime a monome key gets pressed, this function is called. This function has arguments for the XY position, as well as state (x, y, s).

<<monolith_page_contents>>=
void (*press)(monolith_page *,int,int,int);
<<monolith_page_init>>=
p->press = NULL;

The page press callback is set with the function monolith_page_press_set.

<<function_declarations>>=
void monolith_page_press_set(monolith_page *p,
                             void (*press)(monolith_page *,int,int,int));
<<functions>>=
void monolith_page_press_set(monolith_page *p,
                             void (*press)(monolith_page *,int,int,int))
{
    p->press = press;
}

It is called with the function monolith_page_press.

<<function_declarations>>=
void monolith_page_press(monolith_page *pg, int x, int y, int s);
<<functions>>=
void monolith_page_press(monolith_page *pg, int x, int y, int s)
{
    if(pg != NULL) {
        if(pg->press != NULL) pg->press(pg, x, y, s);
    }
}

11.8.4. Page Griffin Turn

Anytime the Griffin turns, this function is called. This function has an integer indicating the direction it is turning in.

<<monolith_page_contents>>=
void (*turn)(monolith_page *,int);
<<monolith_page_init>>=
p->turn = NULL;

The page turn callback is set with the function monolith_page_turn_set.

<<function_declarations>>=
void monolith_page_turn_set(monolith_page *p,
                             void (*turn)(monolith_page *,int));
<<functions>>=
void monolith_page_turn_set(monolith_page *p,
                             void (*turn)(monolith_page *,int))
{
    p->turn = turn;
}

It can be called with monolith_page_turn.

<<function_declarations>>=
void monolith_page_turn(monolith_page *pg, int s);
<<functions>>=
void monolith_page_turn(monolith_page *pg, int s)
{
    if(pg != NULL) {
        if(pg->turn != NULL) pg->turn(pg, s);
    }
}

11.8.5. Page Griffin Push

Anytime the Griffin gets pushed down, this function is called. This function has a single integer passed to it, indicating state.

<<monolith_page_contents>>=
void (*push)(monolith_page *,int);
<<monolith_page_init>>=
p->push = NULL;

The page push callback is set with the function monolith_page_push_set.

<<function_declarations>>=
void monolith_page_push_set(monolith_page *p,
                             void (*push)(monolith_page *,int));
<<functions>>=
void monolith_page_push_set(monolith_page *p,
                             void (*push)(monolith_page *,int))
{
    p->push = push;
}

It can be called with the function monolith_page_push.

<<function_declarations>>=
void monolith_page_push(monolith_page *pg, int s);
<<functions>>=
void monolith_page_push(monolith_page *pg, int s)
{
    if(pg != NULL) {
        if(pg->push != NULL) pg->push(pg, s);
    }
}

11.8.6. Page Arc Delta

Any time an arc turns, the delta callback gets called.

<<monolith_page_contents>>=
void (*delta)(monolith_page *,int,int);
<<monolith_page_init>>=
p->delta = NULL;

The page press callback is set with the function monolith_page_press_set.

<<function_declarations>>=
void monolith_page_delta_set(monolith_page *p,
                             void (*delta)(monolith_page *,int,int));
<<functions>>=
void monolith_page_delta_set(monolith_page *p,
                             void (*delta)(monolith_page *,int,int))
{
    p->delta = delta;
}

It is called with the function monolith_page_press.

<<function_declarations>>=
void monolith_page_delta(monolith_page *pg, int n, int delta);
<<functions>>=
void monolith_page_delta(monolith_page *pg, int n, int delta)
{
    if(pg != NULL) {
        if(pg->delta != NULL) pg->delta(pg, n, delta);
    }
}

11.8.7. Page Free

When a page is freed, this function is called. Any user data allocated should be freed here.

<<monolith_page_contents>>=
void (*free)(monolith_page *);
<<monolith_page_init>>=
p->free = NULL;

The page free callback is set with the function monolith_page_free_set.

<<function_declarations>>=
void monolith_page_free_set(monolith_page *p,
                             void (*free)(monolith_page *));
<<functions>>=
void monolith_page_free_set(monolith_page *p,
                             void (*free)(monolith_page *))
{
    p->free = free;
}

It is called with the function monolith_page_free.

<<function_declarations>>=
void monolith_page_free(monolith_page *pg);
<<functions>>=
void monolith_page_free(monolith_page *pg)
{
    if(pg != NULL) {
        if(pg->free != NULL) pg->free(pg);
    }
}

11.8.8. Page State Callbacks

Page states can be loaded and saved from disk via using the state interface. More information can be found in the section State** 11.8.8.1. Page Save Page state data is saved with the save callback. The callback, in addition to having the monolith page data, has a pointer to the monolith state parameter, as well as a unique keyword to write it to.

<<callback_prototypes>>=
typedef int (*monolith_statefun)(monolith_page *, monolith_state*, const char*, unsigned int);
<<monolith_page_contents>>=
monolith_statefun save;
<<monolith_page_init>>=
p->save = NULL;

The page save callback is set with the function monolith_page_free_set.

<<function_declarations>>=
void monolith_page_save_set(monolith_page *p, monolith_statefun save);
<<functions>>=
void monolith_page_save_set(monolith_page *p, monolith_statefun save)
{
    p->save = save;
}

It is called with the function monolith_page_save.

<<function_declarations>>=
void monolith_page_save(monolith_page *pg,
                        monolith_state *ms,
                        const char *key,
                        unsigned int len);
<<functions>>=
void monolith_page_save(monolith_page *pg,
                        monolith_state *ms,
                        const char *key,
                        unsigned int len)
{
    if(pg != NULL) {
        if(pg->save != NULL) pg->save(pg, ms, key, len);
    }
}
11.8.8.2. Page Load

Page state data is saved with the load callback.

<<monolith_page_contents>>=
monolith_statefun load;
<<monolith_page_init>>=
p->load = NULL;

The page save callback is set with the function monolith_page_free_set.

<<function_declarations>>=
void monolith_page_load_set(monolith_page *p, monolith_statefun load);
<<functions>>=
void monolith_page_load_set(monolith_page *p, monolith_statefun load)
{
    p->load = load;
}

It is called with the function monolith_page_save.

<<function_declarations>>=
void monolith_page_load(monolith_page *pg,
                        monolith_state *ms,
                        const char *key,
                        unsigned int len);
<<functions>>=
void monolith_page_load(monolith_page *pg,
                        monolith_state *ms,
                        const char *key,
                        unsigned int len)
{
    if(pg != NULL) {
        if(pg->load != NULL) pg->load(pg, ms, key, len);
    }
}

11.9. Loading/Saving Page State

11.9.1. Saving Page Data

11.9.1.1. Saving Page Data in C

A page in scheme can be looked up and saved with the function monolith_page_find_and_save.

<<function_declarations>>=
int monolith_page_find_and_save(monolith_d *m,
                                const char *pgname,
                                unsigned int pglen,
                                const char *key,
                                unsigned int keylen);
<<functions>>=
int monolith_page_find_and_save(monolith_d *m,
                                const char *pgname,
                                unsigned int pglen,
                                const char *key,
                                unsigned int keylen)
{
    monolith_dict *dict;
    monolith_page *pg;
    int rc;

    dict = monolith_dict_get(m);
    rc = monolith_dict_lookup(dict, &pg, pgname, pglen);
    if(!rc) return 0;
    monolith_page_save(pg, &m->state, key, keylen);
    return 1;
}
11.9.1.2. DONE Saving Page Data in Scheme

CLOSED: [2019-01-19 Sat 16:49] Page data can be saved in scheme using the function monolith:page-save.

<<primitive_entries>>=
{"monolith:page-save", pp_page_save, 2, 2, {STR,STR,___}},
<<scheme_functions>>=
static cell pp_page_save(cell p) {
    monolith_d *m;
    const char *pgname;
    unsigned int pglen;
    const char *key;
    unsigned int keylen;

    pgname = string(car(p));
    pglen = strlen(pgname);
    p = cdr(p);
    key = string(car(p));
    keylen = strlen(key);
    m = monolith_data_get();
    if(m->state.db == NULL) {
        return error("State not open. Could not save page.", car(p));
    }
    if(m != NULL) monolith_page_find_and_save(m, pgname, pglen, key, keylen);
    return UNSPECIFIC;
}
11.9.1.3. Saving Page Data in Janet

Done with monolith/page-save.

<<core_janet_entries>>=
{
"monolith/page-save",
j_page_save,
"Saves a page.\n"
},
<<janet_functions>>=
static Janet j_page_save(int32_t argc, Janet *argv)
{
    monolith_d *m;
    const char *pgname;
    unsigned int pglen;
    const char *key;
    unsigned int keylen;
    monolith_state *state;

    janet_fixarity(argc, 2);

    pgname = (const char *)janet_getstring(argv, 0);
    pglen = strlen(pgname);

    key = (const char *)janet_getstring(argv, 1);
    keylen = strlen(key);

    m = monolith_data_get();

    state = monolith_state_get(m);

    if (monolith_state_empty(state)) {
        fprintf(stderr,
                "State not open. Could not save page.");
        return janet_wrap_nil();
    }

    if (m != NULL) {
        monolith_page_find_and_save(m,
                                    pgname, pglen,
                                    key, keylen);
    }

    return janet_wrap_nil();
}

11.9.2. Loading Page Data

11.9.2.1. Loading Page Data in C

A page in scheme can be looked up and loaded with the function monolith_page_find_and_load.

<<function_declarations>>=
int monolith_page_find_and_load(monolith_d *m,
                                const char *pgname,
                                unsigned int pglen,
                                const char *key,
                                unsigned int keylen);
<<functions>>=
int monolith_page_find_and_load(monolith_d *m,
                                const char *pgname,
                                unsigned int pglen,
                                const char *key,
                                unsigned int keylen)
{
    monolith_dict *dict;
    monolith_page *pg;
    int rc;

    dict = monolith_dict_get(m);
    rc = monolith_dict_lookup(dict, &pg, pgname, pglen);
    if(!rc) return 0;
    monolith_page_load(pg, &m->state, key, keylen);
    return 1;
}
11.9.2.2. Loading Page Data in Scheme

Page data can be saved in scheme using the function monolith:page-save.

<<primitive_entries>>=
{"monolith:page-load", pp_page_load, 2, 2, {STR,STR,___}},
<<scheme_functions>>=
static cell pp_page_load(cell p) {
    monolith_d *m;
    const char *pgname;
    unsigned int pglen;
    const char *key;
    unsigned int keylen;

    pgname = string(car(p));
    pglen = strlen(pgname);
    p = cdr(p);
    key = string(car(p));
    keylen = strlen(key);
    m = monolith_data_get();
    if(m->state.db == NULL) {
        return error("State not open. Could not save page.", car(p));
    }
    if(m != NULL) monolith_page_find_and_load(m, pgname, pglen, key, keylen);
    return UNSPECIFIC;
}
11.9.2.3. Loading Page Data in Janet

AKA monolith/page-load.

<<core_janet_entries>>=
{
"monolith/page-load",
j_page_load,
"Loads a page.\n"
},
<<janet_functions>>=
static Janet j_page_load(int32_t argc, Janet *argv)
{
    monolith_d *m;
    const char *pgname;
    unsigned int pglen;
    const char *key;
    unsigned int keylen;
    monolith_state *state;

    janet_fixarity(argc, 2);

    pgname = (const char *)janet_getstring(argv, 0);
    pglen = strlen(pgname);

    key = (const char *)janet_getstring(argv, 1);
    keylen = strlen(key);

    m = monolith_data_get();
    state = monolith_state_get(m);


    if (monolith_state_empty(state)) {
        fprintf(stderr, "State not open. Could not save page.");
        return janet_wrap_nil();
    }

    if (m != NULL) {
        monolith_page_find_and_load(m,
                                    pgname, pglen,
                                    key, keylen);
    }
    return janet_wrap_nil();
}

11.10. Page Pin

An integer counter keeps track of how many processes are accessing the page at a given point. When a page is non-zero, it is said to be "pinned" down. When a page is pinned down, it is cannot be destroyed.

When something intends to use a particular page, it must be pin it down. When this happens, the counter is incremented by one.

When a page is done using a page, it will "unpin" the page. When this happens, the counter goes down by one.

A page can be checked if it is pinned down or checking if the counter is non-zero.

11.11. Monome Page State

Most pages will want to utilize the monome. This small interface provides a layer to access monome interfaces while also providing the ability to save/recall grid states.

11.11.1. Monome Page State Data

11.11.1.1. Struct Declarations

Monome page state data is defined in a struct called monolith_page_mstate.

<<typedefs>>=
typedef struct monolith_page_mstate monolith_page_mstate;

Internally, there isn't a great deal of monome page state data needed. All that is really required is a pointer to the monome virtual interface, and an array of bytes, whose bits store the grid data. The monome grid that I am using is a 16x8 grid, which means that 128 bits are needed. This yields 16 bytes (look at those dimmensions and it makes sense).

<<structs>>=
struct monolith_page_mstate {
    monolith_monome_d *vmonome;
    unsigned char data[16];
    monolith_page *pg;
};
11.11.1.2. Struct Initialization

The monolith page data is initialized with the function monolith_page_mstate_init.

<<function_declarations>>=
void monolith_page_mstate_init(monolith_page *p, monolith_page_mstate *ms);
<<functions>>=
void monolith_page_mstate_init(monolith_page *p, monolith_page_mstate *ms)
{
    int i;
    ms->vmonome = &p->m->vmonome;
    for(i = 0; i < 16; i++) ms->data[i] = 0;
    ms->pg = p;
}
11.11.1.3. Struct Creation/Destruction

These are opaque structs which must be manually allocated and freed by the page. This is done with the functions monolith_page_mstate_new and monolith_page_mstate_free, respectively.

<<function_declarations>>=
int monolith_page_mstate_new(monolith_page *pg, monolith_page_mstate **ms);
int monolith_page_mstate_free(monolith_page_mstate **ms);
<<functions>>=
int monolith_page_mstate_new(monolith_page *pg, monolith_page_mstate **ms)
{
    monolith_page_mstate *pms;

    pms = calloc(1, sizeof(monolith_page_mstate));

    if(pms == NULL) return 0;

    monolith_page_mstate_init(pg, pms);
    *ms = pms;
    return 1;
}

int monolith_page_mstate_free(monolith_page_mstate **ms)
{
    free(*ms);
    return 1;
}
11.11.1.4. State Data Getter

The state data can be aqcuired as an unsigned short array with the function. monolith_page_mstate_data_get.

<<function_declarations>>=
unsigned short * monolith_page_mstate_data_get(monolith_page_mstate *ms);
<<functions>>=
unsigned short * monolith_page_mstate_data_get(monolith_page_mstate *ms)
{
    return (unsigned short *)ms->data;
}

11.11.2. Monome Interface Wrapper

The following functions are used as wrappers around the virtual monome interface. In addition to sending messages to the virtual monome interface, it will also write the changes inside of the supplied bits.

11.11.2.1. Wrapper for led_set

A single LED can be set using the function monolith_page_mstate_led_set.

<<function_declarations>>=
void monolith_page_mstate_led_set(monolith_page_mstate *ms,
                                  int x, int y, int s);
<<functions>>=
void monolith_page_mstate_led_set(monolith_page_mstate *ms,
                                  int x, int y, int s)
{
    unsigned short *g;
    g = (unsigned short *)ms->data;
    if(s) {
        g[y] |= 1 << x;
    } else {
        g[y] &= ~(1 << x);
    }
    if(monolith_page_mstate_selected(ms)) {
        monolith_monome_led_set(ms->vmonome, x, y, s);
    }
}
11.11.2.2. Wrapper led_row

To be honest, I'm not exactly sure how led_row fully works, and I haven't bothered to really figure it out. For now, the X axis is split up into bytes. If the X is 255, it is the upper bytes, otherwise the lower bytes. This is how it seems to work with the monome interface, and I'm just going to live with it for now.

<<function_declarations>>=
void monolith_page_mstate_led_row(monolith_page_mstate *ms,
                                  int x, int y, int s);
<<functions>>=
void monolith_page_mstate_led_row(monolith_page_mstate *ms,
                                  int x, int y, int s)
{
    unsigned short *g;
    g = (unsigned short *)ms->data;
    if(x == 0xff) {
        g[y] &= 0xff; /* keep lower bits, zero out upper bits */
        g[y] |= (s << 8);
    } else {
        /* set lower bits without disrupting upper bits*/
        g[y] = (s & 0xff) | (g[y] & 0xff00);
    }
    if(monolith_page_mstate_selected(ms)) {
        monolith_monome_led_row(ms->vmonome, x, y, s);
    }
}
11.11.2.3. Wrapper led_row16

This wraps the row16 function... instead of needing to split up a row into two 8-bit bytes, it stores it all in a short.

<<function_declarations>>=
void monolith_page_mstate_led_row16(monolith_page_mstate *ms,
                                    int row,
                                    unsigned short s);
<<functions>>=
void monolith_page_mstate_led_row16(monolith_page_mstate *ms,
                                    int row,
                                    unsigned short s)
{
    unsigned short *g;
    g = (unsigned short *)ms->data;
    g[row] = s;
    if(monolith_page_mstate_selected(ms)) {
        monolith_monome_led_row16(ms->vmonome, row, s);
    }
}
11.11.2.4. Wrapper led_col

<<function_declarations>>=
void monolith_page_mstate_led_col(monolith_page_mstate *ms,
                                  int x, int y, int s);
<<functions>>=
void monolith_page_mstate_led_col(monolith_page_mstate *ms,
                                  int x, int y, int s)
{
    unsigned short *g;
    unsigned short tmp;
    int c;
    unsigned char bit;

    g = (unsigned short *)ms->data;

    for (c = 0; c < 8; c++) {
        tmp = g[c];
        bit = (s & (1 << c)) != 0;
        if (bit) tmp |= 1 << x;
        else tmp &= ~(1 << x);
        g[c] = tmp;
    }

    if(monolith_page_mstate_selected(ms)) {
        monolith_monome_led_col(ms->vmonome,
                                x, y,
                                s);
    }
}

11.11.3. Monome Page State Recall

The for the monome is recalled with the function monolith_page_mstate_recall. This will apply the state stored to the monome. This function is expected to be called anytime the page is opened or re-opened.

<<function_declarations>>=
void monolith_page_mstate_recall(monolith_page_mstate *ms);
<<functions>>=
void monolith_page_mstate_recall(monolith_page_mstate *ms)
{
    int y;
    int x;
    int c;
    unsigned char *g;
    g = ms->data;
    c = 0;
    for(y = 0; y < 8; y++) {
        for(x = 0; x < 2; x++) {
            if(x == 0) {
                monolith_monome_led_row(ms->vmonome, 0, y, g[c]);
            } else {
                monolith_monome_led_row(ms->vmonome, 255, y, g[c]);
            }
            c++;
        }
    }
}

11.11.4. Setting/Getting XY value state

The state of a particular position can be read and written to with the function monolith_page_mstate_get and monolith_page_mstate_set, respectively.

<<function_declarations>>=
int monolith_page_mstate_get(monolith_page_mstate *ms, int x, int y);
<<functions>>=
int monolith_page_mstate_get(monolith_page_mstate *ms, int x, int y)
{
    unsigned short *g;
    g = (unsigned short *)ms->data;
    return (g[y] & (1 << x)) != 0;
}

monolith_page_mstate_set is just a wrapper around the function monolith_page_mstate_led_set.

<<function_declarations>>=
void monolith_page_mstate_set(monolith_page_mstate *ms, int x, int y, int s);
<<functions>>=
void monolith_page_mstate_set(monolith_page_mstate *ms, int x, int y, int s)
{
    monolith_page_mstate_led_set(ms, x, y, s);
}

11.11.5. Checking if Monome State is actively selected

The monome state has the ability to indirectly access the monome LED panel. This allows pages to simultaneously manipulate the actual LEDs while storing the grid state for recall later. If the page that the monome is is attached to is not currentely active, it shouldn't be able to turn on LED lights.

To check of the monome state is actively selected, the function monolith_page_mstate_selected. This will compare the pointer address of the page (stored internally) with the currently selected monolith page via the function monolith_page_selected.

<<function_declarations>>=
int monolith_page_mstate_selected(monolith_page_mstate *ms);
<<functions>>=
int monolith_page_mstate_selected(monolith_page_mstate *ms)
{
    return monolith_page_selected(ms->pg);
}

11.12. Arc Page State

If variable brightness is not needed, Arc LED state can be manipulated using the monolith_page_arcstate struct. This will store the LED encoder states for each of the four encoders in a memory efficient way.

11.12.1. Arc Page State Data

11.12.1.1. Struct Declarations

Arc page state data is defined in a struct called monolith_page_arcstate.

<<typedefs>>=
typedef struct monolith_page_arcstate monolith_page_arcstate;

Because vari-brightness is discarded, each encoder can be stored in 8 bytes of data instead of 64 bytes of data. Each encoder state is stored inside of a local struct called arc_encoder_state.

<<structs>>=
typedef struct {
    unsigned char state[8];
} arc_encoder_state;

struct monolith_page_arcstate {
    monolith_arc_d *varc;
    arc_encoder_state encoder[4];
    monolith_page *pg;
};
11.12.1.2. Struct Initialization

<<function_declarations>>=
void monolith_page_arcstate_init(monolith_page *p, monolith_page_arcstate *as);
<<functions>>=
void monolith_page_arcstate_init(monolith_page *p, monolith_page_arcstate *as)
{
    int i;
    as->varc = &p->m->varc;
    for(i = 0; i < 4; i++) arc_encoder_state_init(&as->encoder[i]);
    as->pg = p;
}

Arc encoders are each initialized using arc_encoder_state_init, a static function.

<<static_function_declarations>>=
static void arc_encoder_state_init(arc_encoder_state *enc);
<<functions>>=
static void arc_encoder_state_init(arc_encoder_state *enc)
{
    int i;
    for(i = 0; i < 8; i++) enc->state[i] = 0;
}
11.12.1.3. Struct Creation/Destruction

These are opaque structs which must be manually allocated and freed by the page. This is done with the functions monolith_page_arcstate_new and monolith_page_arcstate_free, respectively.

<<function_declarations>>=
int monolith_page_arcstate_new(monolith_page *pg, monolith_page_arcstate **as);
int monolith_page_arcstate_free(monolith_page_arcstate **as);
<<functions>>=
int monolith_page_arcstate_new(monolith_page *pg, monolith_page_arcstate **as)
{
    monolith_page_arcstate *pas;

    pas = calloc(1, sizeof(monolith_page_arcstate));

    if(pas == NULL) return 0;

    monolith_page_arcstate_init(pg, pas);
    *as = pas;
    return 1;
}

int monolith_page_arcstate_free(monolith_page_arcstate **as)
{
    free(*as);
    return 1;
}
11.12.1.4. State Data Getter/Setter

Data for each encoder can be retrieved using the function monolith_page_arcstate_get. The encoder number must be supplied. If the number is out of range, null will be returned.

<<function_declarations>>=
unsigned char * monolith_page_arcstate_data_get(monolith_page_arcstate *as,
                                                int n);
<<functions>>=
unsigned char * monolith_page_arcstate_data_get(monolith_page_arcstate *as,
                                                int n)
{
    if(n < 0 || n > 3) return NULL;
    return as->encoder[n].state;
}

Data can be set using the function monolith_page_arcstate_data_set. This function expects an unsigned char array of size 8. This is to be copied over to the internal arcstate data.

<<function_declarations>>=
void monolith_page_arcstate_data_set(monolith_page_arcstate *as,
                                     int n,
                                     unsigned char *data);
<<functions>>=
void monolith_page_arcstate_data_set(monolith_page_arcstate *as,
                                     int n,
                                     unsigned char *data)
{
    unsigned char *state;
    int i;
    if(n < 0 || n > 3) return;
    state = monolith_page_arcstate_data_get(as, n);

    for(i = 0; i < 8; i++) {
        state[i] = data[i];
    }
}

11.12.2. Arc Interface Wrapper

11.12.2.1. Wrapper for Map

The function monolith_page_arcstate_map will take in a bitmap size 64 (an array of 8 8-bit unsigned ints), and then set it to a knob n.

This function will also take the time to copy the mapped data into the internal state.

<<function_declarations>>=
void monolith_page_arcstate_map(monolith_page_arcstate *as,
                               int n, unsigned char *map);
<<functions>>=
static void map2arc(unsigned char *map, unsigned char *out)
{
    int i;
    unsigned char byte;
    int bytepos;
    for(i = 0; i < 64; i++) {
        byte = map[i / 8];
        bytepos = i % 8;
        if(byte & 1 << bytepos) {
            out[i] = 15;
        } else {
            out[i] = 0;
        }
    }
}

void monolith_page_arcstate_map(monolith_page_arcstate *as,
                               int n, unsigned char *map)
{
    unsigned char out[64];

    map2arc(map, out);

    monolith_page_arcstate_data_set(as, n, map);

    if(monolith_page_arcstate_selected(as)) {
        monolith_arc_map(as->varc, n, out);
    }
}

11.12.3. Checking if Arc is actively selected

<<function_declarations>>=
int monolith_page_arcstate_selected(monolith_page_arcstate *as);
<<functions>>=
int monolith_page_arcstate_selected(monolith_page_arcstate *as)
{
    return monolith_page_selected(as->pg);
}

11.12.4. Arc State Recall

Recalls arc based on internal state.

<<function_declarations>>=
void monolith_page_arcstate_recall(monolith_page_arcstate *as);
<<functions>>=
void monolith_page_arcstate_recall(monolith_page_arcstate *as)
{
    unsigned char out[64];
    unsigned char *state;
    int k;
    for(k = 0; k < 4; k++) {
        state = monolith_page_arcstate_data_get(as, k);
        map2arc(state, out);
        monolith_arc_map(as->varc, k, out);
    }
}

11.12.5. Map value to Arc

A common operation is to take a normalized floating point value and display it on one of the arcs. This does just that with monolith_arcstate_mapval.

<<function_declarations>>=
void monolith_arcstate_mapval(monolith_page_arcstate *as,
                              int pos,
                              GFFLT val);
<<functions>>=
void monolith_arcstate_mapval(monolith_page_arcstate *as,
                              int pos,
                              GFFLT val)
{
    uint32_t ring[2];
    int ringsize;

    if (val < 0) val = 0;
    else if (val > 1) val = 1;

    ringsize = round(val * 63); /* N - 1 */

    /* this is done so that 0.5 maps to exactly half */
    if (val != 0) ringsize++;

    if(ringsize == 0) {
        ring[0] = 0;
        ring[1] = 0;
    } else if(ringsize == 1) {
        ring[0] = 1;
        ring[1] = 0;
    } else if(ringsize < 32) {
        ring[0] = (1 << ringsize) - 1;
        ring[1] = 0;
    } else {
        ring[0] = 0xffffffff;
        ringsize -= 32;
        if(ringsize == 1) {
            ring[1] = 1;
        } else if(ringsize == 32) {
            ring[1] = 0xffffffff;
        } else {
            ring[1] = (1 << ringsize) - 1;
        }
    }
    monolith_page_arcstate_map(as,
                               pos,
                               (unsigned char *)ring);

}

11.13. Page Lookup from Runt

The task of looking for a particular page in the monolith dictionary is a common task. The process can be automated with the function runt_monolith_lookup_page. This function handles the dictionary lookup, type checking, and error messages.

<<function_declarations>>=
int runt_monolith_lookup_page(runt_vm *vm,
                              monolith_d *m,
                              const char *name,
                              const char *page,
                              int (*is_type)(monolith_page *),
                              monolith_page **pg);
<<functions>>=
int runt_monolith_lookup_page(runt_vm *vm,
                              monolith_d *m,
                              const char *name,
                              const char *page,
                              int (*is_type)(monolith_page *),
                              monolith_page **pg)
{
    int rc;
    rc = monolith_lookup_page(m, pg, name, strlen(name));
    if(!rc) {
        runt_print(vm, "Could not find page '%s'\n", name);
        return RUNT_NOT_OK;
    }
    if(!is_type(*pg)) {
        runt_print(vm, "Page '%s' is not a %s page\n", name, page);
        return RUNT_NOT_OK;
    }
    return RUNT_OK;
}

11.14. Retrieve Graforge from Page

This is a function which allows one to directly retrieve the gf_patch instance from a page.

<<function_declarations>>=
gf_patch * monolith_page_graforge(monolith_page *pg);
<<functions>>=
gf_patch * monolith_page_graforge(monolith_page *pg)
{
    monolith_d *m;

    m = monolith_page_monolith(pg);

    return m->patch;
}

11.15. Print Page List

11.15.1. DONE Page List Creation

CLOSED: [2019-10-12 Sat 13:48] It is sometimes ideal to be able to get a list of all the pages. This is done in monolith by allocating a list of page entries that are pages. This is created with the function monolith_pagelist_create, which will allocate and return a pointer list of monolith_dict_entry types, and the number of elements.

<<function_declarations>>=
void monolith_pagelist_create(monolith_d *m,
                              monolith_dict_entry ***pages,
                              int *npages);

The list created must be freed manually with monolith_pagelist_destroy.

<<function_declarations>>=
void monolith_pagelist_destroy(monolith_dict_entry ***pages);

monolith_pagelist_create works by querying the dictionary twice. The first time is to get the number of pages. The second time is to store the pages.

<<functions>>=
void monolith_pagelist_create(monolith_d *m,
                              monolith_dict_entry ***pages,
                              int *npages)
{
    int np, pos;
    monolith_dict_entry **pgs;
    monolith_dict_entrylist *el;
    monolith_dict_entry *ent;
    monolith_dict *d;
    unsigned int i, lst;

    np = 0;
    d = &m->dict;

    for (lst=0; lst < MONOLITH_DICT_SIZE; lst++) {
        el = &d->lists[lst];
        ent = el->head;
        for (i = 0; i < el->size; i++) {
            if (ent->type == MONOLITH_ENTRY_PAGE) np++;
            ent = ent->nxt;
        }
    }

    pgs = calloc(1, sizeof(monolith_dict_entry *) * np);

    pos = 0;

    for (lst=0; lst < MONOLITH_DICT_SIZE; lst++) {
        el = &d->lists[lst];
        ent = el->head;
        for (i = 0; i < el->size; i++) {
            if (ent->type == MONOLITH_ENTRY_PAGE) {
                pgs[pos] = ent;
                pos++;
            }
            ent = ent->nxt;
        }
    }
    *npages = np;

    *pages = pgs;
}

monolith_pagelist_destroy frees the pagelist using free.

<<functions>>=
void monolith_pagelist_destroy(monolith_dict_entry ***pages)
{
    free(*pages);
    pages = NULL;
}

11.15.2. Sorting the page list

Done via qsort.

<<function_declarations>>=
void monolith_pagelist_sort(monolith_dict_entry **pages,
                            int npages);
<<functions>>=
static int cmpstring(const void *p1, const void *p2)
{
    monolith_dict_entry *ent[2];

    ent[0] = *(monolith_dict_entry **)p1;
    ent[1] = *(monolith_dict_entry **)p2;
    return strcmp(ent[0]->key, ent[1]->key);
}

void monolith_pagelist_sort(monolith_dict_entry **pages,
                            int npages)
{
    qsort(pages,
          npages,
          sizeof(monolith_dict_entry *),
          cmpstring);
}

11.15.3. in C

The function monolith_print_pages will print all pages to a filehandle fp.

<<function_declarations>>=
void monolith_print_pages(monolith_d *m, FILE *fp);
<<functions>>=
void monolith_print_pages(monolith_d *m, FILE *fp)
{
    monolith_dict_entry **pgs;
    int size;
    int n;

    pgs = NULL;
    size = 0;
    monolith_pagelist_create(m, &pgs, &size);
    monolith_pagelist_sort(pgs, size);

    for (n = 0; n < size; n++) {
        fprintf(fp, "%s\n", pgs[n]->key);
    }

    monolith_pagelist_destroy(&pgs);
}

11.15.4. in Scheme

<<primitive_entries>>=
{"monolith:print-pages", pp_print_pages, 0, 0, {___,___,___}},
<<scheme_functions>>=
static cell pp_print_pages(cell x)
{
    monolith_d *m;
    m = monolith_data_get();
    monolith_print_pages(m, stdout);
    return UNSPECIFIC;
}



prev | home | next