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
.
monolith_page *curpage;
By default, it is set to point to NULL.
m->curpage = NULL;
The current page is set with the function
monolith_curpage_set
.
void monolith_curpage_set(monolith_d *m, monolith_page *pg);
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).
int monolith_curpage_find_and_select(monolith_d *m,
const char *name,
unsigned int len);
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
.
{"monolith:page-select", pp_page_select, 1, 1, {STR,___,___}},
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
{
"monolith/page-select",
j_page_select,
"Selects a page."
},
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.
int monolith_page_selected(monolith_page *pg);
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.
int monolith_dict_lookup(monolith_dict *d,
monolith_page **pg,
const char *name,
unsigned int len);
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.
int monolith_lookup_page(monolith_d *m,
monolith_page **pg,
const char *key,
unsigned int len);
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.
int monolith_dict_find(monolith_dict *d,
monolith_dict_entry **pent,
const char *name,
unsigned int len);
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
.
typedef struct monolith_dict_entry monolith_dict_entry;
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 *);
};
enum {
MONOLITH_ENTRY_NONE,
MONOLITH_ENTRY_PAGE,
MONOLITH_ENTRY_CHAN,
MONOLITH_ENTRY_IMAGE,
MONOLITH_ENTRY_FTBL
};
const char *monolith_dict_entry_key(monolith_dict_entry *ent);
const char *monolith_dict_entry_key(monolith_dict_entry *ent)
{
return ent->key;
}
monolith_page * monolith_dict_entry_page(monolith_dict_entry *ent);
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
.
void * monolith_dict_entry_data(monolith_dict_entry *ent);
void * monolith_dict_entry_data(monolith_dict_entry *ent)
{
return ent->ud;
}
void monolith_dict_entry_data_set(
monolith_dict_entry *ent,
void *ud);
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
.
void monolith_dict_entry_clean(
monolith_dict_entry *ent,
void (*cleanup)(monolith_dict_entry *)
);
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
typedef struct monolith_dict_entrylist monolith_dict_entrylist;
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
.
void monolith_dict_entrylist_init(monolith_dict_entrylist *e);
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
.
void monolith_dict_entrylist_free(monolith_dict_entrylist *el);
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
.
void monolith_dict_entrylist_append(monolith_dict_entrylist *el,
monolith_dict_entry *ent);
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
.
typedef struct monolith_dict monolith_dict;
<<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.
void monolith_dict_init(monolith_d *m, monolith_dict *d);
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.
void monolith_dict_cleanup(monolith_dict *d);
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.
monolith_dict dict;
It can be accessed from the opaque pointer using the function
monolith_dict_get
.
monolith_dict * monolith_dict_get(monolith_d *m);
monolith_dict * monolith_dict_get(monolith_d *m)
{
return &m->dict;
}
monolith_dict_init(m, &m->dict);
It is allocated initialized in the top-level init function, and freed in the top level free function.
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 unsigned int dict_hash(const char *str,
unsigned int len,
unsigned int nlists);
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_d *m;
d->m = m;
11.3.8.2. Number of Types
The ntypes
variable keeps track of how many page types
there are.
unsigned int ntypes;
There are of course zero types to begin with because there are no pages.
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
.
unsigned int nwords;
d->nwords = 0;
11.3.8.4. Entry Lists
#ifndef MONOLITH_DICT_SIZE
#define MONOLITH_DICT_SIZE 32
#endif
monolith_dict_entrylist lists[MONOLITH_DICT_SIZE];
for(i = 0; i < MONOLITH_DICT_SIZE; i++) {
monolith_dict_entrylist_init(&d->lists[i]);
}
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.
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.
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.
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.
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.
int monolith_dict_newentry(monolith_dict *d,
monolith_dict_entry **pent,
const char *name,
unsigned int len);
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
.
typedef struct monolith_page monolith_page;
<<monolith_dict_typedefs>>
struct monolith_page {
<<monolith_page_contents>>
};
<<monolith_dict_struct>>
The monolith page struct is initialized with the function
monolith_page_init
.
void monolith_page_init(monolith_d *m, monolith_page *p);
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.
void *ud;
p->ud = NULL;
Data can be set using the function monolith_page_data_set
,
and retrieved using monolith_page_data_get
.
void* monolith_page_data_get(monolith_page *pg);
void monolith_page_data_set(monolith_page *pg, void *ud);
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_d *m;
p->m = m;
This can be retrieved using the function
monolith_page_monolith
.
monolith_d * monolith_page_monolith(monolith_page *pg);
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.
unsigned int type;
p->type = 0;
This type variable can be set/get with the functions
monolith_page_type_set
and monolith_page_type_get
,
respectively.
void monolith_page_type_set(monolith_page *pg, unsigned int type);
unsigned int monolith_page_type_get(monolith_page *pg);
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.
void (*open)(monolith_page *);
p->open = NULL;
The monolith open callback is set with the function monolith_page_open_set
.
void monolith_page_open_set(monolith_page *p, void (*open)(monolith_page *));
void monolith_page_open_set(monolith_page *p, void (*open)(monolith_page *))
{
p->open = open;
}
It is called with the function monolith_page_open
.
void monolith_page_open(monolith_page *pg);
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.
void (*close)(monolith_page *);
p->close = NULL;
The monolith close callback is set with the function monolith_page_close_set
.
void monolith_page_close_set(monolith_page *p, void (*close)(monolith_page *));
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
.
void monolith_page_close(monolith_page *pg);
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).
void (*press)(monolith_page *,int,int,int);
p->press = NULL;
The page press callback is set with the function
monolith_page_press_set
.
void monolith_page_press_set(monolith_page *p,
void (*press)(monolith_page *,int,int,int));
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
.
void monolith_page_press(monolith_page *pg, int x, int y, int s);
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.
void (*turn)(monolith_page *,int);
p->turn = NULL;
The page turn callback is set with the function
monolith_page_turn_set
.
void monolith_page_turn_set(monolith_page *p,
void (*turn)(monolith_page *,int));
void monolith_page_turn_set(monolith_page *p,
void (*turn)(monolith_page *,int))
{
p->turn = turn;
}
It can be called with monolith_page_turn
.
void monolith_page_turn(monolith_page *pg, int s);
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.
void (*push)(monolith_page *,int);
p->push = NULL;
The page push callback is set with the function
monolith_page_push_set
.
void monolith_page_push_set(monolith_page *p,
void (*push)(monolith_page *,int));
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
.
void monolith_page_push(monolith_page *pg, int s);
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.
void (*delta)(monolith_page *,int,int);
p->delta = NULL;
The page press callback is set with the function
monolith_page_press_set
.
void monolith_page_delta_set(monolith_page *p,
void (*delta)(monolith_page *,int,int));
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
.
void monolith_page_delta(monolith_page *pg, int n, int delta);
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.
void (*free)(monolith_page *);
p->free = NULL;
The page free callback is set with the function monolith_page_free_set
.
void monolith_page_free_set(monolith_page *p,
void (*free)(monolith_page *));
void monolith_page_free_set(monolith_page *p,
void (*free)(monolith_page *))
{
p->free = free;
}
It is called with the function monolith_page_free
.
void monolith_page_free(monolith_page *pg);
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.
typedef int (*monolith_statefun)(monolith_page *, monolith_state*, const char*, unsigned int);
monolith_statefun save;
p->save = NULL;
The page save callback is set with the function
monolith_page_free_set
.
void monolith_page_save_set(monolith_page *p, monolith_statefun save);
void monolith_page_save_set(monolith_page *p, monolith_statefun save)
{
p->save = save;
}
It is called with the function monolith_page_save
.
void monolith_page_save(monolith_page *pg,
monolith_state *ms,
const char *key,
unsigned int len);
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_statefun load;
p->load = NULL;
The page save callback is set with the function
monolith_page_free_set
.
void monolith_page_load_set(monolith_page *p, monolith_statefun load);
void monolith_page_load_set(monolith_page *p, monolith_statefun load)
{
p->load = load;
}
It is called with the function monolith_page_save
.
void monolith_page_load(monolith_page *pg,
monolith_state *ms,
const char *key,
unsigned int len);
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
.
int monolith_page_find_and_save(monolith_d *m,
const char *pgname,
unsigned int pglen,
const char *key,
unsigned int keylen);
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
.
{"monolith:page-save", pp_page_save, 2, 2, {STR,STR,___}},
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
{
"monolith/page-save",
j_page_save,
"Saves a page.\n"
},
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
.
int monolith_page_find_and_load(monolith_d *m,
const char *pgname,
unsigned int pglen,
const char *key,
unsigned int keylen);
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
.
{"monolith:page-load", pp_page_load, 2, 2, {STR,STR,___}},
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
{
"monolith/page-load",
j_page_load,
"Loads a page.\n"
},
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
.
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).
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
.
void monolith_page_mstate_init(monolith_page *p, monolith_page_mstate *ms);
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.
int monolith_page_mstate_new(monolith_page *pg, monolith_page_mstate **ms);
int monolith_page_mstate_free(monolith_page_mstate **ms);
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
.
unsigned short * monolith_page_mstate_data_get(monolith_page_mstate *ms);
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
.
void monolith_page_mstate_led_set(monolith_page_mstate *ms,
int x, int y, int s);
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.
void monolith_page_mstate_led_row(monolith_page_mstate *ms,
int x, int y, int s);
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.
void monolith_page_mstate_led_row16(monolith_page_mstate *ms,
int row,
unsigned short s);
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
void monolith_page_mstate_led_col(monolith_page_mstate *ms,
int x, int y, int s);
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.
void monolith_page_mstate_recall(monolith_page_mstate *ms);
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.
int monolith_page_mstate_get(monolith_page_mstate *ms, int x, int y);
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
.
void monolith_page_mstate_set(monolith_page_mstate *ms, int x, int y, int s);
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
.
int monolith_page_mstate_selected(monolith_page_mstate *ms);
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
.
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
.
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
void monolith_page_arcstate_init(monolith_page *p, monolith_page_arcstate *as);
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 void arc_encoder_state_init(arc_encoder_state *enc);
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.
int monolith_page_arcstate_new(monolith_page *pg, monolith_page_arcstate **as);
int monolith_page_arcstate_free(monolith_page_arcstate **as);
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.
unsigned char * monolith_page_arcstate_data_get(monolith_page_arcstate *as,
int n);
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.
void monolith_page_arcstate_data_set(monolith_page_arcstate *as,
int n,
unsigned char *data);
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.
void monolith_page_arcstate_map(monolith_page_arcstate *as,
int n, unsigned char *map);
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
int monolith_page_arcstate_selected(monolith_page_arcstate *as);
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.
void monolith_page_arcstate_recall(monolith_page_arcstate *as);
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
.
void monolith_arcstate_mapval(monolith_page_arcstate *as,
int pos,
GFFLT val);
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.
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 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.
gf_patch * monolith_page_graforge(monolith_page *pg);
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.
void monolith_pagelist_create(monolith_d *m,
monolith_dict_entry ***pages,
int *npages);
The list created must be freed manually with
monolith_pagelist_destroy
.
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.
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
.
void monolith_pagelist_destroy(monolith_dict_entry ***pages)
{
free(*pages);
pages = NULL;
}
11.15.2. Sorting the page list
void monolith_pagelist_sort(monolith_dict_entry **pages,
int npages);
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
.
void monolith_print_pages(monolith_d *m, FILE *fp);
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
{"monolith:print-pages", pp_print_pages, 0, 0, {___,___,___}},
static cell pp_print_pages(cell x)
{
monolith_d *m;
m = monolith_data_get();
monolith_print_pages(m, stdout);
return UNSPECIFIC;
}
prev | home | next