added sprite editor spred

This commit is contained in:
aiju 2014-07-30 15:57:14 +02:00
parent 555a05018b
commit 712fd30652
11 changed files with 1895 additions and 0 deletions

58
sys/man/1/spred Normal file
View file

@ -0,0 +1,58 @@
b.TH SPRED 1
.SH NAME
spred \- sprite editor
.SH SYNOPSIS
.B spred
.SH DESCRIPTION
.I Spred
is an editor for small images using a limited palette.
It uses a window system mimicking
.IR samterm (1).
There is a command window which uses a command language described below.
There is also an arbitrary number of palette and sprite windows.
Each open sprite file has an associated palette file.
.PP
A left click on a color in a palette window selects that color.
Colors in different palettes can be selected indepedently.
A left click on a pixel in a sprite window sets that pixel to the selected color.
.PP
A right click brings up the global menu to create windows etc.
It also lists all currently open files, including those that are not open in any window.
A middle click brings up the menu for the local window, if applicable. Available commands there are:
.TP
.I pal
The \fIpal\fR command sets the palette for the current sprite window. The palette is selected with a middle click.
.PP
The command language is a very stripped down version of
.IR rc (1),
currently only supporting "simple" commands consisting of a name and an arbitrary number of arguments separated by spaces. Quoting works just like with
.IR rc (1).
Available commands are:
.TP
.B q
Quits the program. If any files have unsaved changes, it will fail on the first attempt to quit.
.TP
.BI pal file
.TP
.BI spr file
Open a palette (\fIpal\fR) or sprite (\fIspr\fR) file named \fIfile\fR.
If the file does not exist it is created.
.TP
.BI w [file]
Write the currently selected file to \fIfile\fR. If \fIfile\fR is not specified, the name specified to the command opening the file is used.
.TP
.BI size sz
Sets the size of the current file to \fIsz\fR.
\fISz\fR should be of the form \fIn\fR for palettes or \fIn\fB*\fIm\fR for sprites where \fIn\fR and \fIm\fR are integers.
.TP
.B set 0x\fIrrggbb\fR
Sets the currently selected color to the rgb color \fI(rr,gg,bb)\fR where
.IR rr , gg and bb
are in hexadecimal notation.
.TP
.B zoom n
Sets the current zoom factor to \fIn\fR.
.SH SOURCE
.B /sys/src/cmd/spred
.SH SEE ALSO
.IR sam (1)

188
sys/src/cmd/spred/cmd.c Normal file
View file

@ -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");
}

199
sys/src/cmd/spred/cmdw.c Normal file
View file

@ -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
}
};

98
sys/src/cmd/spred/dat.h Normal file
View file

@ -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;

157
sys/src/cmd/spred/fil.c Normal file
View file

@ -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);
}

37
sys/src/cmd/spred/fns.h Normal file
View file

@ -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 *);

16
sys/src/cmd/spred/mkfile Normal file
View file

@ -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

280
sys/src/cmd/spred/pal.c Normal file
View file

@ -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,
},
};

371
sys/src/cmd/spred/spr.c Normal file
View file

@ -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,
},
};

210
sys/src/cmd/spred/spred.c Normal file
View file

@ -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, }
};

281
sys/src/cmd/spred/win.c Normal file
View file

@ -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,
};