#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KEY_CTRL 76 #define KEY_LSHIFT 99 #define KEY_RSHIFT 110 #define FPS 50 extern const char *__progname; #define NPOINTS 1000 static const char *fbpath = "/dev/cgsix0"; static const char *kbpath = "/dev/kbd"; typedef struct fxy FXY; typedef struct figure FIGURE; typedef struct family FAMILY; typedef union pval PVAL; typedef struct textopriv TEXTOPRIV; typedef struct chg CHG; struct chg { FIGURE **figp; int kind; #define CHK_FAMILY 1 #define CHK_PARAM 2 #define CHK_ROT 3 #define CHK_NEGX 4 #define CHK_NEGY 5 #define CHK_TRANSP 6 #define CHK_REV 7 #define CHK_INC 8 int p; } ; struct textopriv { int x0; int x; int y; } ; struct fxy { float x; float y; } ; union pval { int i; double d; } ; struct family { int closed; int npar; const char *partype; PVAL *pmin; PVAL *pdef; PVAL *pmax; const char *(*parname)(int); void (*makefig)(FIGURE *); } ; struct figure { int dyn; FAMILY *family; PVAL *params; int rot; int trans; #define TRANS_NEGX 1 #define TRANS_NEGY 2 #define TRANS_TRANSP 4 #define TRANS_REV 8 #define TRANS_MASK 15 FXY pts[NPOINTS]; } ; static float curcol_r; static float curcol_g; static float curcol_b; static int curcol_plane; static float curcol_da; static float curcol_dx; static float curcol_dy; static float *curcol_xp; static float *curcol_yp; static float *curcol_zp; static void *fbmap; static int fbmaplen; static volatile struct cg6fbc *fbc; static volatile struct cg6thc *thc; static volatile struct brooktree *bt; static volatile unsigned char *vram; static int timerfd; static int kbfd; static int kbflags; #define KBF_SL 0x00000001 #define KBF_SR 0x00000002 #define KBF_S (KBF_SL|KBF_SR) #define KBF_C 0x00000004 static unsigned int flags; #define F_WANTDISP 0x00000001 #define F_DBLBUF 0x00000002 #define F_INTERACT 0x00000004 #define F_FWD 0x00000008 #define F_REV 0x00000010 #define F_SSTEP 0x00000020 #define F_FULLSIZE 0x00000040 #define F_NEWVALUE 0x00000080 #define F_PTMARK 0x00000100 #define F_IFLAGS 0x000000fc static int nfamilies; static FAMILY **families; static int nfigures; static FIGURE **figures; static FIGURE *prevfigure; static double fraction; static double fracinc; static FIGURE *nextfigure; static float blend[NPOINTS]; static CHG prevchg; static CHG blendchg; static CHG nextchg; static CHG *curchg; static int nvtype; #define NVT_INT 1 #define NVT_FLOAT 2 #define NVT_BOOL 3 #define NVT_MENU 4 static char *nvibuf; static int nviblen; static int nviballoc; static PVAL nvmin; static PVAL nvmax; static int nvv_menu_n; static const char **nvv_menu_strs; static int nvv_menu_choice; static int *nvv_int; static double *nvv_float; static int *nvv_bit_loc; static int nvv_bit_bit; static int nvv_bool; static int (*nvkey)(int); static void (*nvv_done)(void); #define COL_BG 0x00 #define COL_LINE 0x11 #define COL_BOX 0x22 #define COL_GRAPHIC 0x33 #define COL_TEXT 0x44 #define COL_HLTEXT 0x55 #define COL_HLBG 0x66 #define COL_CURSBG 0x77 #define SH_0 0 #define SH_1 4 #define PM 15 #define PM_0 (PM<max)?max:v); } static __inline__ int gcd(int, int) __attribute__((__const__)); static __inline__ int gcd(int a, int b) { while (b) { int t; t = a % b; a = b; b = t; } return(a); } static void setcmap(int s) { int i; unsigned int r; unsigned int g; unsigned int b; bt->addr = 0; for (i=0;i<256;i++) { switch ((i>>s) & PM) { case PM & COL_BG: default: r = 0; g = 0; b = 0; break; case PM & COL_LINE: r = fclamp(curcol_r,0,1) * 0xffffffff; g = fclamp(curcol_g,0,1) * 0xffffffff; b = fclamp(curcol_b,0,1) * 0xffffffff; break; case PM & COL_BOX: r = 0x80000000; g = 0x80000000; b = 0x80000000; break; case PM & COL_GRAPHIC: case PM & COL_HLTEXT: r = 0xff000000; g = 0xff000000; b = 0xff000000; break; case PM & COL_TEXT: r = 0xa0000000; g = 0xa0000000; b = 0xa0000000; break; case PM & COL_HLBG: r = 0x00000000; g = 0x00000000; b = 0xff000000; break; case PM & COL_CURSBG: r = 0xff000000; g = 0x00000000; b = 0xff000000; break; } bt->cmap = 0xff000000 & r; bt->cmap = 0xff000000 & g; bt->cmap = 0xff000000 & b; } } static void resetcmap(void) { int i; unsigned int g; bt->addr = 0; for (i=0;i<256;i++) { g = (i & 1) ? 0xff000000 : 0; bt->cmap = g; bt->cmap = g; bt->cmap = g; } } static void draw(void) { while ((fbc->draw & 0xa0000000) == 0xa0000000) ; } static void drain(void) { while (fbc->s & 0x10000000) ; } static FIGURE *pickfigure(FIGURE *exclude) { int i; int n; FIGURE *f; FIGURE *p; if (nfigures < 1) abort(); p = 0; n = 0; for (i=nfigures-1;i>=0;i--) { f = figures[i]; if (f == exclude) continue; n ++; if (random() % n) continue; p = f; } return(p?:figures[0]); } static void set_curcol_xyz(void) { switch (curcol_plane) { case 0: curcol_xp = &curcol_r; curcol_yp = &curcol_g; curcol_zp = &curcol_b; break; case 1: curcol_xp = &curcol_g; curcol_yp = &curcol_b; curcol_zp = &curcol_r; break; case 2: curcol_xp = &curcol_b; curcol_yp = &curcol_r; curcol_zp = &curcol_g; break; default: abort(); break; } } static void set_curcol_dxy(void) { while (curcol_da < -M_PI) curcol_da += 2*M_PI; while (curcol_da > M_PI) curcol_da -= 2*M_PI; curcol_dx = cos(curcol_da) / (10 * FPS); curcol_dy = sin(curcol_da) / (10 * FPS); } static int rand_trans(void) { return(random()&TRANS_MASK); } static FAMILY *add_family(int closed, const char *(*parname)(int), void (*makefig)(FIGURE *), int npar, ...) { FAMILY *f; char *partypes; va_list ap; char t; int i; f = malloc(sizeof(FAMILY)); families = realloc(families,(nfamilies+1)*sizeof(FAMILY *)); families[nfamilies++] = f; f->closed = closed; f->npar = npar; partypes = malloc(npar+1); f->pmin = malloc(npar*3*sizeof(PVAL)); f->pdef = f->pmin + npar; f->pmax = f->pdef + npar; va_start(ap,npar); for (i=0;ipmin[i].i = va_arg(ap,int); f->pdef[i].i = va_arg(ap,int); f->pmax[i].i = va_arg(ap,int); break; case 'f': f->pmin[i].d = va_arg(ap,double); f->pdef[i].d = va_arg(ap,double); f->pmax[i].d = va_arg(ap,double); break; default: abort(); break; } } va_end(ap); partypes[npar] = '\0'; /* largely for debugging convenience */ f->partype = partypes; f->parname = parname; f->makefig = makefig; return(f); } static void add_figure(FAMILY *fam, ...) { va_list ap; int i; FIGURE *f; va_start(ap,fam); f = malloc(sizeof(FIGURE)); f->dyn = 0; f->family = fam; f->params = malloc(fam->npar*sizeof(PVAL)); for (i=0;inpar;i++) { switch (fam->partype[i]) { case 'i': f->params[i].i = va_arg(ap,int); break; case 'f': f->params[i].d = va_arg(ap,double); break; default: abort(); break; } } va_end(ap); (*fam->makefig)(f); figures = realloc(figures,(nfigures+1)*sizeof(FIGURE *)); figures[nfigures++] = f; } static const char *liss_parname(int x) { switch (x) { case -1: return("Lissajous figure"); break; case 0: return("X cycles"); break; case 1: return("Y cycles"); break; case 2: return("Phase"); break; } abort(); } static void liss_makefig(FIGURE *f) { int i; double p; p = f->params[2].d * (M_PI / 180); for (i=0;ipts[i].x = (sin((f->params[0].i*i*2*M_PI)/(NPOINTS-1)) + 1) / 2; f->pts[i].y = (sin(((f->params[1].i*i*2*M_PI)/(NPOINTS-1))+p) + 1) / 2; } } static const char *open_sin_parname(int x) { switch (x) { case -1: return("Open sine wave"); break; case 0: return("Cycles"); break; } abort(); } static void open_sin_makefig(FIGURE *f) { int i; for (i=0;ipts[i].x = i / (double)(NPOINTS-1); f->pts[i].y = (sin(f->params[0].i*i*2*M_PI/(NPOINTS-1)) + 1) / 2; } } static const char *spiral_parname(int x) { switch (x) { case -1: return("Spiral"); break; case 0: return("Turns"); break; } abort(); } static void spiral_makefig(FIGURE *f) { int i; for (i=0;iparams[0].i * i * M_PI / (NPOINTS-1); f->pts[i].x = ((r * cos(a)) + 1) / 2; f->pts[i].y = ((r * sin(a)) + 1) / 2; } } static const char *wiggle_parname(int x) { switch (x) { case -1: return("Wiggly circle"); break; case 0: return("Cycles"); break; case 1: return("Phase"); break; case 2: return("Amplitude"); break; } abort(); } static void wiggle_makefig(FIGURE *f) { int i; double p; p = f->params[1].d * 2 * M_PI; for (i=0;iparams[0].i*2*M_PI/(NPOINTS-1))+p)+1) * f->params[2].d * .5); a = 2 * i * M_PI / (NPOINTS-1); f->pts[i].x = ((r * cos(a)) + 1) / 2; f->pts[i].y = ((r * sin(a)) + 1) / 2; } } static const char *box1_parname(int x) { switch (x) { case -1: return("Smooth box"); break; } abort(); } static void box1_makefig(FIGURE *f) { int c1; int c2; int c3; int i; c2 = (NPOINTS-1) / 2; c1 = c2 / 2; c3 = (c2 + NPOINTS-1) / 2; for (i=0;ipts[i].x = i / (double)c1; f->pts[i].y = 0; } for (;ipts[i].x = 1; f->pts[i].y = (i - c1) / (double)(c2-c1); } for (;ipts[i].x = (c3 - i) / (double)(c3-c2); f->pts[i].y = 1; } for (;ipts[i].x = 0; f->pts[i].y = (NPOINTS-1 - i) / (double)(NPOINTS-1-c3); } } static const char *box2_parname(int x) { switch (x) { case -1: return("Corner box"); break; } abort(); } static void box2_makefig(FIGURE *f) { int c1; int c2; int c3; int i; c2 = (NPOINTS-1) / 2; c1 = c2 / 2; c3 = (c2 + NPOINTS-1) / 2; for (i=0;ipts[i].x = 0; f->pts[i].y = 0; } for (;ipts[i].x = 1; f->pts[i].y = 0; } for (;ipts[i].x = 1; f->pts[i].y = 1; } for (;ipts[i].x = 0; f->pts[i].y = 1; } f->pts[NPOINTS-1] = f->pts[0]; } static const char *modulated_parname(int x) { switch (x) { case -1: return("Modulated sine wave"); break; case 0: return("Half-cycles"); break; } abort(); } static void modulated_makefig(FIGURE *f) { int i; double d; for (i=0;ipts[i].x = d; f->pts[i].y = ((sin(d*M_PI) * sin(d*f->params[0].i*M_PI)) + 1) / 2; } } static const char *spirograph_parname(int x) { switch (x) { case -1: return("Spirograph figure"); break; case 0: return("Ring teeth"); break; case 1: return("Wheel teeth"); break; case 2: return("Rotation"); break; case 3: return("Pen position"); break; } abort(); } static void spirograph_makefig(FIGURE *f) { int rteeth; int wteeth; int i; double r; double wca; double wa; double x; double y; double rcos; double rsin; rteeth = f->params[0].i; wteeth = f->params[1].i; i = gcd(rteeth,wteeth); rteeth /= i; wteeth /= i; r = 1 - (wteeth * (1-f->params[3].d) / rteeth); rcos = cos(f->params[2].d*2*M_PI/rteeth); rsin = sin(f->params[2].d*2*M_PI/rteeth); for (i=0;iparams[3].d / rteeth) ) / r); y = ( ( ((rteeth-wteeth) * sin(wca) / rteeth) + (wteeth * sin(wa) * f->params[3].d / rteeth) ) / r); f->pts[i].x = (((x * rcos) + (y * rsin)) + 1) / 2; f->pts[i].y = (((y * rcos) - (x * rsin)) + 1) / 2; } } static void init(void) { int i; FAMILY *f; flags = 0; families = 0; figures = 0; f = add_family(1,&liss_parname,&liss_makefig,3,'i',1,1,99,'i',1,1,99,'f',0.0,0.0,360.0); add_figure(f,1,1,90.0); add_figure(f,1,3,90.0); add_figure(f,1,5,90.0); add_figure(f,2,1,90.0); add_figure(f,2,3,90.0); add_figure(f,2,5,90.0); add_figure(f,3,5,90.0); add_figure(f,4,1,90.0); add_figure(f,4,3,90.0); add_figure(f,4,5,90.0); f = add_family(0,&open_sin_parname,&open_sin_makefig,1,'i',1,1,99); add_figure(f,1); add_figure(f,2); add_figure(f,3); add_figure(f,4); add_figure(f,5); f = add_family(0,&spiral_parname,&spiral_makefig,1,'i',1,3,99); add_figure(f,3); add_figure(f,5); add_figure(f,7); f = add_family(1,&wiggle_parname,&wiggle_makefig,3,'i',1,16,99,'f',0.0,0.0,1.0,'f',0.0,0.25,1.0); add_figure(f,16,0.0,1.0/4); add_figure(f,32,0.0,1.0/4); add_figure(f,37,0.0,1.0/3); add_figure(f,23,0.0,1.0/3); f = add_family(1,&box1_parname,&box1_makefig,0); add_figure(f); f = add_family(1,&box2_parname,&box2_makefig,0); add_figure(f); f = add_family(0,&modulated_parname,&modulated_makefig,1,'i',1,15,99); add_figure(f,15); f = add_family(1,&spirograph_parname,&spirograph_makefig,4,'i',1,5,999,'i',1,3,999,'f',0.0,0.0,1.0,'f',0.0,.8,1.0); add_figure(f,5,3,0.0,.8); add_figure(f,7,3,0.0,.8); add_figure(f,11,4,0.0,.8); add_figure(f,11,8,0.0,.8); add_figure(f,15,8,0.0,1.0); srandom(time(0)); for (i=getpid();i>0;i--) random(); fracinc = 1 / (5.0 * FPS); prevfigure = figures[0]; prevfigure->rot = 0; prevfigure->trans = 0; nextfigure = pickfigure(prevfigure); nextfigure->rot = nextfigure->family->closed ? random() % (NPOINTS-1) : 0; nextfigure->trans = rand_trans(); prevchg = (CHG){ .figp=&prevfigure, .kind=CHK_FAMILY }; blendchg = (CHG){ .figp=0, .kind=CHK_INC }; nextchg = (CHG){ .figp=&nextfigure, .kind=CHK_FAMILY }; curchg = &prevchg; fraction = 1; curcol_r = 1; curcol_g = 1; curcol_b = 1; curcol_plane = random() % 3; curcol_da = ((random() & 0x7fffffff) * (M_PI/2) / (double)0x80000000U) - M_PI; set_curcol_dxy(); set_curcol_xyz(); for (i=0;ibg = 0; fbc->pixelm = ~0; fbc->s = 0; fbc->mode = MODE_NORMAL | MODE_COLOR8; fbc->alu = ALU_NORMAL | ALU_FG; fbc->clip = 0; fbc->offx = 0; fbc->offy = 0; fbc->clipminx = 0; fbc->clipminy = 0; fbc->clipmaxx = 1151; fbc->clipmaxy = 899; fbc->fg = 0; fbc->pm = ~0; drain(); fbc->arecty = 0; fbc->arectx = 0; fbc->arecty = 900; fbc->arectx = 1152; draw(); drain(); close(fd); } static void kbsetup(void) { int type; int mode; kbfd = open(kbpath,O_RDWR,0); if (kbfd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,kbpath,strerror(errno)); exit(1); } if ( (ioctl(kbfd,KIOCTYPE,&type) < 0) || (type != KB_SUN3) ) { close(kbfd); fprintf(stderr,"%s: %s: not a type-3 keyboard\n",__progname,kbpath); exit(1); } mode = 1; ioctl(kbfd,KIOCSDIRECT,&mode); mode = TR_UNTRANS_EVENT; ioctl(kbfd,KIOCTRANS,&mode); mode = 1; ioctl(kbfd,FIONBIO,&mode); kbflags = 0; } static void timersetup(void) { struct itimerval itv; int mode; timerfd = socket(AF_TIMER,SOCK_STREAM,0); if (timerfd < 0) { fprintf(stderr,"%s: AF_TIMER socket: %s\n",__progname,strerror(errno)); exit(1); } mode = 1; ioctl(timerfd,FIONBIO,&mode); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 1000000 / FPS; itv.it_value = itv.it_interval; write(timerfd,&itv,sizeof(itv)); } static FXY getpoint(FIGURE *f, int inx) { FXY p; p = f->family->closed ? f->pts[(((f->trans&TRANS_REV)?(NPOINTS*2)-3-inx:inx)+f->rot)%(NPOINTS-1)] : f->pts[(f->trans&TRANS_REV)?NPOINTS-1-inx:inx]; if (f->trans & TRANS_NEGX) p.x = 1 - p.x; if (f->trans & TRANS_NEGY) p.y = 1 - p.y; return((f->trans&TRANS_TRANSP)?(FXY){x:p.y,y:p.x}:p); } static FXY interp(FXY, float, FXY) __attribute__((__const__)); static FXY interp(FXY a, float f, FXY b) { return((FXY){x:(a.x*(1-f))+(b.x*f),y:(a.y*(1-f))+(b.y*f)}); } static __inline__ double blend_rate(double f) { return((cos(f*M_PI) + 1) / 2); } static void draw_blended(double f, int xbase, int xsize, int ybase, int ysize) { int i; FXY prev; FXY next; FXY cur; f = blend_rate(f); for (i=0;iapointy = ybase + (int)(cur.y*ysize); fbc->apointx = xbase + (int)(cur.x*xsize); draw(); } else { fbc->aliney = ybase + (int)(cur.y*ysize); fbc->alinex = xbase + (int)(cur.x*xsize); if (i) draw(); } } } static void run_drawit(void) { fbc->fg = COL_LINE; draw_blended(fraction,0,1151,0,899); drain(); } static void solid_rect(int x0, int y0, int w, int h) { fbc->arecty = y0; fbc->arectx = x0; fbc->rrecty = h - 1; fbc->rrectx = w - 1; draw(); } static void single_line(int x0, int y0, int x1,int y1) { fbc->aliney = y0; fbc->alinex = x0; fbc->aliney = y1; fbc->alinex = x1; draw(); } static void text_string(int x, int y, const char *s, int l) { const unsigned int *fdata; int i; fbc->mode = MODE_NORMAL | MODE_COLOR1; fbc->alu = ALU_NORMAL | (ALU_FG & ALU_SRC) | (ALU_BG & ~ALU_SRC); fbc->incx = 0; fbc->incy = 1; for (;l>0;s++,l--) { fbc->x0 = x; fbc->x1 = x + font_w; fbc->y0 = y; fdata = &fontdata[font_h**(const unsigned char *)s]; for (i=font_h;i>0;i--) fbc->font = *fdata++; x += font_w; } } static int textout_w(void *pvp, const char *buf, int len) { TEXTOPRIV *p; int i; int l; p = pvp; l = len; while (l > 0) { for (i=0;(ix = p->x0; p->y += font_h; i = 1; } else { text_string(p->x,p->y,buf,i); p->x += i * font_w; } buf += i; l -= i; } return(len); } static int textout_c(void *pvp) { free(pvp); return(0); } static FILE *fopen_textout(int x, int y) { TEXTOPRIV *p; FILE *f; p = malloc(sizeof(TEXTOPRIV)); p->x0 = x; p->x = x; p->y = y; f = funopen(p,0,&textout_w,0,&textout_c); if (! f) free(p); return(f); } static int chgmatch(int k1, int p1, int k2, int p2) { if (k1 != k2) return(0); if (k1 != CHK_PARAM) return(1); return(p1==p2); } static void text_desc_setup(FILE *f, CHG *chg, int kind, int p) { fflush(f); if (chg && chgmatch(chg->kind,chg->p,kind,p)) { if (chg == curchg) { fbc->fg = COL_HLTEXT; fbc->bg = COL_HLBG; } else { fbc->fg = COL_HLTEXT; fbc->bg = COL_BG; } } else { fbc->fg = COL_TEXT; fbc->bg = COL_BG; } } static void text_figdesc(FIGURE *fig, CHG *chg, int x, int y) { int i; FILE *f; f = fopen_textout(x,y); text_desc_setup(f,chg,CHK_FAMILY,0); fprintf(f,"%s\n\n",(*fig->family->parname)(-1)); for (i=0;ifamily->npar;i++) { text_desc_setup(f,chg,CHK_PARAM,i); fprintf(f,"%s: ",(*fig->family->parname)(i)); switch (fig->family->partype[i]) { case 'i': fprintf(f,"%d\n",fig->params[i].i); break; case 'f': fprintf(f,"%g\n",fig->params[i].d); break; default: abort(); break; } } fprintf(f,"\n"); if (fig->family->closed) { text_desc_setup(f,chg,CHK_ROT,0); fprintf(f,"Rotate: %d\n",fig->rot); } text_desc_setup(f,chg,CHK_NEGX,0); fprintf(f,"Negate X: %s\n",(fig->trans&TRANS_NEGX)?"yes":"no"); text_desc_setup(f,chg,CHK_NEGY,0); fprintf(f,"Negate Y: %s\n",(fig->trans&TRANS_NEGY)?"yes":"no"); text_desc_setup(f,chg,CHK_TRANSP,0); fprintf(f,"Transpose: %s\n",(fig->trans&TRANS_TRANSP)?"yes":"no"); text_desc_setup(f,chg,CHK_REV,0); fprintf(f,"Reverse: %s\n",(fig->trans&TRANS_REV)?"yes":"no"); fclose(f); } static void text_blenddesc(int x, int y) { FILE *f; fbc->bg = COL_BG; fbc->fg = COL_TEXT; f = fopen_textout(x,y); text_desc_setup(f,0,0,0); fprintf(f,"Linear: %g\nCurved: %g\n",fraction,1-blend_rate(fraction)); text_desc_setup(f,&blendchg,CHK_INC,0); fprintf(f,"Increment: %g\n",fracinc); fclose(f); } static void text_newvalue(void) { int x; int y; switch (nvtype) { case NVT_INT: case NVT_FLOAT: fbc->fg = COL_HLTEXT; fbc->bg = COL_HLBG; x = 576 - (((nviblen+1) * font_w) / 2); text_string(x,800,nvibuf,nviblen); fbc->fg = COL_CURSBG; fbc->bg = COL_CURSBG; text_string(x+(nviblen*font_w),800," ",1); break; case NVT_BOOL: fbc->fg = COL_HLTEXT; fbc->bg = nvv_bool ? COL_CURSBG : COL_HLBG; x = 576 - ((7 * font_w) / 2); text_string(x,800,"Yes",3); x += 3 * font_w; fbc->bg = COL_HLBG; text_string(x,800," ",2); x += 2 * font_w; fbc->bg = nvv_bool ? COL_HLBG : COL_CURSBG; text_string(x,800,"No",2); break; case NVT_MENU: for (y=0;yfg = COL_HLTEXT; fbc->bg = COL_HLBG; } else { fbc->fg = COL_TEXT; fbc->bg = COL_BG; } text_string(x,800-(y*font_h),nvv_menu_strs[y],l); } break; default: abort(); break; } } static void stop_drawit(void) { int x1; int x2; if (flags & F_FULLSIZE) { run_drawit(); return; } fbc->fg = COL_BOX; solid_rect(127,49,258,202); solid_rect(447,49,258,202); solid_rect(767,49,258,202); solid_rect(100,300,952,20); fbc->fg = COL_BG; solid_rect(128,50,256,200); solid_rect(448,50,256,200); solid_rect(768,50,256,200); solid_rect(101,301,950,18); fbc->fg = COL_LINE; draw_blended(0,128,256,50,200); draw_blended(fraction,448,256,50,200); draw_blended(1,768,256,50,200); fbc->fg = COL_GRAPHIC; x1 = 100 + rint(951*fraction); x2 = 100 + rint(951*(1-blend_rate(fraction))); single_line(x2,300,x1,319); text_figdesc(prevfigure,&prevchg,127,350); text_blenddesc(447,350); text_figdesc(nextfigure,&nextchg,767,350); if (flags & F_NEWVALUE) text_newvalue(); drain(); } static void setblend(void) { int x; int i; x = random() % NPOINTS; for (i=0;idyn) return; free(f->params); free(f); } static void change_figures(void) { free_figure(prevfigure); prevfigure = nextfigure; prevfigure->rot = prevfigure->family->closed ? random() % (NPOINTS-1) : 0; nextfigure = pickfigure(prevfigure); nextfigure->rot = nextfigure->family->closed ? random() % (NPOINTS-1) : 0; nextfigure->trans = rand_trans(); setblend(); prevchg = nextchg; prevchg.figp = &prevfigure; nextchg.kind = CHK_FAMILY; } static void change_curchg(CHG *c, ...) { va_list ap; CHG *c2; va_start(ap,c); while (c) { c2 = va_arg(ap,CHG *); if (c2 && (curchg == c)) { curchg = c2; break; } c = c2; } va_end(ap); } static void chg_up(CHG *c) { switch (c->kind) { case CHK_FAMILY: break; case CHK_PARAM: if (c->p < 1) { c->kind = CHK_FAMILY; } else { c->p --; } break; case CHK_NEGX: if (c->figp[0]->family->closed) { c->kind = CHK_ROT; break; } /* fall through */ case CHK_ROT: if (c->figp[0]->family->npar > 0) { c->kind = CHK_PARAM; c->p = c->figp[0]->family->npar - 1; } else { c->kind = CHK_FAMILY; } break; case CHK_NEGY: c->kind = CHK_NEGX; break; case CHK_TRANSP: c->kind = CHK_NEGY; break; case CHK_REV: c->kind = CHK_TRANSP; break; case CHK_INC: break; default: abort(); break; } } static void chg_dn(CHG *c) { switch (c->kind) { case CHK_FAMILY: if (c->figp[0]->family->npar) { c->kind = CHK_PARAM; c->p = 0; } else { if (0) { case CHK_PARAM: c->p ++; if (c->p < c->figp[0]->family->npar) break; } c->kind = (c->figp[0]->family->closed) ? CHK_ROT : CHK_NEGX; } break; case CHK_ROT: c->kind = CHK_NEGX; break; case CHK_NEGX: c->kind = CHK_NEGY; break; case CHK_NEGY: c->kind = CHK_TRANSP; break; case CHK_TRANSP: c->kind = CHK_REV; break; case CHK_REV: break; case CHK_INC: break; default: abort(); break; } } static void dynamic_figure(FIGURE **fp) { FIGURE *f; FIGURE *n; f = *fp; if (f->dyn) return; n = malloc(sizeof(FIGURE)); *n = *f; n->dyn = 1; n->params = malloc(f->family->npar*sizeof(PVAL)); bcopy(f->params,n->params,f->family->npar*sizeof(PVAL)); *fp = n; } static void set_nvi_char(int x, char ch) { if (x >= nviballoc) nvibuf = realloc(nvibuf,nviballoc=x+8); nvibuf[x] = ch; } static int nvkey_bool(int key) { switch (key) { case 59: /* Y */ nvv_bool = 1; return(1); break; case 105: /* N */ nvv_bool = 0; return(1); break; } return(0); } static int nvkey_menu(int key) { switch (key) { case 69: /* up */ if (nvv_menu_choice < nvv_menu_n-1) nvv_menu_choice ++; return(1); break; case 113: /* down */ if (nvv_menu_choice > 0) nvv_menu_choice --; return(1); break; } return(0); } static int nvkey_int(int key) { char ch; switch (key) { default: return(0); break; case 43: /* Back Space */ case 66: /* Delete */ case 111: /* Line Feed */ if (nviblen > 0) nviblen --; return(1); break; case 30: ch = '1'; break; case 31: ch = '2'; break; case 32: ch = '3'; break; case 33: ch = '4'; break; case 34: ch = '5'; break; case 35: ch = '6'; break; case 36: ch = '7'; break; case 37: ch = '8'; break; case 38: ch = '9'; break; case 39: ch = '0'; break; } nviblen ++; set_nvi_char(nviblen-1,ch); return(1); } static int valid_float_prefix(const char *s, int l) { int i; int anydig; int inexp; int negexp; int gotdot; /* Note: no leading sign! */ anydig = 0; inexp = 0; negexp = 0; gotdot = 0; for (i=0;i 0) nviblen --; return(1); break; case 30: ch = '1'; break; case 31: ch = '2'; break; case 32: ch = '3'; break; case 33: ch = '4'; break; case 34: ch = '5'; break; case 35: ch = '6'; break; case 36: ch = '7'; break; case 37: ch = '8'; break; case 38: ch = '9'; break; case 39: ch = '0'; break; case 40: ch = '-'; break; case 56: ch = 'e'; break; case 108: ch = '.'; break; } nviblen ++; set_nvi_char(nviblen-1,ch); if (! valid_float_prefix(nvibuf,nviblen)) { nviblen --; return(0); } return(1); } static void nvdone_repattern(void) { (*curchg->figp[0]->family->makefig)(curchg->figp[0]); setblend(); } static void nvdone_chgfamily(void) { FIGURE *f; f = malloc(sizeof(FIGURE)); f->dyn = 1; f->family = families[nvv_menu_choice]; f->params = malloc(f->family->npar*sizeof(PVAL)); bcopy(f->family->pdef,f->params,f->family->npar*sizeof(PVAL)); f->rot = 0; f->trans = 0; (*f->family->makefig)(f); free_figure(curchg->figp[0]); curchg->figp[0] = f; setblend(); } static void newvalue_return(int esc) { char *t; if (flags & F_NEWVALUE) { if (esc) { flags &= ~F_NEWVALUE; return; } switch (nvtype) { default: abort(); break; case NVT_INT: { int v; set_nvi_char(nviblen,'\0'); v = atoi(nvibuf); if ((v < nvmin.i) || (v > nvmax.i)) return; *nvv_int = v; } break; case NVT_FLOAT: { double v; char *ep; set_nvi_char(nviblen,'\0'); v = strtod(nvibuf,&ep); if (*ep || (ep == nvibuf) || (v < nvmin.d) || (v > nvmax.d)) return; *nvv_float = v; } break; case NVT_BOOL: if (nvv_bool) *nvv_bit_loc |= nvv_bit_bit; else *nvv_bit_loc &= ~nvv_bit_bit; break; case NVT_MENU: break; } flags &= ~F_NEWVALUE; if (nvv_done) (*nvv_done)(); } else { flags |= F_NEWVALUE; if (curchg->figp && !curchg->figp[0]->dyn) dynamic_figure(curchg->figp); switch (curchg->kind) { default: abort(); break; case CHK_FAMILY: { int i; nvtype = NVT_MENU; nvv_menu_n = nfamilies; nvv_menu_strs = malloc(nvv_menu_n*sizeof(const char *)); nvv_menu_choice = -1; for (i=0;iparname)(-1); if (families[i] == curchg->figp[0]->family) nvv_menu_choice = i; } if (nvv_menu_choice < 0) abort(); nvv_done = &nvdone_chgfamily; } break; case CHK_PARAM: switch (curchg->figp[0]->family->partype[curchg->p]) { case 'i': nvtype = NVT_INT; nvv_int = &curchg->figp[0]->params[curchg->p].i; break; case 'f': nvtype = NVT_FLOAT; nvv_float = &curchg->figp[0]->params[curchg->p].d; break; default: abort(); break; } nvmin = curchg->figp[0]->family->pmin[curchg->p]; nvmax = curchg->figp[0]->family->pmax[curchg->p]; nvv_done = &nvdone_repattern; break; case CHK_ROT: nvtype = NVT_INT; nvv_int = &curchg->figp[0]->rot; nvmin.i = 0; nvmax.i = NPOINTS - 1; nvv_done = &nvdone_repattern; break; case CHK_NEGX: nvv_bit_bit = TRANS_NEGX; if (0) { case CHK_NEGY: nvv_bit_bit = TRANS_NEGY; } if (0) { case CHK_TRANSP: nvv_bit_bit = TRANS_TRANSP; } if (0) { case CHK_REV: nvv_bit_bit = TRANS_REV; } nvtype = NVT_BOOL; nvv_bit_loc = &curchg->figp[0]->trans; nvv_done = &nvdone_repattern; break; case CHK_INC: nvtype = NVT_FLOAT; nvv_float = &fracinc; nvmin.d = 0; nvmax.d = 1; nvv_done = 0; break; } t = 0; switch (nvtype) { case NVT_INT: nviblen = asprintf(&t,"%d",*nvv_int); nvkey = &nvkey_int; break; case NVT_FLOAT: nviblen = asprintf(&t,"%g",*nvv_float); nvkey = &nvkey_float; break; case NVT_BOOL: nvv_bool = (nvv_bit_bit & *nvv_bit_loc) ? 1 : 0; nvkey = &nvkey_bool; break; case NVT_MENU: nvkey = &nvkey_menu; break; } if (t) { if (nviblen > nviballoc) { free(nvibuf); nviballoc = nviblen; nvibuf = t; } else { bcopy(t,nvibuf,nviblen); free(t); } } } } static void keyboard_event(int key, int press) { if (keyboard_flagproc(key,press)) return; if ( press && ((key == 102) && (kbflags & KBF_C)) ) { drain(); resetcmap(); fbc->pm = ~0; fbc->fg = 0; drain(); solid_rect(0,0,1152,900); drain(); exit(0); } flags |= F_WANTDISP; if (! (flags & F_INTERACT)) { if (! press) return; flags |= F_INTERACT; return; } if (flags & F_NEWVALUE) { if (press && (*nvkey)(key)) return; switch (key) { case 29: /* Esc */ if (press) newvalue_return(1); break; case 89: /* Return */ if (press) newvalue_return(0); break; } return; } switch (key) { default: printf("%d %s\n",key,press?"dn":"up"); break; case 40: /* -_ */ if (press) flags |= F_REV; else flags &= ~F_REV; break; case 41: /* =+ */ if (press) flags |= F_FWD; else flags &= ~F_FWD; break; case 63: /* P */ if (press) flags ^= F_PTMARK; break; case 69: /* up */ if (press) chg_up(curchg); break; case 78: /* S */ if (press) flags |= F_SSTEP; else flags &= ~F_SSTEP; break; case 80: /* F */ if (press) { change_figures(); fraction = 0; } break; case 89: /* Return */ if (press) newvalue_return(0); break; case 91: /* left */ if (press) change_curchg(&nextchg,&blendchg,&prevchg,(CHG *)0); break; case 93: /* right */ if (press) change_curchg(&prevchg,&blendchg,&nextchg,(CHG *)0); break; case 100: /* Z */ if (press) flags |= F_FULLSIZE; else flags &= ~F_FULLSIZE; break; case 113: /* down */ if (press) chg_dn(curchg); break; case 121: /* space */ if (press) flags &= ~F_IFLAGS; break; } } static void kbcheck(void) { struct firm_event ev; int r; r = read(kbfd,&ev,sizeof(ev)); if (r == sizeof(ev)) keyboard_event(ev.id&0x7f,ev.value!=VKEY_UP); } static void colourdrift(void) { float cx; float cy; int set; set = 0; cx = *curcol_xp += curcol_dx; cy = *curcol_yp += curcol_dy; if (cx < 0) { *curcol_xp = 0; curcol_da = M_PI - curcol_da; set = 1; } else if (cy < 0) { *curcol_yp = 0; curcol_da = - curcol_da; set = 1; } else if (cx > 1) { *curcol_xp = 1; curcol_plane = (curcol_plane+1) % 3; curcol_da -= M_PI / 2; set = 1; } else if (cy > 1) { *curcol_yp = 1; curcol_plane = (curcol_plane+2) % 3; curcol_da += M_PI / 2; set = 1; } if (set) { set_curcol_xyz(); curcol_da += ((random() & 0x7fffffff) / (double)0x80000000U) * .1; set_curcol_dxy(); } } static void run_tick(void) { if (fraction == 1) { change_figures(); fraction = 0; } else { fraction += fracinc; if (fraction > 1) fraction = 1; } colourdrift(); } static void stop_tick(void) { switch (flags & (F_FWD|F_REV)) { case F_FWD: fraction += fracinc; if (fraction > 1) fraction = 1; colourdrift(); flags |= F_WANTDISP; if (flags & F_SSTEP) flags &= ~F_FWD; break; case F_REV: fraction -= fracinc; if (fraction < 0) fraction = 0; colourdrift(); flags |= F_WANTDISP; if (flags & F_SSTEP) flags &= ~F_REV; break; } } static void timercheck(void) { struct timersock_event e; int r; r = read(timerfd,&e,sizeof(e)); if (r == sizeof(e)) { if (! (flags & F_INTERACT)) { run_tick(); flags |= F_WANTDISP; return; } else { stop_tick(); } } } static void handleargs(int ac, char **av) { int errs; int skip; errs = 0; skip = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs another argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-fb") || !strcmp(*av,"-fbpath")) { WANTARG(); fbpath = av[skip]; } else if (!strcmp(*av,"-kbd") || !strcmp(*av,"-kb") || !strcmp(*av,"-kbpath")) { WANTARG(); kbpath = av[skip]; } else if (**av == '-') { fprintf(stderr,"%s: unrecognized flag %s\n",__progname,*av); errs ++; } else { fprintf(stderr,"%s: stray argument `%s'\n",__progname,*av); errs ++; } #undef WANTARG } if (errs) exit(1); } static void dodisp(void) { fbc->pixelm = ~0; fbc->s = 0; fbc->mode = 0x00229540; /* not all bits known */ fbc->alu = 0xc0000000 /* GX_PLANE_MASK */ | 0x20000000 /* GX_PIXEL_ONES */ | 0x00800000 /* GX_ATTR_SUPP (?) */ | 0x00000000 /* GX_RAST_BOOL (?) */ | 0x00000000 /* GX_PLOT_PLOT (?) */ | 0x08000000 /* GX_PATTERN_ONES */ | 0x01000000 /* GX_POLYG_OVERLAP */ | ALU_FG; fbc->clip = 0; fbc->offx = 0; fbc->offy = 0; fbc->clipminx = 0; fbc->clipminy = 0; fbc->clipmaxx = 1151; fbc->clipmaxy = 899; fbc->pm = (flags & F_DBLBUF) ? PM_1 : PM_0; fbc->fg = 0; drain(); fbc->arecty = 0; fbc->arectx = 0; fbc->arecty = 900; fbc->arectx = 1152; draw(); fbc->mode = MODE_NORMAL | MODE_COLOR8; if (! (flags & F_INTERACT)) run_drawit(); else stop_drawit(); drain(); setcmap((flags & F_DBLBUF) ? SH_1 : SH_0); flags ^= F_DBLBUF; } static void step(void) { struct pollfd pfd[2]; int prv; pfd[0].fd = timerfd; pfd[0].events = POLLIN | POLLRDNORM; pfd[1].fd = kbfd; pfd[1].events = POLLIN | POLLRDNORM; prv = poll(&pfd[0],2,(flags&F_WANTDISP)?0:INFTIM); if (prv < 0) { if (errno == EINTR) return; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } if (prv == 0) { flags &= ~F_WANTDISP; dodisp(); return; } if (pfd[0].revents) timercheck(); if (pfd[1].revents) kbcheck(); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); init(); fbsetup(); kbsetup(); timersetup(); while (1) step(); }