diff options
author | aiju <devnull@localhost> | 2014-07-30 15:57:14 +0200 |
---|---|---|
committer | aiju <devnull@localhost> | 2014-07-30 15:57:14 +0200 |
commit | 712fd30652d29dc9e936f11d7837d1cb079575fc (patch) | |
tree | 2915c820a5c05761f1d2ac4d525067f701aa474b /sys/src | |
parent | 555a05018b60b28bdfd6ada0310848c68fe20e48 (diff) |
added sprite editor spred
Diffstat (limited to 'sys/src')
-rw-r--r-- | sys/src/cmd/spred/cmd.c | 188 | ||||
-rw-r--r-- | sys/src/cmd/spred/cmdw.c | 199 | ||||
-rw-r--r-- | sys/src/cmd/spred/dat.h | 98 | ||||
-rw-r--r-- | sys/src/cmd/spred/fil.c | 157 | ||||
-rw-r--r-- | sys/src/cmd/spred/fns.h | 37 | ||||
-rw-r--r-- | sys/src/cmd/spred/mkfile | 16 | ||||
-rw-r--r-- | sys/src/cmd/spred/pal.c | 280 | ||||
-rw-r--r-- | sys/src/cmd/spred/spr.c | 371 | ||||
-rw-r--r-- | sys/src/cmd/spred/spred.c | 210 | ||||
-rw-r--r-- | sys/src/cmd/spred/win.c | 281 |
10 files changed, 1837 insertions, 0 deletions
diff --git a/sys/src/cmd/spred/cmd.c b/sys/src/cmd/spred/cmd.c new file mode 100644 index 000000000..8509a6394 --- /dev/null +++ b/sys/src/cmd/spred/cmd.c @@ -0,0 +1,188 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <frame.h> +#include "dat.h" +#include "fns.h" + +extern Mousectl *mc; + +static void +dopal(int, char **argv) +{ + Pal *p; + + p = findpal("", argv[1], 2); + if(p == nil){ + cmdprint("?%r\n"); + p = newpal(argv[1]); + } + if(newwinsel(PAL, mc, p) == nil){ + if(p->ref == 0) + putfil(p); + return; + } +} + +static void +dosize(int, char **argv) +{ + char *p; + int n, m; + + if(actf == nil) + goto err; + switch(actf->type){ + case PAL: + n = strtol(argv[1], &p, 0); + if(*p != 0 || n < 0) + goto err; + palsize((Pal *) actf->f, n); + return; + case SPR: + n = strtol(argv[1], &p, 0); + if(*p != '*' || n < 0) + goto err; + m = strtol(++p, &p, 0); + if(*p != 0 || m < 0) + goto err; + sprsize((Spr *) actf->f, n, m); + return; + } +err: + cmdprint("?\n"); +} + +static void +doset(int, char **argv) +{ + int n; + char *p; + Pal *q; + + if(actf == nil) + goto err; + switch(actf->type){ + case PAL: + n = strtol(argv[1], &p, 0); + if(*p != 0 || n < 0 || n > 0xffffff) + goto err; + q = (Pal *) actf->f; + palset(q, q->sel, n); + return; + } +err: + cmdprint("?\n"); +} + +static void +dozoom(int, char **argv) +{ + int n; + char *p; + + if(actf == nil) + goto err; + n = strtol(argv[1], &p, 0); + if(*p != 0 || n <= 0) + goto err; + actf->zoom = n; + actf->tab->draw(actf); + return; +err: + cmdprint("?\n"); +} + +static void +dospr(int, char **argv) +{ + Win *w; + Spr *s; + Biobuf *bp; + + s = newspr(argv[1]); + bp = Bopen(argv[1], OREAD); + if(bp == nil) + cmdprint("?%r\n"); + else{ + if(readspr(s, bp) < 0) + cmdprint("?%r\n"); + Bterm(bp); + } + w = newwinsel(SPR, mc, s); + if(w == nil){ + putfil(s); + return; + } + if(s->palfile != nil){ + s->pal = findpal(argv[1], s->palfile, 1); + if(s->pal == nil) + cmdprint("?palette: %r\n"); + else{ + incref(s->pal); + w->tab->draw(w); + } + } +} + +static void +dowrite(int argc, char **argv) +{ + char *f; + + if(argc > 2) + cmdprint("?\n"); + if(argc == 2) + f = argv[1]; + else + f = nil; + if(actf == nil) + cmdprint("?\n"); + winwrite(actf, f); +} + +static void +doquit(int, char **) +{ + if(quit() < 0) + threadexitsall(nil); +} + +static struct cmd { + char *name; + int argc; + void (*f)(int, char **); +} cmds[] = { + {"pal", 2, dopal}, + {"size", 2, dosize}, + {"set", 2, doset}, + {"spr", 2, dospr}, + {"w", 0, dowrite}, + {"q", 1, doquit}, + {"zoom", 2, dozoom}, + {nil, nil} +}; + +void +docmd(char *s) +{ + char *t[32]; + int nt; + struct cmd *c; + + nt = tokenize(s, t, nelem(t)); + if(nt == 0) + return; + for(c = cmds; c->name != 0; c++) + if(strcmp(t[0], c->name) == 0){ + if(c->argc != 0 && c->argc != nt) + cmdprint("?\n"); + else + c->f(nt, t); + return; + } + cmdprint("?\n"); +} diff --git a/sys/src/cmd/spred/cmdw.c b/sys/src/cmd/spred/cmdw.c new file mode 100644 index 000000000..0c838d9aa --- /dev/null +++ b/sys/src/cmd/spred/cmdw.c @@ -0,0 +1,199 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <frame.h> +#include "dat.h" +#include "fns.h" + +static int +cmdinit(Win *) +{ + return 0; +} + +static void +scrollbar(Win *w) +{ + int h, t0, t1; + + h = Dy(w->inner); + draw(w->im, rectaddpt(Rect(0, 0, SCRBSIZ+1, h), w->inner.min), w->tab->cols[BORD], nil, ZP); + t0 = w->toprune * h; + t1 = (w->toprune + w->fr.nchars) * h; + if(w->nrunes == 0){ + t0 = 0; + t1 = h; + }else{ + t0 /= w->nrunes; + t1 /= w->nrunes; + } + draw(w->im, rectaddpt(Rect(0, t0, SCRBSIZ, t1), w->inner.min), w->tab->cols[BACK], nil, ZP); +} + +static void +cmddraw(Win *w) +{ + Rectangle r; + + frclear(&w->fr, 0); + r = insetrect(w->inner, 1); + r.min.x += SCRTSIZ; + scrollbar(w); + frinit(&w->fr, r, display->defaultfont, w->im, w->tab->cols); + frinsert(&w->fr, w->runes + w->toprune, w->runes + w->nrunes, 0); +} + +void +cmdscroll(Win *w, int l) +{ + int r; + + if(l == 0) + return; + if(l > 0){ + for(r = w->toprune; r < w->nrunes && l != 0; r++) + if(w->runes[r] == '\n') + l--; + frdelete(&w->fr, 0, r - w->toprune); + w->toprune = r; + }else{ + for(r = w->toprune; r > 0; r--) + if(w->runes[r] == '\n' && --l == 0) + break; + frinsert(&w->fr, w->runes + r, w->runes + w->toprune, 0); + w->toprune = r; + } + scrollbar(w); +} + +static void +cmdclick(Win *w, Mousectl *mc) +{ + if(mc->xy.x <= w->inner.min.x + SCRBSIZ){ + cmdscroll(w, -5); + return; + } + frselect(&w->fr, mc); +} + +static int +cmdrmb(Win *w, Mousectl *mc) +{ + if(mc->xy.x > w->inner.min.x + SCRBSIZ) + return -1; + cmdscroll(w, 5); + return 0; +} + +void +cmdinsert(Win *w, Rune *r, int nr, int rp) +{ + Rune *s; + + if(nr < 0) + for(nr = 0, s = r; *s++ != 0; nr++) + ; + if(rp < 0 || rp > w->nrunes) + rp = w->nrunes; + if(w->nrunes + nr > w->arunes){ + w->runes = realloc(w->runes, w->arunes = w->arunes + (nr + RUNEBLK - 1) & ~(RUNEBLK - 1)); + if(w->runes == nil) + sysfatal("realloc: %r"); + } + if(rp != w->nrunes) + memmove(w->runes + rp, w->runes + rp + nr, (w->nrunes - rp) * sizeof(Rune)); + memmove(w->runes + rp, r, nr * sizeof(Rune)); + w->nrunes += nr; + if(w->toprune > rp) + w->toprune += nr; + else{ + frinsert(&w->fr, w->runes + rp, w->runes + rp + nr, rp - w->toprune); + if(rp == w->nrunes - nr){ + if(w->fr.lastlinefull) + cmdscroll(w, 1); + } + } +} + +static void +cmddel(Win *w, int a, int b) +{ + if(a >= b) + return; + memmove(w->runes + a, w->runes + b, w->nrunes - b); + w->nrunes -= b - a; + if(w->toprune >= b) + w->toprune -= b - a; + else{ + frdelete(&w->fr, a - w->toprune, b - w->toprune); + if(w->toprune >= a) + w->toprune = a; + } +} + +static void +cmdkey(Win *w, Rune r) +{ + static char buf[4096]; + char *p; + Rune *q; + + if(w->fr.p0 < w->fr.p1) + cmddel(w, w->toprune + w->fr.p0, w->toprune + w->fr.p1); + switch(r){ + case 0x00: + case 0x1b: + break; + case '\b': + if(w->fr.p0 > 0) + cmddel(w, w->toprune + w->fr.p0 - 1, w->toprune + w->fr.p0); + break; + case '\n': + cmdinsert(w, &r, 1, w->fr.p0 + w->toprune); + if(w->toprune + w->fr.p0 == w->nrunes){ + q = w->runes + w->toprune + w->fr.p0 - 1; + p = buf; + while(*--q != 0xa && q > w->runes) + ; + if(*q == 0xa) + q++; + while(q < w->runes + w->nrunes && p < buf + nelem(buf) + 1 && *q != 0xa) + p += runetochar(p, q++); + *p = 0; + docmd(buf); + } + break; + default: + cmdinsert(w, &r, 1, w->fr.p0 + w->toprune); + } +} + +void +cmdprint(char *fmt, ...) +{ + Rune *r; + va_list va; + + va_start(va, fmt); + r = runevsmprint(fmt, va); + va_end(va); + if(r != nil) + cmdinsert(cmdw, r, -1, -1); +} + +Wintab cmdtab = { + .init = cmdinit, + .draw = cmddraw, + .click = cmdclick, + .rmb = cmdrmb, + .key = cmdkey, + .hexcols = { + [BORD] DPurpleblue, + [DISB] 0xCCCCEEFF, + [BACK] 0xCCFFFFFF, + [HIGH] DPalegreygreen + } +}; diff --git a/sys/src/cmd/spred/dat.h b/sys/src/cmd/spred/dat.h new file mode 100644 index 000000000..63ac1f75f --- /dev/null +++ b/sys/src/cmd/spred/dat.h @@ -0,0 +1,98 @@ +typedef struct Ident Ident; +typedef struct Win Win; +typedef struct Wintab Wintab; +typedef struct Pal Pal; +typedef struct Spr Spr; +typedef struct File File; + +enum { + BORDSIZ = 5, + MINSIZ = 3 * BORDSIZ, + SELSIZ = 2, + SCRBSIZ = 11, + SCRTSIZ = 14, + RUNEBLK = 4096, +}; + +enum { + DISB = NCOL, + NCOLS +}; + +enum { + CMD, + PAL, + SPR, + NTYPES +}; + +struct Wintab { + int (*init)(Win *); + void (*die)(Win *); + void (*click)(Win *, Mousectl *); + void (*menu)(Win *, Mousectl *); + int (*rmb)(Win *, Mousectl *); + void (*key)(Win *, Rune); + void (*draw)(Win *); + void (*zerox)(Win *, Win *); + u32int hexcols[NCOLS]; + Image *cols[NCOLS]; +}; + +struct Win { + Rectangle entire; + Rectangle inner; + Image *im; + Win *next, *prev; + Win *wnext, *wprev; + int type; + Wintab *tab; + + Frame fr; + Rune *runes; + int nrunes, arunes; + int toprune; + + int zoom; + Point scr; + File *f; + Rectangle sprr; +}; + +struct Ident { + uint type, dev; + Qid; +}; + +struct File { + int type; + Ref; + File *next, *prev; + char *name; + int change; + Ident id; + Win wins; +}; + +struct Pal { + File; + int ncol; + u32int *cols; + Image **ims; + int sel; +}; + +struct Spr { + File; + Pal *pal; + int w, h; + u32int *data; + char *palfile; +}; + +extern Win wlist; +extern File flist; +extern Win *actw, *actf, *cmdw; +extern Screen *scr; +extern Image *invcol; +extern int quitok; diff --git a/sys/src/cmd/spred/fil.c b/sys/src/cmd/spred/fil.c new file mode 100644 index 000000000..8cb3708b2 --- /dev/null +++ b/sys/src/cmd/spred/fil.c @@ -0,0 +1,157 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <frame.h> +#include "dat.h" +#include "fns.h" + +int +tline(Biobuf *bp, char **str, char **args, int max) +{ + char *s, *p; + int q, dq, rc; + + do{ + s = Brdstr(bp, '\n', 10); + if(s == nil) + return -1; + q = dq = 0; + for(p = s; *p != 0; p++) + if(*p == '\'') + dq = !dq; + else{ + if(dq){ + q = !q; + dq = 0; + } + if(*p == '#' && !q){ + *p = 0; + break; + } + } + rc = tokenize(s, args, max); + }while(rc == 0 && (free(s), 1)); + *str = s; + return rc; +} + +Ident +getident(int fd) +{ + Dir *d; + Ident i; + + d = dirfstat(fd); + if(d == nil) + return (Ident){-1, -1, (Qid){0, 0, 0}}; + i = (Ident){d->type, d->dev, d->qid}; + free(d); + return i; +} + +void +putident(Ident) +{ +} + +int +identcmp(Ident *a, Ident *b) +{ + return a->type != b->type || a->dev != b->dev || a->path != b->path; +} + +int +filcmp(File *f, File *g) +{ + if(f->type != g->type) + return f->type - g->type; + if(f->name == nil || g->name == nil) + return -1; + return strcmp(f->name, g->name); +} + +void +filinit(File *f, char *t) +{ + File *g; + + f->wins.wnext = f->wins.wprev = &f->wins; + f->name = strdup(t); + for(g = flist.next; g != &flist && filcmp(g, f) < 0; g = g->next) + ; + f->prev = g->prev; + f->next = g; + g->prev->next = f; + g->prev = f; +} + +void +putfil(File *f) +{ + switch(f->type){ + case PAL: putpal((Pal *) f); break; + case SPR: putspr((Spr *) f); break; + } + f->prev->next = f->next; + f->next->prev = f->prev; + free(f->name); + free(f); +} + +static char phasetitle[] = "??? phase error ???"; + +int +filtitlelen(File *f) +{ + if(f->name != nil) + return utflen(f->name) + 4; + return strlen(phasetitle); +} + +char * +filtitle(File *f, char *s, char *e) +{ + if(f->name == nil) + return strecpy(s, e, phasetitle); + *s++ = f->change ? '\'' : ' '; + if(f->wins.wnext != &f->wins) + if(f->wins.wnext->wnext != &f->wins) + *s++ = '*'; + else + *s++ = '+'; + else + *s++ = '-'; + *s++ = actf != nil && f == actf->f ? '.' : ' '; + *s++ = ' '; + return strecpy(s, e, f->name); +} + +void +winwrite(Win *w, char *f) +{ + if(w->f == nil){ + cmdprint("?\n"); + return; + } + switch(w->type){ + case PAL: + writepal((Pal *) w->f, f); + return; + case SPR: + writespr((Spr *) w->f, f); + return; + } + cmdprint("?\n"); +} + +void +filredraw(File *f) +{ + Win *w; + + for(w = f->wins.wnext; w != &f->wins; w = w->wnext) + w->tab->draw(w); +} diff --git a/sys/src/cmd/spred/fns.h b/sys/src/cmd/spred/fns.h new file mode 100644 index 000000000..e6056cb2c --- /dev/null +++ b/sys/src/cmd/spred/fns.h @@ -0,0 +1,37 @@ +void cmdprint(char *, ...); +void docmd(char *); +void* emalloc(ulong); +void filinit(File *, char *); +void filredraw(File *); +char* filtitle(File *, char *, char *); +int filtitlelen(File *); +Pal* findpal(char *, char *, int); +Ident getident(int); +int identcmp(Ident*, Ident *); +void initwin(void); +Pal* newpal(char *); +Spr* newspr(char *); +Win* newwin(int, Rectangle, File *); +Win* newwinsel(int, Mousectl *, File *); +void paldraw(Win *); +void palset(Pal *, int, u32int); +void palsize(Pal *, int); +void putfil(File *); +void putident(Ident); +void putpal(Pal *); +void putspr(Spr *); +int quit(void); +int readpal(Pal *, Biobuf *); +int readspr(Spr *, Biobuf *); +void resize(void); +void setfocus(Win *); +void sprsize(Spr *, int, int); +int tline(Biobuf *, char **, char **, int); +void winclick(Mousectl *); +void winclose(Win *); +void winresize(Win *, Mousectl *); +Win* winsel(Mousectl*, int); +void winwrite(Win *, char *); +void winzerox(Win *, Mousectl *); +int writepal(Pal *, char *); +int writespr(Spr *, char *); diff --git a/sys/src/cmd/spred/mkfile b/sys/src/cmd/spred/mkfile new file mode 100644 index 000000000..76d70ae19 --- /dev/null +++ b/sys/src/cmd/spred/mkfile @@ -0,0 +1,16 @@ +</$objtype/mkfile + +TARG=spred +BIN=/$objtype/bin +OFILES=\ + spred.$O\ + win.$O\ + cmd.$O\ + pal.$O\ + cmdw.$O\ + spr.$O\ + fil.$O\ + +HFILES=dat.h fns.h + +</sys/src/cmd/mkone diff --git a/sys/src/cmd/spred/pal.c b/sys/src/cmd/spred/pal.c new file mode 100644 index 000000000..fcc57726f --- /dev/null +++ b/sys/src/cmd/spred/pal.c @@ -0,0 +1,280 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include "dat.h" +#include "fns.h" + +Pal * +newpal(char *f) +{ + Pal *p; + + p = emalloc(sizeof(*p)); + p->type = PAL; + p->sel = -1; + filinit(p, f); + return p; +} + +void +putpal(Pal *p) +{ + int i; + + for(i = 0; i < p->ncol; i++) + freeimage(p->ims[i]); + free(p->cols); + free(p->ims); +} + +int +readpal(Pal *p, Biobuf *bp) +{ + char *s, *sp; + char *args[8]; + int nc, i, c; + + s = nil; + if(tline(bp, &s, args, nelem(args)) != 2) + goto err; + if(strcmp(args[0], "pal") != 0) + goto err; + nc = strtol(args[1], &sp, 0); + if(*sp != 0 || nc < 0) + goto err; + free(s); + s = nil; + p->ncol = nc; + p->cols = emalloc(nc * sizeof(*p->cols)); + p->ims = emalloc(nc * sizeof(*p->ims)); + for(i = 0; i < nc; i++){ + if(tline(bp, &s, args, nelem(args)) != 1) + goto err; + c = strtol(args[0], &sp, 0); + if(*sp != 0 || c < 0 || c > 0xffffff) + goto err; + p->cols[i] = c; + free(s); + s = nil; + } + for(i = 0; i < nc; i++) + p->ims[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, p->cols[i] << 8 | 0xff); + p->id = getident(bp->fid); + return 0; +err: + if(s != nil) + free(s); + werrstr("invalid format"); + return -1; +} + +int +writepal(Pal *p, char *f) +{ + Biobuf *bp; + int i, rc, n; + + if(f == nil) + f = p->name; + bp = Bopen(f, OWRITE); + if(bp == nil){ + cmdprint("?%r\n"); + return -1; + } + n = 0; + rc = Bprint(bp, "pal %d\n", p->ncol); + if(rc < 0) goto err; + n += rc; + for(i = 0; i < p->ncol; i++){ + rc = Bprint(bp, "%#.6x\n", p->cols[i]); + if(rc < 0) goto err; + n += rc; + } + if(Bterm(bp) < 0){ + cmdprint("?%r\n"); + return -1; + } + p->change = 0; + cmdprint("%s: #%d\n", f, n); + return 0; +err: + cmdprint("?%r\n"); + Bterm(bp); + return -1; +} + +Pal * +findpal(char *sf, char *fn, int op) +{ + File *f; + char *s, *q; + Ident i; + int fd; + Biobuf *bp; + Pal *p; + + if(sf == nil) + sf = ""; + s = emalloc(strlen(sf) + strlen(fn) + 2); + strcpy(s, sf); + q = strrchr(s, '/'); + if(q != nil) + *++q = 0; + else + *s = 0; + strcpy(s, fn); + fd = open(s, OREAD); + if(fd < 0){ + free(s); + return nil; + } + i = getident(fd); + if(i.type == (uint)-1){ + close(fd); + return nil; + } + for(f = flist.next; f != &flist; f = f->next) + if(f->type == PAL && identcmp(&f->id, &i) == 0){ + close(fd); + putident(i); + return (Pal *) f; + } + putident(i); + if(op == 0){ + close(fd); + return nil; + } + bp = emalloc(sizeof(*bp)); + Binit(bp, fd, OREAD); + p = newpal(s); + if(readpal(p, bp) < 0){ + putfil(p); + p = nil; + goto end; + } +end: + Bterm(bp); + close(fd); + free(bp); + free(s); + return p; +} + +static void +palredraw(Pal *p) +{ + File *f; + + filredraw(p); + for(f = flist.next; f != &flist; f = f->next) + if(f->type == SPR && ((Spr *) f)->pal == p) + filredraw(f); +} + +void +palsize(Pal *p, int sz) +{ + int i; + + if(sz == p->ncol) + return; + p->cols = realloc(p->cols, sz * sizeof(*p->cols)); + p->ims = realloc(p->ims, sz * sizeof(*p->ims)); + if(sz > p->ncol){ + memset(p->cols + p->ncol, 0, sz); + for(i = p->ncol; i < sz; i++) + p->ims[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0); + } + p->ncol = sz; + p->change = 1; + quitok = 0; + palredraw(p); +} + +void +paldraw(Win *w) +{ + Pal *p; + int n, i; + Rectangle r; + + if(w->type != PAL || w->f == nil) + sysfatal("paldraw: phase error"); + p = (Pal *) w->f; + n = Dx(w->inner) / w->zoom; + draw(w->im, w->inner, w->tab->cols[BACK], nil, ZP); + for(i = 0; i < p->ncol; i++){ + r.min = addpt(w->inner.min, mulpt(Pt(i%n, i/n), w->zoom)); + r.max.x = r.min.x + w->zoom; + r.max.y = r.min.y + w->zoom; + draw(w->im, r, p->ims[i], nil, ZP); + if(p->sel == i) + border(w->im, r, SELSIZ, display->white, ZP); + } +} + +void +palset(Pal *p, int s, u32int c) +{ + if(s < 0 || s >= p->ncol || p->cols[s] == c) + return; + p->cols[s] = c; + freeimage(p->ims[s]); + p->ims[s] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, c << 8 | 0xff); + p->change = 1; + quitok = 0; + palredraw(p); +} + +static int +palinit(Win *w) +{ + w->zoom = 32; + return 0; +} + +static void +palzerox(Win *w, Win *v) +{ + v->zoom = w->zoom; +} + +static void +palclick(Win *w, Mousectl *mc) +{ + int n, i; + Point pt; + Pal *p; + + if(!ptinrect(mc->xy, w->inner)) + return; + if(w->f == nil) + sysfatal("palclick: phase error"); + p = (Pal *) w->f; + n = Dx(w->inner) / w->zoom; + pt = subpt(mc->xy, w->inner.min); + if(pt.x >= n * w->zoom) + return; + i = pt.x / w->zoom + pt.y / w->zoom * n; + if(i >= p->ncol) + return; + p->sel = i; + palredraw(p); +} + +Wintab paltab = { + .init = palinit, + .click = palclick, + .draw = paldraw, + .zerox = palzerox, + .hexcols = { + [BORD] 0xAA0000FF, + [DISB] 0xCC8888FF, + [BACK] 0xFFCCFFFF, + }, +}; diff --git a/sys/src/cmd/spred/spr.c b/sys/src/cmd/spred/spr.c new file mode 100644 index 000000000..f61b59781 --- /dev/null +++ b/sys/src/cmd/spred/spr.c @@ -0,0 +1,371 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include "dat.h" +#include "fns.h" + +Spr * +newspr(char *f) +{ + Spr *s; + + s = emalloc(sizeof(*s)); + s->type = SPR; + filinit(s, f); + return s; +} + +void +putspr(Spr *s) +{ + if(s->pal != nil && !decref(s->pal) && s->pal->change <= 0) + putfil(s->pal); + free(s->palfile); + free(s->data); +} + +int +readspr(Spr *s, Biobuf *bp) +{ + char *args0[8], *p, *ss, **args; + int n, i, j; + + args = nil; + ss = nil; + if(tline(bp, &ss, args0, nelem(args0)) != 4) + goto err; + if(strcmp(args0[0], "sprite") != 0) + goto err; + n = strtol(args0[1], &p, 0); + if(*p != 0 || n < 0) + goto err; + s->w = n; + n = strtol(args0[2], &p, 0); + if(*p != 0 || n < 0) + goto err; + s->h = n; + if(*args0[3] != 0) + s->palfile = strdup(args0[3]); + else + s->palfile = nil; + free(ss); + ss = nil; + s->data = emalloc(s->w * s->h * sizeof(u32int)); + args = emalloc((s->w + 1) * sizeof(char *)); + for(i = 0; i < s->h; i++){ + if(tline(bp, &ss, args, s->w + 1) != s->w) + goto err; + for(j = 0; j < s->w; j++){ + n = strtol(args[j], &p, 0); + if(*p != 0 || n < 0) + goto err; + s->data[i * s->w + j] = n; + } + free(ss); + ss = nil; + } + free(args); + return 0; +err: + werrstr("invalid format"); + free(s->data); + free(args); + s->w = 0; + s->h = 0; + return -1; +} + +int +writespr(Spr *s, char *file) +{ + Biobuf *bp; + int n, rc; + int i, j; + + if(file == nil) + file = s->name; + bp = Bopen(file, OWRITE); + if(bp == nil){ + cmdprint("?%r\n"); + return -1; + } + rc = Bprint(bp, "sprite %d %d %q\n", s->w, s->h, s->palfile != nil ? s->palfile : ""); + if(rc < 0) goto err; + n = rc; + for(i = 0; i < s->h; i++) + for(j = 0; j < s->w; j++){ + rc = Bprint(bp, "%d%c", s->data[s->w * i + j], j == s->w - 1 ? '\n' : ' '); + if(rc < 0) goto err; + n += rc; + } + if(Bterm(bp) < 0){ + cmdprint("?%r\n"); + return -1; + } + s->change = 0; + quitok = 0; + cmdprint("%s: #%d\n", file, n); + return 0; +err: + cmdprint("?%r\n"); + Bterm(bp); + return -1; +} + +int +sprinit(Win *w) +{ + w->zoom = 4; + return 0; +} + +static Rectangle +sprrect(Win *w, Rectangle s) +{ + Rectangle r; + Point p, q; + Spr *t; + + t = (Spr *) w->f; + p = Pt(t->w * w->zoom, t->h * w->zoom); + q = addpt(divpt(addpt(s.min, s.max), 2), w->scr); + r.min = subpt(q, divpt(p, 2)); + r.max = addpt(r.min, p); + return r; +} + +static void +scrollbars(Win *w) +{ + Rectangle r, s; + int dx, dy; + int t0, t1; + + if(rectinrect(w->sprr, w->inner)) + return; + r = w->inner; + dx = Dx(r) - SCRTSIZ; + dy = Dy(r) - SCRTSIZ; + if(dx <= 0 || dy <= 0) + return; + s = r; + if(!rectclip(&s, w->sprr)) + return; + draw(w->im, Rect(r.min.x, r.max.y - SCRBSIZ, r.max.x - SCRTSIZ, r.max.y), w->tab->cols[BORD], nil, ZP); + draw(w->im, Rect(r.max.x - SCRBSIZ, r.min.y, r.max.x, r.max.y - SCRTSIZ), w->tab->cols[BORD], nil, ZP); + t0 = (s.min.x - w->sprr.min.x) * dx / Dx(w->sprr) + r.min.x; + t1 = (s.max.x - w->sprr.min.x) * dx / Dx(w->sprr) + r.min.x; + draw(w->im, Rect(t0, r.max.y - SCRBSIZ + 1, t1, r.max.y), w->tab->cols[BACK], nil, ZP); + t0 = (s.min.y - w->sprr.min.y) * dy / Dy(w->sprr) + r.min.y; + t1 = (s.max.y - w->sprr.min.y) * dy / Dy(w->sprr) + r.min.y; + draw(w->im, Rect(r.max.x - SCRBSIZ, t0, r.max.x, t1), w->tab->cols[BACK], nil, ZP); +} + +void +sprdraw(Win *w) +{ + Rectangle r, t; + Spr *s; + Pal *p; + int i, j; + Image *im; + u32int *d; + + if(w->type != SPR || w->f == nil) + sysfatal("sprdraw: phase error"); + s = (Spr *) w->f; + p = s->pal; + draw(w->im, w->inner, w->tab->cols[BACK], nil, ZP); + r = sprrect(w, w->inner); + w->sprr = r; + if(!rectinrect(r, w->inner)){ + t = w->inner; + t.max.x -= SCRTSIZ; + t.max.y -= SCRTSIZ; + r = sprrect(w, t); + w->sprr = r; + rectclip(&r, t); + scrollbars(w); + } + d = s->data; + for(j = 0; j < s->h; j++) + for(i = 0; i < s->w; i++, d++){ + t.min = addpt(w->sprr.min, Pt(i * w->zoom, j * w->zoom)); + t.max = addpt(t.min, Pt(w->zoom, w->zoom)); + if(!rectclip(&t, r)) + continue; + if(p != nil && *d < p->ncol) + im = p->ims[*d]; + else + im = invcol; + draw(w->im, t, im, nil, ZP); + } +} + +static int +sprbars(Win *w, Mousectl *mc) +{ + int d; + + if(rectinrect(w->sprr, w->inner)) + return -1; + if(mc->xy.x >= w->inner.max.x - SCRBSIZ){ + d = Dy(w->inner) / 5; + switch(mc->buttons){ + case 1: w->scr.y += d; break; + case 4: w->scr.y -= d; break; + default: return 0; + } + sprdraw(w); + return 0; + } + if(mc->xy.y >= w->inner.max.y - SCRBSIZ){ + d = Dx(w->inner) / 5; + switch(mc->buttons){ + case 1: w->scr.x += d; break; + case 4: w->scr.x -= d; break; + default: return 0; + } + sprdraw(w); + return 0; + } + return -1; +} + +void +sprclick(Win *w, Mousectl *mc) +{ + Spr *s; + Pal *p; + Point q; + + if(w->f == nil) + sysfatal("sprclick: phase error"); + if(sprbars(w, mc) >= 0) + return; + s = (Spr *) w->f; + p = s->pal; + if(p == nil || p->sel < 0 || p->sel >= p->ncol) + return; + do{ + q = divpt(subpt(mc->xy, w->sprr.min), w->zoom); + if(q.x < 0 || q.y < 0 || q.x >= s->w || q.y >= s->h) + continue; + if(s->data[q.y * s->w + q.x] != p->sel){ + s->data[q.y * s->w + q.x] = p->sel; + s->change = 1; + quitok = 0; + sprdraw(w); + } + }while(readmouse(mc) >= 0 && (mc->buttons & 1) != 0); +} + +void +sprsize(Spr *s, int n, int m) +{ + u32int *v; + int i, j, w, h; + + v = s->data; + if(s->w == n && s->h == m) + return; + s->data = emalloc(n * m * sizeof(u32int)); + w = n < s->w ? n : s->w; + h = m < s->h ? m : s->h; + for(j = 0; j < h; j++) + for(i = 0; i < w; i++) + s->data[j * n + i] = v[j * w + i]; + s->w = n; + s->h = m; + s->change = 1; + quitok = 0; + filredraw(s); +} + +static char * +palfile(char *, char *n) +{ + return strdup(n); +} + +static void +sprmenu(Win *w, Mousectl *mc) +{ + enum { MPAL }; + static char *menus[] = { + "pal", + nil, + }; + static Menu menu = {menus}; + Win *wp; + Spr *s; + + if(w->f == nil) + sysfatal("sprmenu: phase error"); + s = (Spr *) w->f; + switch(menuhit(2, mc, &menu, scr)){ + case MPAL: + wp = winsel(mc, 2); + if(wp == nil || wp->type != PAL) + break; + if(wp->f == nil) + sysfatal("sprmenu: pal phase error"); + if(s->pal != (Pal *) wp->f){ + if(s->pal != nil && decref(s->pal) == 0 && s->pal->change <= 0) + putfil(s->pal); + incref(wp->f); + s->pal = (Pal *) wp->f; + free(s->palfile); + s->palfile = palfile(s->name, s->pal->name); + s->change = 1; + quitok = 0; + filredraw(s); + } + break; + } +} + +static void +sprzerox(Win *w, Win *v) +{ + v->zoom = w->zoom; + v->scr = w->scr; +} + +static void +sprkey(Win *w, Rune r) +{ + static char keys[] = "1234567890qwertyuiop"; + char *p; + Spr *s; + + s = (Spr *) w->f; + if(s == nil) + sysfatal("sprkey: phase error"); + if(r < 0x100 && (p = strchr(keys, r)) != nil){ + if(s->pal == nil || p - keys >= s->pal->ncol) + return; + s->pal->sel = p - keys; + filredraw(s->pal); + } +} + +Wintab sprtab = { + .init = sprinit, + .click = sprclick, + .draw = sprdraw, + .menu = sprmenu, + .rmb = sprbars, + .zerox = sprzerox, + .key = sprkey, + .hexcols = { + [BORD] 0x00AA00FF, + [DISB] 0x88CC88FF, + [BACK] 0xCCFFCCFF, + }, +}; diff --git a/sys/src/cmd/spred/spred.c b/sys/src/cmd/spred/spred.c new file mode 100644 index 000000000..08472c205 --- /dev/null +++ b/sys/src/cmd/spred/spred.c @@ -0,0 +1,210 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <cursor.h> +#include "dat.h" +#include "fns.h" + +Mousectl *mc; +Keyboardctl *kc; +int quitok; + +enum { + ZEROX, + RESIZE, + CLOSE, + WRITE, + QUIT, + WIN +}; + +int +quit(void) +{ + File *f; + + if(!quitok) + for(f = flist.next; f != &flist; f = f->next) + if(f->change > 0){ + cmdprint("?\n"); + quitok = 1; + return 0; + } + return -1; +} + +static char * +menugen(int n) +{ + File *f; + static int mw; + static char buf[512]; + int rc; + char *p; + + switch(n){ + case ZEROX: return "zerox"; + case CLOSE: return "close"; + case RESIZE: return "resize"; + case WRITE: return "write"; + case QUIT: return "quit"; + } + if(n < WIN) + sysfatal("menugen: no string for n=%d", n); + n -= WIN; + if(n == 0){ + mw = 0; + for(f = flist.next; f != &flist; f = f->next){ + rc = filtitlelen(f); + if(rc > mw) + mw = rc; + } + return "~~spred~~"; + } + for(f = flist.next; f != &flist; f = f->next) + if(--n == 0){ + p = filtitle(f, buf, buf + sizeof(buf)); + rc = mw - utflen(buf); + if(p + rc >= buf + sizeof(buf)) + rc = buf + sizeof(buf) - p - 1; + memset(p, ' ', rc); + p[rc] = 0; + return buf; + } + return nil; + +} + +static int +rmb(void) +{ + static Menu menu = {nil, menugen}; + int n; + Win *w; + File *f; + + if(actw != nil && actw->tab->rmb != nil && actw->tab->rmb(actw, mc) >= 0) + return 0; + n = menuhit(3, mc, &menu, nil); + if(n < 0) + return 0; + switch(n){ + case ZEROX: + w = winsel(mc, 3); + if(w != nil) + winzerox(w, mc); + return 0; + case CLOSE: + w = winsel(mc, 3); + if(w != nil) + winclose(w); + return 0; + case RESIZE: + winresize(winsel(mc, 3), mc); + return 0; + case WRITE: + w = winsel(mc, 3); + if(w != nil) + winwrite(w, nil); + return 0; + case QUIT: + return quit(); + } + if(n < WIN) + sysfatal("rmb: no action for n=%d", n); + if(n == 0){ + setfocus(cmdw); + return 0; + } + n -= WIN; + for(f = flist.next; f != &flist; f = f->next) + if(--n == 0){ + if(f->wins.wnext == &f->wins){ + newwinsel(f->type, mc, f); + return 0; + } + for(w = f->wins.wnext; w != &f->wins && w != actw; w = w->wnext) + ; + if(w->wnext == &f->wins) + w = w->wnext; + setfocus(w->wnext); + return 0; + } + return 0; +} + +static void +loop(void) +{ + Rune r; + int n; + + Alt a[] = { + {mc->c, &mc->Mouse, CHANRCV}, + {kc->c, &r, CHANRCV}, + {mc->resizec, &n, CHANRCV}, + {nil, nil, CHANEND} + }; + + for(;;){ + flushimage(display, 1); + switch(alt(a)){ + case 0: + if((mc->buttons & 1) != 0) + winclick(mc); + if((mc->buttons & 2) != 0) + if(actw != nil && actw->tab->menu != nil) + actw->tab->menu(actw, mc); + if((mc->buttons & 4) != 0) + if(rmb() < 0) + return; + break; + case 1: + if(actw != nil && actw->tab->key != nil) + actw->tab->key(actw, r); + break; + case 2: + resize(); + break; + } + } +} + +void +threadmain(int argc, char **argv) +{ + ARGBEGIN { + default: + ; + } ARGEND; + + quotefmtinstall(); + if(initdraw(nil, nil, nil) < 0) + sysfatal("initdraw: %r"); + initwin(); + mc = initmouse(nil, screen); + if(mc == nil) + sysfatal("initmouse: %r"); + kc = initkeyboard(nil); + if(kc == nil) + sysfatal("initkeyboard: %r"); + loop(); + threadexitsall(nil); +} + +Cursor crosscursor = { + {-7, -7}, + {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, }, + {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, + 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, } +}; diff --git a/sys/src/cmd/spred/win.c b/sys/src/cmd/spred/win.c new file mode 100644 index 000000000..6b831a940 --- /dev/null +++ b/sys/src/cmd/spred/win.c @@ -0,0 +1,281 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <cursor.h> +#include <frame.h> +#include "dat.h" +#include "fns.h" + +Screen *scr; +extern Wintab *tabs[]; +Win wlist; +File flist; +Win *actw, *actf, *cmdw; +Image *invcol; + +void* +emalloc(ulong sz) +{ + void *v; + + v = malloc(sz); + if(v == nil) + sysfatal("malloc: %r"); + memset(v, 0, sz); + setmalloctag(v, getcallerpc(&sz)); + return v; +} + +void +initwin(void) +{ + Rectangle r; + int i, j; + + scr = allocscreen(screen, display->white, 0); + if(scr == nil) + sysfatal("allocscreen: %r"); + for(i = 0; i < NTYPES; i++) + for(j = 0; j < NCOLS; j++) + tabs[i]->cols[j] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, tabs[i]->hexcols[j]); + invcol = allocimage(display, Rect(0, 0, 2, 2), screen->chan, 1, 0); + draw(invcol, Rect(1, 0, 2, 1), display->white, nil, ZP); + draw(invcol, Rect(0, 1, 1, 2), display->white, nil, ZP); + wlist.next = wlist.prev = &wlist; + flist.next = flist.prev = &flist; + r = screen->r; + r.max.y = r.min.y + Dy(r) / 5; + cmdw = newwin(CMD, r, nil); + if(cmdw == nil) + sysfatal("newwin: %r"); +} + +Win * +newwin(int t, Rectangle r, File *f) +{ + Win *w; + + w = emalloc(sizeof(*w)); + w->next = &wlist; + w->prev = wlist.prev; + w->next->prev = w; + w->prev->next = w; + w->type = t; + w->tab = tabs[t]; + w->entire = r; + w->inner = insetrect(r, BORDSIZ); + w->im = allocwindow(scr, r, Refbackup, 0); + draw(w->im, w->inner, w->tab->cols[BACK], nil, ZP); + if(f != nil){ + incref(f); + w->wprev = f->wins.wprev; + w->wnext = &f->wins; + f->wins.wprev->wnext = w; + f->wins.wprev = w; + w->f = f; + } + w->tab->init(w); + setfocus(w); + w->tab->draw(w); + return w; +} + +Win * +newwinsel(int t, Mousectl *mc, File *f) +{ + Rectangle u; + + u = getrect(3, mc); + if(Dx(u) < MINSIZ || Dy(u) < MINSIZ) + return nil; + rectclip(&u, screen->r); + return newwin(t, u, f); +} + +void +winzerox(Win *w, Mousectl *mc) +{ + Win *v; + + if(w->tab->zerox == nil){ + cmdprint("?\n"); + return; + } + v = newwinsel(w->type, mc, w->f); + if(v == nil) + return; + w->tab->zerox(w, v); + v->tab->draw(v); +} + +void +winclose(Win *w) +{ + if(w->f == nil){ + cmdprint("?\n"); + return; + } + if(!decref(w->f)){ + if(w->f->change > 0){ + cmdprint("?\n"); + incref(w->f); + w->f->change = -1; + return; + } + putfil(w->f); + w->f = nil; + } + freeimage(w->im); + if(w->f != nil){ + w->wnext->wprev = w->wprev; + w->wprev->wnext = w->wnext; + } + w->next->prev = w->prev; + w->prev->next = w->next; + if(w == actw) + actw = nil; + if(w == actf) + actf = nil; + free(w); +} + +void +setfocus(Win *w) +{ + if(actw != nil) + border(actw->im, actw->entire, BORDSIZ, actw->tab->cols[DISB], ZP); + actw = w; + if(w != cmdw) + actf = w; + if(w == nil) + return; + if(w->im == nil) + sysfatal("setfocus: phase error"); + topwindow(w->im); + w->prev->next = w->next; + w->next->prev = w->prev; + w->prev = wlist.prev; + w->next = &wlist; + w->prev->next = w; + w->next->prev = w; + border(w->im, w->entire, BORDSIZ, w->tab->cols[BORD], ZP); +} + +static Win * +winpoint(Point p) +{ + Win *w; + + for(w = wlist.prev; w != &wlist; w = w->prev) + if(ptinrect(p, w->entire)) + return w; + return nil; +} + +void +winclick(Mousectl *mc) +{ + Win *w; + + w = winpoint(mc->xy); + if(w != nil){ + if(w != actw) + setfocus(w); + w->tab->click(w, mc); + } + while((mc->buttons & 1) != 0) + readmouse(mc); +} + +Win * +winsel(Mousectl *mc, int but) +{ + extern Cursor crosscursor; + int m; + Win *w; + + m = 1 << but - 1; + setcursor(mc, &crosscursor); + for(;;){ + readmouse(mc); + if((mc->buttons & ~m) != 0){ + w = nil; + goto end; + } + if((mc->buttons & m) != 0) + break; + } + w = winpoint(mc->xy); +end: + while(readmouse(mc), mc->buttons != 0) + ; + setcursor(mc, nil); + return w; +} + +void +winresize(Win *w, Mousectl *mc) +{ + Rectangle r; + + if(w == nil) + return; + r = getrect(3, mc); + if(Dx(r) < MINSIZ || Dy(r) < MINSIZ) + return; + rectclip(&r, screen->r); + freeimage(w->im); + w->entire = r; + w->inner = insetrect(r, BORDSIZ); + w->im = allocwindow(scr, r, Refbackup, 0); + draw(w->im, w->inner, w->tab->cols[BACK], nil, ZP); + setfocus(w); + w->tab->draw(w); +} + +void +resize(void) +{ + Rectangle old, r; + int dxo, dyo, dxn, dyn; + Win *w; + + old = screen->r; + dxo = Dx(old); + dyo = Dy(old); + if(getwindow(display, Refnone) < 0) + sysfatal("resize failed: %r"); + dxn = Dx(screen->r); + dyn = Dy(screen->r); + freescreen(scr); + scr = allocscreen(screen, display->white, 0); + if(scr == nil) + sysfatal("allocscreen: %r"); + for(w = wlist.next; w != &wlist; w = w->next){ + r = rectsubpt(w->entire, old.min); + r.min.x = muldiv(r.min.x, dxn, dxo); + r.max.x = muldiv(r.max.x, dxn, dxo); + r.min.y = muldiv(r.min.y, dyn, dyo); + r.max.y = muldiv(r.max.y, dyn, dyo); + w->entire = rectaddpt(r, screen->r.min); + w->inner = insetrect(w->entire, BORDSIZ); + freeimage(w->im); + w->im = allocwindow(scr, w->entire, Refbackup, 0); + if(w->im == nil) + sysfatal("allocwindow: %r"); + draw(w->im, w->inner, w->tab->cols[BACK], nil, ZP); + border(w->im, w->entire, BORDSIZ, w->tab->cols[w == actw ? BORD : DISB], ZP); + w->tab->draw(w); + } +} + +extern Wintab cmdtab, paltab, sprtab; + +Wintab *tabs[] = { + [CMD] &cmdtab, + [PAL] &paltab, + [SPR] &sprtab, +}; |