Skip to content

Instantly share code, notes, and snippets.

@vurtun
Last active October 4, 2023 15:44
Show Gist options
  • Select an option

  • Save vurtun/0bf28ba10d5cbf69c11f5ef638e3ca45 to your computer and use it in GitHub Desktop.

Select an option

Save vurtun/0bf28ba10d5cbf69c11f5ef638e3ca45 to your computer and use it in GitHub Desktop.

Revisions

  1. vurtun revised this gist May 21, 2017. 1 changed file with 54 additions and 35 deletions.
    89 changes: 54 additions & 35 deletions ui.c
    Original file line number Diff line number Diff line change
    @@ -27,6 +27,7 @@ struct rect {int x,y,w,h;};
    #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;}

    @@ -48,7 +49,7 @@ static inline void unused_impl(int dummy,...){(void)dummy;}
    #define h16(s,i,x) h4(s,i,h4(s,i+4,h4(s,i+8,h4(s,i+12,x))))
    #define h64(s,i,x) h16(s,i,h16(s,i+16,h16(s,i+32,h16(s,i+48,x))))
    #define idx(s,i) tid(((uint)(h64(s,0,i)^(h64(s,0,i)>>16))),0)
    #define id(s) idx(s,42)
    #define id(s) idx(s,1021)

    struct layout;
    struct context;
    @@ -99,11 +100,12 @@ enum component_flags {
    INTERACTIVE = flag(1),
    PAINTABLE = flag(2),
    STATIC = flag(3),
    BACKGROUND = flag(4),
    IS_LAYER = flag(5),
    SELETABLE = flag(4),
    BACKGROUND = flag(5),
    IS_LAYER = flag(6),
    LAYER = IS_LAYER|PAINTABLE,
    IS_MOVABLE_X = flag(6),
    IS_MOVABLE_Y = flag(7),
    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,
    @@ -179,11 +181,12 @@ struct module {
    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;
    void *buf; int sz, cap;
    char *buf; int sz, cap;
    };

    /* extension */
    @@ -268,25 +271,29 @@ api void End(struct context*);
    /* layout */
    api void clear(struct layout*);
    api void layouting(struct layout*);
    api void reorder(struct layout*, struct component*);
    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))
    #define apply(ui,t,f,u) for(const struct extension *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)
    api void reorder(struct layout*, struct component*);

    /* fold */
    /* 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_custom(r,t,p,s,a) reducer_push(r,&(union reducible){.ext={._={.type=RED_EXT},.id=t}},p,s,a)
    #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))
    api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt);

    /* 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*);
    @@ -306,9 +313,9 @@ api void dispatch(uint index, struct context*, struct layout*, XEvent*);
    intern inline int
    ceili(float x)
    {
    if (x >= 0) return (int)x;
    int t = (int)x;
    float r = x - (float)t;
    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
    @@ -390,7 +397,7 @@ setup(struct component *c, const struct definition *d, struct surface *s)
    intern inline int
    cond(const struct function *f, int d, int src)
    {
    float s = (float)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)
    @@ -410,7 +417,7 @@ cond(const struct function *f, int d, int src)
    intern inline void
    eval(const struct function *f, int *d, int src)
    {
    float s = (float)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;
    @@ -448,12 +455,12 @@ 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) /* O(n) sub-nodes sort by zorder */
    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]);
    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;}
    @@ -466,15 +473,15 @@ 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 next;
    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);
    next: c = p;
    nxt: c = p;
    }
    return;
    }
    @@ -670,18 +677,18 @@ 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 = (char*)r->buf + r->sz;
    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 = (int)(nxt-end);
    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 = (int)(usr-end);
    a->ext.off = cast(int,usr-end);
    a->ext.align = align;
    a->ext.size = size;
    r->max_ext++;
    @@ -691,6 +698,16 @@ reducer_push(struct reducer *r, union reducible *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)
    {
    @@ -733,7 +750,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,

    /* 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((char*)r->buf+r->sz, 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;

    @@ -780,15 +797,15 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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)]++;
    tbl[find(res, a->lnk.parent)]++;
    else if (RED_COMP == a->com.type) {
    const uint index = cast(uint, res->comp_cnt++);
    ui->comp[index].usr = a->comp.usr;
    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);
    ui->comp[index].id = tree[index].id = a->comp.tid;
    setup(ui->comp + index, def + index, ui->comp[0].surf);
    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);
    @@ -813,7 +830,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    } /* V.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    const struct extension *e = 0;
    map(ui, ed[i]->info.id, e)
    map(res, ed[i]->info.id, e)
    ed[i]->link(res, (void*)e->addr);
    }
    }
    @@ -862,20 +879,22 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    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 const %s %s[] = {", lnk->def->info.type, lnk->name);
    apply(ui, lnk->def->info.id, lnk->def->commit, fp);
    fputs("};\n",fp);
    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{.id = %lu, .size = %d, .align = %d, .addr = (addr)&%s[%u]},",
    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}}", fp);
    fputs("};\n",fp);
    } 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);
  2. vurtun revised this gist May 19, 2017. 1 changed file with 40 additions and 30 deletions.
    70 changes: 40 additions & 30 deletions ui.c
    Original file line number Diff line number Diff line change
    @@ -66,6 +66,7 @@ typedef void(*commit_f)(FILE*, void*);
    /* interaction */
    enum {Enter = LASTEvent, Leave, Drag};
    enum interaction_type {
    INTERACTION_NOP,
    INTERACTION_SET,
    INTERACTION_SIGNAL,
    INTERACTION_ENABLE,
    @@ -117,19 +118,21 @@ struct definition {
    paint_f paint;
    blueprint_f blueprint;
    struct rect size;
    uint flags;
    unsigned flags;
    int zorder;
    const char *i, *p, *b;
    };
    struct component {
    uint id;
    int attr[ATTR_CNT];
    uint flags;
    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;
    @@ -160,7 +163,7 @@ struct constraint {
    int anch;
    };

    /* reduce */
    /* reducer */
    enum reducible_type {RED_COMP, RED_CON, RED_LNK, RED_EXT};
    union reducible {
    struct com {int type, next;} com;
    @@ -243,7 +246,6 @@ struct layout {
    struct context {
    struct reducer red;
    struct canvas *canvas;
    struct surface *scrn;
    struct layout *root;
    struct layout *layout;
    #define MAX_COMPONENT_DEPTH 32
    @@ -256,7 +258,7 @@ 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, void *mem, int size, struct surface*);
    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]
    @@ -489,7 +491,8 @@ intern void
    interact(struct context *ctx, struct layout *ui, struct component *c,
    struct interaction *i, XEvent *e)
    {
    if (INTERACTION_SIGNAL == i->type)
    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;}
    @@ -606,12 +609,13 @@ update(struct context *ctx, struct layout *ui, XEvent *e)
    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->hovered = true;
    cur->entered = cur->hovered = true;
    e->xmotion.type = Enter;
    interact(ctx, ui, cur, &cur->slot.enter, e);
    dispatch(cur->id, ctx, ui, e);
    @@ -747,14 +751,14 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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;
    void *payload = ext + r->max_ext;
    *ret = res;

    /* II.) copy old state */
    @@ -781,10 +785,9 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    const uint index = cast(uint, res->comp_cnt++);
    ui->comp[index].usr = a->comp.usr;
    def[index] = a->comp.d;
    if (r->mode == IMMEDIATE) {
    ui->comp[index].id = tree[index].id = a->comp.tid;
    if (r->mode == IMMEDIATE)
    insert(res->tbl, res->tbl_cnt, a->comp.tid, index);
    } else ui->comp[index].id = tree[index].id = index;
    ui->comp[index].id = tree[index].id = a->comp.tid;
    setup(ui->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);
    @@ -805,8 +808,8 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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);
    tree[id].parent = 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;
    @@ -820,38 +823,44 @@ 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},\n",
    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",12);
    if (d->flags & INTERACTIVE) n += copy(buf+n, "|INTERACTABLE",18);
    if (d->flags & PAINTABLE) n += copy(buf+n, "|PAINTABLE",15);
    if (d->flags & IS_LAYER) n += copy(buf+n, "|LAYER",11);
    if (d->flags & IS_MOVABLE_X) n += copy(buf+n, "|MOVABLE_X",15);
    if (d->flags & IS_MOVABLE_Y) n += copy(buf+n, "|MOVABLE_Y",15);
    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);
    fprintf(fp, "global const uint %s[] = {", com->nodes);
    for (int i = 0; i < ui->node_cnt; ++i) {
    if (!(i & 31)) fprintf(fp,"\n%s", tab);
    fprintf(fp, "%lu,", ui->nodes[i]);
    } fputs("\n};\n",fp);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extlnk *lnk = com->ext_list + i;
    fprintf(fp, "global const %s %s[] = {", lnk->def->info.type, lnk->name);
    @@ -865,7 +874,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    map(ui, lnk->def->info.id, e)
    fprintf(fp, "\n%s{.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", fp);
    } if (!ui->ext_cnt) fputs("{{0}}", fp);
    fputs("};\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);
    @@ -879,22 +888,23 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    }
    api void
    Begin(struct context *ctx, struct layout *ui, enum layout_type type,
    void *mem, int siz, struct surface *scrn)
    uint root, void *mem, int siz, struct surface *scrn)
    {
    ui->comp[ROOT].surf = 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].dragged = 0;
    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)
    @@ -904,4 +914,4 @@ End(struct context *ctx)
    ctx->layout = ctx->root;
    ctx->stk[0] = ctx->cur = 0;
    }
    #endif
    #endif
  3. vurtun revised this gist May 18, 2017. 1 changed file with 15 additions and 20 deletions.
    35 changes: 15 additions & 20 deletions ui.c
    Original file line number Diff line number Diff line change
    @@ -268,8 +268,7 @@ api void clear(struct layout*);
    api void layouting(struct layout*);
    api void reorder(struct layout*, struct component*);
    api void reposit(FILE*, in struct layout*, in struct commit*);
    api inline uint lookup(in struct layout*, uint id);
    api inline uint find(const struct layout *ui, uint id);
    api inline uint find(const struct layout*, uint id);
    #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t))
    #define apply(ui,t,f,u) for(const struct extension *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)

    @@ -423,27 +422,23 @@ 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 index = tbl[id];
    if ((!id && val) || index) continue;
    do {uint idx = tbl[id];
    if ((!id && val) || idx) continue;
    tbl[id] = val; return;
    } while ((mod(++id, cast(uint, tbl_cnt))) != begin);
    }
    api uint
    lookup(const struct layout *ui, uint tid)
    {
    uint begin = mod(key(tid), cast(uint, ui->tbl_cnt)), id = begin;
    do {uint index = ui->tbl[id];
    if (!index) return 0;
    struct component *c = ui->comp + index;
    if (c->id == id) return id+off(tid);
    } while ((mod(++id, cast(uint, ui->tbl_cnt))) != begin);
    return 0;
    }
    api inline uint
    find(const struct layout *ui, uint id)
    find(const struct layout *ui, uint tid)
    {
    if (ui->tbl) return lookup(ui,id);
    uint idx = key(id) + off(id);
    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
    @@ -856,7 +851,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    for (int i = 0; i < ui->node_cnt; ++i) {
    if (!(i & 31)) fprintf(fp,"\n%s", tab);
    fprintf(fp, "%lu,", ui->nodes[i]);
    } fputs("};\n",fp);
    } fputs("\n};\n",fp);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extlnk *lnk = com->ext_list + i;
    fprintf(fp, "global const %s %s[] = {", lnk->def->info.type, lnk->name);
    @@ -886,10 +881,10 @@ api void
    Begin(struct context *ctx, struct layout *ui, enum layout_type type,
    void *mem, int siz, struct surface *scrn)
    {
    ui->comp[ROOT].surf = scrn;
    ctx->layout = ui;
    ctx->cur = ctx->stk[0] = 0;
    reducer_init(&ctx->red, type, mem, siz);
    ui->comp[ROOT].surf = scrn;
    for (int i = 0; i < ui->comp_cnt; ++i)
    ui->comp[i].pressed = ui->comp[i].released = ui->comp[i].dragged = 0;
    if (!ui->init) {
  4. vurtun revised this gist May 17, 2017. 1 changed file with 58 additions and 45 deletions.
    103 changes: 58 additions & 45 deletions ui.c
    Original file line number Diff line number Diff line change
    @@ -38,14 +38,17 @@ static inline void unused_impl(int dummy,...){(void)dummy;}
    #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)<x0)||(y1>(y0+h0))||(y1+h1)<y0)))

    #define off(id) (((id)>>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)<len(s)?len(s)-1-(i):len(s)])
    #define h4(s,i,x) h1(s,i,h1(s,i+1,h1(s,i+2,h1(s,i+3,x))))
    #define h16(s,i,x) h4(s,i,h4(s,i+4,h4(s,i+8,h4(s,i+12,x))))
    #define h64(s,i,x) h16(s,i,h16(s,i+16,h16(s,i+32,h16(s,i+48,x))))
    #define idx(s,i) ((uint)(h64(s,0,i)^(h64(s,0,i)>>16)))
    #define idx(s,i) tid(((uint)(h64(s,0,i)^(h64(s,0,i)>>16))),0)
    #define id(s) idx(s,42)
    #define tid(id,off) (((id)&(~(255u<<24u)))|((off&255u)<<24u))

    struct layout;
    struct context;
    @@ -239,6 +242,8 @@ struct layout {
    #define ROOT 0
    struct context {
    struct reducer red;
    struct canvas *canvas;
    struct surface *scrn;
    struct layout *root;
    struct layout *layout;
    #define MAX_COMPONENT_DEPTH 32
    @@ -249,7 +254,6 @@ 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);
    api void clear(struct context*);

    /* declaration */
    api void Begin(struct context*, struct layout*, enum layout_type, void *mem, int size, struct surface*);
    @@ -260,23 +264,27 @@ api void Begin(struct context*, struct layout*, enum layout_type, void *mem, int
    api void End(struct context*);

    /* layout */
    api void clear(struct layout*);
    api void layouting(struct layout*);
    api void reorder(struct layout*, struct component*);
    api void reposit(FILE*, in struct layout*, in struct commit*);
    api inline uint lookup(in struct layout*, uint id);
    #define find(ui,id) ((ui->tbl)?lookup(ui,id):id)
    api inline uint find(const struct layout *ui, uint id);
    #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t))
    #define apply(ui,t,f,u) for(const struct extension *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)

    /* fold */
    api void reducer_begin(struct reducer*, enum layout_type, void *mem, int sz);
    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_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_custom(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_end(struct reducer*, in struct layout*, out int *mem);
    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))
    api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt);

    /* component */
    @@ -413,34 +421,37 @@ eval(const struct function *f, int *d, int src)
    intern void
    insert(uint *tbl, int tbl_cnt, uint tid, uint val)
    {
    #define off(id) (((id)>>24u)&255u)
    #define key(id) ((id)&(~(255u<<24u)))
    uint key = key(tid), off = off(tid);
    if (off) return;
    uint begin = mod(key, cast(uint, tbl_cnt)), id = begin;
    if (off(tid)) return;
    uint begin = mod(key(tid), cast(uint, tbl_cnt)), id = begin;
    do {uint index = tbl[id];
    if ((id == 0 && val != 0) || index) continue;
    if ((!id && val) || index) continue;
    tbl[id] = val; return;
    } while ((mod(++id, cast(uint, tbl_cnt))) != begin);
    }
    api uint
    lookup(const struct layout *ui, uint tid)
    {
    uint key = key(tid), off = off(tid);
    uint begin = mod(key, cast(uint, ui->tbl_cnt)), id = begin;
    uint begin = mod(key(tid), cast(uint, ui->tbl_cnt)), id = begin;
    do {uint index = ui->tbl[id];
    if (!index) return 0;
    struct component *c = ui->comp + index;
    if (c->id == id) return id+off;
    if (c->id == id) return id+off(tid);
    } while ((mod(++id, cast(uint, ui->tbl_cnt))) != begin);
    return 0;
    }
    api inline uint
    find(const struct layout *ui, uint id)
    {
    if (ui->tbl) return lookup(ui,id);
    uint idx = key(id) + off(id);
    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)
    for (int i = 0; i < n->cnt; ++i) /* O(n) sub-nodes sort by zorder */
    tbl[ui->comp[find(ui, ui->nodes[n->sub+i])].zorder] = ui->nodes[n->sub+i];

    *x += c->off.x; *y += c->off.y;
    @@ -641,15 +652,14 @@ update(struct context *ctx, struct layout *ui, XEvent *e)
    } break;}
    }
    api void
    clear(struct context *ctx)
    clear(struct layout *ui)
    {
    struct layout *ui = ctx->layout;
    for (int i = 1; i < ui->comp_cnt; ++i)
    if (ui->comp[i].surf)
    surf_del(ui->comp[i].surf);
    }
    api void
    reducer_begin(struct reducer *r, enum layout_type type, void *mem, int sz)
    reducer_init(struct reducer *r, enum layout_type type, void *mem, int sz)
    {
    r->mode = type;
    r->buf = mem; r->cap = sz;
    @@ -662,17 +672,17 @@ reducer_push(struct reducer *r, union reducible *a,
    {
    assert(r->sz + szof(union reducible) + size + align + alignof(union reducible) < r->cap);
    char *end = (char*)r->buf + r->sz;
    char *usr = align(end + szof(union reducible), align);
    char *nxt = end + r->sz + szof(union reducible) + size + align;
    a->com.next = (int)((char*)(align(nxt, alignof(union reducible))) - end);
    char *usr = align(end + szof(union reducible), max(align,1));
    char *nxt = (align(usr + size + align, alignof(union reducible)));
    a->com.next = (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 = (int)(usr - end);
    a->ext.off = (int)(usr-end);
    a->ext.align = align;
    a->ext.size = size;
    r->max_ext++;
    @@ -694,7 +704,7 @@ reducer_add(struct reducer *r, in struct module *m, int cnt)
    }
    }
    api void
    reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    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;
    @@ -724,7 +734,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,

    /* 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((char*)r->buf+r->max_nodes, alignof(int));
    int *tbl = align((char*)r->buf+r->sz, alignof(int));
    for (int i = 0; i < ui->comp_cnt; ++i)
    tbl[i] = ui->tree[i].cnt;

    @@ -743,10 +753,11 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    res->con = con = align(res->tree + r->max_com, alignof(struct constraint));
    res->ext = ext = align(res->con + r->max_con, alignof(struct extension));
    res->payload = ui->payload + r->payload;
    res->comp_cnt = ui->con_cnt;
    res->comp_cnt = ui->comp_cnt;
    res->con_cnt = ui->con_cnt;
    res->ext_cnt = ui->ext_cnt;
    res->init = True;
    res->node_cnt = r->max_nodes;
    res->init = true;
    res->size = sz;
    void *payload = ext + r->max_ext;
    *ret = res;
    @@ -766,11 +777,11 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    payload = (char*)ext[i].addr + ext[i].info.size;
    }
    /* III.) add new state */
    for (a=r->buf; (char*)a<(char*)r->buf+r->sz; a=(void*)((char*)a+a->com.next)) {
    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(ui,a->lnk.parent)]++;
    tbl[find(res,a->lnk.parent)]++;
    else if (RED_COMP == a->com.type) {
    const uint index = cast(uint, res->comp_cnt++);
    ui->comp[index].usr = a->comp.usr;
    @@ -792,13 +803,14 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    ext[res->ext_cnt++].addr = (addr)dst;
    }
    } /* IV.) setup tree nodes */
    for (int i = 1; i < r->max_com-1; ++i)
    tree[i+1].sub = tbl[i] + tbl[i-1];
    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=r->buf; (char*)a<(char*)r->buf+r->sz; a=(void*)((char*)a+a->com.next)) {
    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);
    tree[id].parent = a->lnk.parent;
    nodes[tree[id].sub + tree[id].cnt++] = a->lnk.child;
    } /* V.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    @@ -837,12 +849,12 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    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},",
    fprintf(fp, "%s{.id = %lu, .parent = %lu, .cnt = %d, .sub = %d},\n",
    tab, n->id, n->parent, n->cnt, n->sub);
    } fputs("};\n", fp);
    fprintf(fp, "global const uint %s[] = {\n", com->nodes);
    fprintf(fp, "global const uint %s[] = {", com->nodes);
    for (int i = 0; i < ui->node_cnt; ++i) {
    if (i && !(i & 31)) fputs("\n", fp);
    if (!(i & 31)) fprintf(fp,"\n%s", tab);
    fprintf(fp, "%lu,", ui->nodes[i]);
    } fputs("};\n",fp);
    for (int i = 0; i < com->cnt; ++i) {
    @@ -860,22 +872,23 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    tab,e->info.id, e->info.size, e->info.align, lnk->name, cnt++);
    } if (!ui->ext_cnt) fputs("0", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s[cntof(%s)];\n",com->buffer,com->def);
    fprintf(fp, "global int %s[cntof(%s)];\n",com->seq,com->def);
    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_cnt = cntof(%s),\n%s.con_cnt = cntof(%s),\n", tab,com->def,tab,com->con);
    fprintf(fp, "%s.ext_cnt = cntof(%s)\n,%s.buffer = %s\n",tab,com->ext,tab,com->buffer);
    fprintf(fp, "%s.comp = %s,\n%s.tree = %s,\n%s.seq = %s,\n", tab,com->comp, tab, com->tree,tab,com->seq);
    fprintf(fp, "%s.def = %s,\n%s.con = %s,\n%s.ext = %s,\n};\n",tab,com->def,tab,com->con,tab,com->ext);
    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,
    void *mem, int siz, struct surface *scrn)
    {
    ctx->layout = ui;
    ctx->cur = ctx->stk[0] = 0;
    reducer_begin(&ctx->red, type, mem, siz);
    reducer_init(&ctx->red, type, mem, siz);
    ui->comp[ROOT].surf = scrn;
    for (int i = 0; i < ui->comp_cnt; ++i)
    ui->comp[i].pressed = ui->comp[i].released = ui->comp[i].dragged = 0;
    @@ -885,14 +898,14 @@ Begin(struct context *ctx, struct layout *ui, enum layout_type type,
    struct component *c = ui->comp + i;
    c->id = cast(uint, n->id);
    setup(c, ui->def + i, scrn);
    } ui->init = True;
    } ui->init = true;
    }
    }
    api void
    End(struct context *ctx)
    {
    struct layout *ui = ctx->layout;
    reducer_end(&ctx->red, ui, &ui->requested);
    reducer_calc(&ctx->red, ui, &ui->requested);
    ctx->layout = ctx->root;
    ctx->stk[0] = ctx->cur = 0;
    }
  5. vurtun renamed this gist May 16, 2017. 1 changed file with 18 additions and 20 deletions.
    38 changes: 18 additions & 20 deletions ui.h → ui.c
    Original file line number Diff line number Diff line change
    @@ -19,15 +19,15 @@ struct rect {int x,y,w,h;};
    #define rect(x,y,w,h) (struct rect){x,y,w,h}

    #define cast(t,p) ((t)(p))
    #define unused(...) unused_impl(0,__VA_ARGS__)
    #define zero(d,sz) memset(d,0,(size_t)(sz))
    #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 copy(d,s,sz) ((int)(((char*)d+sz)-((char*)memcpy(d,s,(size_t)(sz)))))
    #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 mod(x,N) ((uint)(((unsigned long long)(x)*(unsigned long long)(N)) >> 32))
    #define align(x, mask) ((void*)(((long long)((const char*)(x)+(mask-1))&~(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))
    @@ -94,14 +94,13 @@ enum component_flags {
    HIDDEN = flag(0),
    INTERACTIVE = flag(1),
    PAINTABLE = flag(2),
    LAYER = flag(3),
    STATIC = flag(4),
    BACKGROUND = flag(5),

    STATIC = flag(3),
    BACKGROUND = flag(4),
    IS_LAYER = flag(5),
    LAYER = IS_LAYER|PAINTABLE,
    IS_MOVABLE_X = flag(6),
    IS_MOVABLE_Y = flag(7),
    IS_MOVABLE = IS_MOVABLE_X|IS_MOVABLE_Y,

    MOVABLE_X = IS_MOVABLE_X|INTERACTIVE,
    MOVABLE_Y = IS_MOVABLE_Y|INTERACTIVE,
    MOVABLE = IS_MOVABLE|INTERACTIVE,
    @@ -275,7 +274,7 @@ api void reducer_push(struct reducer*, union reducible*, in void *data, int sz,
    #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)
    #define reducer_custom(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_end(struct reducer*, in struct layout*, out int *mem);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt);
    @@ -504,7 +503,7 @@ 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 & LAYER))
    if (!(c->flags & PAINTABLE) || (c->flags & IS_LAYER))
    continue;
    if (ui->def[i].paint)
    ui->def[i].paint(ctx,ui,c);
    @@ -514,23 +513,21 @@ api void
    draw(struct context *ctx, struct layout *ui, uint root_id)
    {
    uint head = 0;
    uint *tbl = ui->seq;
    uint *stk = ui->buffer;
    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 & LAYER) && (c->flags & PAINTABLE)) {
    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 & LAYER) && (c->id != root_id))) continue;
    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];
    @@ -657,7 +654,7 @@ reducer_begin(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;
    r->max_ext = r->payload = r->sz = 0;
    }
    api void
    reducer_push(struct reducer *r, union reducible *a,
    @@ -815,7 +812,7 @@ 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 *cons[] = {"CON_CPY","CON_SET","CON_MIN","CON_MAX"};
    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;
    @@ -830,7 +827,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    if (d->flags & HIDDEN) n += copy(buf+n, "|HIDDEN",12);
    if (d->flags & INTERACTIVE) n += copy(buf+n, "|INTERACTABLE",18);
    if (d->flags & PAINTABLE) n += copy(buf+n, "|PAINTABLE",15);
    if (d->flags & LAYER) n += copy(buf+n, "|LAYER",11);
    if (d->flags & IS_LAYER) n += copy(buf+n, "|LAYER",11);
    if (d->flags & IS_MOVABLE_X) n += copy(buf+n, "|MOVABLE_X",15);
    if (d->flags & IS_MOVABLE_Y) n += copy(buf+n, "|MOVABLE_Y",15);
    buf[n] = 0;
    @@ -897,5 +894,6 @@ End(struct context *ctx)
    struct layout *ui = ctx->layout;
    reducer_end(&ctx->red, ui, &ui->requested);
    ctx->layout = ctx->root;
    ctx->stk[0] = ctx->cur = 0;
    }
    #endif
  6. vurtun revised this gist May 16, 2017. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -33,8 +33,6 @@ 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 min3(a,b,c) min(min(a,b),c)
    #define max3(a,b,c) max(max(a,b),c)
    #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))
  7. vurtun revised this gist May 16, 2017. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -707,12 +707,12 @@ reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    *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 ext);
    *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 ext);
    *mem += alignof(struct constraint) + alignof(struct extension);
    *mem += alignof(struct node) + alignof(int);
    *mem += ui->payload + r->payload;
    }
    @@ -746,7 +746,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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 ext));
    res->ext = ext = align(res->con + r->max_con, alignof(struct extension));
    res->payload = ui->payload + r->payload;
    res->comp_cnt = ui->con_cnt;
    res->con_cnt = ui->con_cnt;
    @@ -761,7 +761,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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 ext)*ui->ext_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) {
    @@ -807,7 +807,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    nodes[tree[id].sub + tree[id].cnt++] = a->lnk.child;
    } /* V.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    const struct ext *e = 0;
    const struct extension *e = 0;
    map(ui, ed[i]->info.id, e)
    ed[i]->link(res, (void*)e->addr);
    }
  8. vurtun revised this gist May 16, 2017. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -269,7 +269,7 @@ api void reposit(FILE*, in struct layout*, in struct commit*);
    api inline uint lookup(in struct layout*, uint id);
    #define find(ui,id) ((ui->tbl)?lookup(ui,id):id)
    #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t))
    #define apply(ui,t,f,u) for(const struct ext *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)
    #define apply(ui,t,f,u) for(const struct extension *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)

    /* fold */
    api void reducer_begin(struct reducer*, enum layout_type, void *mem, int sz);
    @@ -724,7 +724,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    struct node *tree;
    struct definition *def;
    struct constraint *con;
    struct ext *ext;
    struct extension *ext;
    uint *nodes;

    /* 0.) setup temp node memory for stage III and IV */
    @@ -858,7 +858,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    }
    fprintf(fp, "global const struct extension %s[] = {", com->ext);
    for (int i = 0; i < com->cnt; ++i) {
    const struct ext *e = 0; int cnt = 0;
    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{.id = %lu, .size = %d, .align = %d, .addr = (addr)&%s[%u]},",
  9. vurtun revised this gist May 16, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ui.h
    Original file line number Diff line number Diff line change
    @@ -277,7 +277,7 @@ api void reducer_push(struct reducer*, union reducible*, in void *data, int sz,
    #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_custom(r,t,p,s,a) reducer_push(r,&(union reducible){.custom={._={.type=RED_CUSTOM},.id=t}},p,s,a)
    #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_end(struct reducer*, in struct layout*, out int *mem);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt);
  10. vurtun revised this gist May 16, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ui.h
    Original file line number Diff line number Diff line change
    @@ -161,7 +161,7 @@ struct constraint {
    };

    /* reduce */
    enum reducible_type {RED_COMP, RED_CON, RED_LNK, RED_CUSTOM};
    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;
  11. vurtun revised this gist May 16, 2017. 1 changed file with 25 additions and 25 deletions.
    50 changes: 25 additions & 25 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -167,7 +167,7 @@ union reducible {
    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 custom {struct com _; uint id; int align, size, off;} custom;
    struct ext {struct com _; uint id; int align, size, off;} ext;
    };
    struct module {
    struct definition def;
    @@ -177,7 +177,7 @@ struct module {
    int cnt;
    };
    struct reducer {
    int mode, custom;
    int mode, payload;
    int max_com, max_con;
    int max_ext, max_nodes;
    void *buf; int sz, cap;
    @@ -194,7 +194,7 @@ struct extdef {
    commit_f commit;
    link_f link;
    };
    struct ext {
    struct extension {
    struct extinfo info;
    addr addr;
    };
    @@ -226,15 +226,15 @@ struct layout {
    struct component *comp;
    const struct definition *def;
    const struct constraint *con;
    const struct ext *ext;
    const struct extension *ext;
    uint *buffer, *tbl, *seq;

    int comp_cnt, con_cnt;
    int ext_cnt, tbl_cnt;
    int node_cnt;

    int requested;
    int custom;
    int payload;
    int size;
    };

    @@ -659,7 +659,7 @@ reducer_begin(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->custom = r->sz = 0;
    r->max_ext = r->payload = r->sz = 0;
    }
    api void
    reducer_push(struct reducer *r, union reducible *a,
    @@ -675,11 +675,11 @@ reducer_push(struct reducer *r, union reducible *a,
    case RED_COMP: r->max_com++; break;
    case RED_CON: r->max_con++; break;
    case RED_LNK: r->max_nodes++; break;
    case RED_CUSTOM:
    r->custom += size + a->custom.align;
    a->custom.off = (int)(usr - end);
    a->custom.align = align;
    a->custom.size = size;
    case RED_EXT:
    r->payload += size + a->ext.align;
    a->ext.off = (int)(usr - end);
    a->ext.align = align;
    a->ext.size = size;
    r->max_ext++;
    default: break;}

    @@ -714,7 +714,7 @@ reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    *mem += alignof(struct component) + alignof(struct definition);
    *mem += alignof(struct constraint) + alignof(struct ext);
    *mem += alignof(struct node) + alignof(int);
    *mem += ui->custom + r->custom;
    *mem += ui->payload + r->payload;
    }
    api void
    fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    @@ -747,13 +747,13 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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 ext));
    res->custom = ui->custom + r->custom;
    res->payload = ui->payload + r->payload;
    res->comp_cnt = ui->con_cnt;
    res->con_cnt = ui->con_cnt;
    res->ext_cnt = ui->ext_cnt;
    res->init = True;
    res->size = sz;
    void *custom = ext + r->max_ext;
    void *payload = ext + r->max_ext;
    *ret = res;

    /* II.) copy old state */
    @@ -766,9 +766,9 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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(custom, ext[i].info.align);
    ext[i].addr = (addr)align(payload, ext[i].info.align);
    copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].info.size);
    custom = (char*)ext[i].addr + ext[i].info.size;
    payload = (char*)ext[i].addr + ext[i].info.size;
    }
    /* III.) add new state */
    for (a=r->buf; (char*)a<(char*)r->buf+r->sz; a=(void*)((char*)a+a->com.next)) {
    @@ -785,15 +785,15 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    insert(res->tbl, res->tbl_cnt, a->comp.tid, index);
    } else ui->comp[index].id = tree[index].id = index;
    setup(ui->comp + index, def + index, ui->comp[0].surf);
    } else if (RED_CUSTOM == a->com.type) {
    const void *obj = (const void*)((const char*)a + a->custom.off);
    char *dst = align(custom, a->custom.align);
    copy(dst, obj, a->custom.size);
    custom = dst + a->custom.size;

    ext[res->ext_cnt].info.id = a->custom.id;
    ext[res->ext_cnt].info.size = a->custom.size;
    ext[res->ext_cnt].info.align = a->custom.align;
    } 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 */
  12. vurtun revised this gist May 16, 2017. 1 changed file with 13 additions and 16 deletions.
    29 changes: 13 additions & 16 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -23,11 +23,11 @@ struct rect {int x,y,w,h;};
    #define zero(d,sz) memset(d,0,(size_t)(sz))
    #define szof(a) ((int)sizeof(a))
    #define cntof(a) ((int)(sizeof(a)/sizeof((a)[0])))
    #define copy(d,s,sz) ((int)(((char*)dst+sz)-((char*)memcpy(d,s,(size_t)sz))))
    #define copy(d,s,sz) ((int)(((char*)d+sz)-((char*)memcpy(d,s,(size_t)(sz)))))
    #define fourcc(a,b,c,d) ((unsigned long)(((d)<<24)|((c)<<16)|((b)<<8)|(a)))
    #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 mod(x,N) ((uint)(((unsigned long long)x*(unsigned long long)N) >> 32))
    #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))
    @@ -249,7 +249,7 @@ struct context {
    };
    /* context */
    api void update(struct context*, struct layout*, XEvent*);
    api void blueprint(struct layout*, struct context*);
    api void blueprint(struct context*,struct layout*);
    api void paint(struct context*,struct layout*);
    api void draw(struct context*, struct layout*, uint root);
    api void clear(struct context*);
    @@ -295,10 +295,6 @@ api void dispatch(uint index, struct context*, struct layout*, XEvent*);
    #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}}

    #endif /* UI_INCLUDED */

    #ifdef UI_IMPLEMENTATION

    #include <assert.h>

    intern inline int
    @@ -361,7 +357,7 @@ adjust(struct context *ctx, struct layout *ui,

    solve(c, L, R, CX, W, CX, W);
    solve(c, T, B, CY, H, CY, H);
    blueprint(ui, ctx);
    blueprint(ctx, ui);
    layouting(ui);
    }
    intern inline void
    @@ -546,7 +542,7 @@ draw(struct context *ctx, struct layout *ui, uint root_id)
    ui->buffer = stk;
    }
    api void
    blueprint(struct layout *ui, struct context *ctx)
    blueprint(struct context *ctx, struct layout *ui)
    {
    uint head = 0, tail = 1;
    uint *que = ui->buffer; que[tail] = ROOT;
    @@ -595,7 +591,7 @@ update(struct context *ctx, struct layout *ui, XEvent *e)
    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->layout, ctx);
    blueprint(ctx, ui);
    layouting(ui);
    for (int i = 0; i < ui->comp_cnt; ++i)
    dispatch(cast(uint,i),ctx,ui,e);
    @@ -825,7 +821,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    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 = {%u,%s}, .src = {%u,%s}, .cons = {.mul = %.2ff, .off = %d}}, .anch = %s},\n",
    fprintf(fp, "%s{{.eq = %s, .dst = {%lu,%s}, .src = {%lu,%s}, .cons = {.mul = %.2ff, .off = %d}}, .anch = %s},\n",
    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]);
    } fputs("};\n", fp);
    @@ -846,12 +842,13 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    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 = %u, .parent = %u, .cnt = %d, .sub = %d},",tab, n->id, n->parent, n->cnt, n->sub);
    fprintf(fp, "%s{.id = %lu, .parent = %lu, .cnt = %d, .sub = %d},",
    tab, n->id, n->parent, n->cnt, n->sub);
    } fputs("};\n", fp);
    fprintf(fp, "global const uint %s[] = {\n", com->nodes);
    for (int i = 0; i < ui->node_cnt; ++i) {
    if (i && !(i & 31)) fputs("\n", fp);
    fprintf(fp, "%d,", ui->nodes[i]);
    fprintf(fp, "%lu,", ui->nodes[i]);
    } fputs("};\n",fp);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extlnk *lnk = com->ext_list + i;
    @@ -864,13 +861,13 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    const struct ext *e = 0; int cnt = 0;
    const struct extlnk *lnk = com->ext_list + i;
    map(ui, lnk->def->info.id, e)
    fprintf(fp, "\n%s{.id = %d, .size = %d, .align = %d, .addr = (addr)&%s[%u]},",
    fprintf(fp, "\n%s{.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", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s[cntof(%s)];\n",com->buffer,com->def);
    fprintf(fp, "global int %s[cntof(%s)];\n",com->seq,com->def);
    fprintf(fp, "global struct component %s[cntof(%s)];\n", com->comp,ui->comp_cnt,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_cnt = cntof(%s),\n%s.con_cnt = cntof(%s),\n", tab,com->def,tab,com->con);
    fprintf(fp, "%s.ext_cnt = cntof(%s)\n,%s.buffer = %s\n",tab,com->ext,tab,com->buffer);
    @@ -903,4 +900,4 @@ End(struct context *ctx)
    reducer_end(&ctx->red, ui, &ui->requested);
    ctx->layout = ctx->root;
    }
    #endif
    #endif
  13. vurtun revised this gist May 15, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -861,8 +861,8 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    }
    fprintf(fp, "global const struct extension %s[] = {", com->ext);
    for (int i = 0; i < com->cnt; ++i) {
    const struct ext *e = 0;
    int cnt = 0; const struct extlnk *lnk = com->ext_list + i;
    const struct ext *e = 0; int cnt = 0;
    const struct extlnk *lnk = com->ext_list + i;
    map(ui, lnk->def->info.id, e)
    fprintf(fp, "\n%s{.id = %d, .size = %d, .align = %d, .addr = (addr)&%s[%u]},",
    tab,e->info.id, e->info.size, e->info.align, lnk->name, cnt++);
  14. vurtun revised this gist May 15, 2017. 1 changed file with 8 additions and 8 deletions.
    16 changes: 8 additions & 8 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -650,6 +650,14 @@ update(struct context *ctx, struct layout *ui, XEvent *e)
    } break;}
    }
    api void
    clear(struct context *ctx)
    {
    struct layout *ui = ctx->layout;
    for (int i = 1; i < ui->comp_cnt; ++i)
    if (ui->comp[i].surf)
    surf_del(ui->comp[i].surf);
    }
    api void
    reducer_begin(struct reducer *r, enum layout_type type, void *mem, int sz)
    {
    r->mode = type;
    @@ -895,12 +903,4 @@ End(struct context *ctx)
    reducer_end(&ctx->red, ui, &ui->requested);
    ctx->layout = ctx->root;
    }
    api void
    clear(struct context *ctx)
    {
    struct layout *ui = ctx->layout;
    for (int i = 1; i < ui->comp_cnt; ++i)
    if (ui->comp[i].surf)
    surf_del(ui->comp[i].surf);
    }
    #endif
  15. vurtun revised this gist May 15, 2017. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -20,10 +20,10 @@ struct rect {int x,y,w,h;};

    #define cast(t,p) ((t)(p))
    #define unused(...) unused_impl(0,__VA_ARGS__)
    #define copy(d,s,sz) memcpy(d,s,(size_t)(sz))
    #define zero(d,sz) memset(d,0,(size_t)(sz))
    #define szof(a) ((int)sizeof(a))
    #define cntof(a) ((int)(sizeof(a)/sizeof((a)[0])))
    #define copy(d,s,sz) ((int)(((char*)dst+sz)-((char*)memcpy(d,s,(size_t)sz))))
    #define fourcc(a,b,c,d) ((unsigned long)(((d)<<24)|((c)<<16)|((b)<<8)|(a)))
    #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))))
    @@ -825,12 +825,12 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    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) {copy(buf+n, "|HIDDEN",12); n += 12;}
    if (d->flags & INTERACTIVE) {copy(buf+n, "|INTERACTABLE",18); n += 18;}
    if (d->flags & PAINTABLE) {copy(buf+n, "|PAINTABLE",15); n += 15;}
    if (d->flags & LAYER) {copy(buf+n, "|LAYER",11); n += 11;}
    if (d->flags & IS_MOVABLE_X) {copy(buf+n, "|MOVABLE_X",15); n += 15;}
    if (d->flags & IS_MOVABLE_Y) {copy(buf+n, "|MOVABLE_Y",15); n += 15;}
    if (d->flags & HIDDEN) n += copy(buf+n, "|HIDDEN",12);
    if (d->flags & INTERACTIVE) n += copy(buf+n, "|INTERACTABLE",18);
    if (d->flags & PAINTABLE) n += copy(buf+n, "|PAINTABLE",15);
    if (d->flags & LAYER) n += copy(buf+n, "|LAYER",11);
    if (d->flags & IS_MOVABLE_X) n += copy(buf+n, "|MOVABLE_X",15);
    if (d->flags & IS_MOVABLE_Y) n += copy(buf+n, "|MOVABLE_Y",15);
    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);
  16. vurtun revised this gist May 15, 2017. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -860,8 +860,9 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    tab,e->info.id, e->info.size, e->info.align, lnk->name, cnt++);
    } if (!ui->ext_cnt) fputs("0", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s[%d];\nglobal int %s[%d];\n", com->buffer,ui->comp_cnt,com->seq,ui->comp_cnt);
    fprintf(fp, "global struct component %s[%d];\n", com->comp,ui->comp_cnt);
    fprintf(fp, "global int %s[cntof(%s)];\n",com->buffer,com->def);
    fprintf(fp, "global int %s[cntof(%s)];\n",com->seq,com->def);
    fprintf(fp, "global struct component %s[cntof(%s)];\n", com->comp,ui->comp_cnt,com->def);
    fprintf(fp, "global struct layout %s = {\n", com->layout);
    fprintf(fp, "%s.comp_cnt = cntof(%s),\n%s.con_cnt = cntof(%s),\n", tab,com->def,tab,com->con);
    fprintf(fp, "%s.ext_cnt = cntof(%s)\n,%s.buffer = %s\n",tab,com->ext,tab,com->buffer);
  17. vurtun revised this gist May 15, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ui.h
    Original file line number Diff line number Diff line change
    @@ -861,7 +861,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    } if (!ui->ext_cnt) fputs("0", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s[%d];\nglobal int %s[%d];\n", com->buffer,ui->comp_cnt,com->seq,ui->comp_cnt);
    fprintf(fp, "global struct component %s[%d];\n", com->comp,ui->comp_cn);
    fprintf(fp, "global struct component %s[%d];\n", com->comp,ui->comp_cnt);
    fprintf(fp, "global struct layout %s = {\n", com->layout);
    fprintf(fp, "%s.comp_cnt = cntof(%s),\n%s.con_cnt = cntof(%s),\n", tab,com->def,tab,com->con);
    fprintf(fp, "%s.ext_cnt = cntof(%s)\n,%s.buffer = %s\n",tab,com->ext,tab,com->buffer);
  18. vurtun revised this gist May 15, 2017. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -201,10 +201,10 @@ struct ext {

    /* commit */
    struct commit {
    const char *nodes;
    const char *comp, *nodes;
    const char *con, *def;
    const char *tree, *ext;
    const char *comp, *buffer;
    const char *buffer, *seq;
    const char *layout, *tab;
    const struct extlnk {
    const char *name;
    @@ -860,11 +860,12 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    tab,e->info.id, e->info.size, e->info.align, lnk->name, cnt++);
    } if (!ui->ext_cnt) fputs("0", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s;\nglobal struct component %s;\n", com->buffer,com->comp);
    fprintf(fp, "global int %s[%d];\nglobal int %s[%d];\n", com->buffer,ui->comp_cnt,com->seq,ui->comp_cnt);
    fprintf(fp, "global struct component %s[%d];\n", com->comp,ui->comp_cn);
    fprintf(fp, "global struct layout %s = {\n", com->layout);
    fprintf(fp, "%s.comp_cnt = cntof(%s),\n%s.con_cnt = cntof(%s),\n", tab,com->def,tab,com->con);
    fprintf(fp, "%s.ext_cnt = cntof(%s)\n,%s.buffer = %s\n",tab,com->ext,tab,com->buffer);
    fprintf(fp, "%s.comp = %s,\n%s.tree = %s,\n", tab,com->comp, tab, com->tree);
    fprintf(fp, "%s.comp = %s,\n%s.tree = %s,\n%s.seq = %s,\n", tab,com->comp, tab, com->tree,tab,com->seq);
    fprintf(fp, "%s.def = %s,\n%s.con = %s,\n%s.ext = %s,\n};\n",tab,com->def,tab,com->con,tab,com->ext);
    }
    api void
  19. vurtun revised this gist May 15, 2017. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -273,11 +273,11 @@ api inline uint lookup(in struct layout*, uint id);

    /* fold */
    api void reducer_begin(struct reducer*, enum layout_type, void *mem, int sz);
    api void reducer_cons(struct reducer*, union reducible*, in void *data, int sz, int ualign);
    #define reducer_comp(r,ID,def,user) reducer_cons(r,&(union reducible){.comp={.tid=ID,._={.type=RED_COMP},.d=def,.usr=user}},0,0,0)
    #define reducer_con(r,c) reducer_cons(r,&(union reducible){.con={._={.type=RED_CON},.con=c}},0,0,0)
    #define reducer_lnk(r,c,p) reducer_cons(r,&(union reducible){.lnk={._={.type=RED_LNK},.child=c,.parent=p}},0,0,0)
    #define reducer_custom(r,t,p,s,a) reducer_cons(r,&(union reducible){.custom={._={.type=RED_CUSTOM},.id=t}},p,s,a)
    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_custom(r,t,p,s,a) reducer_push(r,&(union reducible){.custom={._={.type=RED_CUSTOM},.id=t}},p,s,a)
    api void reducer_add(struct reducer*, in struct module*, int cnt);
    api void reducer_end(struct reducer*, in struct layout*, out int *mem);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt);
    @@ -658,7 +658,7 @@ reducer_begin(struct reducer *r, enum layout_type type, void *mem, int sz)
    r->max_ext = r->custom = r->sz = 0;
    }
    api void
    reducer_cons(struct reducer *r, union reducible *a,
    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);
  20. vurtun revised this gist May 15, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ui.h
    Original file line number Diff line number Diff line change
    @@ -842,7 +842,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    } fputs("};\n", fp);
    fprintf(fp, "global const uint %s[] = {\n", com->nodes);
    for (int i = 0; i < ui->node_cnt; ++i) {
    if (i && i & 31) fputs("\n", fp);
    if (i && !(i & 31)) fputs("\n", fp);
    fprintf(fp, "%d,", ui->nodes[i]);
    } fputs("};\n",fp);
    for (int i = 0; i < com->cnt; ++i) {
  21. vurtun revised this gist May 15, 2017. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -127,10 +127,10 @@ struct component {
    uint flags;
    int zorder;

    uint pressed:1;
    uint released:1;
    uint hovered:1;
    uint dragged:1;
    unsigned pressed:1;
    unsigned released:1;
    unsigned hovered:1;
    unsigned dragged:1;

    struct v2 off, total;
    struct surface *surf;
  22. vurtun revised this gist May 15, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ui.h
    Original file line number Diff line number Diff line change
    @@ -53,7 +53,7 @@ struct layout;
    struct context;
    struct component;

    typedef unsigned uint;
    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*);
  23. vurtun revised this gist May 15, 2017. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,7 @@ static inline void unused_impl(int dummy,...){(void)dummy;}
    #define h64(s,i,x) h16(s,i,h16(s,i+16,h16(s,i+32,h16(s,i+48,x))))
    #define idx(s,i) ((uint)(h64(s,0,i)^(h64(s,0,i)>>16)))
    #define id(s) idx(s,42)
    #define tid(id,off) (((id) & (~(31u<<28)))|((off&31)>>28))
    #define tid(id,off) (((id)&(~(255u<<24u)))|((off&255u)<<24u))

    struct layout;
    struct context;
    @@ -420,8 +420,8 @@ eval(const struct function *f, int *d, int src)
    intern void
    insert(uint *tbl, int tbl_cnt, uint tid, uint val)
    {
    #define off(id) (((id) >> 28u) & 31u)
    #define key(id) ((id) & (~(31u << 28u)))
    #define off(id) (((id)>>24u)&255u)
    #define key(id) ((id)&(~(255u<<24u)))
    uint key = key(tid), off = off(tid);
    if (off) return;
    uint begin = mod(key, cast(uint, tbl_cnt)), id = begin;
  24. vurtun revised this gist May 15, 2017. 1 changed file with 192 additions and 164 deletions.
    356 changes: 192 additions & 164 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    #define UI_INCLUDED_

    #include <stdio.h> /* fputs, fprintf */
    #include <string.h> /* memcpy */
    #include <string.h> /* memcpy, memset */

    #define out
    #define in const
    @@ -18,16 +18,16 @@ 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 stringify(x) #x
    #define cast(t,p) ((t)p)
    #define cast(t,p) ((t)(p))
    #define unused(...) unused_impl(0,__VA_ARGS__)
    #define copy(d,s,sz) memcpy(d,s,(size_t)(sz))
    #define zero(d,sz) memset(d,0,(size_t)(sz))
    #define szof(a) ((int)sizeof(a))
    #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 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 mod(x,N) ((unsigned)(((unsigned long long)x*(unsigned long long)N) >> 32))
    #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))
    @@ -45,13 +45,15 @@ static inline void unused_impl(int dummy,...){(void)dummy;}
    #define h4(s,i,x) h1(s,i,h1(s,i+1,h1(s,i+2,h1(s,i+3,x))))
    #define h16(s,i,x) h4(s,i,h4(s,i+4,h4(s,i+8,h4(s,i+12,x))))
    #define h64(s,i,x) h16(s,i,h16(s,i+16,h16(s,i+32,h16(s,i+48,x))))
    #define idx(s,i) ((unsigned)(h64(s,0,i)^(h64(s,0,i)>>16)))
    #define id(s) idx(s,0)
    #define idx(s,i) ((uint)(h64(s,0,i)^(h64(s,0,i)>>16)))
    #define id(s) idx(s,42)
    #define tid(id,off) (((id) & (~(31u<<28)))|((off&31)>>28))

    struct layout;
    struct context;
    struct component;

    typedef unsigned 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*);
    @@ -73,12 +75,14 @@ enum interaction_type {
    };
    struct interaction {
    enum interaction_type type;
    union {int i; unsigned f; signal_f s;} src;
    union {int i; uint f; signal_f s;} src;
    void *dst;
    };
    struct interaction_slots {
    struct interaction left;
    struct interaction right;
    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;
    @@ -105,25 +109,29 @@ enum component_flags {
    MOVABLE = IS_MOVABLE|INTERACTIVE,
    };
    struct node {
    unsigned id, parent;
    #define MAX_NODES_PER_NODE 32
    unsigned sub[MAX_NODES_PER_NODE];
    int cnt;
    uint id, parent;
    int sub, cnt;
    };
    struct definition {
    handle_f handle;
    paint_f paint;
    blueprint_f blueprint;
    struct rect size;
    unsigned flags;
    uint flags;
    int zorder;
    const char *i, *p, *b;
    };
    struct component {
    unsigned id;
    uint id;
    int attr[ATTR_CNT];
    unsigned flags;
    uint flags;
    int zorder;

    uint pressed:1;
    uint released:1;
    uint hovered:1;
    uint dragged:1;

    struct v2 off, total;
    struct surface *surf;
    struct interaction_slots slot;
    @@ -139,7 +147,7 @@ enum condition_eq {
    };
    enum constraint_eq {CON_CPY, CON_SET, CON_MIN, CON_MAX};
    struct cons {float mul; int off;};
    struct var {unsigned comp; int attr;};
    struct var {uint comp; int attr;};
    struct function {
    int eq;
    struct var dst;
    @@ -156,29 +164,29 @@ struct constraint {
    enum reducible_type {RED_COMP, RED_CON, RED_LNK, RED_CUSTOM};
    union reducible {
    struct com {int type, next;} com;
    struct comp {struct com _; unsigned id; struct definition d; addr usr;} comp;
    struct comp {struct com _; uint tid; struct definition d; addr usr;} comp;
    struct con {struct com _; struct constraint con;} con;
    struct lnk {struct com _; unsigned child, parent;} lnk;
    struct custom {struct com _; unsigned id; int align, size, off;} custom;
    struct lnk {struct com _; uint child, parent;} lnk;
    struct custom {struct com _; uint id; int align, size, off;} custom;
    };
    struct module {
    struct definition def;
    unsigned id, parent;
    int cnt;
    uint tid, parent;
    #define MODULE_MAX_CON 16
    struct constraint con[MODULE_MAX_CON];
    int cnt;
    };
    struct reducer {
    int mode;
    int mode, custom;
    int max_com, max_con;
    int max_ext, custom;
    int max_ext, max_nodes;
    void *buf; int sz, cap;
    };

    /* extension */
    struct extinfo {
    const char *type;
    unsigned id;
    uint id;
    int size, align;
    };
    struct extdef {
    @@ -193,37 +201,41 @@ struct ext {

    /* commit */
    struct commit {
    const char *nodes;
    const char *con, *def;
    const char *tree, *ext;
    const char *comp, *buffer;
    const char *layout;
    const char *tab;
    int cnt;
    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;
    unsigned active;
    unsigned origin;
    unsigned hot;
    uint active;
    uint origin;
    uint hot;

    unsigned *buffer, *tbl;
    const uint *nodes;
    const struct node *tree;
    struct component *comp;
    const struct definition *def;
    const struct node *tree;
    const struct constraint *con;
    const struct ext *ext;
    uint *buffer, *tbl, *seq;

    int comp_cnt, con_cnt;
    int ext_cnt, tbl_cnt;
    int node_cnt;

    int requested;
    int custom;
    int size;
    };

    /* context */
    @@ -233,45 +245,46 @@ struct context {
    struct layout *root;
    struct layout *layout;
    #define MAX_COMPONENT_DEPTH 32
    unsigned cur, stk[MAX_COMPONENT_DEPTH];

    uint cur, stk[MAX_COMPONENT_DEPTH];
    };
    /* context */
    api void begin(struct context*, struct layout*, enum layout_type, void*, int, struct surface*);
    api void end(struct context*);
    api void update(struct context*, struct layout*, XEvent*);
    api void blueprint(struct layout*, struct context*);
    api void paint(struct context*,struct layout*);
    api void draw(struct context*, struct layout*, unsigned root);
    api void draw(struct context*, struct layout*, uint root);
    api void clear(struct context*);
    #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);

    /* declaration */
    api void Begin(struct context*, struct layout*, enum layout_type, 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 layouting(struct layout*);
    api void reorder(struct layout*, struct component*);
    api void reposit(FILE*, in struct layout*, in struct commit*);
    api inline unsigned lookup(in struct layout*, unsigned id);
    api inline uint lookup(in struct layout*, uint id);
    #define find(ui,id) ((ui->tbl)?lookup(ui,id):id)
    #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t))
    #define apply(ui,t,f,u) for(const struct ext *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)

    /* fold */
    api void reducer_begin(struct reducer*, enum layout_type, void *mem, int sz);
    api void reducer_cons(struct reducer*, union reducible*, in void *data, int sz, int ualign);
    #define reducer_comp(r,ID,def,user) reducer_cons(r,&(union reducible){.comp={.id=ID,._={.type=RED_COMP},.d=def,.usr=user}},0,0,0)
    #define reducer_comp(r,ID,def,user) reducer_cons(r,&(union reducible){.comp={.tid=ID,._={.type=RED_COMP},.d=def,.usr=user}},0,0,0)
    #define reducer_con(r,c) reducer_cons(r,&(union reducible){.con={._={.type=RED_CON},.con=c}},0,0,0)
    #define reducer_lnk(r,c,p) reducer_cons(r,&(union reducible){.lnk={._={.type=RED_LNK},.child=c,.parent=p}},0,0,0)
    #define reducer_custom(r,t,p,s,a) reducer_cons(r,&(union reducible){.custom={._={.type=RED_CUSTOM},.id=t}},p,s,a)
    api void reducer_mod(struct reducer*, in struct module*, int cnt);
    api void reducer_add(struct reducer*, in struct module*, int cnt);
    api void reducer_end(struct reducer*, in struct layout*, out int *mem);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, out void *mem, in struct extdef**, int cnt);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, void*mem, int sz, in struct extdef**, int cnt);

    /* component */
    api inline unsigned parent(struct layout *ui, unsigned id);
    api void adjust(struct context*, struct layout*, struct component*, XEvent*, void*);
    api void dispatch(struct component*,struct context*, struct layout*, XEvent*);
    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}}
    @@ -283,8 +296,9 @@ api void dispatch(struct component*,struct context*, struct layout*, XEvent*);
    #define bind_signal(d,u) (struct interaction){INTERACTION_SIGNAL, .dst = (void*)(addr)u, .src = {.s = d}}

    #endif /* UI_INCLUDED */
    #include "ui.h"
    #include "draw.h"

    #ifdef UI_IMPLEMENTATION

    #include <assert.h>

    intern inline int
    @@ -335,19 +349,11 @@ solve(struct component *c, int lo, int hi,
    c->attr[center] = c->attr[lo] + (c->attr[len]/2);
    c->attr[len] = max(c->attr[len], 0);
    }
    api inline unsigned
    parent(struct layout *ui, unsigned id)
    {
    const struct node *n = ui->tree + id;
    if (n->parent != n->id)
    return n->parent;
    return 0;
    }
    api void
    adjust(struct context *ctx, struct layout *ui,
    struct component *c, XEvent *e, void *usr)
    {
    unused(ctx,ui,usr);
    unused(usr);
    if (c->flags & IS_MOVABLE_X)
    c->attr[CX] += e->xmotion.x_root;
    if (c->flags & IS_MOVABLE_Y)
    @@ -375,7 +381,7 @@ setup(struct component *c, const struct definition *d, struct surface *s)
    if (!(d->flags & HIDDEN))
    c->attr[ACT] = true;
    if (d->flags & PAINTABLE)
    c->surf = surf_mk(s->dpy, s->root, s->scrn, c->attr[W], c->attr[H]);
    c->surf = surf_mk(s->canvas, c->attr[W], c->attr[H]);
    if (d->flags & IS_MOVABLE)
    c->slot.dragged = bind_signal(adjust, 0);
    }
    @@ -412,38 +418,41 @@ eval(const struct function *f, int *d, int src)
    *d = max(*d, ceili(f->cons.mul*s)+f->cons.off);
    }
    intern void
    insert(unsigned *tbl, int tbl_cnt, unsigned key, unsigned val)
    insert(uint *tbl, int tbl_cnt, uint tid, uint val)
    {
    unsigned begin = mod(key,cast(unsigned, tbl_cnt)), id = begin;
    do {unsigned index = tbl[id];
    #define off(id) (((id) >> 28u) & 31u)
    #define key(id) ((id) & (~(31u << 28u)))
    uint key = key(tid), off = off(tid);
    if (off) return;
    uint begin = mod(key, cast(uint, tbl_cnt)), id = begin;
    do {uint index = tbl[id];
    if ((id == 0 && val != 0) || index) continue;
    tbl[id] = val; return;
    } while ((mod(++id, cast(unsigned, tbl_cnt))) != begin);
    } while ((mod(++id, cast(uint, tbl_cnt))) != begin);
    }
    api unsigned
    lookup(const struct layout *ui, unsigned key)
    api uint
    lookup(const struct layout *ui, uint tid)
    {
    assert(ui->tbl && "use 'find' instead");
    unsigned begin = mod(key,cast(unsigned,ui->tbl_cnt)), id = begin;
    do {unsigned index = ui->tbl[id];
    uint key = key(tid), off = off(tid);
    uint begin = mod(key, cast(uint, ui->tbl_cnt)), id = begin;
    do {uint index = ui->tbl[id];
    if (!index) return 0;
    struct component *c = ui->comp + index;
    if (c->id == id) return id;
    } while ((mod(++id, cast(unsigned, ui->tbl_cnt))) != begin);
    if (c->id == id) return id+off;
    } while ((mod(++id, cast(uint, ui->tbl_cnt))) != begin);
    return 0;
    }
    intern unsigned
    intern uint
    at(const struct layout *ui, const struct component *c, int *x, int *y)
    {
    loop:; unsigned id = c->id;
    unsigned tbl[MAX_NODES_PER_NODE];
    const struct node *n = ui->tree + find(ui,id);
    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,n->sub[i])].zorder] = n->sub[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]);
    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;}
    @@ -455,11 +464,11 @@ 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, parent(ui,c->id));
    struct component *p = ui->comp + find(ui,n->parent);
    if (p->flags & STATIC) goto next;
    n = ui->tree + find(ui,p->id);
    for (int i = 0; i < n->cnt; ++i) {
    struct component *sub = ui->comp + find(ui,n->sub[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--;
    @@ -469,11 +478,13 @@ reorder(struct layout *ui, struct component *c)
    return;
    }
    api inline void
    dispatch(struct component *c, struct context *ctx, struct layout *ui, XEvent *e)
    dispatch(uint id, struct context *ctx, struct layout *ui, XEvent *e)
    {
    do if (ui->def[c->id].handle)
    if (ui->def[c->id].handle(ctx,ui,c,e)) break;
    while ((c = ui->comp + parent(ui,c->id)));
    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,
    @@ -484,9 +495,9 @@ interact(struct context *ctx, struct layout *ui, struct component *c,
    else if (INTERACTION_SET == i->type)
    {int *v = (int*)i->dst; *v = i->src.i;}
    else if (INTERACTION_ENABLE == i->type)
    {unsigned *v = (unsigned*)i->dst; *v |= i->src.f;}
    {uint *v = (uint*)i->dst; *v |= i->src.f;}
    else if (INTERACTION_DISABLE == i->type)
    {unsigned *v = (unsigned*)i->dst; *v &= ~i->src.f;}
    {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) ?
    @@ -506,11 +517,11 @@ paint(struct context *ctx, struct layout *ui)
    }
    }
    api void
    draw(struct context *ctx, struct layout *ui, unsigned root_id)
    draw(struct context *ctx, struct layout *ui, uint root_id)
    {
    unsigned head = 0;
    unsigned *stk = ui->buffer;
    unsigned tbl[MAX_NODES_PER_NODE];
    uint head = 0;
    uint *tbl = ui->seq;
    uint *stk = ui->buffer;
    struct component *r = ui->comp + find(ui, root_id);

    stk[head++] = root_id;
    @@ -528,7 +539,7 @@ draw(struct context *ctx, struct layout *ui, unsigned root_id)
    if (!c->attr[ACT] || ((c->flags & 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,n->sub[i])].zorder] = n->sub[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];
    }
    @@ -537,20 +548,16 @@ draw(struct context *ctx, struct layout *ui, unsigned root_id)
    api void
    blueprint(struct layout *ui, struct context *ctx)
    {
    unsigned head = 0, tail = 1;
    unsigned *que = ui->buffer;
    que[tail] = ROOT;
    uint head = 0, tail = 1;
    uint *que = ui->buffer; que[tail] = ROOT;
    while (head < tail) {
    const struct node *n;
    n = ui->tree + find(ui, que[++head]);
    const struct node *n = ui->tree + find(ui, que[++head]);
    for (int i = 0; i < n->cnt; ++i)
    que[++tail] = n->sub[i];
    que[++tail] = ui->nodes[n->sub+i];
    }
    for (unsigned i = tail; i > 0; --i) {
    const struct definition *d;
    struct component *c;
    d = ui->def + find(ui, que[i]);
    c = ui->comp + find(ui, que[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);
    }
    @@ -579,11 +586,7 @@ api void
    update(struct context *ctx, struct layout *ui, XEvent *e)
    {
    switch (e->type) {
    default: {
    struct component *a;
    a = ui->comp + find(ui,ui->active);
    dispatch(a, ctx, ui, e);
    } break;
    default: dispatch(ui->active, ctx, ui, e); break;
    case Expose:
    case ConfigureNotify: {
    /* resize event */
    @@ -595,30 +598,33 @@ update(struct context *ctx, struct layout *ui, XEvent *e)
    blueprint(ctx->layout, ctx);
    layouting(ui);
    for (int i = 0; i < ui->comp_cnt; ++i)
    dispatch(ui->comp + i,ctx,ui,e);
    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;
    unsigned last_hot = ui->hot;
    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->hovered = false;
    e->xmotion.type = Leave;
    interact(ctx, ui, prev, &prev->slot.leave, e);
    dispatch(prev,ctx, ui, e);
    dispatch(prev->id, ctx, ui, e);

    cur->hovered = true;
    e->xmotion.type = Enter;
    interact(ctx, ui, cur, &cur->slot.enter, e);
    dispatch(cur,ctx, ui, 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,ctx,ui,e);
    dispatch(a->id,ctx,ui,e);
    a->dragged = true;
    }
    e->xmotion.x = mx, e->xmotion.y = my;
    e->xmotion.type = MotionNotify;
    @@ -629,13 +635,18 @@ update(struct context *ctx, struct layout *ui, XEvent *e)
    ui->active = (down) ? ui->hot: ui->active;
    ui->origin = (down) ? ui->hot: ROOT;
    struct component *a = ui->comp + find(ui,ui->active);
    if (e->xbutton.button == Button1 && down) {
    reorder(ui, a);
    interact(ctx,ui, a, &a->slot.left, e);
    } else if (e->xbutton.button == Button3 && down){
    reorder(ui, a);
    interact(ctx,ui, a, &a->slot.right, e);
    } dispatch(a,ctx,ui,e);
    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
    @@ -659,40 +670,42 @@ reducer_cons(struct reducer *r, union reducible *a,
    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_CUSTOM:
    r->custom = size + a->custom.align;
    r->custom += size + a->custom.align;
    a->custom.off = (int)(usr - end);
    a->custom.align = align;
    a->custom.size = size;
    r->max_ext++;
    default: break;}

    copy(end, a, szof(union reducible));
    copy(end, a, szof(*a));
    copy(usr, dat, size);
    r->sz += a->com.next;
    }
    api void
    reducer_mod(struct reducer *r, in struct module *m, int cnt)
    reducer_add(struct reducer *r, in struct module *m, int cnt)
    {
    for (int i = 0; i < cnt; ++i) {
    in struct module *c = m + i;
    reducer_comp(r,c->id,c->def,0);
    reducer_lnk(r,c->id,c->parent);
    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_end(struct reducer *r, in struct layout *ui, out int *mem)
    {
    r->max_ext += ui->ext_cnt;
    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 ext);
    *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 ext);
    @@ -701,12 +714,20 @@ reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    }
    api void
    fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    void *mem, in struct extdef **ed, int cnt)
    void *mem, int sz, in struct extdef **ed, int cnt)
    {
    union reducible *a;
    struct node *tree;
    struct definition *def;
    struct constraint *con;
    struct ext *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((char*)r->buf+r->max_nodes, 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));
    @@ -715,7 +736,9 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    res->tbl = align(res+1, alignof(int));
    res->buffer = res->tbl + res->tbl_cnt;
    } else res->buffer = align(res+1, alignof(int));
    res->comp = align(res->buffer + r->max_com+1, alignof(struct component));
    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));
    @@ -725,6 +748,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    res->con_cnt = ui->con_cnt;
    res->ext_cnt = ui->ext_cnt;
    res->init = True;
    res->size = sz;
    void *custom = ext + r->max_ext;
    *ret = res;

    @@ -735,31 +759,28 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    copy(con, ui->con, szof(struct constraint)*ui->con_cnt);
    copy(ext, ui->ext, szof(struct ext)*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, (unsigned)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(custom, ext[i].info.align);
    copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].info.size);
    custom = (char*)ext[i].addr + ext[i].info.size;
    }
    /* III.) add new state */
    char *end = (char*)r->buf + r->sz;
    for (union reducible *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->com.next)) {
    if (RED_COMP == a->com.type) {
    const unsigned id = a->comp.id;
    const int index = res->comp_cnt++;
    ui->comp[index].id = tree[index].id = id;
    for (a=r->buf; (char*)a<(char*)r->buf+r->sz; a=(void*)((char*)a+a->com.next)) {
    if (RED_CON == a->com.type)
    con[res->con_cnt++] = a->con.con;
    else if (RED_LNK == a->com.type)
    tbl[find(ui,a->lnk.parent)]++;
    else if (RED_COMP == a->com.type) {
    const uint index = cast(uint, res->comp_cnt++);
    ui->comp[index].usr = a->comp.usr;
    def[index] = a->comp.d;
    if (r->mode == IMMEDIATE)
    insert(res->tbl, res->tbl_cnt, id, (unsigned)index);
    setup(ui->comp + id, def + id, ui->comp[0].surf);
    } else if (RED_CON == a->com.type) {
    con[res->con_cnt++] = a->con.con;
    } else if (RED_LNK == a->com.type) {
    struct node *n = tree + a->lnk.parent;
    n->sub[n->cnt] = a->lnk.child;
    res->comp[a->lnk.child].zorder = n->cnt++;
    if (r->mode == IMMEDIATE) {
    ui->comp[index].id = tree[index].id = a->comp.tid;
    insert(res->tbl, res->tbl_cnt, a->comp.tid, index);
    } else ui->comp[index].id = tree[index].id = index;
    setup(ui->comp + index, def + index, ui->comp[0].surf);
    } else if (RED_CUSTOM == a->com.type) {
    const void *obj = (const void*)((const char*)a + a->custom.off);
    char *dst = align(custom, a->custom.align);
    @@ -771,7 +792,16 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    ext[res->ext_cnt].info.align = a->custom.align;
    ext[res->ext_cnt++].addr = (addr)dst;
    }
    } /* IV.) setup extension state */
    } /* IV.) setup tree nodes */
    for (int i = 1; i < r->max_com-1; ++i)
    tree[i+1].sub = tbl[i] + tbl[i-1];
    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=r->buf; (char*)a<(char*)r->buf+r->sz; a=(void*)((char*)a+a->com.next)) {
    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;
    } /* V.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    const struct ext *e = 0;
    map(ui, ed[i]->info.id, e)
    @@ -795,31 +825,26 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    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)
    {copy(buf+n, "|HIDDEN",12); n += 12;}
    if (d->flags & INTERACTIVE)
    {copy(buf+n, "|INTERACTABLE",18); n += 18;}
    if (d->flags & PAINTABLE)
    {copy(buf+n, "|PAINTABLE",15); n += 15;}
    if (d->flags & LAYER)
    {copy(buf+n, "|LAYER",11); n += 11;}
    if (d->flags & IS_MOVABLE_X)
    {copy(buf+n, "|MOVABLE_X",15); n += 15;}
    if (d->flags & IS_MOVABLE_Y)
    {copy(buf+n, "|MOVABLE_Y",15); n += 15;}
    if (d->flags & HIDDEN) {copy(buf+n, "|HIDDEN",12); n += 12;}
    if (d->flags & INTERACTIVE) {copy(buf+n, "|INTERACTABLE",18); n += 18;}
    if (d->flags & PAINTABLE) {copy(buf+n, "|PAINTABLE",15); n += 15;}
    if (d->flags & LAYER) {copy(buf+n, "|LAYER",11); n += 11;}
    if (d->flags & IS_MOVABLE_X) {copy(buf+n, "|MOVABLE_X",15); n += 15;}
    if (d->flags & IS_MOVABLE_Y) {copy(buf+n, "|MOVABLE_Y",15); n += 15;}
    buf[n] = 0;
    fprintf(fp, "%s{TBL(%s,%s,%s), .sz={%d,%d,%d,%d}, .flags = %s},\n",
    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 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 = %u, .parent = %u, .cnt = %d, .sub = {",tab, n->id, n->parent, n->cnt);
    for (int j = 0; j < n->cnt; ++j)
    fprintf(fp, "%u,", n->sub[j]);
    if (!n->cnt) fputs("0", fp);
    fputs("}},\n", fp);
    fprintf(fp, "%s{.id = %u, .parent = %u, .cnt = %d, .sub = %d},",tab, n->id, n->parent, n->cnt, n->sub);
    } fputs("};\n", fp);
    fprintf(fp, "global const uint %s[] = {\n", com->nodes);
    for (int i = 0; i < ui->node_cnt; ++i) {
    if (i && i & 31) fputs("\n", fp);
    fprintf(fp, "%d,", ui->nodes[i]);
    } fputs("};\n",fp);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extlnk *lnk = com->ext_list + i;
    fprintf(fp, "global const %s %s[] = {", lnk->def->info.type, lnk->name);
    @@ -843,24 +868,26 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    fprintf(fp, "%s.def = %s,\n%s.con = %s,\n%s.ext = %s,\n};\n",tab,com->def,tab,com->con,tab,com->ext);
    }
    api void
    begin(struct context *ctx, struct layout *ui, enum layout_type type,
    Begin(struct context *ctx, struct layout *ui, enum layout_type type,
    void *mem, int siz, struct surface *scrn)
    {
    ctx->layout = ui;
    ctx->cur = ctx->stk[0] = 0;
    reducer_begin(&ctx->red, type, mem, siz);
    ui->comp[ROOT].surf = scrn;
    for (int i = 0; i < ui->comp_cnt; ++i)
    ui->comp[i].pressed = ui->comp[i].released = 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(unsigned,n->id);
    c->id = cast(uint, n->id);
    setup(c, ui->def + i, scrn);
    } ui->init = True;
    }
    }
    api void
    end(struct context *ctx)
    End(struct context *ctx)
    {
    struct layout *ui = ctx->layout;
    reducer_end(&ctx->red, ui, &ui->requested);
    @@ -874,3 +901,4 @@ clear(struct context *ctx)
    if (ui->comp[i].surf)
    surf_del(ui->comp[i].surf);
    }
    #endif
  25. vurtun revised this gist May 9, 2017. 1 changed file with 160 additions and 166 deletions.
    326 changes: 160 additions & 166 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -19,14 +19,15 @@ struct rect {int x,y,w,h;};
    #define rect(x,y,w,h) (struct rect){x,y,w,h}

    #define stringify(x) #x
    #define cast(t,p) ((t)p)
    #define unused(...) unused_impl(0,__VA_ARGS__)
    #define copy(d,s,sz) memcpy(d,s,(size_t)(sz))
    #define szof(a) ((int)sizeof(a))
    #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 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 mod(x,N) ((unsigned)(((unsigned long long)x * (unsigned long long)N) >> 32))
    #define mod(x,N) ((unsigned)(((unsigned long long)x*(unsigned long long)N) >> 32))
    static inline void unused_impl(int dummy,...){(void)dummy;}

    #define flag(n) (1<<(n))
    @@ -50,7 +51,6 @@ static inline void unused_impl(int dummy,...){(void)dummy;}
    struct layout;
    struct context;
    struct component;
    struct extension;

    typedef unsigned long long addr;
    typedef void(*paint_f)(struct context*,struct layout*,struct component*);
    @@ -85,8 +85,7 @@ struct interaction_slots {
    };

    /* component */
    #define COMPONENT_ROOT 0
    #define TBL(hn,pn,bp) .handle=hn,.paint=pn,.blueprint = bp,.i=#hn, .p=#pn, .b=#bp
    #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,
    @@ -115,7 +114,7 @@ struct definition {
    handle_f handle;
    paint_f paint;
    blueprint_f blueprint;
    struct rect sz;
    struct rect size;
    unsigned flags;
    int zorder;
    const char *i, *p, *b;
    @@ -132,16 +131,13 @@ struct component {
    };

    /* constraint */
    enum constraint_eq {
    CONSTRAINT_CPY, CONSTRAINT_SET,
    CONSTRAINT_MIN, CONSTRAINT_MAX
    };
    enum condition_eq {
    CONDITION_TRUE, CONDITION_FALSE,
    CONDITION_EQ, CONDITION_NEQ,
    CONDITION_GR, CONDITION_LS,
    CONDITION_GRE, CONDITION_LSE
    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 {unsigned comp; int attr;};
    struct function {
    @@ -153,27 +149,24 @@ struct function {
    struct constraint {
    struct function self;
    struct function cond;
    enum attributes anch;
    int anch;
    };

    /* fold */
    enum layout_type {
    RETAINED,
    IMMEDIATE
    /* reduce */
    enum reducible_type {RED_COMP, RED_CON, RED_LNK, RED_CUSTOM};
    union reducible {
    struct com {int type, next;} com;
    struct comp {struct com _; unsigned id; struct definition d; addr usr;} comp;
    struct con {struct com _; struct constraint con;} con;
    struct lnk {struct com _; unsigned child, parent;} lnk;
    struct custom {struct com _; unsigned id; int align, size, off;} custom;
    };
    enum action_id {
    ACTION_MKCOMP,
    ACTION_MKCON,
    ACTION_LNKNODE,
    ACTION_CUSTOM,
    };
    struct common {int type, next;};
    union action {
    struct common com;
    struct {struct common _; unsigned id; struct definition d; addr usr;} mkcomp;
    struct {struct common _; struct constraint con;} mkcon;
    struct {struct common _; unsigned child, parent;} lnknode;
    struct {struct common _; unsigned id; int align, size, off;} custom;
    struct module {
    struct definition def;
    unsigned id, parent;
    int cnt;
    #define MODULE_MAX_CON 16
    struct constraint con[MODULE_MAX_CON];
    };
    struct reducer {
    int mode;
    @@ -183,20 +176,28 @@ struct reducer {
    };

    /* extension */
    struct extinfo {const char *type; unsigned id; int size, align;};
    struct extension {struct extinfo info; addr addr;};
    struct extinfo {
    const char *type;
    unsigned id;
    int size, align;
    };
    struct extdef {
    struct extinfo info;
    commit_f commit;
    link_f link;
    };
    struct ext {
    struct extinfo info;
    addr addr;
    };

    /* commit */
    struct commit {
    const char *con, *def;
    const char *tree, *ext;
    const char *comp, *buffer;
    const char *layout;
    const char *tab;
    int cnt;
    const struct extlnk {
    const char *name;
    @@ -205,35 +206,40 @@ struct commit {
    };

    /* layout */
    enum layout_type {RETAINED,IMMEDIATE};
    struct layout {
    int initialized;
    int init;
    unsigned active;
    unsigned origin;
    unsigned hot;

    unsigned *buffer, *tbl;
    struct component *comp;
    const struct definition *def;
    const struct node *tree;
    const struct constraint *con;
    const struct extension *ext;
    const struct ext *ext;

    int comp_cnt, con_cnt;
    int ext_cnt, tbl_cnt;
    int requested;
    int custom;
    };

    /* context */
    #define MAX_COMPONENT_DEPTH 32
    #define ROOT 0
    struct context {
    struct reducer red;
    struct layout root;
    struct layout *root;
    struct layout *layout;
    #define MAX_COMPONENT_DEPTH 32
    unsigned cur, stk[MAX_COMPONENT_DEPTH];
    unsigned active;
    unsigned origin;
    unsigned hot;

    };
    /* context */
    api void begin(struct context*, struct layout*, enum layout_type, void*, int, struct surface*);
    api void end(struct context*);
    api void update(struct context*, XEvent*);
    api void update(struct context*, struct layout*, XEvent*);
    api void blueprint(struct layout*, struct context*);
    api void paint(struct context*,struct layout*);
    api void draw(struct context*, struct layout*, unsigned root);
    @@ -245,32 +251,29 @@ api void clear(struct context*);
    /* layout */
    api void layouting(struct layout*);
    api void reorder(struct layout*, struct component*);
    api void reposit(FILE*, in struct layout*, const struct commit*, const char *tab);
    api inline unsigned lookup(const struct layout*, unsigned id);
    api void reposit(FILE*, in struct layout*, in struct commit*);
    api inline unsigned lookup(in struct layout*, unsigned id);
    #define find(ui,id) ((ui->tbl)?lookup(ui,id):id)
    #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t))
    #define apply(ui,t,f,u) for(const struct extension *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)
    #define apply(ui,t,f,u) for(const struct ext *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)

    /* fold */
    api void reducer_begin(struct reducer*, enum layout_type, void *mem, int sz);
    api void reducer_cons(struct reducer*, union action*, in void *data, int sz, int ualign);
    api void reducer_push(struct reducer*, in struct layout*);
    #define mkcomp(r,ID,def,user) reducer_cons(r,&(union action){.mkcomp={.id=ID,.h={.type=ACTION_MKCOMP},.d=def,.usr=user}},0,0,0)
    #define mkcon(r,c) reducer_cons(r,&(union action){.mkcon={._={.type=ACTION_MKCON},.con=c}},0,0,0)
    #define lnknode(r,c,p) reducer_cons(r,&(union action){.lnknode={._={.type=ACTION_LNKNODE},.child=c,.parent=p}},0,0,0)
    #define reducer_add(r,t,p,s,a) reducer_cons(r,&(union action){.custom={._={.type=ACTION_CUSTOM},.id=t}},p,s,a)
    api void reducer_cons(struct reducer*, union reducible*, in void *data, int sz, int ualign);
    #define reducer_comp(r,ID,def,user) reducer_cons(r,&(union reducible){.comp={.id=ID,._={.type=RED_COMP},.d=def,.usr=user}},0,0,0)
    #define reducer_con(r,c) reducer_cons(r,&(union reducible){.con={._={.type=RED_CON},.con=c}},0,0,0)
    #define reducer_lnk(r,c,p) reducer_cons(r,&(union reducible){.lnk={._={.type=RED_LNK},.child=c,.parent=p}},0,0,0)
    #define reducer_custom(r,t,p,s,a) reducer_cons(r,&(union reducible){.custom={._={.type=RED_CUSTOM},.id=t}},p,s,a)
    api void reducer_mod(struct reducer*, in struct module*, int cnt);
    api void reducer_end(struct reducer*, in struct layout*, out int *mem);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, out void *mem, const struct extdef**, int cnt);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, out void *mem, in struct extdef**, int cnt);

    /* component */
    api inline struct component* component_parent(struct layout*, unsigned id);
    api inline unsigned parent(struct layout *ui, unsigned id);
    api void adjust(struct context*, struct layout*, struct component*, XEvent*, void*);
    api void dispatch(struct component*,struct context*, struct layout*, XEvent*);
    #define DECLARATION_BEGIN(name) enum name {__##name##_ROOT_,
    #define DECLARATION_END };

    /* bindings */
    #define slot_con(i,f) *(i) = (f)
    #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}}
    @@ -280,7 +283,6 @@ api void dispatch(struct component*,struct context*, struct layout*, XEvent*);
    #define bind_signal(d,u) (struct interaction){INTERACTION_SIGNAL, .dst = (void*)(addr)u, .src = {.s = d}}

    #endif /* UI_INCLUDED */

    #include "ui.h"
    #include "draw.h"
    #include <assert.h>
    @@ -351,17 +353,16 @@ adjust(struct context *ctx, struct layout *ui,
    if (c->flags & IS_MOVABLE_Y)
    c->attr[CY] += e->xmotion.y_root;

    solve(c, L, R, CX, W, L, W);
    solve(c, T, B, CY, H, T, H);
    solve(c, L, R, CX, W, CX, W);
    solve(c, T, B, CY, H, CY, H);
    blueprint(ui, ctx);
    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[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);
    }
    @@ -370,64 +371,65 @@ setup(struct component *c, const struct definition *d, struct surface *s)
    {
    c->flags = d->flags;
    c->zorder = d->zorder;
    build(c, d->sz.x, d->sz.y, d->sz.w, d->sz.h);
    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->dpy, s->root, s->scrn, c->attr[W], c->attr[H]);
    if (d->flags & IS_MOVABLE)
    slot_con(&c->slot.dragged, bind_signal(adjust, 0));
    c->slot.dragged = bind_signal(adjust, 0);
    }
    intern inline int
    cond(const struct function *f, int d, int src)
    {
    float s = (float)src;
    if (f->eq == CONDITION_TRUE) return 1;
    else if (f->eq == CONDITION_FALSE) return 0;
    else if (f->eq == CONDITION_EQ)
    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 == CONDITION_NEQ)
    else if (f->eq == COND_NEQ)
    return d != ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_GR)
    else if (f->eq == COND_GR)
    return d > ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_LS)
    else if (f->eq == COND_LS)
    return d < ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_GRE)
    else if (f->eq == COND_GRE)
    return d >= ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_LSE)
    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 = (float)src;
    if (f->eq == CONSTRAINT_CPY) *d = src;
    else if (f->eq == CONSTRAINT_SET)
    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 == CONSTRAINT_MIN)
    else if (f->eq == CON_MIN)
    *d = min(*d, ceili(f->cons.mul*s)+f->cons.off);
    else if (f->eq == CONSTRAINT_MAX)
    else if (f->eq == CON_MAX)
    *d = max(*d, ceili(f->cons.mul*s)+f->cons.off);
    }
    intern void
    insert(unsigned *tbl, int tbl_cnt, unsigned key, unsigned val)
    {
    unsigned begin = mod(key,(unsigned)tbl_cnt), id = begin;
    unsigned begin = mod(key,cast(unsigned, tbl_cnt)), id = begin;
    do {unsigned index = tbl[id];
    if ((id == 0 && val != 0) || index) continue;
    tbl[id] = val; return;
    } while ((++id % (unsigned)tbl_cnt) != begin);
    } while ((mod(++id, cast(unsigned, tbl_cnt))) != begin);
    }
    api unsigned
    lookup(const struct layout *ui, unsigned id)
    lookup(const struct layout *ui, unsigned key)
    {
    assert(ui->tbl && "use 'find' instead");
    unsigned begin = mod(id, (unsigned)ui->tbl_cnt), index = begin;
    do {struct component *c = ui->comp + ui->tbl[index];
    if (!c->id) break;
    if (c->id == id) return index;
    } while ((++index % (unsigned)ui->tbl_cnt) != begin);
    unsigned begin = mod(key,cast(unsigned,ui->tbl_cnt)), id = begin;
    do {unsigned index = ui->tbl[id];
    if (!index) return 0;
    struct component *c = ui->comp + index;
    if (c->id == id) return id;
    } while ((mod(++id, cast(unsigned, ui->tbl_cnt))) != begin);
    return 0;
    }
    intern unsigned
    @@ -453,17 +455,16 @@ 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,parent(ui,c->id));
    if (p->flags & STATIC) goto nxt;
    struct component *p = ui->comp + find(ui, parent(ui,c->id));
    if (p->flags & STATIC) goto next;
    n = ui->tree + find(ui,p->id);
    for (int i = 0; i < n->cnt; ++i) {
    struct component *sub = ui->comp + find(ui,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;
    } c->zorder = max(n->cnt-1, 0);
    next: c = p;
    }
    return;
    }
    @@ -538,7 +539,7 @@ blueprint(struct layout *ui, struct context *ctx)
    {
    unsigned head = 0, tail = 1;
    unsigned *que = ui->buffer;
    que[tail] = COMPONENT_ROOT;
    que[tail] = ROOT;
    while (head < tail) {
    const struct node *n;
    n = ui->tree + find(ui, que[++head]);
    @@ -575,20 +576,18 @@ layouting(struct layout *ui)
    }
    }
    api void
    update(struct context *ctx, XEvent *e)
    update(struct context *ctx, struct layout *ui, XEvent *e)
    {
    struct layout *ui;
    ui = ctx->layout;
    switch (e->type) {
    default: {
    struct component *a;
    a = ui->comp + ctx->active;
    a = ui->comp + find(ui,ui->active);
    dispatch(a, ctx, ui, e);
    } break;
    case Expose:
    case ConfigureNotify: {
    /* resize event */
    struct component *c = ui->comp + COMPONENT_ROOT;
    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);
    @@ -601,11 +600,11 @@ update(struct context *ctx, XEvent *e)
    case MotionNotify: {
    /* enter and leave event */
    int mx = e->xmotion.x, my = e->xmotion.y;
    unsigned last_hot = ctx->hot;
    ctx->hot = at(ui, ui->comp, &e->xmotion.x, &e->xmotion.y);
    if (last_hot != ctx->hot) {
    unsigned last_hot = ui->hot;
    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 + ctx->hot;
    struct component *cur = ui->comp + ui->hot;

    e->xmotion.type = Leave;
    interact(ctx, ui, prev, &prev->slot.leave, e);
    @@ -614,10 +613,9 @@ update(struct context *ctx, XEvent *e)
    e->xmotion.type = Enter;
    interact(ctx, ui, cur, &cur->slot.enter, e);
    dispatch(cur,ctx, ui, e);
    e->xmotion.type = MotionNotify;
    } /* dragging event */
    if (ctx->active == ctx->origin) {
    struct component *a = ui->comp + find(ui,ctx->active);
    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,ctx,ui,e);
    @@ -628,17 +626,16 @@ update(struct context *ctx, XEvent *e)
    case ButtonRelease:
    case ButtonPress: {
    int down = e->type == ButtonPress;
    ctx->active = (down) ? ctx->hot: ctx->active;
    ctx->origin = (down) ? ctx->hot: COMPONENT_ROOT;
    struct component *a = ui->comp + find(ui,ctx->active);
    ui->active = (down) ? ui->hot: ui->active;
    ui->origin = (down) ? ui->hot: ROOT;
    struct component *a = ui->comp + find(ui,ui->active);
    if (e->xbutton.button == Button1 && down) {
    reorder(ui, a);
    interact(ctx,ui, a, &a->slot.left, e);
    } else if (e->xbutton.button == Button3 && down){
    reorder(ui, a);
    interact(ctx,ui, a, &a->slot.right, e);
    }
    dispatch(a,ctx,ui,e);
    } dispatch(a,ctx,ui,e);
    } break;}
    }
    api void
    @@ -650,42 +647,40 @@ reducer_begin(struct reducer *r, enum layout_type type, void *mem, int sz)
    r->max_ext = r->custom = r->sz = 0;
    }
    api void
    reducer_cons(struct reducer *r, union action *a,
    const void *dat, int sz, int align)
    reducer_cons(struct reducer *r, union reducible *a,
    const void *dat, int size, int align)
    {
    /* calculate buffer offset for current action, next action and user data */
    assert(r->sz + szof(union action) + sz + align + alignof(union action) < r->cap);
    assert(r->sz + szof(union reducible) + size + align + alignof(union reducible) < r->cap);
    char *end = (char*)r->buf + r->sz;
    char *usr = align(end + szof(union action), align);
    char *nxt = end + r->sz + szof(union action) + sz + align;
    a->com.next = (int)((char*)(align(nxt, alignof(union action))) - end);

    if (ACTION_CUSTOM == a->com.type) {
    r->custom = sz + a->custom.align;
    char *usr = align(end + szof(union reducible), align);
    char *nxt = end + r->sz + szof(union reducible) + size + align;
    a->com.next = (int)((char*)(align(nxt, alignof(union reducible))) - end);

    switch (a->com.type) {
    case RED_COMP: r->max_com++; break;
    case RED_CON: r->max_con++; break;
    case RED_CUSTOM:
    r->custom = size + a->custom.align;
    a->custom.off = (int)(usr - end);
    a->custom.align = align;
    a->custom.size = sz;
    a->custom.size = size;
    r->max_ext++;
    } else if (ACTION_MKCOMP == a->com.type) {
    r->max_com++;
    } else if (ACTION_MKCON == a->com.type)
    r->max_con++;

    /* copy action and userdata to buffer */
    copy(end, a, szof(union action));
    copy(usr, dat, sz);
    default: break;}

    copy(end, a, szof(union reducible));
    copy(usr, dat, size);
    r->sz += a->com.next;
    }
    api void
    reducer_push(struct reducer *r, in struct layout *ui)
    reducer_mod(struct reducer *r, in struct module *m, int cnt)
    {
    for (int i = 0; i < ui->comp_cnt; ++i) {
    mkcomp(r, p->id, p->def, 0);
    lnknode(r, p->id, p->parent);
    } for (int i = 0; i < ui->con_cnt; ++i)
    mkcon(r, p->con[j]);
    for (int i = 0; i < ui->ext_cnt; ++i)
    reducer_add(r,t,p,s,a)
    for (int i = 0; i < cnt; ++i) {
    in struct module *c = m + i;
    reducer_comp(r,c->id,c->def,0);
    reducer_lnk(r,c->id,c->parent);
    for (int j = 0; j < c->cnt; ++j)
    reducer_con(r,c->con[j]);
    }
    }
    api void
    reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    @@ -695,41 +690,41 @@ reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    *mem = r->max_com * szof(struct node);
    *mem += r->max_com * szof(struct definition);
    *mem += r->max_con * szof(struct constraint);
    *mem += r->max_ext * szof(struct extension);
    *mem += r->max_com * szof(struct component);
    *mem += (r->max_com+1) * szof(int) * ((r->mode == RETAINED) ?1:3);
    *mem += r->max_ext * szof(struct ext);
    *mem += (r->max_com+1) * szof(int) * ((r->mode == RETAINED) ?1: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 constraint) + alignof(struct ext);
    *mem += alignof(struct node) + alignof(int);
    *mem += ui->custom + r->custom;
    }
    api void
    fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    void *mem, const struct extdef **ed, int cnt)
    void *mem, in struct extdef **ed, int cnt)
    {
    struct node *tree;
    struct definition *def;
    struct constraint *con;
    struct extension *ext;
    struct ext *ext;

    /* I.) setup layout in memory block */
    struct layout *res = align(mem, alignof(struct layout));
    if (r->mode == RETAINED) {
    res->tbl_cnt = 2 * r->max_com;
    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->comp = align(res->buffer + r->max_com+1, 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));
    res->ext = ext = align(res->con + r->max_con, alignof(struct ext));
    res->custom = ui->custom + r->custom;
    res->comp_cnt = ui->con_cnt;
    res->con_cnt = ui->con_cnt;
    res->ext_cnt = ui->ext_cnt;
    res->initialized = True;
    res->init = True;
    void *custom = ext + r->max_ext;
    *ret = res;

    @@ -738,7 +733,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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);
    copy(ext, ui->ext, szof(struct ext)*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, (unsigned)i);
    for (int i = 0; i < ui->ext_cnt; ++i) {
    @@ -749,23 +744,23 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    }
    /* III.) add new state */
    char *end = (char*)r->buf + r->sz;
    for (union action *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->com.next)) {
    if (ACTION_MKCOMP == a->com.type) {
    const unsigned id = a->mkcomp.id;
    for (union reducible *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->com.next)) {
    if (RED_COMP == a->com.type) {
    const unsigned id = a->comp.id;
    const int index = res->comp_cnt++;
    ui->comp[index].id = tree[index].id = id;
    ui->comp[index].usr = a->mkcomp.usr;
    def[index] = a->mkcomp.d;
    ui->comp[index].usr = a->comp.usr;
    def[index] = a->comp.d;
    if (r->mode == IMMEDIATE)
    insert(res->tbl, res->tbl_cnt, id, (unsigned)index);
    setup(ui->comp + id, def + id, ui->comp[0].surf);
    } else if (ACTION_MKCON == a->com.type) {
    con[res->con_cnt++] = a->mkcon.con;
    } else if (ACTION_LNKNODE == a->com.type) {
    struct node *n = tree + a->lnknode.parent;
    n->sub[n->cnt] = a->lnknode.child;
    res->comp[a->lnknode.child].zorder = n->cnt++;
    } else if (ACTION_CUSTOM == a->com.type) {
    } else if (RED_CON == a->com.type) {
    con[res->con_cnt++] = a->con.con;
    } else if (RED_LNK == a->com.type) {
    struct node *n = tree + a->lnk.parent;
    n->sub[n->cnt] = a->lnk.child;
    res->comp[a->lnk.child].zorder = n->cnt++;
    } else if (RED_CUSTOM == a->com.type) {
    const void *obj = (const void*)((const char*)a + a->custom.off);
    char *dst = align(custom, a->custom.align);
    copy(dst, obj, a->custom.size);
    @@ -778,17 +773,17 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    }
    } /* IV.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    const struct extension *e = 0;
    const struct ext *e = 0;
    map(ui, 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)
    reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    {
    tab = (!tab) ? " ": tab;
    const char *tab = (!com->tab) ? " ": com->tab;
    const char *attrs[] = {"0","ACT","L","T","R","B","W","H","CX","CY"};
    const char *cons[] = {"CONSTRAINT_CPY", "CONSTRAINT_SET", "CONSTRAINT_MIN", "CONSTRAINT_MAX"};
    const char *cons[] = {"CON_CPY", "CON_SET", "CON_MIN", "CON_MAX"};
    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;
    @@ -814,7 +809,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com, const char *ta
    {copy(buf+n, "|MOVABLE_Y",15); n += 15;}
    buf[n] = 0;
    fprintf(fp, "%s{TBL(%s,%s,%s), .sz={%d,%d,%d,%d}, .flags = %s},\n",
    tab,d->i, d->p, d->b, d->sz.x, d->sz.y, d->sz.w, d->sz.h, buf);
    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 struct node %s[] = {\n", com->tree);
    for (int i = 0; i < ui->comp_cnt; ++i) {
    @@ -833,7 +828,7 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com, const char *ta
    }
    fprintf(fp, "global const struct extension %s[] = {", com->ext);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extension *e = 0;
    const struct ext *e = 0;
    int cnt = 0; const struct extlnk *lnk = com->ext_list + i;
    map(ui, lnk->def->info.id, e)
    fprintf(fp, "\n%s{.id = %d, .size = %d, .align = %d, .addr = (addr)&%s[%u]},",
    @@ -854,23 +849,22 @@ begin(struct context *ctx, struct layout *ui, enum layout_type type,
    ctx->layout = ui;
    ctx->cur = ctx->stk[0] = 0;
    reducer_begin(&ctx->red, type, mem, siz);
    ui->comp[COMPONENT_ROOT].surf = scrn;
    if (!ui->initialized) {
    ui->initialized = True;
    ui->comp[ROOT].surf = scrn;
    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 = (unsigned int)n->id;
    c->id = cast(unsigned,n->id);
    setup(c, ui->def + i, scrn);
    }
    } ui->init = True;
    }
    }
    api void
    end(struct context *ctx)
    {
    struct layout *ui = ctx->layout;
    reducer_end(&ctx->red, ui, &ui->requested);
    ctx->layout = &ctx->root;
    ctx->layout = ctx->root;
    }
    api void
    clear(struct context *ctx)
  26. vurtun revised this gist May 9, 2017. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -26,6 +26,7 @@ struct rect {int x,y,w,h;};
    #define fourcc(a,b,c,d) ((unsigned long)(((d)<<24)|((c)<<16)|((b)<<8)|(a)))
    #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 mod(x,N) ((unsigned)(((unsigned long long)x * (unsigned long long)N) >> 32))
    static inline void unused_impl(int dummy,...){(void)dummy;}

    #define flag(n) (1<<(n))
    @@ -412,7 +413,7 @@ eval(const struct function *f, int *d, int src)
    intern void
    insert(unsigned *tbl, int tbl_cnt, unsigned key, unsigned val)
    {
    unsigned begin = key % (unsigned)tbl_cnt, id = begin;
    unsigned begin = mod(key,(unsigned)tbl_cnt), id = begin;
    do {unsigned index = tbl[id];
    if ((id == 0 && val != 0) || index) continue;
    tbl[id] = val; return;
    @@ -422,7 +423,7 @@ api unsigned
    lookup(const struct layout *ui, unsigned id)
    {
    assert(ui->tbl && "use 'find' instead");
    unsigned begin = id % (unsigned)ui->tbl_cnt, index = begin;
    unsigned begin = mod(id, (unsigned)ui->tbl_cnt), index = begin;
    do {struct component *c = ui->comp + ui->tbl[index];
    if (!c->id) break;
    if (c->id == id) return index;
  27. vurtun revised this gist May 4, 2017. 1 changed file with 359 additions and 88 deletions.
    447 changes: 359 additions & 88 deletions ui.h
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,285 @@
    #ifndef UI_INCLUDED_
    #define UI_INCLUDED_

    #include <stdio.h> /* fputs, fprintf */
    #include <string.h> /* memcpy */

    #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 stringify(x) #x
    #define unused(...) unused_impl(0,__VA_ARGS__)
    #define copy(d,s,sz) memcpy(d,s,(size_t)(sz))
    #define szof(a) ((int)sizeof(a))
    #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 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))))
    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 min3(a,b,c) min(min(a,b),c)
    #define max3(a,b,c) max(max(a,b),c)
    #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)<x0)||(y1>(y0+h0))||(y1+h1)<y0)))

    #define len(s) (cntof(s)-1)
    #define h1(s,i,x) (x*65599u+(unsigned char)s[(i)<len(s)?len(s)-1-(i):len(s)])
    #define h4(s,i,x) h1(s,i,h1(s,i+1,h1(s,i+2,h1(s,i+3,x))))
    #define h16(s,i,x) h4(s,i,h4(s,i+4,h4(s,i+8,h4(s,i+12,x))))
    #define h64(s,i,x) h16(s,i,h16(s,i+16,h16(s,i+32,h16(s,i+48,x))))
    #define idx(s,i) ((unsigned)(h64(s,0,i)^(h64(s,0,i)>>16)))
    #define id(s) idx(s,0)

    struct layout;
    struct context;
    struct component;
    struct extension;

    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_SET,
    INTERACTION_SIGNAL,
    INTERACTION_ENABLE,
    INTERACTION_DISABLE,
    INTERACTION_DRAG_X,
    INTERACTION_DRAG_Y,
    INTERACTION_CNT,
    };
    struct interaction {
    enum interaction_type type;
    union {int i; unsigned f; signal_f s;} src;
    void *dst;
    };
    struct interaction_slots {
    struct interaction left;
    struct interaction right;
    struct interaction dragged;
    struct interaction enter;
    struct interaction leave;
    };

    /* component */
    #define COMPONENT_ROOT 0
    #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),
    LAYER = flag(3),
    STATIC = flag(4),
    BACKGROUND = flag(5),

    IS_MOVABLE_X = flag(6),
    IS_MOVABLE_Y = flag(7),
    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 {
    unsigned id, parent;
    #define MAX_NODES_PER_NODE 32
    unsigned sub[MAX_NODES_PER_NODE];
    int cnt;
    };
    struct definition {
    handle_f handle;
    paint_f paint;
    blueprint_f blueprint;
    struct rect sz;
    unsigned flags;
    int zorder;
    const char *i, *p, *b;
    };
    struct component {
    unsigned id;
    int attr[ATTR_CNT];
    unsigned flags;
    int zorder;
    struct v2 off, total;
    struct surface *surf;
    struct interaction_slots slot;
    addr usr;
    };

    /* constraint */
    enum constraint_eq {
    CONSTRAINT_CPY, CONSTRAINT_SET,
    CONSTRAINT_MIN, CONSTRAINT_MAX
    };
    enum condition_eq {
    CONDITION_TRUE, CONDITION_FALSE,
    CONDITION_EQ, CONDITION_NEQ,
    CONDITION_GR, CONDITION_LS,
    CONDITION_GRE, CONDITION_LSE
    };
    struct cons {float mul; int off;};
    struct var {unsigned comp; int attr;};
    struct function {
    int eq;
    struct var dst;
    struct var src;
    struct cons cons;
    };
    struct constraint {
    struct function self;
    struct function cond;
    enum attributes anch;
    };

    /* fold */
    enum layout_type {
    RETAINED,
    IMMEDIATE
    };
    enum action_id {
    ACTION_MKCOMP,
    ACTION_MKCON,
    ACTION_LNKNODE,
    ACTION_CUSTOM,
    };
    struct common {int type, next;};
    union action {
    struct common com;
    struct {struct common _; unsigned id; struct definition d; addr usr;} mkcomp;
    struct {struct common _; struct constraint con;} mkcon;
    struct {struct common _; unsigned child, parent;} lnknode;
    struct {struct common _; unsigned id; int align, size, off;} custom;
    };
    struct reducer {
    int mode;
    int max_com, max_con;
    int max_ext, custom;
    void *buf; int sz, cap;
    };

    /* extension */
    struct extinfo {const char *type; unsigned id; int size, align;};
    struct extension {struct extinfo info; addr addr;};
    struct extdef {
    struct extinfo info;
    commit_f commit;
    link_f link;
    };

    /* commit */
    struct commit {
    const char *con, *def;
    const char *tree, *ext;
    const char *comp, *buffer;
    const char *layout;
    int cnt;
    const struct extlnk {
    const char *name;
    const struct extdef *def;
    } *ext_list;
    };

    /* layout */
    struct layout {
    int initialized;
    unsigned *buffer, *tbl;
    struct component *comp;
    const struct definition *def;
    const struct node *tree;
    const struct constraint *con;
    const struct extension *ext;
    int comp_cnt, con_cnt;
    int ext_cnt, tbl_cnt;
    int requested;
    int custom;
    };

    /* context */
    #define MAX_COMPONENT_DEPTH 32
    struct context {
    struct reducer red;
    struct layout root;
    struct layout *layout;
    unsigned cur, stk[MAX_COMPONENT_DEPTH];
    unsigned active;
    unsigned origin;
    unsigned hot;
    };
    /* context */
    api void begin(struct context*, struct layout*, enum layout_type, void*, int, struct surface*);
    api void end(struct context*);
    api void update(struct context*, XEvent*);
    api void blueprint(struct layout*, struct context*);
    api void paint(struct context*,struct layout*);
    api void draw(struct context*, struct layout*, unsigned root);
    api void clear(struct context*);
    #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);

    /* layout */
    api void layouting(struct layout*);
    api void reorder(struct layout*, struct component*);
    api void reposit(FILE*, in struct layout*, const struct commit*, const char *tab);
    api inline unsigned lookup(const struct layout*, unsigned id);
    #define find(ui,id) ((ui->tbl)?lookup(ui,id):id)
    #define map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->info.id==(t))
    #define apply(ui,t,f,u) for(const struct extension *_=(ui)->ext;(_)<(ui)->ext+(ui)->ext_cnt;++(_)) if((_)->info.id==(t)) f(u,(void*)_->addr)

    /* fold */
    api void reducer_begin(struct reducer*, enum layout_type, void *mem, int sz);
    api void reducer_cons(struct reducer*, union action*, in void *data, int sz, int ualign);
    api void reducer_push(struct reducer*, in struct layout*);
    #define mkcomp(r,ID,def,user) reducer_cons(r,&(union action){.mkcomp={.id=ID,.h={.type=ACTION_MKCOMP},.d=def,.usr=user}},0,0,0)
    #define mkcon(r,c) reducer_cons(r,&(union action){.mkcon={._={.type=ACTION_MKCON},.con=c}},0,0,0)
    #define lnknode(r,c,p) reducer_cons(r,&(union action){.lnknode={._={.type=ACTION_LNKNODE},.child=c,.parent=p}},0,0,0)
    #define reducer_add(r,t,p,s,a) reducer_cons(r,&(union action){.custom={._={.type=ACTION_CUSTOM},.id=t}},p,s,a)
    api void reducer_end(struct reducer*, in struct layout*, out int *mem);
    api void fold(out struct layout**, in struct reducer*, in struct layout*, out void *mem, const struct extdef**, int cnt);

    /* component */
    api inline struct component* component_parent(struct layout*, unsigned id);
    api void adjust(struct context*, struct layout*, struct component*, XEvent*, void*);
    api void dispatch(struct component*,struct context*, struct layout*, XEvent*);
    #define DECLARATION_BEGIN(name) enum name {__##name##_ROOT_,
    #define DECLARATION_END };

    /* bindings */
    #define slot_con(i,f) *(i) = (f)
    #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}}

    #endif /* UI_INCLUDED */

    #include "ui.h"
    #include "draw.h"
    #include <assert.h>
    @@ -50,12 +332,12 @@ solve(struct component *c, int lo, int hi,
    c->attr[center] = c->attr[lo] + (c->attr[len]/2);
    c->attr[len] = max(c->attr[len], 0);
    }
    api inline struct component*
    component_parent(struct layout *ui, unsigned id)
    api inline unsigned
    parent(struct layout *ui, unsigned id)
    {
    const struct node *n = ui->tree + id;
    if (n->parent != n->id)
    return ui->comp + n->parent;
    return n->parent;
    return 0;
    }
    api void
    @@ -92,7 +374,7 @@ setup(struct component *c, const struct definition *d, struct surface *s)
    c->attr[ACT] = true;
    if (d->flags & PAINTABLE)
    c->surf = surf_mk(s->dpy, s->root, s->scrn, c->attr[W], c->attr[H]);
    if ((d->flags & IS_MOVABLE))
    if (d->flags & IS_MOVABLE)
    slot_con(&c->slot.dragged, bind_signal(adjust, 0));
    }
    intern inline int
    @@ -154,7 +436,7 @@ at(const struct layout *ui, const struct component *c, int *x, int *y)
    unsigned tbl[MAX_NODES_PER_NODE];
    const struct node *n = ui->tree + find(ui,id);
    for (int i = 0; i < n->cnt; ++i)
    tbl[ui->comp[n->sub[i]].zorder] = n->sub[i];
    tbl[ui->comp[find(ui,n->sub[i])].zorder] = n->sub[i];

    *x += c->off.x; *y += c->off.y;
    for (int i = n->cnt-1; i >= 0; --i) {
    @@ -170,13 +452,11 @@ 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;
    p = component_parent(ui, c->id);
    struct component *p = ui->comp + find(ui,parent(ui,c->id));
    if (p->flags & STATIC) goto nxt;
    n = ui->tree + find(ui,p->id);
    for (int i = 0; i < n->cnt; ++i) {
    struct component *sub;
    sub = ui->comp + find(ui, n->sub[i]);
    struct component *sub = ui->comp + find(ui,n->sub[i]);
    if (sub->flags & BACKGROUND) continue;
    if (sub->zorder > c->zorder)
    sub->zorder--;
    @@ -187,12 +467,11 @@ reorder(struct layout *ui, struct component *c)
    return;
    }
    api inline void
    dispatch(struct component *c, struct context *ctx, struct layout *ui,
    XEvent *e)
    dispatch(struct component *c, struct context *ctx, struct layout *ui, XEvent *e)
    {
    do if (ui->def[c->id].handle)
    if (ui->def[c->id].handle(ctx,ui,c,e)) break;
    while ((c = component_parent(ui,c->id)));
    while ((c = ui->comp + parent(ui,c->id)));
    }
    intern void
    interact(struct context *ctx, struct layout *ui, struct component *c,
    @@ -247,7 +526,7 @@ draw(struct context *ctx, struct layout *ui, unsigned root_id)
    if (!c->attr[ACT] || ((c->flags & 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[n->sub[i]].zorder] = n->sub[i];
    tbl[ui->comp[find(ui,n->sub[i])].zorder] = n->sub[i];
    for (int i = n->cnt-1; i >= 0; --i)
    stk[head++] = tbl[i];
    }
    @@ -362,51 +641,50 @@ update(struct context *ctx, XEvent *e)
    } break;}
    }
    api void
    reducer_begin(struct reducer *r, enum fold_mode mode, void *mem, int sz)
    reducer_begin(struct reducer *r, enum layout_type type, void *mem, int sz)
    {
    r->mode = mode;
    r->mode = type;
    r->buf = mem; r->cap = sz;
    r->max_com = r->max_con = 0;
    r->max_ext = r->custom = r->sz = 0;
    }
    api void
    reducer_cons(struct reducer *r, union action *a,
    const void *dat, int sz, int ualign)
    const void *dat, int sz, int align)
    {
    storage const int align = alignof(union action);
    storage const int asz = szof(union action);
    assert((r->sz + asz + sz + ualign + align) < r->cap);

    /* calculate buffer offset for current action, next action and user data */
    assert(r->sz + szof(union action) + sz + align + alignof(union action) < r->cap);
    char *end = (char*)r->buf + r->sz;
    char *nxt = align(end + r->sz + asz + sz + ualign, align);
    char *usr = align(end + asz, ualign);
    a->h.nxt = (int)(nxt - end);
    char *usr = align(end + szof(union action), align);
    char *nxt = end + r->sz + szof(union action) + sz + align;
    a->com.next = (int)((char*)(align(nxt, alignof(union action))) - end);

    if (ACTION_CUSTOM == a->h.type) {
    if (ACTION_CUSTOM == a->com.type) {
    r->custom = sz + a->custom.align;
    a->custom.off = (int)(usr - end);
    a->custom.align = ualign;
    a->custom.sz = sz;
    a->custom.align = align;
    a->custom.size = sz;
    r->max_ext++;
    } else if (ACTION_MKCOMP == a->h.type)
    } else if (ACTION_MKCOMP == a->com.type) {
    r->max_com++;
    else if (ACTION_MKCON == a->h.type)
    } else if (ACTION_MKCON == a->com.type)
    r->max_con++;

    copy(end, a, asz);
    /* copy action and userdata to buffer */
    copy(end, a, szof(union action));
    copy(usr, dat, sz);
    r->sz += a->h.nxt;
    r->sz += a->com.next;
    }
    api void
    reducer_push(struct reducer *r, in struct module *m, int cnt)
    reducer_push(struct reducer *r, in struct layout *ui)
    {
    for (int i = 0; i < cnt; ++i) {
    const struct module *p = m + i;
    for (int i = 0; i < ui->comp_cnt; ++i) {
    mkcomp(r, p->id, p->def, 0);
    lnknode(r, p->id, p->parent);
    for (int j = 0; j < p->cnt; ++j)
    mkcon(r, p->con[j]);
    }
    } for (int i = 0; i < ui->con_cnt; ++i)
    mkcon(r, p->con[j]);
    for (int i = 0; i < ui->ext_cnt; ++i)
    reducer_add(r,t,p,s,a)
    }
    api void
    reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    @@ -418,7 +696,7 @@ reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    *mem += r->max_con * szof(struct constraint);
    *mem += r->max_ext * szof(struct extension);
    *mem += r->max_com * szof(struct component);
    *mem += (r->max_com+1) * szof(int) * ((r->mode == FOLD_RETAINED) ?1:3);
    *mem += (r->max_com+1) * szof(int) * ((r->mode == RETAINED) ?1:3);
    *mem += szof(struct layout) + alignof(struct layout);
    *mem += alignof(struct component) + alignof(struct definition);
    *mem += alignof(struct constraint) + alignof(struct extension);
    @@ -432,13 +710,11 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    struct node *tree;
    struct definition *def;
    struct constraint *con;
    struct layout *res;
    struct extension *ext;
    void *custom;

    /* I.) setup layout in memory block */
    res = align(mem, alignof(struct layout));
    if (r->mode == FOLD_RETAINED) {
    struct layout *res = align(mem, alignof(struct layout));
    if (r->mode == RETAINED) {
    res->tbl_cnt = 2 * r->max_com;
    res->tbl = align(res+1, alignof(int));
    res->buffer = res->tbl + res->tbl_cnt;
    @@ -453,7 +729,7 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    res->con_cnt = ui->con_cnt;
    res->ext_cnt = ui->ext_cnt;
    res->initialized = True;
    custom = ext + r->max_ext;
    void *custom = ext + r->max_ext;
    *ret = res;

    /* II.) copy old state */
    @@ -462,59 +738,61 @@ fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    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 == FOLD_IMMEDIATE && i < ui->comp_cnt; ++i)
    for (int i = 0; r->mode == IMMEDIATE && i < ui->comp_cnt; ++i)
    insert(res->tbl, res->tbl_cnt, ui->comp[i].id, (unsigned)i);
    for (int i = 0; i < ui->ext_cnt; ++i) {
    ext[i] = ui->ext[i];
    ext[i].addr = (addr)align(custom, ext[i].info.algn);
    copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].info.sz);
    custom = (char*)ext[i].addr + ext[i].info.sz;
    ext[i].addr = (addr)align(custom, ext[i].info.align);
    copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].info.size);
    custom = (char*)ext[i].addr + ext[i].info.size;
    }
    /* III.) add new state */
    char *end = (char*)r->buf + r->sz;
    for (union action *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->h.nxt)) {
    if (ACTION_MKCOMP == a->h.type) {
    for (union action *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->com.next)) {
    if (ACTION_MKCOMP == a->com.type) {
    const unsigned id = a->mkcomp.id;
    const int index = res->comp_cnt++;
    ui->comp[index].id = tree[index].id = id;
    ui->comp[index].usr = a->mkcomp.usr;
    def[index] = a->mkcomp.d;
    if (r->mode == FOLD_IMMEDIATE)
    if (r->mode == IMMEDIATE)
    insert(res->tbl, res->tbl_cnt, id, (unsigned)index);
    setup(ui->comp + id, def + id, ui->comp[0].surf);
    } else if (ACTION_MKCON == a->h.type) {
    } else if (ACTION_MKCON == a->com.type) {
    con[res->con_cnt++] = a->mkcon.con;
    } else if (ACTION_LNKNODE == a->h.type) {
    } else if (ACTION_LNKNODE == a->com.type) {
    struct node *n = tree + a->lnknode.parent;
    n->sub[n->cnt] = a->lnknode.child;
    res->comp[a->lnknode.child].zorder = n->cnt++;
    } else if (ACTION_CUSTOM == a->h.type) {
    } else if (ACTION_CUSTOM == a->com.type) {
    const void *obj = (const void*)((const char*)a + a->custom.off);
    char *dst = align(custom, a->custom.align);
    copy(dst, obj, a->custom.sz);
    custom = dst + a->custom.sz;
    ext[res->ext_cnt].info.sz = a->custom.sz;
    ext[res->ext_cnt].info.type = a->custom.type;
    ext[res->ext_cnt].info.algn = a->custom.align;
    copy(dst, obj, a->custom.size);
    custom = dst + a->custom.size;

    ext[res->ext_cnt].info.id = a->custom.id;
    ext[res->ext_cnt].info.size = a->custom.size;
    ext[res->ext_cnt].info.align = a->custom.align;
    ext[res->ext_cnt++].addr = (addr)dst;
    }
    } /* IV.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    const struct extension *e = 0;
    map(ui, ed[i]->info.type, e)
    map(ui, 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)
    reposit(FILE *fp, in struct layout *ui, const struct commit *com, const char *tab)
    {
    tab = (!tab) ? " ": tab;
    const char *attrs[] = {"0","ACT","L","T","R","B","W","H","CX","CY"};
    const char *cons[] = {"CONSTRAINT_CPY", "CONSTRAINT_SET", "CONSTRAINT_MIN", "CONSTRAINT_MAX"};
    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, " {{.eq = %s, .dst = {%u,%s}, .src = {%u,%s}, .cons = {.mul = %.2ff, .off = %d}}, .anch = %s},\n",
    cons[c->self.eq], c->self.dst.comp, attrs[c->self.dst.attr], c->self.src.comp, attrs[c->self.src.attr],
    fprintf(fp, "%s{{.eq = %s, .dst = {%u,%s}, .src = {%u,%s}, .cons = {.mul = %.2ff, .off = %d}}, .anch = %s},\n",
    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]);
    } fputs("};\n", fp);
    fprintf(fp, "global const struct definition %s[] = {\n", com->def);
    @@ -534,55 +812,47 @@ reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    if (d->flags & IS_MOVABLE_Y)
    {copy(buf+n, "|MOVABLE_Y",15); n += 15;}
    buf[n] = 0;
    fprintf(fp, " {TBL(%s,%s,%s), .sz={%d,%d,%d,%d}, .flags = %s},\n",
    d->i, d->p, d->b, d->sz.x, d->sz.y, d->sz.w, d->sz.h, buf);
    fprintf(fp, "%s{TBL(%s,%s,%s), .sz={%d,%d,%d,%d}, .flags = %s},\n",
    tab,d->i, d->p, d->b, d->sz.x, d->sz.y, d->sz.w, d->sz.h, buf);
    } fputs("};\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, " {.id = %u, .parent = %u, .cnt = %d, .sub = {", n->id, n->parent, n->cnt);
    fprintf(fp, "%s{.id = %u, .parent = %u, .cnt = %d, .sub = {",tab, n->id, n->parent, n->cnt);
    for (int j = 0; j < n->cnt; ++j)
    fprintf(fp, "%u,", n->sub[j]);
    if (!n->cnt) fputs("0", fp);
    fputs("}},\n", fp);
    } fputs("};\n", fp);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extlnk *lnk = com->ext_list + i;
    fprintf(fp, "global const %s %s[] = {", lnk->type, lnk->name);
    apply(ui, lnk->def->info.type, lnk->def->commit, fp);
    fprintf(fp, "global const %s %s[] = {", lnk->def->info.type, lnk->name);
    apply(ui, lnk->def->info.id, lnk->def->commit, fp);
    fputs("};\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.type, e)
    fprintf(fp, "\n {.type = %d, .sz = %d, .algn = %d, .addr = (addr)&%s[%u]},",
    e->info.type, e->info.sz, e->info.algn, lnk->type, cnt++);
    map(ui, lnk->def->info.id, e)
    fprintf(fp, "\n%s{.id = %d, .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", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s;\n", com->buffer);
    fprintf(fp, "global struct component %s;\n", com->comp);
    fprintf(fp, "global int %s;\nglobal struct component %s;\n", com->buffer,com->comp);
    fprintf(fp, "global struct layout %s = {\n", com->layout);
    fprintf(fp, " .comp_cnt = cntof(%s),\n", com->def);
    fprintf(fp, " .con_cnt = cntof(%s),\n", com->con);
    fprintf(fp, " .ext_cnt = cntof(%s),\n", com->ext);
    fprintf(fp, " .buffer = %s\n", com->buffer);
    fprintf(fp, " .comp = %s,\n", com->comp);
    fprintf(fp, " .tree = %s,\n", com->tree);
    fprintf(fp, " .def = %s,\n", com->def);
    fprintf(fp, " .con = %s,\n", com->con);
    fprintf(fp, " .ext = %s,\n", com->ext);
    fputs("};\n",fp);
    fprintf(fp, "%s.comp_cnt = cntof(%s),\n%s.con_cnt = cntof(%s),\n", tab,com->def,tab,com->con);
    fprintf(fp, "%s.ext_cnt = cntof(%s)\n,%s.buffer = %s\n",tab,com->ext,tab,com->buffer);
    fprintf(fp, "%s.comp = %s,\n%s.tree = %s,\n", tab,com->comp, tab, com->tree);
    fprintf(fp, "%s.def = %s,\n%s.con = %s,\n%s.ext = %s,\n};\n",tab,com->def,tab,com->con,tab,com->ext);
    }
    api void
    begin(struct context *ctx, struct layout *ui, void *mem, int siz, struct surface *scrn)
    begin(struct context *ctx, struct layout *ui, enum layout_type type,
    void *mem, int siz, struct surface *scrn)
    {
    ctx->layout = ui;
    ctx->buf.mem = mem;
    ctx->buf.siz = siz;
    ctx->cur = ctx->stk[0] = 0;

    reducer_begin(&ctx->red, type, mem, siz);
    ui->comp[COMPONENT_ROOT].surf = scrn;
    if (!ui->initialized) {
    ui->initialized = True;
    @@ -597,14 +867,15 @@ begin(struct context *ctx, struct layout *ui, void *mem, int siz, struct surface
    api void
    end(struct context *ctx)
    {
    ctx->layout = 0;
    struct layout *ui = ctx->layout;
    reducer_end(&ctx->red, ui, &ui->requested);
    ctx->layout = &ctx->root;
    }
    api void
    clear(struct context *ctx)
    {
    struct layout *ui = ctx->layout;
    for (int i = COMPONENT_ROOT+1; i < ui->comp_cnt; ++i) {
    for (int i = 1; i < ui->comp_cnt; ++i)
    if (ui->comp[i].surf)
    surf_del(ui->comp[i].surf);
    }
    }
  28. vurtun revised this gist May 2, 2017. No changes.
  29. vurtun renamed this gist May 2, 2017. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  30. vurtun revised this gist May 2, 2017. 1 changed file with 323 additions and 568 deletions.
    891 changes: 323 additions & 568 deletions quark.h
    Original file line number Diff line number Diff line change
    @@ -1,287 +1,17 @@
    #ifndef QK_INCLUDED
    #define QK_INCLUDED
    #include "ui.h"
    #include "draw.h"
    #include <assert.h>

    #include <stdio.h> /* fputs, fprintf */
    #include <string.h> /* memcpy */

    #define qk_out
    #define qk_in const
    #define qk_api extern
    #define qk_hook static
    #define qk_intern static
    #define qk_global static
    #define qk_storage static

    enum {qk_false, qk_true};
    struct qk_v2 {int x,y;};
    #define qk_v2(x,y) (struct v2){x,y}

    #define qk_stringify(x) #x
    #define qk_unused(...) qk_unused_impl(0,__VA_ARGS__)
    #define qk_copy(d,s,sz) memcpy(d,s,(size_t)(sz))
    #define qk_szof(a) ((int)sizeof(a))
    #define qk_cntof(a) ((int)(sizeof(a)/sizeof((a)[0])))
    #define qk_fourcc(a,b,c,d) ((unsigned long)(((d)<<24)|((c)<<16)|((b)<<8)|(a)))
    #define qk_alignof(t) ((int)((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0))
    #define qk_align(x, mask) ((void*)(((long long)((const char*)(x) + (mask-1)) & ~(mask-1))))
    static inline void qk_unused_impl(int dummy,...){(void)dummy;}

    #define qk_flag(n) (1<<(n))
    #define qk_min(a,b) ((a)<(b)?(a):(b))
    #define qk_max(a,b) ((a)>(b)?(a):(b))
    #define qk_min3(a,b,c) qk_min(qk_min(a,b),c)
    #define qk_max3(a,b,c) qk_max(qk_max(a,b),c)
    #define qk_clamp(a,v,b) (qk_max(qk_min(b,v),a))
    #define qk_between(x,a,b) ((a) <= (x) && (x) <= (b))
    #define qk_inbox(px,py,x,y,w,h) (qk_between(px,x,x+w) && qk_between(py,y,y+h))
    #define qk_intersect(x0,y0,w0,h0,x1,y1,w1,h1) (!(((x1>(x0+w0))||((x1+w1)<x0)||(y1>(y0+h0))||(y1+h1)<y0)))

    #define qk_len(s) (qk_cntof(s)-1)
    #define qk_h1(s,i,x) (x*65599u+(unsigned char)s[(i)<qk_len(s)?qk_len(s)-1-(i):qk_len(s)])
    #define qk_h4(s,i,x) qk_h1(s,i,qk_h1(s,i+1,qk_h1(s,i+2,qk_h1(s,i+3,x))))
    #define qk_h16(s,i,x) qk_h4(s,i,qk_h4(s,i+4,qk_h4(s,i+8,qk_h4(s,i+12,x))))
    #define qk_h64(s,i,x) qk_h16(s,i,qk_h16(s,i+16,qk_h16(s,i+32,qk_h16(s,i+48,x))))
    #define qk_idx(s,i) ((unsigned)(qk_h64(s,0,i)^(qk_h64(s,0,i)>>16)))
    #define qk_id(s) qk_idx(s,0)

    struct qk_layout;
    struct qk_context;
    struct qk_component;
    struct qk_extension;

    typedef unsigned long long qk_addr;
    typedef void(*qk_paint_f)(struct qk_context*,struct qk_layout*,struct qk_component*);
    typedef int(*qk_handle_f)(struct qk_context*,struct qk_layout*,struct qk_component*, const XEvent*);
    typedef void(*qk_signal_f)(struct qk_context*,struct qk_layout*,struct qk_component*, qk_in XEvent*,void *usr);
    typedef void(*qk_blueprint_f)(struct qk_context*,struct qk_layout*,struct qk_component*);
    typedef void(*qk_apply_f)(struct qk_layout*, const struct qk_extension*, void*);

    /* interaction */
    enum {Enter = LASTEvent, Leave, Drag};
    enum qk_interaction_type {
    QK_INTERACTION_SET,
    QK_INTERACTION_SIGNAL,
    QK_INTERACTION_ENABLE,
    QK_INTERACTION_DISABLE,
    QK_INTERACTION_DRAG_X,
    QK_INTERACTION_DRAG_Y,
    QK_INTERACTION_CNT,
    };
    struct qk_interaction {
    enum qk_interaction_type type;
    union {int i; unsigned f; qk_signal_f s;} src;
    void *dst;
    };
    struct qk_interaction_slots {
    struct qk_interaction left;
    struct qk_interaction right;
    struct qk_interaction dragged;
    struct qk_interaction enter;
    struct qk_interaction leave;
    };

    /* component */
    #define QK_COMPONENT_ROOT 0
    #define QK_TBL(hn,pn,bp) .handle=hn,.paint=pn,.blueprint = bp,.i=#hn, .p=#pn, .b=#bp
    enum qk_attributes {ANIM, ACT, L, T, R, B, W, H, CX, CY, ATTR_CNT};
    enum qk_component_flags {
    QK_DEFAULT,
    QK_HIDDEN = qk_flag(0),
    QK_INTERACTIVE = qk_flag(1),
    QK_PAINTABLE = qk_flag(2),
    QK_LAYER = qk_flag(3),
    QK_STATIC = qk_flag(4),
    QK_BACKGROUND = qk_flag(5),
    QK_IS_MOVABLE_X = qk_flag(6),
    QK_IS_MOVABLE_Y = qk_flag(7),
    QK_IS_MOVABLE = QK_IS_MOVABLE_X|QK_IS_MOVABLE_Y,
    QK_MOVABLE_X = QK_IS_MOVABLE_X|QK_INTERACTIVE,
    QK_MOVABLE_Y = QK_IS_MOVABLE_Y|QK_INTERACTIVE,
    QK_MOVABLE = QK_IS_MOVABLE|QK_INTERACTIVE,
    };
    struct qk_node {
    unsigned id, parent;
    #define QK_MAX_NODES_PER_NODE 32
    unsigned sub[QK_MAX_NODES_PER_NODE];
    int cnt;
    };
    struct qk_definition {
    qk_handle_f handle;
    qk_paint_f paint;
    qk_blueprint_f blueprint;
    int x, y, w, h;
    unsigned flags;
    const char *i, *p, *b;
    };
    struct qk_component {
    unsigned id;
    int attr[ATTR_CNT];
    unsigned flags;
    int zorder;
    struct qk_v2 off, total;
    struct surface *surf;
    qk_addr usr;

    unsigned pressed:1;
    unsigned released:1;
    unsigned hovered:1;
    unsigned dragged:1;
    struct qk_interaction_slots slot;
    };

    /* constraint */
    enum qk_constraint_eq {
    QK_CONSTRAINT_CPY, QK_CONSTRAINT_SET,
    QK_CONSTRAINT_MIN, QK_CONSTRAINT_MAX
    };
    enum qk_condition_eq {
    QK_CONDITION_TRUE, QK_CONDITION_FALSE,
    QK_CONDITION_EQ, QK_CONDITION_NEQ,
    QK_CONDITION_GR, QK_CONDITION_LS,
    QK_CONDITION_GRE, QK_CONDITION_LSE
    };
    struct qk_cons {float mul; int off;};
    struct qk_var {unsigned comp; int attr;};
    struct qk_function {
    int eq;
    struct qk_var dst;
    struct qk_var src;
    struct qk_cons cons;
    };
    struct qk_constraint {
    struct qk_function self;
    struct qk_function cond;
    enum qk_attributes anch;
    };

    /* fold */
    enum qk_fold_mode {
    QK_FOLD_RETAINED,
    QK_FOLD_IMMEDIATE
    };
    #define QK_MAX_CON_PER_OBJECT 16
    struct qk_module {
    struct qk_definition def;
    unsigned id, parent;
    struct qk_constraint con[QK_MAX_CON_PER_OBJECT];
    int cnt;
    };
    enum qk_action_id {
    QK_ACTION_MKCOMP,
    QK_ACTION_MKCON,
    QK_ACTION_LNKNODE,
    QK_ACTION_CUSTOM,
    };
    struct qk_header {int type, nxt;};
    union qk_action {
    struct qk_header h;
    struct {struct qk_header h; unsigned id; struct qk_definition d; qk_addr usr;} mkcomp;
    struct {struct qk_header h; struct qk_constraint con;} mkcon;
    struct {struct qk_header h; unsigned child, parent;} lnknode;
    struct {struct qk_header h; int type, align, sz, off;} custom;
    };
    struct qk_reducer {
    int mode;
    int max_com, max_con;
    int max_ext, custom;
    void *buf; int sz, cap;
    };

    /* layout */
    struct qk_extension {
    int type, sz, algn;
    qk_addr addr;
    };
    struct qk_layout {
    unsigned *buffer, *tbl;
    struct qk_component *comp;
    const struct qk_definition *def;
    const struct qk_node *tree;
    const struct qk_constraint *con;
    const struct qk_extension *ext;
    int comp_cnt, con_cnt;
    int ext_cnt, tbl_cnt;
    int custom;
    };

    /* context */
    #define QK_MAX_COMPONENT_DEPTH 32
    struct qk_context {
    struct qk_layout *layout;
    struct qk_reducer buf;
    /* declaration */
    unsigned stk[QK_MAX_COMPONENT_DEPTH];
    unsigned cur;
    /* components */
    unsigned active;
    unsigned origin;
    unsigned hot;
    };

    /* context */
    qk_api void qk_init(struct qk_context*, struct qk_layout*, struct surface*);
    qk_api void qk_finish(struct qk_context*);
    qk_api void qk_update(struct qk_context*, XEvent*);
    qk_api void qk_blueprint(struct qk_layout*, struct qk_context*);
    qk_api void qk_paint(struct qk_context*,struct qk_layout*);
    qk_api void qk_draw(struct qk_context*, struct qk_layout*, unsigned root);
    qk_api void qk_clear(struct qk_context*);
    #define qk_push(ctx,id) (ctx)->stk[++(ctx)->cur] = (id)
    #define qk_peek(ctx) (ctx)->stk[(ctx)->cur]
    #define qk_pop(ctx) (ctx)->cur = qk_max((ctx)->cur-1,0);

    /* layout */
    qk_api void qk_layouting(struct qk_layout*);
    qk_api unsigned qk_at(const struct qk_layout*, qk_in struct qk_component *root, int x, int y);
    qk_api void qk_reorder(struct qk_layout*, struct qk_component*);
    qk_api void qk_commit(FILE*, qk_in struct qk_layout*, const char *con, const char *def, const char *tree);
    qk_api inline unsigned qk_lookup(const struct qk_layout*, unsigned id);
    #define qk_find(ui,id) ((ui->tbl)?qk_lookup(ui,id):id)
    #define qk_apply(ui,t,a,p) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->type==(t)) a(ui,e,p);
    #define qk_map(ui,t,e) for((e)=(ui)->ext;(e)<(ui)->ext+(ui)->ext_cnt;++(e)) if((e)->type==(t))

    /* fold */
    qk_api void qk_reducer_begin(struct qk_reducer*, enum qk_fold_mode, void *mem, int sz);
    qk_api void qk_reducer_push(struct qk_reducer*, qk_in struct qk_module *, int cnt);
    qk_api void qk_reducer_cons(struct qk_reducer*, union qk_action*, qk_in void *data, int sz, int ualign);
    #define qk_mkcomp(r,ID,def,user) qk_reducer_cons(r,&(union qk_action){.mkcomp={.id=ID,.h={.type=QK_ACTION_MKCOMP},.d=def,.usr=user}},0,0,0)
    #define qk_mkcon(r,c) qk_reducer_cons(r,&(union qk_action){.mkcon={.h={.type=QK_ACTION_MKCON},.con=c}},0,0,0)
    #define qk_lnknode(r,c,p) qk_reducer_cons(r,&(union qk_action){.lnknode={.h={.type=QK_ACTION_LNKNODE},.child=c,.parent=p}},0,0,0)
    #define qk_reducer_add(r,t,p,s,a) qk_reducer_cons(r,&(union qk_action){.custom={.h={.type=QK_ACTION_CUSTOM},.type=t}},p,s,a)
    qk_api void qk_reducer_end(struct qk_reducer*, qk_in struct qk_layout*, qk_out int *mem);
    qk_api void qk_fold(qk_out struct qk_layout**, qk_in struct qk_reducer*, qk_in struct qk_layout*, qk_out void *mem);

    /* component */
    qk_api inline struct qk_component* qk_component_parent(struct qk_layout*, unsigned id);
    qk_api void qk_adjust(struct qk_context*, struct qk_layout*, struct qk_component*, qk_in XEvent*, void*);
    qk_api void qk_dispatch(struct qk_component*,struct qk_context*, struct qk_layout*, qk_in XEvent*);
    #define QK_DECLARATION_BEGIN(name) enum name {__QK_##name##_ROOT_,
    #define QK_DECLARATION_END };

    /* bindings */
    #define qk_slot_con(i,f) *(i) = (f)
    #define qk_bind_set(d,v) (struct qk_interaction){QK_INTERACTION_SET, .dst = d, .src = {.i = v}}
    #define qk_bind_not(d) (struct qk_interaction){QK_INTERACTION_NOT, .dst = d}
    #define qk_bind_enable(d,v) (struct qk_interaction){QK_INTERACTION_ENABLE, .dst = d, .src = {.f = v}}
    #define qk_bind_disable(d,v) (struct qk_interaction){QK_INTERACTION_DISABLE, .dst = d, .src = {.f = v}}
    #define qk_bind_drag_x(d) (struct qk_interaction){QK_INTERACTION_DRAG_X, .dst = d}
    #define qk_bind_drag_y(d) (struct qk_interaction){QK_INTERACTION_DRAG_Y, .dst = d}
    #define qk_bind_signal(d,u) (struct qk_interaction){QK_INTERACTION_SIGNAL, .dst = (void*)(qk_addr)u, .src = {.s = d}}

    #endif /* QK_CORE_INCLUDED */

    #ifdef QK_IMPLEMENTATION

    qk_intern inline int
    qk_ceili(float x)
    intern inline int
    ceili(float x)
    {
    if (x >= 0) return (int)x;
    int t = (int)x;
    float r = x - (float)t;
    return (r > 0.0f) ? t+1: t;
    }
    qk_intern inline void
    qk_solve(struct qk_component *c, int lo, int hi,
    intern inline void
    solve(struct component *c, int lo, int hi,
    int center, int len, int mod, int an)
    {
    if (mod == lo) {
    @@ -318,538 +48,563 @@ qk_solve(struct qk_component *c, int lo, int hi,
    }
    }
    c->attr[center] = c->attr[lo] + (c->attr[len]/2);
    c->attr[len] = qk_max(c->attr[len], 0);
    c->attr[len] = max(c->attr[len], 0);
    }
    qk_api inline struct qk_component*
    qk_component_parent(struct qk_layout *ui, unsigned id)
    api inline struct component*
    component_parent(struct layout *ui, unsigned id)
    {
    const struct qk_node *n = ui->tree + id;
    const struct node *n = ui->tree + id;
    if (n->parent != n->id)
    return ui->comp + n->parent;
    return 0;
    }
    qk_api void
    qk_adjust(struct qk_context *ctx, struct qk_layout *ui,
    struct qk_component *c, qk_in XEvent *e, void *usr)
    api void
    adjust(struct context *ctx, struct layout *ui,
    struct component *c, XEvent *e, void *usr)
    {
    qk_unused(ctx,ui,usr);
    if (c->flags & QK_IS_MOVABLE_X)
    unused(ctx,ui,usr);
    if (c->flags & IS_MOVABLE_X)
    c->attr[CX] += e->xmotion.x_root;
    if (c->flags & QK_IS_MOVABLE_Y)
    if (c->flags & IS_MOVABLE_Y)
    c->attr[CY] += e->xmotion.y_root;

    qk_solve(c, L, R, CX, W, L, W);
    qk_solve(c, T, B, CY, H, T, H);
    qk_blueprint(ui, ctx);
    qk_layouting(ui);
    solve(c, L, R, CX, W, L, W);
    solve(c, T, B, CY, H, T, H);
    blueprint(ui, ctx);
    layouting(ui);
    }
    qk_intern inline void
    qk_build(struct qk_component *c, int x, int y, int w, int h)
    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] = qk_max(w,0);
    c->attr[H] = qk_max(h,0);
    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);
    }
    qk_intern void
    qk_setup(struct qk_component *c, const struct qk_definition *d, struct surface *s)
    intern void
    setup(struct component *c, const struct definition *d, struct surface *s)
    {
    c->flags = d->flags;
    qk_build(c, d->x, d->y, d->w, d->h);
    if (!(d->flags & QK_HIDDEN))
    c->attr[ACT] = qk_true;
    if (d->flags & QK_PAINTABLE)
    c->zorder = d->zorder;
    build(c, d->sz.x, d->sz.y, d->sz.w, d->sz.h);
    if (!(d->flags & HIDDEN))
    c->attr[ACT] = true;
    if (d->flags & PAINTABLE)
    c->surf = surf_mk(s->dpy, s->root, s->scrn, c->attr[W], c->attr[H]);
    if ((d->flags & QK_IS_MOVABLE))
    qk_slot_con(&c->slot.dragged, qk_bind_signal(qk_adjust, 0));
    if ((d->flags & IS_MOVABLE))
    slot_con(&c->slot.dragged, bind_signal(adjust, 0));
    }
    qk_intern inline int
    qk_cond(const struct qk_function *f, int d, int src)
    intern inline int
    cond(const struct function *f, int d, int src)
    {
    float s = (float)src;
    if (f->eq == QK_CONDITION_TRUE) return 1;
    else if (f->eq == QK_CONDITION_FALSE) return 0;
    else if (f->eq == QK_CONDITION_EQ)
    return d == qk_ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == QK_CONDITION_NEQ)
    return d != qk_ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == QK_CONDITION_GR)
    return d > qk_ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == QK_CONDITION_LS)
    return d < qk_ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == QK_CONDITION_GRE)
    return d >= qk_ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == QK_CONDITION_LSE)
    return d <= qk_ceili(f->cons.mul*s)+f->cons.off;
    if (f->eq == CONDITION_TRUE) return 1;
    else if (f->eq == CONDITION_FALSE) return 0;
    else if (f->eq == CONDITION_EQ)
    return d == ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_NEQ)
    return d != ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_GR)
    return d > ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_LS)
    return d < ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_GRE)
    return d >= ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONDITION_LSE)
    return d <= ceili(f->cons.mul*s)+f->cons.off;
    return 0;
    }
    qk_intern inline void
    qk_eval(const struct qk_function *f, int *d, int src)
    intern inline void
    eval(const struct function *f, int *d, int src)
    {
    float s = (float)src;
    if (f->eq == QK_CONSTRAINT_CPY) *d = src;
    else if (f->eq == QK_CONSTRAINT_SET)
    *d = qk_ceili(f->cons.mul*s) + f->cons.off;
    else if (f->eq == QK_CONSTRAINT_MIN)
    *d = qk_min(*d, qk_ceili(f->cons.mul*s) + f->cons.off);
    else if (f->eq == QK_CONSTRAINT_MAX)
    *d = qk_max(*d, qk_ceili(f->cons.mul*s) + f->cons.off);
    if (f->eq == CONSTRAINT_CPY) *d = src;
    else if (f->eq == CONSTRAINT_SET)
    *d = ceili(f->cons.mul*s)+f->cons.off;
    else if (f->eq == CONSTRAINT_MIN)
    *d = min(*d, ceili(f->cons.mul*s)+f->cons.off);
    else if (f->eq == CONSTRAINT_MAX)
    *d = max(*d, ceili(f->cons.mul*s)+f->cons.off);
    }
    qk_intern void
    qk_insert(unsigned *tbl, int tbl_cnt, unsigned key, unsigned val)
    intern void
    insert(unsigned *tbl, int tbl_cnt, unsigned key, unsigned val)
    {
    unsigned begin = key % (unsigned)tbl_cnt, id = begin;
    do {unsigned index = tbl[id];
    if ((id == 0 && val != 0) || index) continue;
    tbl[id] = val; return;
    } while ((++id % (unsigned)tbl_cnt) != begin);
    }
    qk_api unsigned
    qk_lookup(const struct qk_layout *ui, unsigned id)
    api unsigned
    lookup(const struct layout *ui, unsigned id)
    {
    assert(ui->tbl && "use 'qk_find' instead");
    assert(ui->tbl && "use 'find' instead");
    unsigned begin = id % (unsigned)ui->tbl_cnt, index = begin;
    do {struct qk_component *c = ui->comp + ui->tbl[index];
    do {struct component *c = ui->comp + ui->tbl[index];
    if (!c->id) break;
    if (c->id == id) return index;
    } while ((++index % (unsigned)ui->tbl_cnt) != begin);
    return 0;
    }
    qk_api unsigned
    qk_at(const struct qk_layout *ui, const struct qk_component *c, int x, int y)
    intern unsigned
    at(const struct layout *ui, const struct component *c, int *x, int *y)
    {
    loop:; unsigned id = c->id;
    unsigned tbl[QK_MAX_NODES_PER_NODE];
    const struct qk_node *n = ui->tree + qk_find(ui,id);
    unsigned tbl[MAX_NODES_PER_NODE];
    const struct node *n = ui->tree + find(ui,id);
    for (int i = 0; i < n->cnt; ++i)
    tbl[ui->comp[n->sub[i]].zorder] = n->sub[i];

    x += c->off.x; y += c->off.y;
    *x += c->off.x; *y += c->off.y;
    for (int i = n->cnt-1; i >= 0; --i) {
    const struct qk_component *sub = ui->comp + qk_find(ui, tbl[i]);
    if (!sub->attr[ACT] || !(sub->flags & QK_INTERACTIVE)) continue;
    if (qk_inbox(x, y, sub->attr[L], sub->attr[T], sub->attr[W], sub->attr[H]))
    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;
    }
    qk_api void
    qk_reorder(struct qk_layout *ui, struct qk_component *c)
    api void
    reorder(struct layout *ui, struct component *c)
    {
    const struct qk_node *n = ui->tree + qk_find(ui,c->id);
    const struct node *n = ui->tree + find(ui,c->id);
    while (n->parent != n->id) {
    struct qk_component *p;
    p = qk_component_parent(ui, c->id);
    if (p->flags & QK_STATIC) goto nxt;
    n = ui->tree + qk_find(ui,p->id);
    struct component *p;
    p = component_parent(ui, c->id);
    if (p->flags & STATIC) goto nxt;
    n = ui->tree + find(ui,p->id);
    for (int i = 0; i < n->cnt; ++i) {
    struct qk_component *sub;
    sub = ui->comp + qk_find(ui, n->sub[i]);
    if (sub->flags & QK_BACKGROUND) continue;
    struct component *sub;
    sub = ui->comp + find(ui, n->sub[i]);
    if (sub->flags & BACKGROUND) continue;
    if (sub->zorder > c->zorder)
    sub->zorder--;
    }
    c->zorder = qk_max(n->cnt-1, 0);
    c->zorder = max(n->cnt-1, 0);
    nxt: c = p;
    }
    return;
    }
    qk_api inline void
    qk_dispatch(struct qk_component *c, struct qk_context *ctx, struct qk_layout *ui,
    qk_in XEvent *e)
    api inline void
    dispatch(struct component *c, struct context *ctx, struct layout *ui,
    XEvent *e)
    {
    do if (ui->def[c->id].handle)
    if (ui->def[c->id].handle(ctx,ui,c,e)) break;
    while ((c = qk_component_parent(ui,c->id)));
    while ((c = component_parent(ui,c->id)));
    }
    qk_intern void
    qk_interact(struct qk_context *ctx, struct qk_layout *ui, struct qk_component *c,
    struct qk_interaction *in, const XEvent *e)
    intern void
    interact(struct context *ctx, struct layout *ui, struct component *c,
    struct interaction *i, XEvent *e)
    {
    if (QK_INTERACTION_SIGNAL == in->type)
    in->src.s(ctx, ui, c, e, in->dst);
    else if (QK_INTERACTION_SET == in->type)
    {int *v = (int*)in->dst; *v = in->src.i;}
    else if (QK_INTERACTION_ENABLE == in->type)
    {unsigned *v = (unsigned*)in->dst; *v |= in->src.f;}
    else if (QK_INTERACTION_DISABLE == in->type)
    {unsigned *v = (unsigned*)in->dst; *v &= ~in->src.f;}
    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)
    {unsigned *v = (unsigned*)i->dst; *v |= i->src.f;}
    else if (INTERACTION_DISABLE == i->type)
    {unsigned *v = (unsigned*)i->dst; *v &= ~i->src.f;}
    else if (e->type == MotionNotify) {
    int *d = (int*)in->dst;
    *d += (in->type == QK_INTERACTION_DRAG_X) ?
    int *d = (int*)i->dst;
    *d += (i->type == INTERACTION_DRAG_X) ?
    e->xmotion.x_root: e->xmotion.y_root;
    }
    }
    qk_api void
    qk_paint(struct qk_context *ctx, struct qk_layout *ui)
    api void
    paint(struct context *ctx, struct layout *ui)
    {
    for (int i = 0; i < ui->comp_cnt; ++i) {
    struct qk_component *c = ui->comp + i;
    struct component *c = ui->comp + i;
    surf_resize(c->surf, c->attr[W], c->attr[H]);
    if (!(c->flags & QK_PAINTABLE) || (c->flags & QK_LAYER))
    if (!(c->flags & PAINTABLE) || (c->flags & LAYER))
    continue;
    if (ui->def[i].paint)
    ui->def[i].paint(ctx,ui,c);
    }
    }
    qk_api void
    qk_draw(struct qk_context *ctx, struct qk_layout *ui, unsigned root_id)
    api void
    draw(struct context *ctx, struct layout *ui, unsigned root_id)
    {
    unsigned head = 0;
    unsigned *stk = ui->buffer;
    unsigned tbl[QK_MAX_NODES_PER_NODE];
    struct qk_component *r = ui->comp + qk_find(ui, root_id);
    unsigned tbl[MAX_NODES_PER_NODE];
    struct component *r = ui->comp + find(ui, root_id);

    stk[head++] = root_id;
    while (head > 0) {
    struct qk_component *c = ui->comp + qk_find(ui, stk[--head]);
    struct component *c = ui->comp + find(ui, stk[--head]);
    if (c->surf && c->attr[ACT] && c->id != root_id) {
    if ((c->flags & QK_LAYER) && (c->flags & QK_PAINTABLE)) {
    if ((c->flags & LAYER) && (c->flags & PAINTABLE)) {
    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 & QK_LAYER) && (c->id != root_id))) continue;
    const struct qk_node *n = ui->tree + qk_find(ui, c->id);
    if (!c->attr[ACT] || ((c->flags & 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[n->sub[i]].zorder] = n->sub[i];
    for (int i = n->cnt-1; i >= 0; --i)
    stk[head++] = tbl[i];
    }
    ui->buffer = stk;
    }
    qk_api void
    qk_blueprint(struct qk_layout *ui, struct qk_context *ctx)
    api void
    blueprint(struct layout *ui, struct context *ctx)
    {
    unsigned head = 0, tail = 1;
    unsigned *que = ui->buffer;
    que[tail] = QK_COMPONENT_ROOT;
    que[tail] = COMPONENT_ROOT;
    while (head < tail) {
    const struct qk_node *n;
    n = ui->tree + qk_find(ui, que[++head]);
    const struct node *n;
    n = ui->tree + find(ui, que[++head]);
    for (int i = 0; i < n->cnt; ++i)
    que[++tail] = n->sub[i];
    }
    for (unsigned i = tail; i > 0; --i) {
    const struct qk_definition *d;
    struct qk_component *c;
    d = ui->def + qk_find(ui, que[i]);
    c = ui->comp + qk_find(ui, que[i]);
    const struct definition *d;
    struct component *c;
    d = ui->def + find(ui, que[i]);
    c = ui->comp + find(ui, que[i]);
    if (d->blueprint)
    d->blueprint(ctx, ui, c);
    }
    }
    qk_api void
    qk_layouting(struct qk_layout *ui)
    api void
    layouting(struct layout *ui)
    {
    for (int i = 0; i < ui->con_cnt; ++i) {
    const struct qk_constraint *con = ui->con + i;
    const struct qk_function *co = &con->cond;

    struct qk_component *cd, *cs, *d, *s;
    cd = ui->comp + qk_find(ui, co->dst.comp);
    cs = ui->comp + qk_find(ui, co->src.comp);
    d = ui->comp + qk_find(ui, con->self.dst.comp);
    s = ui->comp + qk_find(ui, con->self.src.comp);

    if (qk_cond(co, cd->attr[co->dst.attr], cs->attr[co->src.attr])) {
    qk_eval(&con->self, d->attr+con->self.dst.attr, s->attr[con->self.src.attr]);
    qk_solve(d, L, R, CX, W, con->self.dst.attr, con->anch);
    qk_solve(d, T, B, CY, H, con->self.dst.attr, con->anch);
    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);
    }
    }
    }
    qk_api void
    qk_update(struct qk_context *ctx, XEvent *e)
    api void
    update(struct context *ctx, XEvent *e)
    {
    struct qk_layout *ui;
    struct layout *ui;
    ui = ctx->layout;
    switch (e->type) {
    default: {
    struct qk_component *a;
    struct component *a;
    a = ui->comp + ctx->active;
    qk_dispatch(a, ctx, ui, e);
    dispatch(a, ctx, ui, e);
    } break;
    case Expose:
    case ConfigureNotify: {
    /* resize event */
    struct qk_component *c = ui->comp + QK_COMPONENT_ROOT;
    struct component *c = ui->comp + COMPONENT_ROOT;
    int w = (e->type == Expose) ? e->xexpose.width: e->xconfigure.width;
    int h = (e->type == Expose) ? e->xexpose.height: e->xconfigure.height;
    qk_build(c, 0, 0, w, h);
    build(c, 0, 0, w, h);
    surf_resize(c->surf, w, h);
    qk_blueprint(ctx->layout, ctx);
    qk_layouting(ui);
    blueprint(ctx->layout, ctx);
    layouting(ui);
    for (int i = 0; i < ui->comp_cnt; ++i)
    qk_dispatch(ui->comp + i,ctx,ui,e);
    dispatch(ui->comp + i,ctx,ui,e);
    } break;
    case MotionNotify: {
    /* enter and leave event */
    int mx = e->xmotion.x, my = e->xmotion.y;
    unsigned last_hot = ctx->hot;
    ctx->hot = qk_at(ui, ui->comp, mx,my);
    ctx->hot = at(ui, ui->comp, &e->xmotion.x, &e->xmotion.y);
    if (last_hot != ctx->hot) {
    struct qk_component *prev = ui->comp + last_hot;
    struct qk_component *cur = ui->comp + ctx->hot;
    struct component *prev = ui->comp + last_hot;
    struct component *cur = ui->comp + ctx->hot;

    prev->hovered = qk_false;
    e->xmotion.type = Leave;
    qk_interact(ctx, ui, prev, &prev->slot.leave, e);
    qk_dispatch(prev,ctx, ui,e);
    interact(ctx, ui, prev, &prev->slot.leave, e);
    dispatch(prev,ctx, ui, e);

    cur->hovered = qk_true;
    e->xmotion.type = Enter;
    qk_interact(ctx, ui, cur, &cur->slot.enter, e);
    qk_dispatch(cur,ctx, ui, e);
    interact(ctx, ui, cur, &cur->slot.enter, e);
    dispatch(cur,ctx, ui, e);
    e->xmotion.type = MotionNotify;
    } /* dragging event */
    if (ctx->active == ctx->origin) {
    struct component *a = ui->comp + find(ui,ctx->active);
    interact(ctx,ui, a, &a->slot.dragged, e);
    e->xmotion.type = Drag;
    dispatch(a,ctx,ui,e);
    }
    /* dragging event */
    if (ctx->active != ctx->origin) break;
    struct qk_component *a = ui->comp + qk_find(ui,ctx->active);
    a->dragged = qk_true;
    qk_interact(ctx,ui, a, &a->slot.dragged, e);
    e->xmotion.type = Drag;
    qk_dispatch(a,ctx,ui,e);
    e->xmotion.x = mx, e->xmotion.y = my;
    e->xmotion.type = MotionNotify;
    } break;
    case ButtonRelease:
    case ButtonPress: {
    int down = e->type == ButtonPress;
    if (down) ui->comp[ctx->hot].pressed = qk_true;
    else ui->comp[ctx->origin].released = qk_true;
    ctx->active = (down) ? ctx->hot: ctx->active;
    ctx->origin = (down) ? ctx->hot: QK_COMPONENT_ROOT;

    struct qk_component *a = ui->comp + qk_find(ui,ctx->active);
    ctx->origin = (down) ? ctx->hot: COMPONENT_ROOT;
    struct component *a = ui->comp + find(ui,ctx->active);
    if (e->xbutton.button == Button1 && down) {
    /* left button event */
    qk_reorder(ui, a);
    qk_interact(ctx,ui, a, &a->slot.left, e);
    reorder(ui, a);
    interact(ctx,ui, a, &a->slot.left, e);
    } else if (e->xbutton.button == Button3 && down){
    /* right button event */
    qk_reorder(ui, a);
    qk_interact(ctx,ui, a, &a->slot.right, e);
    reorder(ui, a);
    interact(ctx,ui, a, &a->slot.right, e);
    }
    qk_dispatch(a,ctx,ui,e);
    dispatch(a,ctx,ui,e);
    } break;}
    }
    qk_api void
    qk_reducer_begin(struct qk_reducer *r, enum qk_fold_mode mode, void *mem, int sz)
    api void
    reducer_begin(struct reducer *r, enum fold_mode mode, void *mem, int sz)
    {
    r->mode = mode;
    r->buf = mem; r->cap = sz;
    r->max_com = r->max_con = 0;
    r->max_ext = r->custom = r->sz = 0;
    }
    qk_api void
    qk_reducer_cons(struct qk_reducer *r, union qk_action *a,
    api void
    reducer_cons(struct reducer *r, union action *a,
    const void *dat, int sz, int ualign)
    {
    qk_storage const int align = qk_alignof(union qk_action);
    qk_storage const int asz = qk_szof(union qk_action);
    storage const int align = alignof(union action);
    storage const int asz = szof(union action);
    assert((r->sz + asz + sz + ualign + align) < r->cap);

    char *end = (char*)r->buf + r->sz;
    char *nxt = qk_align(end + r->sz + asz + sz + ualign, align);
    char *usr = qk_align(end + asz, ualign);
    char *nxt = align(end + r->sz + asz + sz + ualign, align);
    char *usr = align(end + asz, ualign);
    a->h.nxt = (int)(nxt - end);

    if (QK_ACTION_CUSTOM == a->h.type) {
    if (ACTION_CUSTOM == a->h.type) {
    r->custom = sz + a->custom.align;
    a->custom.off = (int)(usr - end);
    a->custom.align = ualign;
    a->custom.sz = sz;
    r->max_ext++;
    } else if (QK_ACTION_MKCOMP == a->h.type)
    } else if (ACTION_MKCOMP == a->h.type)
    r->max_com++;
    else if (QK_ACTION_MKCON == a->h.type)
    else if (ACTION_MKCON == a->h.type)
    r->max_con++;

    qk_copy(end, a, asz);
    qk_copy(usr, dat, sz);
    copy(end, a, asz);
    copy(usr, dat, sz);
    r->sz += a->h.nxt;
    }
    qk_api void
    qk_reducer_push(struct qk_reducer *r, qk_in struct qk_module *m, int cnt)
    api void
    reducer_push(struct reducer *r, in struct module *m, int cnt)
    {
    for (int i = 0; i < cnt; ++i) {
    const struct qk_module *p = m + i;
    qk_mkcomp(r, p->id, p->def, 0);
    qk_lnknode(r, p->id, p->parent);
    const struct module *p = m + i;
    mkcomp(r, p->id, p->def, 0);
    lnknode(r, p->id, p->parent);
    for (int j = 0; j < p->cnt; ++j)
    qk_mkcon(r, p->con[j]);
    mkcon(r, p->con[j]);
    }
    }
    qk_api void
    qk_reducer_end(struct qk_reducer *r, qk_in struct qk_layout *ui, qk_out int *mem)
    api void
    reducer_end(struct reducer *r, in struct layout *ui, out int *mem)
    {
    r->max_ext += ui->ext_cnt;
    r->max_com += ui->comp_cnt; r->max_con += ui->con_cnt;
    *mem = r->max_com * qk_szof(struct qk_node);
    *mem += r->max_com * qk_szof(struct qk_definition);
    *mem += r->max_con * qk_szof(struct qk_constraint);
    *mem += r->max_ext * qk_szof(struct qk_extension);
    *mem += r->max_com * qk_szof(struct qk_component);
    *mem += (r->max_com+1) * qk_szof(int) * ((r->mode == QK_FOLD_RETAINED) ?1:3);
    *mem += qk_szof(struct qk_layout) + qk_alignof(struct qk_layout);
    *mem += qk_alignof(struct qk_component) + qk_alignof(struct qk_definition);
    *mem += qk_alignof(struct qk_constraint) + qk_alignof(struct qk_extension);
    *mem += qk_alignof(struct qk_node) + qk_alignof(int);
    *mem = r->max_com * szof(struct node);
    *mem += r->max_com * szof(struct definition);
    *mem += r->max_con * szof(struct constraint);
    *mem += r->max_ext * szof(struct extension);
    *mem += r->max_com * szof(struct component);
    *mem += (r->max_com+1) * szof(int) * ((r->mode == FOLD_RETAINED) ?1:3);
    *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->custom + r->custom;
    }
    qk_api void
    qk_fold(qk_out struct qk_layout **ret, qk_in struct qk_reducer *r,
    qk_in struct qk_layout *ui, void *mem)
    api void
    fold(out struct layout **ret, in struct reducer *r, in struct layout *ui,
    void *mem, const struct extdef **ed, int cnt)
    {
    struct qk_node *tree;
    struct qk_definition *def;
    struct qk_constraint *con;
    struct qk_layout *res;
    struct qk_extension *ext;
    struct node *tree;
    struct definition *def;
    struct constraint *con;
    struct layout *res;
    struct extension *ext;
    void *custom;

    /* setup layout in memory block */
    res = qk_align(mem, qk_alignof(struct qk_layout));
    if (r->mode == QK_FOLD_RETAINED) {
    /* I.) setup layout in memory block */
    res = align(mem, alignof(struct layout));
    if (r->mode == FOLD_RETAINED) {
    res->tbl_cnt = 2 * r->max_com;
    res->tbl = qk_align(res+1, qk_alignof(int));
    res->tbl = align(res+1, alignof(int));
    res->buffer = res->tbl + res->tbl_cnt;
    } else res->buffer = qk_align(res+1, qk_alignof(int));
    res->comp = qk_align(res->buffer + r->max_com+1, qk_alignof(struct qk_component));
    res->def = def = qk_align(res->comp + r->max_com, qk_alignof(struct qk_definition));
    res->tree = tree = qk_align(res->def + r->max_com, qk_alignof(struct qk_node));
    res->con = con = qk_align(res->tree + r->max_com, qk_alignof(struct qk_constraint));
    res->ext = ext = qk_align(res->con + r->max_con, qk_alignof(struct qk_extension));
    } else res->buffer = align(res+1, alignof(int));
    res->comp = align(res->buffer + r->max_com+1, 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));
    res->custom = ui->custom + r->custom;
    res->comp_cnt = ui->con_cnt;
    res->con_cnt = ui->con_cnt;
    res->ext_cnt = ui->ext_cnt;
    res->initialized = True;
    custom = ext + r->max_ext;
    *ret = res;

    /* copy old state */
    qk_copy(res->comp, ui->comp, qk_szof(struct qk_component)*ui->comp_cnt);
    qk_copy(def, ui->def, qk_szof(struct qk_definition)*ui->comp_cnt);
    qk_copy(tree, ui->tree, qk_szof(struct qk_node)*ui->comp_cnt);
    qk_copy(con, ui->con, qk_szof(struct qk_constraint)*ui->con_cnt);
    qk_copy(ext, ui->ext, qk_szof(struct qk_extension)*ui->ext_cnt);
    for (int i = 0; r->mode == QK_FOLD_IMMEDIATE && i < ui->comp_cnt; ++i)
    qk_insert(res->tbl, res->tbl_cnt, ui->comp[i].id, (unsigned)i);
    /* 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 == FOLD_IMMEDIATE && i < ui->comp_cnt; ++i)
    insert(res->tbl, res->tbl_cnt, ui->comp[i].id, (unsigned)i);
    for (int i = 0; i < ui->ext_cnt; ++i) {
    ext[i] = ui->ext[i];
    ext[i].addr = (qk_addr)qk_align(custom, ext[i].algn);
    qk_copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].sz);
    custom = (char*)ext[i].addr + ext[i].sz;
    ext[i].addr = (addr)align(custom, ext[i].info.algn);
    copy((void*)ext[i].addr, (void*)ui->ext[i].addr, ext[i].info.sz);
    custom = (char*)ext[i].addr + ext[i].info.sz;
    }
    /* add new state */
    /* III.) add new state */
    char *end = (char*)r->buf + r->sz;
    for (union qk_action *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->h.nxt)) {
    if (QK_ACTION_MKCOMP == a->h.type) {
    for (union action *a = r->buf; (char*)a < end; a = (void*)((char*)a + a->h.nxt)) {
    if (ACTION_MKCOMP == a->h.type) {
    const unsigned id = a->mkcomp.id;
    const int index = res->comp_cnt++;
    ui->comp[index].id = tree[index].id = id;
    ui->comp[index].usr = a->mkcomp.usr;
    def[index] = a->mkcomp.d;
    if (r->mode == QK_FOLD_IMMEDIATE)
    qk_insert(res->tbl, res->tbl_cnt, id, (unsigned)index);
    qk_setup(ui->comp + id, def + id, ui->comp[0].surf);
    } else if (QK_ACTION_MKCON == a->h.type) {
    if (r->mode == FOLD_IMMEDIATE)
    insert(res->tbl, res->tbl_cnt, id, (unsigned)index);
    setup(ui->comp + id, def + id, ui->comp[0].surf);
    } else if (ACTION_MKCON == a->h.type) {
    con[res->con_cnt++] = a->mkcon.con;
    } else if (QK_ACTION_LNKNODE == a->h.type) {
    struct qk_node *n = tree + a->lnknode.parent;
    } else if (ACTION_LNKNODE == a->h.type) {
    struct node *n = tree + a->lnknode.parent;
    n->sub[n->cnt] = a->lnknode.child;
    res->comp[a->lnknode.child].zorder = n->cnt++;
    } else if (QK_ACTION_CUSTOM == a->h.type) {
    } else if (ACTION_CUSTOM == a->h.type) {
    const void *obj = (const void*)((const char*)a + a->custom.off);
    char *dst = qk_align(custom, a->custom.align);
    qk_copy(dst, obj, a->custom.sz);
    char *dst = align(custom, a->custom.align);
    copy(dst, obj, a->custom.sz);
    custom = dst + a->custom.sz;

    ext[res->ext_cnt].sz = a->custom.sz;
    ext[res->ext_cnt].type = a->custom.type;
    ext[res->ext_cnt].algn = a->custom.align;
    ext[res->ext_cnt++].addr = (qk_addr)dst;
    ext[res->ext_cnt].info.sz = a->custom.sz;
    ext[res->ext_cnt].info.type = a->custom.type;
    ext[res->ext_cnt].info.algn = a->custom.align;
    ext[res->ext_cnt++].addr = (addr)dst;
    }
    } /* IV.) setup extension state */
    for (int i = 0; i < cnt; ++i) {
    const struct extension *e = 0;
    map(ui, ed[i]->info.type, e)
    ed[i]->link(res, (void*)e->addr);
    }
    }
    qk_api void
    qk_commit(FILE *fp, qk_in struct qk_layout *ui,
    const char *con, const char *def, const char *tree)
    api void
    reposit(FILE *fp, in struct layout *ui, const struct commit *com)
    {
    const char *attrs[] = {"0","ACT","L","T","R","B","W","H","CX","CY"};
    const char *cons[] = {"CONSTRAINT_CPY", "CONSTRAINT_SET", "CONSTRAINT_MIN", "CONSTRAINT_MAX"};
    fprintf(fp,"qk_global const struct qk_constraint %s[] = {\n", con);
    fprintf(fp,"global const struct constraint %s[] = {\n", com->con);
    for (int i = 0; i < ui->con_cnt; ++i) {
    const struct qk_constraint *c = ui->con + i;
    const struct constraint *c = ui->con + i;
    fprintf(fp, " {{.eq = %s, .dst = {%u,%s}, .src = {%u,%s}, .cons = {.mul = %.2ff, .off = %d}}, .anch = %s},\n",
    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]);
    } fputs("};\n", fp);
    fprintf(fp, "qk_global const struct qk_definition %s[] = {\n", def);
    fprintf(fp, "global const struct definition %s[] = {\n", com->def);
    for (int i = 0; i < ui->comp_cnt; ++i) {
    const struct qk_definition *d = ui->def + i;
    const struct definition *d = ui->def + i;
    char buf[128]; int n = 0; buf[n++] = '0';
    if (d->flags & QK_HIDDEN)
    {qk_copy(buf+n, "|QK_HIDDEN",12); n += 12;}
    if (d->flags & QK_INTERACTIVE)
    {qk_copy(buf+n, "|QK_INTERACTABLE",18); n += 18;}
    if (d->flags & QK_PAINTABLE)
    {qk_copy(buf+n, "|QK_PAINTABLE",15); n += 15;}
    if (d->flags & QK_LAYER)
    {qk_copy(buf+n, "|QK_LAYER",11); n += 11;}
    if (d->flags & QK_IS_MOVABLE_X)
    {qk_copy(buf+n, "|QK_MOVABLE_X",15); n += 15;}
    if (d->flags & QK_IS_MOVABLE_Y)
    {qk_copy(buf+n, "|QK_MOVABLE_Y",15); n += 15;}
    if (d->flags & HIDDEN)
    {copy(buf+n, "|HIDDEN",12); n += 12;}
    if (d->flags & INTERACTIVE)
    {copy(buf+n, "|INTERACTABLE",18); n += 18;}
    if (d->flags & PAINTABLE)
    {copy(buf+n, "|PAINTABLE",15); n += 15;}
    if (d->flags & LAYER)
    {copy(buf+n, "|LAYER",11); n += 11;}
    if (d->flags & IS_MOVABLE_X)
    {copy(buf+n, "|MOVABLE_X",15); n += 15;}
    if (d->flags & IS_MOVABLE_Y)
    {copy(buf+n, "|MOVABLE_Y",15); n += 15;}
    buf[n] = 0;
    fprintf(fp, " {QK_TBL(%s,%s,%s), .x=%d,.y=%d,w=%d,h=%d, .flags = %s},\n",
    d->i, d->p, d->b, d->x, d->y, d->w, d->h, buf);
    fprintf(fp, " {TBL(%s,%s,%s), .sz={%d,%d,%d,%d}, .flags = %s},\n",
    d->i, d->p, d->b, d->sz.x, d->sz.y, d->sz.w, d->sz.h, buf);
    } fputs("};\n", fp);
    fprintf(fp, "qk_global const struct qk_node %s[] = {\n", tree);
    fprintf(fp, "global const struct node %s[] = {\n", com->tree);
    for (int i = 0; i < ui->comp_cnt; ++i) {
    const struct qk_node *n = ui->tree + i;
    const struct node *n = ui->tree + i;
    fprintf(fp, " {.id = %u, .parent = %u, .cnt = %d, .sub = {", n->id, n->parent, n->cnt);
    for (int j = 0; j < n->cnt; ++j)
    fprintf(fp, "%u,", n->sub[j]);
    if (!n->cnt) fputs("0", fp);
    fputs("}},\n", fp);
    } fputs("};\n", fp);
    for (int i = 0; i < com->cnt; ++i) {
    const struct extlnk *lnk = com->ext_list + i;
    fprintf(fp, "global const %s %s[] = {", lnk->type, lnk->name);
    apply(ui, lnk->def->info.type, lnk->def->commit, fp);
    fputs("};\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.type, e)
    fprintf(fp, "\n {.type = %d, .sz = %d, .algn = %d, .addr = (addr)&%s[%u]},",
    e->info.type, e->info.sz, e->info.algn, lnk->type, cnt++);
    } if (!ui->ext_cnt) fputs("0", fp);
    fputs("};\n",fp);
    fprintf(fp, "global int %s;\n", com->buffer);
    fprintf(fp, "global struct component %s;\n", com->comp);
    fprintf(fp, "global struct layout %s = {\n", com->layout);
    fprintf(fp, " .comp_cnt = cntof(%s),\n", com->def);
    fprintf(fp, " .con_cnt = cntof(%s),\n", com->con);
    fprintf(fp, " .ext_cnt = cntof(%s),\n", com->ext);
    fprintf(fp, " .buffer = %s\n", com->buffer);
    fprintf(fp, " .comp = %s,\n", com->comp);
    fprintf(fp, " .tree = %s,\n", com->tree);
    fprintf(fp, " .def = %s,\n", com->def);
    fprintf(fp, " .con = %s,\n", com->con);
    fprintf(fp, " .ext = %s,\n", com->ext);
    fputs("};\n",fp);
    }
    qk_api void
    qk_init(struct qk_context *ctx, struct qk_layout *layout, struct surface *scrn)
    api void
    begin(struct context *ctx, struct layout *ui, void *mem, int siz, struct surface *scrn)
    {
    struct qk_layout *ui = ctx->layout = layout;
    ui->comp[QK_COMPONENT_ROOT].surf = scrn;
    for (int i = 0; i < ui->comp_cnt; ++i) {
    const struct qk_node *n = ui->tree + i;
    struct qk_component *c = ui->comp + i;
    c->id = (unsigned int)i;
    qk_setup(c, ui->def + i, scrn);
    for (int j = 0; j < n->cnt; ++j)
    ui->comp[n->sub[j]].zorder = j;
    ctx->layout = ui;
    ctx->buf.mem = mem;
    ctx->buf.siz = siz;
    ctx->cur = ctx->stk[0] = 0;

    ui->comp[COMPONENT_ROOT].surf = scrn;
    if (!ui->initialized) {
    ui->initialized = True;
    for (int i = 0; i < ui->comp_cnt; ++i) {
    const struct node *n = ui->tree + i;
    struct component *c = ui->comp + i;
    c->id = (unsigned int)n->id;
    setup(c, ui->def + i, scrn);
    }
    }
    }
    qk_api void
    qk_finish(struct qk_context *ctx)
    api void
    end(struct context *ctx)
    {
    struct qk_layout *ui = ctx->layout;
    for (int i = 0; i < ui->comp_cnt; ++i) {
    ui->comp[i].pressed = 0;
    ui->comp[i].dragged = 0;
    ui->comp[i].released = 0;
    }
    ctx->layout = 0;
    }
    qk_api void
    qk_clear(struct qk_context *ctx)
    api void
    clear(struct context *ctx)
    {
    struct qk_layout *ui = ctx->layout;
    for (int i = QK_COMPONENT_ROOT+1; i < ui->comp_cnt; ++i) {
    struct layout *ui = ctx->layout;
    for (int i = COMPONENT_ROOT+1; i < ui->comp_cnt; ++i) {
    if (ui->comp[i].surf)
    surf_del(ui->comp[i].surf);
    }
    }
    #endif /* QK_CORE_FILE_ONLY */