#ifndef UI_INCLUDED_ #define UI_INCLUDED_ #include /* fputs, fprintf */ #include /* memcpy, memset */ #define out #define in const #define api extern #define hook static #define intern static #define global static #define storage static enum {false, true}; struct v2 {int x,y;}; struct rect {int x,y,w,h;}; #define v2(x,y) (struct v2){x,y} #define rect(x,y,w,h) (struct rect){x,y,w,h} #define cast(t,p) ((t)(p)) #define szof(a) ((int)sizeof(a)) #define zero(d,sz) memset(d,0,(size_t)(sz)) #define unused(...) unused_impl(0,__VA_ARGS__) #define cntof(a) ((int)(sizeof(a)/sizeof((a)[0]))) #define fourcc(a,b,c,d) ((unsigned long)(((d)<<24)|((c)<<16)|((b)<<8)|(a))) #define copy(d,s,sz) ((int)(((char*)d+sz)-((char*)memcpy(d,s,(size_t)(sz))))) #define alignof(t) ((int)((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0)) #define align(x, mask) ((void*)(((long long)((const char*)(x)+(mask-1))&~(mask-1)))) #define alignb(x, mask) ((void*)((long long)(x) & ~(mask-1))) #define mod(x,N) ((uint)(((unsigned long long)(x)*(unsigned long long)(N))>>32)) static inline void unused_impl(int dummy,...){(void)dummy;} #define flag(n) (1<<(n)) #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define clamp(a,v,b) (max(min(b,v),a)) #define between(x,a,b) ((a) <= (x) && (x) <= (b)) #define inbox(px,py,x,y,w,h) (between(px,x,x+w) && between(py,y,y+h)) #define intersect(x0,y0,w0,h0,x1,y1,w1,h1) (!(((x1>(x0+w0))||((x1+w1)(y0+h0))||(y1+h1)>24u)&255u) #define key(id) ((id)&(~(255u<<24u))) #define tid(id,off) (((id)&(~(255u<<24u)))|((off&255u)<<24u)) #define len(s) (cntof(s)-1) #define h1(s,i,x) (x*65599u+(unsigned char)s[(i)>16))),0) #define id(s) idx(s,1021) struct layout; struct context; struct component; typedef unsigned long uint; typedef unsigned long long addr; typedef void(*paint_f)(struct context*,struct layout*,struct component*); typedef int(*handle_f)(struct context*,struct layout*,struct component*, XEvent*); typedef void(*signal_f)(struct context*,struct layout*,struct component*, XEvent*,void *usr); typedef void(*blueprint_f)(struct context*,struct layout*,struct component*); typedef void(*link_f)(struct layout*, void*); typedef void(*commit_f)(FILE*, void*); /* interaction */ enum {Enter = LASTEvent, Leave, Drag}; enum interaction_type { INTERACTION_NOP, INTERACTION_SET, INTERACTION_SIGNAL, INTERACTION_ENABLE, INTERACTION_DISABLE, INTERACTION_DRAG_X, INTERACTION_DRAG_Y, INTERACTION_CNT, }; struct interaction { enum interaction_type type; union {int i; uint f; signal_f s;} src; void *dst; }; struct interaction_slots { struct interaction left_pressed; struct interaction left_released; struct interaction right_pressed; struct interaction right_released; struct interaction dragged; struct interaction enter; struct interaction leave; }; /* component */ #define tbl(hn,pn,bp) .handle=hn,.paint=pn,.blueprint = bp,.i=#hn, .p=#pn, .b=#bp enum attributes {ANIM, ACT, L, T, R, B, W, H, CX, CY, ATTR_CNT}; enum component_flags { DEFAULT, HIDDEN = flag(0), INTERACTIVE = flag(1), PAINTABLE = flag(2), STATIC = flag(3), SELETABLE = flag(4), BACKGROUND = flag(5), IS_LAYER = flag(6), LAYER = IS_LAYER|PAINTABLE, IS_MOVABLE_X = flag(7), IS_MOVABLE_Y = flag(8), IS_MOVABLE = IS_MOVABLE_X|IS_MOVABLE_Y, MOVABLE_X = IS_MOVABLE_X|INTERACTIVE, MOVABLE_Y = IS_MOVABLE_Y|INTERACTIVE, MOVABLE = IS_MOVABLE|INTERACTIVE, }; struct node { uint id, parent; int sub, cnt; }; struct definition { handle_f handle; paint_f paint; blueprint_f blueprint; struct rect size; unsigned flags; int zorder; const char *i, *p, *b; }; struct component { uint id; int attr[ATTR_CNT]; unsigned flags; int zorder; unsigned pressed:1; unsigned released:1; unsigned entered:1; unsigned hovered:1; unsigned left:1; unsigned dragged:1; struct v2 off, total; struct surface *surf; struct interaction_slots slot; addr usr; }; /* constraint */ enum condition_eq { COND_TRUE, COND_FALSE, COND_EQ, COND_NEQ, COND_GR, COND_LS, COND_GRE, COND_LSE }; enum constraint_eq {CON_CPY, CON_SET, CON_MIN, CON_MAX}; struct cons {float mul; int off;}; struct var {uint comp; int attr;}; struct function { int eq; struct var dst; struct var src; struct cons cons; }; struct constraint { struct function self; struct function cond; int anch; }; /* reducer */ enum reducible_type {RED_COMP, RED_CON, RED_LNK, RED_EXT}; union reducible { struct com {int type, next;} com; struct comp {struct com _; uint tid; struct definition d; addr usr;} comp; struct con {struct com _; struct constraint con;} con; struct lnk {struct com _; uint child, parent;} lnk; struct ext {struct com _; uint id; int align, size, off;} ext; }; struct module { struct definition def; uint tid, parent; #define MODULE_MAX_CON 16 struct constraint con[MODULE_MAX_CON]; int cnt; }; struct mem {struct reducer *red; int cap;}; struct reducer { int mode, payload; int max_com, max_con; int max_ext, max_nodes; char *buf; int sz, cap; }; /* extension */ struct extinfo { const char *type; uint id; int size, align; }; struct extdef { struct extinfo info; commit_f commit; link_f link; }; struct extension { struct extinfo info; addr addr; }; /* commit */ struct commit { const char *comp, *nodes; const char *con, *def; const char *tree, *ext; const char *buffer, *seq; const char *layout, *tab; const struct extlnk { const char *name; const struct extdef *def; } *ext_list; int cnt; }; /* layout */ enum layout_type {RETAINED,IMMEDIATE}; struct layout { int init; uint active; uint origin; uint hot; const uint *nodes; const struct node *tree; struct component *comp; const struct definition *def; const struct constraint *con; const struct extension *ext; uint *buffer, *tbl, *seq; int comp_cnt, con_cnt; int ext_cnt, tbl_cnt; int node_cnt; int requested; int payload; int size; }; /* context */ #define ROOT 0 struct context { struct reducer red; struct canvas *canvas; struct layout *root; struct layout *layout; #define MAX_COMPONENT_DEPTH 32 uint cur, stk[MAX_COMPONENT_DEPTH]; }; /* context */ api void update(struct context*, struct layout*, XEvent*); api void blueprint(struct context*,struct layout*); api void paint(struct context*,struct layout*); api void draw(struct context*, struct layout*, uint root); /* declaration */ api void Begin(struct context*, struct layout*, enum layout_type, uint root, void *mem, int size, struct surface*); #define Bind(ctx, id) (ctx)->stk[(ctx)->cur = 0] = id #define Push(ctx,id) (ctx)->stk[++(ctx)->cur] = (id) #define Peek(ctx) (ctx)->stk[(ctx)->cur] #define Pop(ctx) (ctx)->cur = max((ctx)->cur-1,0); api void End(struct context*); /* layout */ api void clear(struct layout*); api void layouting(struct layout*); api void reposit(FILE*, in struct layout*, in struct commit*); api inline uint find(const struct layout*, uint id); api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt); #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t)) api void reorder(struct layout*, struct component*); /* reducer */ api void reducer_init(struct reducer*, enum layout_type, void *mem, int sz); api void reducer_push(struct reducer*, union reducible*, in void *data, int sz, int ualign); #define reducer_comp(r,ID,def,user) reducer_push(r,&(union reducible){.comp={.tid=ID,._={.type=RED_COMP},.d=def,.usr=user}},0,0,0) #define reducer_con(r,c) reducer_push((r),&(union reducible){.con={._={.type=RED_CON},.con=(c)}},0,0,0) #define reducer_lnk(r,c,p) reducer_push(r,&(union reducible){.lnk={._={.type=RED_LNK},.child=c,.parent=p}},0,0,0) #define reducer_ext(r,t,p,s,a) reducer_push(r,&(union reducible){.ext={._={.type=RED_EXT},.id=t}},p,s,a) api void reducer_add(struct reducer*, in struct module*, int cnt); api void reducer_calc(struct reducer*, in struct layout*, out int *mem); #define reducer_begin(r) align((r)->buf, alignof(union reducible)) #define reducer_end(r) (union reducible*)(void*)((char*)(r)->buf+(r)->sz) #define reducer_next(i) (union reducible*)(void*)(((char*)(i)+(i)->com.next)) /* memory */ #define mem_begin(r) (struct mem){.red = (r), .cap = (r)->cap} api void *mem_alloc(struct mem*, int size, int align); #define mem_end(t) ((t)->red->cap = (t)->cap) /* component */ api void adjust(struct context*, struct layout*, struct component*, XEvent*, void*); api void dispatch(uint index, struct context*, struct layout*, XEvent*); /* bindings */ #define bind_set(d,v) (struct interaction){INTERACTION_SET, .dst = d, .src = {.i = v}} #define bind_toggle(d) (struct interaction){INTERACTION_NOT, .dst = d} #define bind_enable(d,v) (struct interaction){INTERACTION_ENABLE, .dst = d, .src = {.f = v}} #define bind_disable(d,v) (struct interaction){INTERACTION_DISABLE, .dst = d, .src = {.f = v}} #define bind_drag_x(d) (struct interaction){INTERACTION_DRAG_X, .dst = d} #define bind_drag_y(d) (struct interaction){INTERACTION_DRAG_Y, .dst = d} #define bind_signal(d,u) (struct interaction){INTERACTION_SIGNAL, .dst = (void*)(addr)u, .src = {.s = d}} #include intern inline int ceili(float x) { if (x >= 0) return cast(int,x); int t = cast(int,x); float r = x - cast(float,t); return (r > 0.0f) ? t+1: t; } intern inline void solve(struct component *c, int lo, int hi, int center, int len, int mod, int an) { if (mod == lo) { if (an == len) c->attr[hi] = c->attr[lo] + c->attr[len]; else if (an == center) { c->attr[len] = (c->attr[center] - c->attr[lo])*2; c->attr[hi] = c->attr[lo] + c->attr[len]; } else c->attr[len] = c->attr[hi] - c->attr[lo]; } else if (mod == hi) { if (an == len) c->attr[lo] = c->attr[hi] - c->attr[len]; else if (an == center) { c->attr[len] = (c->attr[hi] - c->attr[center])*2; c->attr[lo] = c->attr[hi] - c->attr[len]; } else c->attr[len] = c->attr[hi] - c->attr[lo]; } else if (mod == len) { if (an == lo) c->attr[hi] = c->attr[lo] + c->attr[len]; else if (an == center) { c->attr[lo] = c->attr[center] - (c->attr[len]/2); c->attr[hi] = c->attr[center] + (c->attr[len]/2); } else c->attr[lo] = c->attr[hi] - c->attr[len]; } else if (mod == center) { if (an == lo) { c->attr[len] = (c->attr[center] - c->attr[lo])*2; c->attr[hi] = c->attr[lo] + c->attr[len]; } else if (an == hi) { c->attr[len] = (c->attr[hi] - c->attr[center])*2; c->attr[lo] = c->attr[hi] + c->attr[len]; } else { c->attr[lo] = c->attr[center] - (c->attr[len]/2); c->attr[hi] = c->attr[center] + (c->attr[len]/2); } } c->attr[center] = c->attr[lo] + (c->attr[len]/2); c->attr[len] = max(c->attr[len], 0); } api void adjust(struct context *ctx, struct layout *ui, struct component *c, XEvent *e, void *usr) { unused(usr); if (c->flags & IS_MOVABLE_X) c->attr[CX] += e->xmotion.x_root; if (c->flags & IS_MOVABLE_Y) c->attr[CY] += e->xmotion.y_root; solve(c, L, R, CX, W, CX, W); solve(c, T, B, CY, H, CY, H); blueprint(ctx, ui); layouting(ui); } intern inline void build(struct component *c, int x, int y, int w, int h) { c->attr[L] = x; c->attr[T] = y; c->attr[W] = max(w,0); c->attr[H] = max(h,0); c->attr[R] = x+w; c->attr[B] = y+h; c->attr[CX] = x+(w/2); c->attr[CY] = y+(h/2); } intern void setup(struct component *c, const struct definition *d, struct surface *s) { c->flags = d->flags; c->zorder = d->zorder; build(c, d->size.x, d->size.y, d->size.w, d->size.h); if (!(d->flags & HIDDEN)) c->attr[ACT] = true; if (d->flags & PAINTABLE) c->surf = surf_mk(s->canvas, c->attr[W], c->attr[H]); if (d->flags & IS_MOVABLE) c->slot.dragged = bind_signal(adjust, 0); } intern inline int cond(const struct function *f, int d, int src) { float s = cast(float,src); if (f->eq == COND_TRUE) return 1; else if (f->eq == COND_FALSE) return 0; else if (f->eq == COND_EQ) return d == ceili(f->cons.mul*s)+f->cons.off; else if (f->eq == COND_NEQ) return d != ceili(f->cons.mul*s)+f->cons.off; else if (f->eq == COND_GR) return d > ceili(f->cons.mul*s)+f->cons.off; else if (f->eq == COND_LS) return d < ceili(f->cons.mul*s)+f->cons.off; else if (f->eq == COND_GRE) return d >= ceili(f->cons.mul*s)+f->cons.off; else if (f->eq == COND_LSE) return d <= ceili(f->cons.mul*s)+f->cons.off; return 0; } intern inline void eval(const struct function *f, int *d, int src) { float s = cast(float,src); if (f->eq == CON_CPY) *d = src; else if (f->eq == CON_SET) *d = ceili(f->cons.mul*s)+f->cons.off; else if (f->eq == CON_MIN) *d = min(*d, ceili(f->cons.mul*s)+f->cons.off); else if (f->eq == CON_MAX) *d = max(*d, ceili(f->cons.mul*s)+f->cons.off); } intern void insert(uint *tbl, int tbl_cnt, uint tid, uint val) { if (off(tid)) return; uint begin = mod(key(tid), cast(uint, tbl_cnt)), id = begin; do {uint idx = tbl[id]; if ((!id && val) || idx) continue; tbl[id] = val; return; } while ((mod(++id, cast(uint, tbl_cnt))) != begin); } api inline uint find(const struct layout *ui, uint tid) { if (ui->tbl) { uint begin = mod(key(tid), cast(uint, ui->tbl_cnt)), id = begin; do {uint idx = ui->tbl[id]; if (!idx) return 0; struct component *c = ui->comp + idx; if (c->id == id) return id+off(tid); } while ((mod(++id, cast(uint, ui->tbl_cnt))) != begin); return 0; } uint idx = key(tid) + off(tid); return (idx >= cast(uint,ui->comp_cnt)) ? 0: idx; } intern uint at(const struct layout *ui, const struct component *c, int *x, int *y) { loop:; uint *tbl = ui->seq; const struct node *n = ui->tree + find(ui,c->id); for (int i = 0; i < n->cnt; ++i) tbl[ui->comp[find(ui, ui->nodes[n->sub+i])].zorder] = ui->nodes[n->sub+i]; *x += c->off.x; *y += c->off.y; for (int i = n->cnt-1; i >= 0; --i) { const struct component *sub = ui->comp + find(ui, tbl[i]); if (!sub->attr[ACT] || !(sub->flags & INTERACTIVE)) continue; if (inbox(*x, *y, sub->attr[L], sub->attr[T], sub->attr[W], sub->attr[H])) {c = sub; goto loop;} } return c->id; } api void reorder(struct layout *ui, struct component *c) { const struct node *n = ui->tree + find(ui,c->id); while (n->parent != n->id) { struct component *p = ui->comp + find(ui,n->parent); if (p->flags & STATIC) goto nxt; n = ui->tree + find(ui,p->id); for (int i = 0; i < n->cnt; ++i) { struct component *sub = ui->comp + find(ui,ui->nodes[n->sub+i]); if (sub->flags & BACKGROUND) continue; if (sub->zorder > c->zorder) sub->zorder--; } c->zorder = max(n->cnt-1, 0); nxt: c = p; } return; } api inline void dispatch(uint id, struct context *ctx, struct layout *ui, XEvent *e) { uint idx; do {idx = find(ui, id); if (ui->def[idx].handle) if (ui->def[idx].handle(ctx,ui,ui->comp+idx,e)) break; } while ((id = ui->tree[idx].parent)); } intern void interact(struct context *ctx, struct layout *ui, struct component *c, struct interaction *i, XEvent *e) { if (INTERACTION_NOP == i->type); else if (INTERACTION_SIGNAL == i->type) i->src.s(ctx, ui, c, e, i->dst); else if (INTERACTION_SET == i->type) {int *v = (int*)i->dst; *v = i->src.i;} else if (INTERACTION_ENABLE == i->type) {uint *v = (uint*)i->dst; *v |= i->src.f;} else if (INTERACTION_DISABLE == i->type) {uint *v = (uint*)i->dst; *v &= ~i->src.f;} else if (e->type == MotionNotify) { int *d = (int*)i->dst; *d += (i->type == INTERACTION_DRAG_X) ? e->xmotion.x_root: e->xmotion.y_root; } } api void paint(struct context *ctx, struct layout *ui) { for (int i = 0; i < ui->comp_cnt; ++i) { struct component *c = ui->comp + i; surf_resize(c->surf, c->attr[W], c->attr[H]); if (!(c->flags & PAINTABLE) || (c->flags & IS_LAYER)) continue; if (ui->def[i].paint) ui->def[i].paint(ctx,ui,c); } } api void draw(struct context *ctx, struct layout *ui, uint root_id) { uint head = 0; struct component *r = ui->comp + find(ui, root_id); uint *tbl = ui->seq, *stk = ui->buffer; stk[head++] = root_id; while (head > 0) { struct component *c = ui->comp + find(ui, stk[--head]); if (c->surf && c->attr[ACT] && c->id != root_id) { if (c->flags & IS_LAYER) { ui->buffer = stk + head; if (ui->def[c->id].paint) ui->def[c->id].paint(ctx,ui,c); } surf_blit(r->surf, c->surf, c->attr[L] - r->attr[L] - r->off.x, c->attr[T] - r->attr[T] - r->off.y, 0, 0, c->attr[W], c->attr[H]); } if (!c->attr[ACT] || ((c->flags & IS_LAYER) && (c->id != root_id))) continue; const struct node *n = ui->tree + find(ui, c->id); for (int i = 0; i < n->cnt; ++i) tbl[ui->comp[find(ui, ui->nodes[n->sub+i])].zorder] = ui->nodes[n->sub+i]; for (int i = n->cnt-1; i >= 0; --i) stk[head++] = tbl[i]; } ui->buffer = stk; } api void blueprint(struct context *ctx, struct layout *ui) { uint head = 0, tail = 1; uint *que = ui->buffer; que[tail] = ROOT; while (head < tail) { const struct node *n = ui->tree + find(ui, que[++head]); for (int i = 0; i < n->cnt; ++i) que[++tail] = ui->nodes[n->sub+i]; } for (uint i = tail; i > 0; --i) { const struct definition *d = ui->def + find(ui, que[i]); struct component *c = ui->comp + find(ui, que[i]); if (d->blueprint) d->blueprint(ctx, ui, c); } } api void layouting(struct layout *ui) { for (int i = 0; i < ui->con_cnt; ++i) { const struct constraint *con = ui->con + i; const struct function *co = &con->cond; struct component *cd, *cs, *d, *s; cd = ui->comp + find(ui, co->dst.comp); cs = ui->comp + find(ui, co->src.comp); d = ui->comp + find(ui, con->self.dst.comp); s = ui->comp + find(ui, con->self.src.comp); if (cond(co, cd->attr[co->dst.attr], cs->attr[co->src.attr])) { eval(&con->self, d->attr+con->self.dst.attr, s->attr[con->self.src.attr]); solve(d, L, R, CX, W, con->self.dst.attr, con->anch); solve(d, T, B, CY, H, con->self.dst.attr, con->anch); } } } api void update(struct context *ctx, struct layout *ui, XEvent *e) { switch (e->type) { default: dispatch(ui->active, ctx, ui, e); break; case Expose: case ConfigureNotify: { /* resize event */ struct component *c = ui->comp + ROOT; int w = (e->type == Expose) ? e->xexpose.width: e->xconfigure.width; int h = (e->type == Expose) ? e->xexpose.height: e->xconfigure.height; build(c, 0, 0, w, h); surf_resize(c->surf, w, h); blueprint(ctx, ui); layouting(ui); for (int i = 0; i < ui->comp_cnt; ++i) dispatch(cast(uint,i),ctx,ui,e); } break; case MotionNotify: { /* enter and leave event */ uint last_hot = ui->hot; int mx = e->xmotion.x, my = e->xmotion.y; ui->hot = at(ui, ui->comp, &e->xmotion.x, &e->xmotion.y); if (last_hot != ui->hot) { struct component *prev = ui->comp + last_hot; struct component *cur = ui->comp + ui->hot; prev->left = true; prev->hovered = false; e->xmotion.type = Leave; interact(ctx, ui, prev, &prev->slot.leave, e); dispatch(prev->id, ctx, ui, e); cur->entered = cur->hovered = true; e->xmotion.type = Enter; interact(ctx, ui, cur, &cur->slot.enter, e); dispatch(cur->id, ctx, ui, e); } /* dragging event */ if (ui->active == ui->origin) { struct component *a = ui->comp + find(ui,ui->active); interact(ctx,ui, a, &a->slot.dragged, e); e->xmotion.type = Drag; dispatch(a->id,ctx,ui,e); a->dragged = true; } e->xmotion.x = mx, e->xmotion.y = my; e->xmotion.type = MotionNotify; } break; case ButtonRelease: case ButtonPress: { int down = e->type == ButtonPress; ui->active = (down) ? ui->hot: ui->active; ui->origin = (down) ? ui->hot: ROOT; struct component *a = ui->comp + find(ui,ui->active); if (down) a->pressed = true; else a->released = true; if (e->xbutton.button == Button1) { reorder(ui, a); /* Left-Button */ if (down) interact(ctx,ui, a, &a->slot.left_pressed, e); else interact(ctx,ui, a, &a->slot.left_released, e); } else if (e->xbutton.button == Button3){ reorder(ui, a); /* Right-Button */ if (down) interact(ctx,ui, a, &a->slot.right_pressed, e); else interact(ctx,ui, a, &a->slot.right_released, e); } dispatch(a->id,ctx,ui,e); } break;} } api void clear(struct layout *ui) { for (int i = 1; i < ui->comp_cnt; ++i) if (ui->comp[i].surf) surf_del(ui->comp[i].surf); } api void reducer_init(struct reducer *r, enum layout_type type, void *mem, int sz) { r->mode = type; r->buf = mem; r->cap = sz; r->max_com = r->max_con = 0; r->max_ext = r->payload = r->sz = 0; } api void reducer_push(struct reducer *r, union reducible *a, const void *dat, int size, int align) { assert(r->sz + szof(union reducible) + size + align + alignof(union reducible) < r->cap); char *end = r->buf + r->sz; char *usr = align(end + szof(union reducible), max(align,1)); char *nxt = (align(usr + size + align, alignof(union reducible))); a->com.next = cast(int,(nxt-end)); switch (a->com.type) { case RED_COMP: r->max_com++; break; case RED_CON: r->max_con++; break; case RED_LNK: r->max_nodes++; break; case RED_EXT: r->payload += size + a->ext.align; a->ext.off = cast(int,usr-end); a->ext.align = align; a->ext.size = size; r->max_ext++; default: break;} copy(end, a, szof(*a)); copy(usr, dat, size); r->sz += a->com.next; } api void* mem_alloc(struct mem *t, int size, int align) { struct reducer *r = t->red; assert(r->sz + size + align < r->cap); char *begin = r->buf + r->cap - size; char *ptr = alignb(begin, align); r->cap -= (ptr - (r->buf + r->cap)); return ptr; } api void reducer_add(struct reducer *r, in struct module *m, int cnt) { for (int i = 0; i < cnt; ++i) { const struct module *c = m + i; reducer_comp(r,c->tid,c->def,0); reducer_lnk(r,c->tid,c->parent); for (int j = 0; j < c->cnt; ++j) reducer_con(r,c->con[j]); } } api void reducer_calc(struct reducer *r, in struct layout *ui, out int *mem) { r->max_nodes += ui->node_cnt; r->max_ext += ui->ext_cnt; r->max_com += ui->comp_cnt; r->max_con += ui->con_cnt; *mem = r->max_com * szof(struct node); *mem += r->max_com * szof(struct definition); *mem += r->max_con * szof(struct constraint); *mem += r->max_com * szof(struct component); *mem += r->max_ext * szof(struct extension); *mem += (r->max_com+1) * szof(int) * ((r->mode == RETAINED) ?1:2); *mem += (r->max_nodes) * szof(int) * 2; *mem += szof(struct layout) + alignof(struct layout); *mem += alignof(struct component) + alignof(struct definition); *mem += alignof(struct constraint) + alignof(struct extension); *mem += alignof(struct node) + alignof(int); *mem += ui->payload + r->payload; } api void fold(out struct layout **ret, in struct reducer *r, in struct layout *ui, void *mem, int sz, in struct extdef **ed, int cnt) { union reducible *a; struct node *tree; struct definition *def; struct constraint *con; struct extension *ext; uint *nodes; /* 0.) setup temp node memory for stage III and IV */ assert((r->cap - r->sz) > (szof(int) * r->max_com) + alignof(int)); int *tbl = align(r->buf + r->sz, alignof(int)); for (int i = 0; i < ui->comp_cnt; ++i) tbl[i] = ui->tree[i].cnt; /* I.) setup layout in memory block */ struct layout *res = align(mem, alignof(struct layout)); if (r->mode == IMMEDIATE) { res->tbl_cnt = r->max_com; res->tbl = align(res+1, alignof(int)); res->buffer = res->tbl + res->tbl_cnt; } else res->buffer = align(res+1, alignof(int)); res->seq = res->buffer + r->max_com+1; res->nodes = nodes = res->seq + r->max_nodes; res->comp = align(res->nodes + r->max_nodes, alignof(struct component)); res->def = def = align(res->comp + r->max_com, alignof(struct definition)); res->tree = tree = align(res->def + r->max_com, alignof(struct node)); res->con = con = align(res->tree + r->max_com, alignof(struct constraint)); res->ext = ext = align(res->con + r->max_con, alignof(struct extension)); void *payload = ext + r->max_ext; res->payload = ui->payload + r->payload; res->comp_cnt = ui->comp_cnt; res->con_cnt = ui->con_cnt; res->ext_cnt = ui->ext_cnt; res->node_cnt = r->max_nodes; res->init = true; res->size = sz; *ret = res; /* II.) copy old state */ copy(res->comp, ui->comp, szof(struct component)*ui->comp_cnt); copy(def, ui->def, szof(struct definition)*ui->comp_cnt); copy(tree, ui->tree, szof(struct node)*ui->comp_cnt); copy(con, ui->con, szof(struct constraint)*ui->con_cnt); copy(ext, ui->ext, szof(struct extension)*ui->ext_cnt); for (int i = 0; r->mode == IMMEDIATE && i < ui->comp_cnt; ++i) insert(res->tbl, res->tbl_cnt, ui->comp[i].id, cast(uint,i)); for (int i = 0; i < ui->ext_cnt; ++i) { ext[i] = ui->ext[i]; ext[i].addr = (addr)align(payload, ext[i].info.align); copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].info.size); payload = (char*)ext[i].addr + ext[i].info.size; } /* III.) add new state */ for (a = reducer_begin(r); a < reducer_end(r); a = reducer_next(a)) { if (RED_CON == a->com.type) con[res->con_cnt++] = a->con.con; else if (RED_LNK == a->com.type) tbl[find(res, a->lnk.parent)]++; else if (RED_COMP == a->com.type) { const uint index = cast(uint, res->comp_cnt++); res->comp[index].usr = a->comp.usr; def[index] = a->comp.d; if (r->mode == IMMEDIATE) insert(res->tbl, res->tbl_cnt, a->comp.tid, index); res->comp[index].id = tree[index].id = a->comp.tid; setup(res->comp + index, def + index, ui->comp[0].surf); } else if (RED_EXT == a->com.type) { const void *obj = (const void*)((const char*)a + a->ext.off); char *dst = align(payload, a->ext.align); copy(dst, obj, a->ext.size); payload = dst + a->ext.size; ext[res->ext_cnt].info.id = a->ext.id; ext[res->ext_cnt].info.size = a->ext.size; ext[res->ext_cnt].info.align = a->ext.align; ext[res->ext_cnt++].addr = (addr)dst; } } /* IV.) setup tree nodes */ for (int i = 1; i < r->max_com; ++i) tree[i].sub = tbl[i-1] + tree[i-1].sub; for (int i = 0; i < ui->comp_cnt; ++i) copy(nodes+tree[i].sub, ui->nodes+ui->tree[i].sub, ui->tree[i].cnt*szof(int)); for (a = reducer_begin(r); a < reducer_end(r); a = reducer_next(a)) { if (RED_LNK != a->com.type) continue; const uint id = find(res, a->lnk.parent); nodes[tree[id].sub + tree[id].cnt++] = a->lnk.child; tree[find(res, a->lnk.child)].parent = a->lnk.parent; } /* V.) setup extension state */ for (int i = 0; i < cnt; ++i) { const struct extension *e = 0; map(res, ed[i]->info.id, e) ed[i]->link(res, (void*)e->addr); } } api void reposit(FILE *fp, in struct layout *ui, const struct commit *com) { const char *tab = (!com->tab) ? " ": com->tab; const char *attrs[] = {"0","ACT","L","T","R","B","W","H","CX","CY"}; const char *cons[] = {"CON_CPY","CON_SET","CON_MIN","CON_MAX"}; const char *cond[] = {"COND_TRUE","COND_FALSE","COND_EQ","COND_NEQ","COND_GR","COND_LS","COND_GRE","COND_LSE"}; fprintf(fp,"global const struct constraint %s[] = {\n", com->con); for (int i = 0; i < ui->con_cnt; ++i) { const struct constraint *c = ui->con + i; fprintf(fp, "%s{{.eq = %s, .dst = {%lu,%s}, .src = {%lu,%s}, .cons = {.mul = %.2ff, .off = %d}}, .anch = %s,", tab,cons[c->self.eq], c->self.dst.comp, attrs[c->self.dst.attr], c->self.src.comp, attrs[c->self.src.attr], (double)c->self.cons.mul, c->self.cons.off, attrs[c->anch]); if (c->cond.eq != COND_TRUE) { fprintf(fp, " .cond = {.eq = %s, .dst = {%lu,%s}, .src = {%lu,%s}, .cons = {.mul = %.2ff, .off = %d}}},\n", cond[c->cond.eq], c->cond.dst.comp, attrs[c->cond.dst.attr], c->cond.src.comp,attrs[c->cond.src.attr], (double)c->cond.cons.mul, c->cond.cons.off); } else fputs("},\n",fp); } fputs("};\n", fp); fprintf(fp, "global const struct definition %s[] = {\n", com->def); for (int i = 0; i < ui->comp_cnt; ++i) { const struct definition *d = ui->def + i; char buf[128]; int n = 0; buf[n++] = '0'; if (d->flags & HIDDEN) n += copy(buf+n, "|HIDDEN",7); if (d->flags & INTERACTIVE) n += copy(buf+n, "|INTERACTIVE",12); if (d->flags & PAINTABLE) n += copy(buf+n, "|PAINTABLE",10); if (d->flags & IS_LAYER) n += copy(buf+n, "|LAYER",6); if (d->flags & IS_MOVABLE_X) n += copy(buf+n, "|MOVABLE_X",10); if (d->flags & IS_MOVABLE_Y) n += copy(buf+n, "|MOVABLE_Y",10); buf[n] = 0; fprintf(fp, "%s{tbl(%s,%s,%s), .size={%d,%d,%d,%d}, .flags = %s},\n", tab,d->i, d->p, d->b, d->size.x, d->size.y, d->size.w, d->size.h, buf); } fputs("};\n", fp); fprintf(fp, "global const uint %s[] = {", com->nodes); for (int i = 0; i < ui->node_cnt; ++i) { if (!(i & 15)) fprintf(fp,"\n%s", tab); fprintf(fp, "%lu,", ui->nodes[i]); } fputs("\n};\n",fp); fprintf(fp, "global const struct node %s[] = {\n", com->tree); for (int i = 0; i < ui->comp_cnt; ++i) { const struct node *n = ui->tree + i; fprintf(fp, "%s{.id = %lu, .parent = %lu, .cnt = %d, .sub = %d},\n", tab, n->id, n->parent, n->cnt, n->sub); } fputs("};\n", fp); for (int i = 0; i < com->cnt; ++i) { const struct extension *e = 0; const struct extlnk *lnk = com->ext_list + i; fprintf(fp, "global %s %s[] = {\n", lnk->def->info.type, lnk->name); map(ui, lnk->def->info.id, e) { fprintf(fp, "%s",tab); lnk->def->commit(fp, (void*)e->addr); } fputs("0};\n",fp); } fprintf(fp, "global const struct extension %s[] = {", com->ext); for (int i = 0; i < com->cnt; ++i) { const struct extension *e = 0; int cnt = 0; const struct extlnk *lnk = com->ext_list + i; map(ui, lnk->def->info.id, e) fprintf(fp, "\n%s{.info = {.id = %lu, .size = %d, .align = %d}, .addr = (addr)&%s[%u]},", tab,e->info.id, e->info.size, e->info.align, lnk->name, cnt++); } if (!ui->ext_cnt) fputs("{{0}}};\n", fp); else fputs("\n};\n",fp); fprintf(fp, "global uint %s[cntof(%s)];\n",com->buffer,com->def); fprintf(fp, "global uint %s[cntof(%s)];\n",com->seq,com->def); fprintf(fp, "global struct component %s[cntof(%s)];\n", com->comp,com->def); fprintf(fp, "global struct layout %s = {\n", com->layout); fprintf(fp, "%s.comp = %s,\n%s.tree = %s,\n", tab,com->comp, tab, com->tree); fprintf(fp, "%s.buffer = %s,\n%s.seq = %s,\n", tab,com->buffer, tab,com->seq); fprintf(fp, "%s.def = %s,\n%s.con = %s,\n%s.ext = %s,\n",tab,com->def,tab,com->con,tab,com->ext); fprintf(fp, "%s.comp_cnt = %d,\n%s.con_cnt = %d,\n", tab,ui->comp_cnt,tab,ui->con_cnt); fprintf(fp, "%s.ext_cnt = %d,\n%s.node_cnt = %d,\n};\n", tab,ui->ext_cnt, tab,ui->node_cnt); } api void Begin(struct context *ctx, struct layout *ui, enum layout_type type, uint root, void *mem, int siz, struct surface *scrn) { ctx->layout = ui; ctx->cur = ctx->stk[0] = 0; ui->comp[ROOT].surf = scrn; reducer_init(&ctx->red, type, mem, siz); for (int i = 0; i < ui->comp_cnt; ++i) ui->comp[i].pressed = ui->comp[i].released = ui->comp[i].entered = ui->comp[i].left = ui->comp[i].dragged = 0; if (!ui->init) { for (int i = 0; i < ui->comp_cnt; ++i) { const struct node *n = ui->tree + i; struct component *c = ui->comp + i; c->id = cast(uint, n->id); setup(c, ui->def + i, scrn); } ui->init = true; } Push(ctx, root); } api void End(struct context *ctx) { struct layout *ui = ctx->layout; reducer_calc(&ctx->red, ui, &ui->requested); ctx->layout = ctx->root; ctx->stk[0] = ctx->cur = 0; } #endif