#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; static const char *fbpath = "/dev/cgsix0"; static const char *kbpath = "/dev/kbd"; static const char *mspath = "/dev/mouse"; static int kbfd; static int msfd; 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; /* For the sake of text consoles, entry [0] should be black and [15] white. */ static unsigned char cmap[16][3] = { { 0, 0, 0 }, #define COL_BG 0x00 { 255, 0, 0 }, #define COL_LIVE 0x11 { 0, 0, 255 }, #define COL_ENLGRID 0x22 { 0, 0, 0 }, { 255, 0, 0 }, #define COL_ENLCELL 0x44 { 0, 255, 0 }, #define COL_SELBOX 0x55 { 0, 0, 128 }, #define COL_TEXTBG 0x66 { 200, 200, 200 }, #define COL_TEXTFG 0x77 { 255, 255, 0 }, #define COL_TEXTBOX 0x88 { 0, 0, 255 }, #define COL_TEXTCBG 0x99 { 255, 255, 255 }, #define COL_TEXTCFG 0xaa { 255, 255, 0 }, #define COL_FOUND 0xbb { 96, 48, 48 }, #define COL_OFFBOARD 0xcc { 0, 150, 0 }, #define COL_ENLMOVE 0xdd { 255, 150, 0 }, #define COL_ENLBOTH 0xee { 255, 255, 255 } }; #define COL_ENLBOX 0xff #include "shapes.h" typedef struct cursor CURSOR; /* data: space - ignored . - 00 (ie, cursor not present) a - 01 ("background") b - 10 ("foreground 1") c - 11 ("foreground 2") * - . + hotspot A - a + hotspot B - b + hotspot C - c + hotspot / - marks end of row width and height are deduced from the data */ struct cursor { const char *data; int setup; XY hot; unsigned long int mask[32]; unsigned long int bits[32]; } ; typedef struct mapblk MAPBLK; struct mapblk { void *data; int len; } ; static XY mouse; /* Mouse position, display coords */ static CURSOR *curs; #define ENL_X 64 #define ENL_Y 64 #define ENL_XMAG 8 #define ENL_YMAG 8 #define ENL_MX (1+(ENL_X*ENL_XMAG)+1) #define ENL_MY (1+(ENL_Y*ENL_YMAG)+1) #define MIN_SIZE_X 64 #define MIN_SIZE_Y 64 static int enl_up; static XY enl_at; /* Cell coords, UL enlarged cell */ static XY enl_disp; /* Display coords, UL corner of enlargement */ static int fulldisp; static int kbflags; #define KBF_CTRL 0x00000001 #define KBF_LSHIFT 0x00000002 #define KBF_RSHIFT 0x00000004 #define KBF_SHIFT (KBF_LSHIFT|KBF_RSHIFT) static int mousestate; static int dropmove; static void (*input_op)(int, ...); #define IO_CLICK 1 #define IO_MOVE 2 #define IO_KEY 3 #define IO_DRAW 4 static CURSOR *sel_prev_curs; static void (*sel_prev_io)(int, ...); static int sel_prev_box; static int selphase; static int seladj_ninth; static int seladj_carrying; static XY seladj_lastmouse; static XY seladjp1; static XY seladjp2; static XY selp1; /* Cell cords */ static XY selp2; /* Cell cords */ static int selbox; #define SB_HAVE 1 #define SB_SHOW 2 static XY sel2p1; /* Cell cords */ static XY sel2p2; /* Cell cords */ static int sel2box; static XY sel2p1off; /* Delta */ static XY sel2p2off; /* Delta */ static XY sel2anchor; /* Cell coords */ static void (*sel2_fn)(int); #define S2_DONE 1 #define S2_ABORT 2 static int (*sel2_get)(XY); static int sel2xform; #define XF_NEGX 1 #define XF_NEGY 2 #define XF_SWAP 4 #define XFORM_GRIDSIZE 64 #define XFORM_PATX 3 #define XFORM_PATY 3 static int xfpcols[XFORM_PATY][XFORM_PATX] = { { COL_BG, COL_BG, COL_LIVE }, { COL_BG, COL_LIVE, COL_BG }, { COL_BG, COL_LIVE, COL_BG } }; static CURSOR *text_prev_curs; static void (*text_prev_io)(int, ...); static int textlen; static int textdlen; static int textdoff; static int textcurs; static int textfake; static unsigned char *textbuf; static int textballoc; static void (*text_keystroke)(unsigned char, int); static int (*text_acc)(unsigned char, int); static int textpromptlen; static void (*text_gotline)(const char *); #define TEXT_MARGIN 3 static XY savep1; static XY savep2; static char *load_bits; static int load_bw; static int ignore_mouse; static int show_border_widths; static int show_border_widths_timer; static struct timeval show_border_widths_expire; static int show_gen_counter; static unsigned int gen_counter; static int gen_counter_corner = 0; /* low two bits: 0=UL, 1=UR, 2=LL, 3=LR */ typedef unsigned long int WORD; #define WBITS 32 #define HIGHBIT (~((~(WORD)0)>>1)) static int arena_xw; /* X size in WORDs */ static int arena_x; /* X size in cells */ static int arena_y; /* Y size in cells */ static int resize_x; /* New X size in cells */ static int resize_y; /* New Y size in cells */ static XY disp; /* Display coord location of cell (0,0) */ #define DISPLAY_X 1152 #define DISPLAY_Y 900 #include "font.h" static CURSOR cursor_x = { "a a . . . . . . . . . . . a a/" "a c a . . . . . . . . . a c a/" ". a c a . . . . . . . a c a ./" ". . a c a . . . . . a c a . ./" ". . . a c a . . . a c a . . ./" ". . . . a . . . . . a . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . * . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . a . . . . . a . . . ./" ". . . a c a . . . a c a . . ./" ". . a c a . . . . . a c a . ./" ". a c a . . . . . . . a c a ./" "a c a . . . . . . . . . a c a/" "a a . . . . . . . . . . . a a" }; static CURSOR cursor_plus = { ". . . . . . a a a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" "a a a a a a a c a a a a a a a/" "a c c c c c c C c c c c c c c/" "a a a a a a a c a a a a a a a/" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a c a . . . . . ./" ". . . . . . a a a . . . . . ." }; static CURSOR cursor_box_ul = { "a a a a a a a a a a a a a a a/" "a C c c c c c c c c c c c c a/" "a c c c c c c c c c c c c a a/" "a c c a a a a a a a a a a a ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c a a . . . . . . . . . . ./" "a a a . . . . . . . . . . . ." }; static CURSOR cursor_box_um = { "a a a a a a a a a a a a a a a/" "a c c c c c c C c c c c c c a/" "a c c c c c c c c c c c c c a/" "a c c a a a a a a a a a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c a a . . . . . . . a a c a/" "a a a . . . . . . . . . a a a/" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ." }; static CURSOR cursor_box_ur = { "a a a a a a a a a a a a a a a/" "a c c c c c c c c c c c c C a/" "a a c c c c c c c c c c c c a/" ". a a a a a a a a a a a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a a c a/" ". . . . . . . . . . . . a a a" }; static CURSOR cursor_box_ml = { "a a a a a a a a a a . . . . ./" "a c c c c c c c c a . . . . ./" "a c c c c c c c a a . . . . ./" "a c c a a a a a a . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a C c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a a a a a a . . . . . ./" "a c c c c c c c a a . . . . ./" "a c c c c c c c c a . . . . ./" "a a a a a a a a a a . . . . ." }; static CURSOR cursor_box_mm = { "a a a a a a a a a a a a a a a/" "a c c c c c c c c c c c c c a/" "a c c c c c c c c c c c c c a/" "a c c a a a a a a a a a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . * . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a a a a a a a a a c c a/" "a c c c c c c c c c c c c c a/" "a c c c c c c c c c c c c c a/" "a a a a a a a a a a a a a a a" }; static CURSOR cursor_box_mr = { ". . . . . a a a a a a a a a a/" ". . . . . a c c c c c c c c a/" ". . . . . a a c c c c c c c a/" ". . . . . . a a a a a a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c C a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . a a a a a a c c a/" ". . . . . a a c c c c c c c a/" ". . . . . a c c c c c c c c a/" ". . . . . a a a a a a a a a a" }; static CURSOR cursor_box_ll = { "a a a . . . . . . . . . . . ./" "a c a a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a . . . . . . . . . . ./" "a c c a a a a a a a a a a a ./" "a c c c c c c c c c c c c a a/" "a C c c c c c c c c c c c c a/" "a a a a a a a a a a a a a a a" }; static CURSOR cursor_box_lm = { ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" ". . . . . . . . . . . . . . ./" "a a a . . . . . . . . . a a a/" "a c a a . . . . . . . a a c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a . . . . . . . a c c a/" "a c c a a a a a a a a a c c a/" "a c c c c c c c c c c c c c a/" "a c c c c c c C c c c c c c a/" "a a a a a a a a a a a a a a a" }; static CURSOR cursor_box_lr = { ". . . . . . . . . . . . a a a/" ". . . . . . . . . . . a a c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". . . . . . . . . . . a c c a/" ". a a a a a a a a a a a c c a/" "a a c c c c c c c c c c c c a/" "a c c c c c c c c c c c c C a/" "a a a a a a a a a a a a a a a" }; static CURSOR cursor_nil = { "*" }; extern void font_30_to_32( unsigned long int **, int, int, volatile struct cg6fbc *, int, int ); extern void crank( unsigned long int **, unsigned int, unsigned int, unsigned long int **, unsigned long int ); extern unsigned int board_hash(unsigned long int **, int, int); static int drawbit; static WORD **mem; static WORD edgemask; static WORD *tmps[2]; static MAPBLK mapblks[2]; #define NMAPBLKS (sizeof(mapblks)/sizeof(mapblks[0])) static int havemem = 0; static int run; #define RUN_DISP (-1) #define RUN_AUTO (-2) static int autodisp; #define AUTOHIST_N 512 static unsigned int autohash[AUTOHIST_N]; __inline__ static int imax(int, int) __attribute__((__const__)); __inline__ static int imax(int a, int b) { return((a>b)?a:b); } __inline__ static int imin(int, int) __attribute__((__const__)); __inline__ static int imin(int a, int b) { return((a= maxv) val = maxv-1; return(val); } __inline__ static int buttonbit(int) __attribute__((__const__)); __inline__ static int buttonbit(int b) { return(1<<(b-1)); } __inline__ static int div_down(int, int) __attribute__((__const__)); __inline__ static int div_down(int a, int b) { if (b < 0) { b = - b; a = - a; } if (a < 0) { int q; a = - a; q = a / b; if (a != q*b) q ++; return(-q); } else { return(a/b); } } __inline__ static XY xyneg(XY) __attribute__((__const__)); __inline__ static XY xyneg(XY a) { return((XY){x:-a.x,y:-a.y}); } __inline__ static XY xyadd(XY, XY) __attribute__((__const__)); __inline__ static XY xyadd(XY a, XY b) { return((XY){x:a.x+b.x,y:a.y+b.y}); } __inline__ static XY xysub(XY, XY) __attribute__((__const__)); __inline__ static XY xysub(XY a, XY b) { return((XY){x:a.x-b.x,y:a.y-b.y}); } __inline__ static XY xyabsdiff(XY, XY) __attribute__((__const__)); __inline__ static XY xyabsdiff(XY a, XY b) { return((XY){x:abs(a.x-b.x),y:abs(a.y-b.y)}); } static void setcmap(int bit) { int i; int x; bt->addr = 0; for (i=0;i<256;i++) { x = bit ? (i>>4) : (i&15); bt->cmap = cmap[x][0] * 0x01000000; bt->cmap = cmap[x][1] * 0x01000000; bt->cmap = cmap[x][2] * 0x01000000; } } static void draw(void) { while ((fbc->draw & 0xa0000000) == 0xa0000000) ; } static void drain(void) { while (fbc->s & 0x10000000) ; } static int fbsetup(void) { int fd; struct fbgattr a; fd = open(fbpath,O_RDWR,0); if (fd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,fbpath,strerror(errno)); return(1); } if ( (ioctl(fd,FBIOGATTR,&a) < 0) || (a.fbtype.fb_type != FBTYPE_SUNFAST_COLOR) || (a.fbtype.fb_width != 1152) || (a.fbtype.fb_height != 900) || (a.fbtype.fb_depth != 8) ) { fprintf(stderr,"%s: %s: not a usable cgsix\n",__progname,fbpath); return(1); } fbmaplen = 0x16000 + a.fbtype.fb_size; fbmap = mmap(0,fbmaplen,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0x70000000); if (fbmap == MAP_FAILED) { fprintf(stderr,"%s: can't mmap %s: %s\n",__progname,fbpath,strerror(errno)); return(1); } fbc = fbmap; thc = (void *)(0x5000+(unsigned char *)fbmap); bt = (void *)(0x2000+(unsigned char *)fbmap); vram = 0x16000 + (unsigned char *)fbmap; setcmap(1); drawbit = 0; fbc->bg = 0; 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->fg = 0; fbc->pm = ~0; drain(); fbc->arecty = 0; fbc->arectx = 0; fbc->arecty = 900; fbc->arectx = 1152; draw(); drain(); fbc->clipmaxx = DISPLAY_X - 1; fbc->clipmaxy = DISPLAY_Y - 1; close(fd); return(0); } static int 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)); return(1); } if ( (ioctl(kbfd,KIOCTYPE,&type) < 0) || (type != KB_SUN3) ) { close(kbfd); fprintf(stderr,"%s: %s isn't a type-3\n",__progname,kbpath); return(1); } mode = 1; ioctl(kbfd,KIOCSDIRECT,&mode); mode = TR_UNTRANS_EVENT; ioctl(kbfd,KIOCTRANS,&mode); mode = 1; ioctl(kbfd,FIONBIO,&mode); kbflags = 0; return(0); } static void cursor_move(void) { thc->cursxy = (((mouse.x-curs->hot.x) & 0xffff) << 16) | ((mouse.y-curs->hot.y) & 0xffff); } static int place_gen_counter(void) { int can; int ninth; can = 15; if (enl_up) can &= ((enl_disp.x < (DISPLAY_X/2)-(ENL_MX/2)) ? 10 : 5) | ((enl_disp.y < (DISPLAY_Y/2)-(ENL_MY/2)) ? 12 : 3); if (mouse.x < DISPLAY_X/3) ninth = 0; else if (mouse.x < (2*DISPLAY_X)/3) ninth = 1; else ninth = 2; if (mouse.y < DISPLAY_Y/3) ; else if (mouse.y < (2*DISPLAY_Y)/3) ninth += 3; else ninth += 6; can &= "\16\17\15\17\17\17\13\17\07"[ninth]; if (can == 0) abort(); if (! (can & (1 << gen_counter_corner))) { if (can & (1 << (gen_counter_corner^1))) { return(gen_counter_corner^1); } else if (can & (1 << (gen_counter_corner^2))) { return(gen_counter_corner^2); } else { return(gen_counter_corner^3); } } return(gen_counter_corner); } static void move_mouse_slide(XY new) { int moved; if ( enl_up && (new.x >= enl_disp.x+1) && (new.x < enl_disp.x+ENL_MX-1) && (new.y >= enl_disp.y+1) && (new.y < enl_disp.y+ENL_MY-1) ) { } else if ( (new.x >= disp.x) && (new.x < disp.x+arena_x) && (new.y >= disp.y) && (new.y < disp.y+arena_y) ) { } else if ( enl_up && (mouse.x >= enl_disp.x+1) && (mouse.x < enl_disp.x+ENL_MX+1) && (mouse.y >= enl_disp.y+1) && (mouse.y < enl_disp.y+ENL_MY+1) ) { new.x = limit_to(new.x,enl_disp.x+1,enl_disp.x+ENL_MX-1); new.y = limit_to(new.y,enl_disp.y+1,enl_disp.y+ENL_MY-1); } else { new.x = limit_to(new.x,disp.x,disp.x+arena_x); new.y = limit_to(new.y,disp.y,disp.y+arena_y); } moved = 0; if (new.x < 0) { disp.x -= new.x; if (disp.x > 0) disp.x = 0; moved = 1; new.x = 0; } else if (new.x >= DISPLAY_X) { disp.x -= new.x - (DISPLAY_X-1); if (disp.x+arena_x < DISPLAY_X) disp.x = DISPLAY_X - arena_x; moved = 1; new.x = DISPLAY_X - 1; } if (new.y < 0) { disp.y -= new.y; if (disp.y > 0) disp.y = 0; moved = 1; new.y = 0; } else if (new.y >= DISPLAY_Y) { disp.y -= new.y - (DISPLAY_Y-1); if (disp.y+arena_y < DISPLAY_Y) disp.y = DISPLAY_Y - arena_y; moved = 1; new.y = DISPLAY_Y - 1; } if (moved) { fulldisp = 1; gettimeofday(&show_border_widths_expire,0); show_border_widths_expire.tv_sec += 5; show_border_widths_timer = 1; } if ( (new.x < 0) || (new.x >= DISPLAY_X) || (new.y < 0) || (new.y >= DISPLAY_Y) ) abort(); mouse = new; cursor_move(); if (show_gen_counter) { int corner; corner = place_gen_counter(); if (corner != gen_counter_corner) fulldisp = 1; } } /* Args in display coords */ static void warp(int x, int y) { XY new; new.x = x; new.y = y; move_mouse_slide(new); } static void setup_cursor(CURSOR *c) { int i; char ch; int x; int y; int w; int v; int h; int sethot; static void nonrectangular(void) { fprintf(stderr,"%s: non-rectangular cursor\n",__progname); exit(1); } w = -1; y = 0; x = 0; sethot = 0; for (i=0;i<32;i++) { c->mask[i] = 0; c->bits[i] = 0; } for <"charloop"> (i=0;;i++) { ch = c->data[i]; switch (ch) { case '\0': break <"charloop">; case ' ': continue <"charloop">; break; case '.': v = 0; h = 0; break; case 'a': v = 1; h = 0; break; case 'b': v = 2; h = 0; break; case 'c': v = 3; h = 0; break; case '*': v = 0; h = 1; break; case 'A': v = 1; h = 1; break; case 'B': v = 2; h = 1; break; case 'C': v = 3; h = 1; break; case '/': if (w < 0) w = x; if (x != w) nonrectangular(); y ++; x = 0; continue <"charloop">; break; default: fprintf(stderr,"%s: bad char `%c' in cursor bitmap\n",__progname,ch); exit(1); break; } if (y >= 32) { fprintf(stderr,"%s: cursor too high\n",__progname); exit(1); } if (x >= 32) { fprintf(stderr,"%s: cursor too wide\n",__progname); exit(1); } if (v & 1) c->mask[y] |= 1 << (31-x); if (v & 2) c->bits[y] |= 1 << (31-x); if (h) { if (sethot) { fprintf(stderr,"%s: multiple hotspots in cursor\n",__progname); exit(1); } c->hot.x = x; c->hot.y = y; sethot = 1; } x ++; } if (w < 0) { if (x == 0) { fprintf(stderr,"%s: empty cursor\n",__progname); exit(1); } w = x; h = 1; } else { if (x == w) h = y + 1; else if (x == 0) h = y; else nonrectangular(); } if (! sethot) { fprintf(stderr,"%s: no hotspot in cursor\n",__progname); exit(1); } c->setup = 1; } static void load_cursor(void) { int i; thc->cursxy = 0xffc0ffc0; for (i=0;i<32;i++) { thc->cursmask[i] = curs->mask[i]; thc->cursbits[i] = curs->bits[i]; } cursor_move(); } static void use_cursor(CURSOR *c) { if (c->setup == 0) setup_cursor(c); curs = c; load_cursor(); } static int mssetup(void) { int i; msfd = open(mspath,O_RDWR,0); if (msfd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,mspath,strerror(errno)); return(1); } i = 1; ioctl(msfd,FIONBIO,&i); i = 0; bt->addr = 0x01000000; bt->omap = 0x00000000; bt->omap = 0x00000000; bt->omap = 0x00000000; bt->addr = 0x02000000; bt->omap = 0x00000000; bt->omap = 0xff000000; bt->omap = 0x00000000; bt->addr = 0x03000000; bt->omap = 0xff000000; bt->omap = 0xff000000; bt->omap = 0xff000000; return(0); } static void msinit(void) { mouse.x = DISPLAY_X / 2; mouse.y = DISPLAY_Y / 2; use_cursor(&cursor_x); mousestate = 0; ignore_mouse = 0; } /* Args in cell coords */ static void setpixel_(int x, int y, int v, WORD **mv, int xw) #define setpixel(x,y,v) setpixel_((x),(y),(v),mem,arena_xw) { int w; w = x / (WBITS-2); x -= w * (WBITS-2); if (v) mv[y][w] |= (HIGHBIT>>1) >> x; else mv[y][w] &= ~((HIGHBIT>>1) >> x); switch (x) { case 0: if (w > 0) { if (v) mv[y][w-1] |= 1; else mv[y][w-1] &= ~(WORD)1; } break; case WBITS-3: if (w < xw) { if (v) mv[y][w+1] |= HIGHBIT; else mv[y][w+1] &= ~(WORD)HIGHBIT; } break; } } /* Args in cell coords */ static int getpixel_(XY p, WORD **mv) { int w; w = p.x / (WBITS-2); return((mv[p.y][w]>>(30-(p.x-(w*(WBITS-2)))))&1); } /* Would like to #define getpixel(p) getpixel_((p),mem) but that disagrees with getpixel((XY){x:foo,y:bar}). */ static int getpixel(XY p) { return(getpixel_(p,mem)); } /* Args in cell coords */ static void clear_rectangle(int x1, int y1, int x2, int y2) { int w1; int w2; int y; int t; WORD m; if (x1 > x2) { t = x1; x1 = x2; x2 = t; } if (y1 > y2) { t = y1; y1 = y2; y2 = t; } w1 = x1 / (WBITS-2); x1 -= w1 * (WBITS-2); w2 = x2 / (WBITS-2); x2 -= w2 * (WBITS-2); if (w1 == w2) { m = ~((~((~(WORD)0)<<(x2+1-x1)))<<(30-x2)); for (y=y1;y<=y2;y++) mem[y][w1] &= m; if ((x1 == 0) && (w1 > 0)) for (y=y1;y<=y2;y++) mem[y][w1-1] &= ~(WORD)1; if ((x2 == WBITS-3) && (w1 < arena_xw)) for (y=y1;y<=y2;y++) mem[y][w1+1] &= ~HIGHBIT; } else { m = (~(WORD)0) << (WBITS-1-x1); for (y=y1;y<=y2;y++) mem[y][w1] &= m; if ((x1 == 0) && (w1 > 0)) for (y=y1;y<=y2;y++) mem[y][w1-1] &= ~(WORD)1; m = (~(WORD)0) << (WBITS-2-x2); for (y=y1;y<=y2;y++) mem[y][w2] &= ~m; if ((x2 == WBITS-3) && (w2 < arena_xw)) for (y=y1;y<=y2;y++) mem[y][w2+1] &= ~HIGHBIT; if (w2 > w1+1) for (y=y1;y<=y2;y++) bzero(mem[y]+w1+1,(w2-w1-1)*sizeof(WORD)); } } static void random_rectangle(int x1, int y1, int x2, int y2) { int x; int y; unsigned long int r; int n; int t; if (x1 > x2) { t = x1; x1 = x2; x2 = t; } if (y1 > y2) { t = y1; y1 = y2; y2 = t; } n = 0; for (y=y1;y<=y2;y++) { for (x=x1;x<=x2;x++) { if (n < 1) { r = random(); n = 31; } setpixel(x,y,r&1); r >>= 1; n --; } } } /* Args relative to enl_at. */ static void enl_cellbox(int x, int y) { fbc->arecty = enl_disp.y + 1 + (ENL_YMAG * y) + 1; fbc->arectx = enl_disp.x + 1 + (ENL_XMAG * x) + 1; fbc->rrecty = ENL_YMAG - 3; fbc->rrectx = ENL_XMAG - 3; draw(); } static int enl_cellcolour(XY p) { if (sel2box & SB_SHOW) { return( getpixel(xyadd(enl_at,p)) ? (*sel2_get)(p) ? COL_ENLBOTH : COL_ENLCELL : (*sel2_get)(p) ? COL_ENLMOVE : COL_BG ); } else { return(getpixel(xyadd(enl_at,p)) ? COL_ENLCELL : COL_BG); } } /* Args in cell coords */ static void draw_pixel(int x, int y) { int dx; int dy; dx = x + disp.x; dy = y + disp.y; if ((dx < 0) || (dy < 0) || (dx >= DISPLAY_X) || (dy >= DISPLAY_Y)) return; if ( !enl_up || (dx < enl_disp.x) || (dy < enl_disp.y) || (dx >= enl_disp.x+ENL_MX) || (dy >= enl_disp.y+ENL_MY) ) { fbc->fg = getpixel((XY){x:x,y:y}) ? COL_LIVE : COL_BG; fbc->apointy = dy; fbc->apointx = dx; draw(); drain(); } if ( enl_up && (x >= enl_at.x) && (y >= enl_at.y) && (x < enl_at.x+ENL_X) && (y < enl_at.y+ENL_Y) ) { fbc->fg = enl_cellcolour(xysub((XY){x:x,y:y},enl_at)); enl_cellbox(x-enl_at.x,y-enl_at.y); } } /* scr: display coords; *mapped: cell coords */ static int enl_xy_map(XY scr, XY *mapped) { mapped->x = div_down(scr.x-(enl_disp.x+1),ENL_XMAG) + enl_at.x; mapped->y = div_down(scr.y-(enl_disp.y+1),ENL_YMAG) + enl_at.y; return( (scr.x >= enl_disp.x+1) && (scr.y >= enl_disp.y+1) && (scr.x < enl_disp.x+ENL_MX-1) && (scr.y < enl_disp.y+ENL_MY-1) ); } static void enl_hide(void) { XY mapped; enl_up = 0; fulldisp = 1; if (enl_xy_map(mouse,&mapped)) warp(mapped.x+disp.x,mapped.y+disp.y); } /* Args in cell coords */ static void pixel_line(XY o, XY n, int v) { int dx; int dy; int d; int x; int y; int xx; int yy; int i; dx = n.x - o.x; dy = n.y - o.y; if (!dx && !dy) { setpixel(o.x,o.y,v); draw_pixel(o.x,o.y); return; } d = imax(abs(dx),abs(dy)); x = o.x; y = o.y; xx = d / 2; yy = d / 2; for (i=d;i>=0;i--) { setpixel(x,y,v); draw_pixel(x,y); xx += dx; if (xx < 0) { x --; xx += d; } else if (xx >= d) { x ++; xx -= d; } yy += dy; if (yy < 0) { y --; yy += d; } else if (yy >= d) { y ++; yy -= d; } } } static void enl_show(void) { enl_up = 1; fulldisp = 1; enl_at.x = mouse.x - disp.x - (ENL_X/2); if (enl_at.x < 0) enl_at.x = 0; else if (enl_at.x+ENL_X > arena_x) enl_at.x = arena_x - ENL_X; enl_at.y = mouse.y - disp.y - (ENL_Y/2); if (enl_at.y < 0) enl_at.y = 0; else if (enl_at.y+ENL_Y > arena_y) enl_at.y = arena_y - ENL_Y; enl_disp.x = mouse.x - (ENL_MX / 2); if (enl_disp.x < 0) enl_disp.x = 0; if (enl_disp.x+ENL_MX > DISPLAY_X) enl_disp.x = DISPLAY_X - ENL_MX; enl_disp.y = mouse.y - (ENL_MY / 2); if (enl_disp.y < 0) enl_disp.y = 0; if (enl_disp.y+ENL_MY > DISPLAY_Y) enl_disp.y = DISPLAY_Y - ENL_MY; warp( enl_disp.x + 1 + ((mouse.x-disp.x-enl_at.x)*ENL_XMAG) + (ENL_XMAG/2), enl_disp.y + 1 + ((mouse.y-disp.y-enl_at.y)*ENL_YMAG) + (ENL_YMAG/2) ); } static void beep(void) { int t; t = 100; ioctl(kbfd,KIOCBELL,&t); } static void clrsel(void) { if ((selbox & (SB_SHOW|SB_HAVE)) != (SB_SHOW|SB_HAVE)) { beep(); return; } clear_rectangle(selp1.x,selp1.y,selp2.x,selp2.y); fulldisp = 1; selbox &= ~SB_SHOW; } static void map_mem(MAPBLK *b, int nb) { static int pgsiz = 0; void *mrv; if (pgsiz < 1) pgsiz = getpagesize(); nb = ((nb + pgsiz - 1) / pgsiz) * pgsiz; mrv = mmap(0,nb,PROT_READ|PROT_WRITE,MAP_ANON|MAP_PRIVATE,-1,0); if (mrv == MAP_FAILED) { fprintf(stderr,"%s: can't mmap %d bytes: %s\n",__progname,nb,strerror(errno)); exit(1); } b->data = mrv; b->len = nb; } static void step_region_n(int ngens) { XY p0; XY s; int xw; WORD tmpem; WORD **tmpmem; WORD *tmptmps[2]; MAPBLK tmpblks[2]; int i; WORD *buf; XY p; p0 = xymin(selp1,selp2); s = xyadd(xyabsdiff(selp1,selp2),(XY){x:1,y:1}); xw = (s.x + WBITS - 3) / (WBITS - 2); tmpem = (~((WORD)0)) << ((WBITS-2) - ((s.x+(WBITS-3))%(WBITS-2))); map_mem(&tmpblks[0],s.y*sizeof(WORD *)); tmpmem = tmpblks[0].data; map_mem(&tmpblks[1],(xw+1)*(s.y+2)*sizeof(WORD)); buf = tmpblks[1].data; for (i=0;i=0;p.y--) { for (p.x=s.x-1;p.x>=0;p.x--) { if (getpixel(xyadd(p0,p))) setpixel_(p.x,p.y,1,tmpmem,xw); } } for (;ngens>0;ngens--) crank(tmpmem,xw,s.y,&tmptmps[0],tmpem); clear_rectangle(p0.x,p0.y,p0.x+s.x-1,p0.y+s.y-1); for (p.y=s.y-1;p.y>=0;p.y--) { for (p.x=s.x-1;p.x>=0;p.x--) { if (getpixel_(p,tmpmem)) setpixel(p0.x+p.x,p0.y+p.y,1); } } munmap(tmpblks[0].data,tmpblks[0].len); munmap(tmpblks[1].data,tmpblks[1].len); fulldisp = 1; } static void ensure_text_buf(int len) { if (len > textballoc) { textbuf = realloc(textbuf,len); textballoc = len; } } static void text_saves(void) { text_prev_io = input_op; text_prev_curs = curs; use_cursor(&cursor_nil); ignore_mouse = 1; fulldisp = 1; } static void draw_text_char(unsigned char ch, int x, int y, int alu, int bg, int fg) { unsigned short int *fp; fbc->fg = fg; fbc->bg = bg; fbc->incx = 0; fbc->incy = 1; fbc->alu = alu & 0xffff; fbc->x0 = x; fbc->x1 = x + FONT_X - 1; fbc->y0 = y; fp = &font_bits[ch*FONT_Y]; for (y=0;yfont = *fp++ << (32-FONT_X); } #define DTP_BEFORE 1 #define DTP_CENTRE 2 #define DTP_AFTER 3 #define DTP_OPEN_L 1 #define DTP_OPEN_R 2 #define DTP_OPEN_U 4 #define DTP_OPEN_D 8 static int how_pos(int how, int coord, int size) { switch (how) { case DTP_BEFORE: return(coord-size); break; case DTP_CENTRE: return(coord-(size/2)); break; case DTP_AFTER: return(coord); break; } abort(); } static void draw_text_in_box(int xhow, int xloc, int yhow, int yloc, const char *str, int len, int cursx, int opens) { int tw; int th; int tx; int ty; int w; int x; int h; int y; if (len < 0) len = strlen(str); tw = 1 + TEXT_MARGIN + (len * FONT_X) + TEXT_MARGIN + 1; th = 1 + TEXT_MARGIN + FONT_Y + TEXT_MARGIN + 1; tx = how_pos(xhow,xloc,tw); ty = how_pos(yhow,yloc,th); fbc->alu = ALU_FG; fbc->fg = COL_TEXTBOX; fbc->arecty = ty; fbc->arectx = tx; fbc->rrecty = th - 1; fbc->rrectx = tw - 1; draw(); x = tx + 1; y = ty + 1; w = tw - 2; h = th - 2; if (opens & DTP_OPEN_L) { x --; w ++; } if (opens & DTP_OPEN_R) w ++; if (opens & DTP_OPEN_U) { y --; h ++; } if (opens & DTP_OPEN_D) h ++; fbc->fg = COL_TEXTBG; fbc->arecty = y; fbc->arectx = x; fbc->rrecty = h - 1; fbc->rrectx = w - 1; draw(); drain(); for (x=0;x (op) { case IO_CLICK: beep(); break; case IO_MOVE: { XY new; new.x = va_arg(ap,int); new.y = va_arg(ap,int); move_mouse_slide(new); } break; case IO_KEY: { int key; unsigned char ch; key = va_arg(ap,int); switch (key) #define CHARA(v) do { ch = (v); } while (0) #define CHARNC(norm,shift) do { \ ch = (kbflags & KBF_SHIFT) ? (shift) : (norm); } while (0) #define CHARC(norm,shift,ctrl) do { ch = (kbflags & KBF_CTRL) ? (ctrl) :\ (kbflags & KBF_SHIFT) ? (shift) : (norm); } while (0) #define CHARC4(norm,shift,cn,cs) do { ch = (kbflags & KBF_CTRL) ?\ ((kbflags & KBF_SHIFT) ? (cs) : (cn)) : ((kbflags & KBF_SHIFT) ?\ (shift) : (norm)); } while (0) { case 29: CHARA(27); break; /* ESC */ case 30: CHARNC('1','!'); break; /* 1 */ case 31: CHARC('2','@', 0); break; /* 2 */ case 32: CHARNC('3','#'); break; /* 3 */ case 33: CHARNC('4','$'); break; /* 4 */ case 34: CHARNC('5','%'); break; /* 5 */ case 35: CHARC('6','^',30); break; /* 6 */ case 36: CHARNC('7','&'); break; /* 7 */ case 37: CHARNC('8','*'); break; /* 8 */ case 38: CHARNC('9','('); break; /* 9 */ case 39: CHARNC('0',')'); break; /* 0 */ case 40: CHARC('-','_',31); break; /* -_ */ case 41: CHARNC('=','+'); break; /* =+ */ case 42: CHARNC('`','~'); break; /* `~ */ case 43: CHARA(8); break; /* Back Space */ case 53: CHARA(9); break; /* Tab */ case 54: CHARC('q','Q',17); break; /* Q */ case 55: CHARC('w','W',23); break; /* W */ case 56: CHARC('e','E', 5); break; /* E */ case 57: CHARC('r','R',18); break; /* R */ case 58: CHARC('t','T',20); break; /* T */ case 59: CHARC('y','Y',25); break; /* Y */ case 60: CHARC('u','U',21); break; /* U */ case 61: CHARC('i','I', 9); break; /* I */ case 62: CHARC('o','O',15); break; /* O */ case 63: CHARC('p','P',16); break; /* P */ case 64: CHARC4('[',']',27,29); break; /* [] */ case 65: CHARC4('{','}',27,29); break; /* {} */ case 66: CHARA(127); break; /* Delete */ case 77: CHARC('a','A', 1); break; /* A */ case 78: CHARC('s','S',19); break; /* S */ case 79: CHARC('d','D', 4); break; /* D */ case 80: CHARC('f','F', 6); break; /* F */ case 81: CHARC('g','G', 7); break; /* G */ case 82: CHARC('h','H', 8); break; /* H */ case 83: CHARC('j','J',10); break; /* J */ case 84: CHARC('k','K',11); break; /* K */ case 85: CHARC('l','L',11); break; /* L */ case 86: CHARNC(';',':'); break; /* ;: */ case 87: CHARNC('\'','"'); break; /* '" */ case 88: CHARC('\\','|',28);break; /* \| */ case 89: CHARA(13); break; /* Return */ case 100: CHARC('z','Z',26); break; /* Z */ case 101: CHARC('x','X',24); break; /* X */ case 102: CHARC('c','C', 3); break; /* C */ case 103: CHARC('v','V',22); break; /* V */ case 104: CHARC('b','B', 2); break; /* B */ case 105: CHARC('n','N',14); break; /* N */ case 106: CHARC('m','M',13); break; /* M */ case 107: CHARNC(',','<'); break; /* ,< */ case 108: CHARNC('.','>'); break; /* .> */ case 109: CHARNC('/','?'); break; /* /? */ case 121: CHARC(' ',' ', 0); break; /* space */ #undef CHARA #undef CHARNC #undef CHARC #undef CHARC4 default: printf("%d\n",key); break <"op">; } fulldisp = 1; (*text_keystroke)(ch,key); } break; case IO_DRAW: draw_text_in_box( DTP_CENTRE, DISPLAY_X/2, DTP_CENTRE, DISPLAY_Y/2, (const void *)(textbuf+textdoff), textdlen, textcurs-textdoff, ((textdoff>0) ? DTP_OPEN_L : 0) | ((textdoff+textdlen textpromptlen) textcurs --; break; case 4: if (textcurs < textlen) { if (textcurs < textlen-1) bcopy(textbuf+textcurs+1,textbuf+textcurs,textlen-1-textcurs); textlen --; } break; case 5: textcurs = textlen; break; case 6: if (textcurs < textlen) textcurs ++; break; case 7: case 27: text_unsaves(); textlen = -1; (*text_gotline)(0); return; break; case 8: case 127: if (textcurs > textpromptlen) { if (textcurs < textlen) bcopy(textbuf+textcurs,textbuf+textcurs-1,textlen-textcurs); textcurs --; textlen --; } break; case 10: case 13: textbuf_nulterm(); text_unsaves(); textlen = -1; (*text_gotline)(textbuf+textpromptlen); return; break; case 20: if (textcurs-textpromptlen >= 2) { ch = textbuf[textcurs-1]; textbuf[textcurs-1] = textbuf[textcurs-2]; textbuf[textcurs-2] = ch; } break; case 21: case 24: textlen = textpromptlen; textcurs = textlen; break; default: switch ((*text_acc)(ch,key)) { case TACC_CHAR: ensure_text_buf(textlen+1); if (textcurs < textlen) bcopy(textbuf+textcurs,textbuf+textcurs+1,textlen-textcurs); textbuf[textcurs++] = ch; textlen ++; break; case TACC_ERR: beep(); break; case TACC_DONE: break; default: abort(); break; } break; } textfake = (textcurs >= textlen); if (textfake) { ensure_text_buf(textlen+1); textbuf[textlen] = ' '; textcurs = textlen ++; } if (textlen <= 80) { textdlen = textlen; textdoff = 0; } else { textdlen = 80; if (textdoff && (textcurs < textdoff+20)) { textdoff = (textcurs < 20) ? 0 : textcurs - 20; } if ((textdoff+textdlen < textlen) && (textcurs > textdoff+60)) { textdoff = textcurs - 60; } if (textdoff+textdlen > textlen) { textdoff = textlen - textdlen; } } } static void text_getline(int (*)(unsigned char, int), const char *, ...) __attribute__((__format__(__printf__,2,3))); static void text_getline(int (*acc)(unsigned char, int), const char *fmt, ...) { char *t; va_list ap; va_start(ap,fmt); vasprintf(&t,fmt,ap); va_end(ap); textpromptlen = strlen(t); ensure_text_buf(textpromptlen); bcopy(t,textbuf,textpromptlen); free(t); textbuf[textpromptlen] = ' '; textlen = textpromptlen + 1; textdlen = textlen; textdoff = 0; textcurs = textpromptlen; textfake = 1; text_saves(); input_op = text_input_op; text_keystroke = getline_keystroke; text_acc = acc; } static int accc_digits(unsigned char ch, int key __attribute__((__unused__))) { return(((ch >= '0') && (ch <= '9')) ? TACC_CHAR : TACC_ERR); } static void step_region(const char *s) { if (s) step_region_n(atoi(s)); } static void step(void) { if ((selbox & (SB_SHOW|SB_HAVE)) != (SB_SHOW|SB_HAVE)) { beep(); return; } if (kbflags & KBF_SHIFT) { text_getline(accc_digits,"Generations to step region: "); text_gotline = step_region; } else { step_region_n(1); } } /* Args are in their own coordinate system */ static XY xform_xy(int, XY, XY) __attribute__((__const__)); static XY xform_xy(int xf, XY p, XY s) { if (xf & XF_NEGX) p.x = s.x - 1 - p.x; if (xf & XF_NEGY) p.y = s.y - 1 - p.y; return((xf&XF_SWAP)?(XY){x:p.y,y:p.x}:p); } /* returns the transform equivalent to applying xf1 and then xf2 */ static int xform_compound(int xf1, int xf2) { XY c0; XY c1; int xf; static int tbl[2][2][2][2] = { { { { 0 } } } }; if (tbl[0][0][0][0] == 0) { int i; static void set(int xf) { c0 = xform_xy(xf,(XY){x:0,y:0},(XY){x:2,y:2}); c1 = xform_xy(xf,(XY){x:1,y:0},(XY){x:2,y:2}); tbl[c0.x][c0.y][c1.x][c1.y] = xf; } for (i=0;i<16;i++) (&tbl[0][0][0][0])[i] = -1; set( 0| 0| 0); set( 0| 0|XF_SWAP); set( 0|XF_NEGY| 0); set( 0|XF_NEGY|XF_SWAP); set(XF_NEGX| 0| 0); set(XF_NEGX| 0|XF_SWAP); set(XF_NEGX|XF_NEGY| 0); set(XF_NEGX|XF_NEGY|XF_SWAP); } c0 = xform_xy(xf2,xform_xy(xf1,(XY){x:0,y:0},(XY){x:2,y:2}),(XY){x:2,y:2}); c1 = xform_xy(xf2,xform_xy(xf1,(XY){x:1,y:0},(XY){x:2,y:2}),(XY){x:2,y:2}); xf = tbl[c0.x][c0.y][c1.x][c1.y]; if (xf < 0) abort(); return(xf); } /* returns the inverse of a transform */ static int xform_inverse(int xf) { static int tbl[8] = { -1 }; if (tbl[0] < 0) { int i; int j; for <"i"> (i=0;i<8;i++) { for (j=0;j<8;j++) { if (xform_compound(i,j) == 0) { tbl[i] = j; continue <"i">; } } abort(); } } return(tbl[xf]); } /* Can be any coordinate system, but the same for all args */ static int rects_overlap(XY ap1, XY ap2, XY bp1, XY bp2) { return( (imax(ap1.x,ap2.x) >= imin(bp1.x,bp2.x)) && (imax(bp1.x,bp2.x) >= imin(ap1.x,ap2.x)) && (imax(ap1.y,ap2.y) >= imin(bp1.y,bp2.y)) && (imax(bp1.y,bp2.y) >= imin(ap1.y,ap2.y)) ); } static void copy_sel2(int op) { XY src; XY dst; XY d0; XY d1; XY s0; XY s1; XY ds; XY p; int s; int d; XY sp1; XY sp2; int ixf; switch (op) { case S2_DONE: src = xymin(selp1,selp2); dst = xymin(sel2p1,sel2p2); ds = xyadd(xyabsdiff(sel2p1,sel2p2),(XY){x:1,y:1}); d0 = (XY){x:0,y:0}; d1 = ds; if (dst.x < 0) d0.x = - dst.x; if (dst.x+d1.x > arena_x) d1.x = arena_x - dst.x; if (d1.x <= d0.x) return; if (dst.y < 0) d0.y = - dst.y; if (dst.y+d1.y > arena_y) d1.y = arena_y - dst.y; if (d1.y <= d0.y) return; ixf = xform_inverse(sel2xform); sp1 = xform_xy(ixf,d0,ds); sp2 = xform_xy(ixf,xysub(d1,(XY){x:1,y:1}),ds); s0 = xymin(sp1,sp2); s1 = xymax(sp1,sp2); if ( sel2xform && rects_overlap(xyadd(src,s0),xyadd(src,s1),xyadd(dst,d0),xyadd(dst,d1)) ) { char *t; char *tp; int w; int h; w = d1.x - d0.x; h = d1.y - d0.y; t = malloc(w*h); tp = t; for (p.y=d0.y;p.y=d0.y;p.y--) { for (p.x=d1.x-1;p.x>=d0.x;p.x--) { sp1 = xyadd(src,xform_xy(ixf,p,ds)); s = getpixel(sp1); d = getpixel(xyadd(dst,p)); if (s != d) setpixel(dst.x+p.x,dst.y+p.y,s); } } } else if ((src.y > dst.y) || ((src.y == dst.y) && (src.x > dst.x))) { for (p.y=d0.y;p.y= arena_x) || (mapped.y < 0) || (mapped.y >= arena_y) ) abort(); return(mapped); } static void sel2reloc(void) { XY mp; mp = maybe_mapped_mouse(); sel2p1 = xyadd(mp,xform_xy(sel2xform,sel2p1off,(XY){x:1,y:1})); sel2p2 = xyadd(mp,xform_xy(sel2xform,sel2p2off,(XY){x:1,y:1})); } static void sel_saves(void) { sel_prev_io = input_op; sel_prev_curs = curs; sel_prev_box = selbox; } static void sel_unsaves(void) { input_op = sel_prev_io; selbox = sel_prev_box; use_cursor(sel_prev_curs); } #define PTS_CENTRE (-1) static void plain_text_string(const char *s, int x, int y, int alu, int bg, int fg) { int l; l = strlen(s); if (x == PTS_CENTRE) x = (DISPLAY_X - (l*FONT_X)) / 2; if (y == PTS_CENTRE) y = (DISPLAY_Y - FONT_Y) / 2; for (;l>0;l--) { draw_text_char(*s++,x,y,alu,bg,fg); x += FONT_X; } } static void draw_xform(int xf, int cx, int cy) { int x; int y; XY t; cx -= (XFORM_GRIDSIZE * XFORM_PATX) / 2; cy -= (XFORM_GRIDSIZE * XFORM_PATY) / 2; fbc->alu = ALU_FG; fbc->fg = COL_SELBOX; fbc->arecty = cy - 1; fbc->arectx = cx - 1; fbc->rrecty = 1 + (XFORM_GRIDSIZE*XFORM_PATX) + 1 - 1; fbc->rrectx = 1 + (XFORM_GRIDSIZE*XFORM_PATY) + 1 - 1; draw(); for (y=0;yfg = xfpcols[y][x]; fbc->arecty = cy + (t.y * XFORM_GRIDSIZE); fbc->arectx = cx + (t.x * XFORM_GRIDSIZE); fbc->rrecty = XFORM_GRIDSIZE - 1; fbc->rrectx = XFORM_GRIDSIZE - 1; draw(); } } } static void sel2_input_op(int, ...); /* forward */ static void sel2_xform_input_op(int op, ...) { va_list ap; va_start(ap,op); switch (op) { case IO_CLICK: beep(); break; case IO_MOVE: { XY new; new.x = va_arg(ap,int); new.y = va_arg(ap,int); move_mouse_slide(new); } break; case IO_KEY: { int key; key = va_arg(ap,int); switch (key) { case 40: /* -_ */ if (! (kbflags & KBF_SHIFT)) sel2xform = xform_compound(sel2xform,XF_NEGY); break; case 57: /* R */ sel2xform = xform_compound(sel2xform,XF_NEGY|XF_SWAP); break; case 77: /* A */ sel2xform = xform_compound(sel2xform,XF_NEGX|XF_NEGY); break; case 85: /* L */ sel2xform = xform_compound(sel2xform,XF_NEGX|XF_SWAP); break; case 88: /* \| */ sel2xform = xform_compound(sel2xform,(kbflags&KBF_SHIFT)?XF_NEGX:XF_SWAP); break; case 109: /* /? */ if (! (kbflags & KBF_SHIFT)) sel2xform = xform_compound(sel2xform,XF_NEGX|XF_NEGY|XF_SWAP); break; case 121: /* space */ input_op = sel2_input_op; use_cursor(&cursor_plus); ignore_mouse = 0; break; default: printf("%d\n",key); break; } sel2reloc(); } break; case IO_DRAW: plain_text_string( "Type | \\ - / to reflect, L R A to rotate, space when done", PTS_CENTRE, ((DISPLAY_Y*2)/3)-((TEXT_MARGIN+FONT_Y)/2), ((ALU_FG & ALU_SRC) | (ALU_BG & ~ALU_SRC)), COL_TEXTBG, COL_TEXTFG ); draw_xform(0,DISPLAY_X/3,DISPLAY_Y/3); draw_xform(sel2xform,(DISPLAY_X*2)/3,DISPLAY_Y/3); break; } va_end(ap); fulldisp = 1; } static void sel2_input_op(int op, ...) { va_list ap; va_start(ap,op); switch (op) { case IO_CLICK: { int button; fulldisp = 1; button = va_arg(ap,int); switch (button) { case 1: switch (selphase) { case 1: sel2box = 0; sel_unsaves(); selphase = 0; (*sel2_fn)(S2_DONE); dropmove |= buttonbit(1); break; case 2: { XY delta; int ixf; ixf = xform_inverse(sel2xform); delta = xform_xy(ixf,xysub(maybe_mapped_mouse(),sel2anchor),(XY){x:1,y:1}); sel2p1off = xysub(sel2p1off,delta); sel2p2off = xysub(sel2p2off,delta); selphase = 1; } break; } break; case 2: switch (selphase) { case 1: sel2box = 0; sel_unsaves(); selphase = 0; (*sel2_fn)(S2_ABORT); dropmove |= buttonbit(2); break; case 2: selphase = 1; sel2reloc(); break; } break; case 3: if (enl_up) { enl_hide(); } else { enl_show(); dropmove |= buttonbit(3); } break; } } break; case IO_MOVE: { XY new; fulldisp = 1; new.x = va_arg(ap,int); new.y = va_arg(ap,int); move_mouse_slide(new); if (selphase == 1) sel2reloc(); } break; case IO_KEY: { int key; fulldisp = 1; key = va_arg(ap,int); switch (key) { case 77: /* A */ selphase = 2; sel2anchor = maybe_mapped_mouse(); break; case 101: /* X */ input_op = sel2_xform_input_op; use_cursor(&cursor_nil); ignore_mouse = 1; break; default: printf("%d\n",key); break; } } break; case IO_DRAW: break; } va_end(ap); } /* off and size are coordinateless deltas */ static void start_sel2(XY off, XY size, void (*fn)(int), int (*get)(XY)) { sel2box = SB_SHOW; sel2p1off = xyneg(off); sel2p2off = xyadd(size,sel2p1off); fulldisp = 1; sel_saves(); input_op = sel2_input_op; use_cursor(&cursor_plus); selphase = 1; sel2_fn = fn; sel2_get = get; sel2xform = 0; sel2reloc(); } static int get2_copy(XY p) { XY dst; XY ds; dst = xysub(xymin(sel2p1,sel2p2),enl_at); ds = xyadd(xyabsdiff(sel2p1,sel2p2),(XY){x:1,y:1}); if ( (p.x < dst.x) || (p.x >= dst.x+ds.x) || (p.y < dst.y) || (p.y >= dst.y+ds.y) ) return(0); return(getpixel(xyadd( xymin(selp1,selp2), xform_xy( xform_inverse(sel2xform), xysub(p,dst), ds ) ))); } static void copy(void) { if ((selbox & (SB_SHOW|SB_HAVE)) != (SB_SHOW|SB_HAVE)) { beep(); return; } start_sel2(xysub(maybe_mapped_mouse(),selp1),xysub(selp2,selp1),©_sel2,&get2_copy); } static void gen(void) { crank(mem,arena_xw,arena_y,&tmps[0],edgemask); gen_counter ++; } static void onegen(void) { gen(); fulldisp = 1; run = 0; } static void select_input_op(int op, ...) { va_list ap; va_start(ap,op); switch (op) { case IO_CLICK: { int button; button = va_arg(ap,int); switch (button) { case 1: { XY loc; loc = maybe_mapped_mouse(); switch (selphase) { case 1: if (! (selbox & SB_SHOW)) fulldisp = 1; selbox = SB_HAVE | SB_SHOW; selp1 = loc; selp2 = loc; selphase = 2; break; case 2: selp2 = loc; selphase = 0; sel_unsaves(); selbox = SB_HAVE | SB_SHOW; break; } } break; case 2: sel_unsaves(); switch (selphase) { case 1: fulldisp = 1; break; default: selbox = 0; break; } selphase = 0; fulldisp = 1; break; case 3: if (enl_up) { enl_hide(); } else { enl_show(); dropmove |= buttonbit(3); } break; } } break; case IO_MOVE: { XY new; new.x = va_arg(ap,int); new.y = va_arg(ap,int); move_mouse_slide(new); switch (selphase) { case 2: selp2 = maybe_mapped_mouse(); fulldisp = 1; break; } } break; case IO_KEY: { int key; key = va_arg(ap,int); printf("%d\n",key); } break; case IO_DRAW: break; } va_end(ap); } static void seladjust_choose_cursor(void) { XY cm; int third; int a; int b; CURSOR *c; cm = maybe_mapped_mouse(); a = imin(selp1.x,selp2.x); b = imax(selp1.x,selp2.x); third = (b+1-a) / 3; if (cm.x < a+third) seladj_ninth = 0; else if (cm.x > b-third) seladj_ninth = 2; else seladj_ninth = 1; a = imin(selp1.y,selp2.y); b = imax(selp1.y,selp2.y); third = (b+1-a) / 3; if (cm.y < a+third) seladj_ninth += 0; else if (cm.y > b-third) seladj_ninth += 6; else seladj_ninth += 3; switch (seladj_ninth) { case 0: c = &cursor_box_ul; break; case 1: c = &cursor_box_um; break; case 2: c = &cursor_box_ur; break; case 3: c = &cursor_box_ml; break; case 4: c = &cursor_box_mm; break; case 5: c = &cursor_box_mr; break; case 6: c = &cursor_box_ll; break; case 7: c = &cursor_box_lm; break; case 8: c = &cursor_box_lr; break; default: abort(); break; } if (curs != c) use_cursor(c); } static XY limit_to_arena(XY p) { if (p.x < 0) p.x = 0; else if (p.x >= arena_x) p.x = arena_x - 1; if (p.y < 0) p.y = 0; else if (p.y >= arena_y) p.y = arena_y - 1; return(p); } static void seladj_adjust_min(int delta, int *v1, int *v2, int ninthinc) { int *minp; int *maxp; if (*v1 < *v2) { minp = v1; maxp = v2; } else { minp = v2; maxp = v1; } if (delta+*minp <= *maxp) { *minp += delta; } else { delta -= *maxp - *minp; *minp = *maxp; *maxp += delta; seladj_ninth += ninthinc; } } static void seladj_adjust_max(int delta, int *v1, int *v2, int ninthdec) { int *minp; int *maxp; if (*v1 < *v2) { minp = v1; maxp = v2; } else { minp = v2; maxp = v1; } if (delta+*maxp >= *minp) { *maxp += delta; } else { delta -= *minp - *maxp; *maxp = *minp; *minp += delta; seladj_ninth -= ninthdec; } } static void seladjust_carrymove(void) { XY cur; XY d; cur = maybe_mapped_mouse(); d.x = cur.x - seladj_lastmouse.x; d.y = cur.y - seladj_lastmouse.y; seladj_lastmouse = cur; switch (seladj_ninth) { case 4: seladjp1 = xyadd(seladjp1,d); seladjp2 = xyadd(seladjp2,d); break; default: switch (seladj_ninth) { case 0: case 3: case 6: seladj_adjust_min(d.x,&seladjp1.x,&seladjp2.x,2); break; case 2: case 5: case 8: seladj_adjust_max(d.x,&seladjp1.x,&seladjp2.x,2); break; } switch (seladj_ninth) { case 0: case 1: case 2: seladj_adjust_min(d.y,&seladjp1.y,&seladjp2.y,6); break; case 6: case 7: case 8: seladj_adjust_max(d.y,&seladjp1.y,&seladjp2.y,6); break; } break; } cur = limit_to_arena(seladjp1); if ((cur.x != selp1.x) || (cur.y != selp1.y)) { selp1 = cur; fulldisp = 1; } cur = limit_to_arena(seladjp2); if ((cur.x != selp2.x) || (cur.y != selp2.y)) { selp2 = cur; fulldisp = 1; } } static void seladjust_input_op(int op, ...) { va_list ap; va_start(ap,op); switch <"op"> (op) { case IO_CLICK: { int button; button = va_arg(ap,int); switch (button) { case 1: seladjust_choose_cursor(); if (seladj_carrying) { seladj_carrying = 0; } else { seladj_lastmouse = maybe_mapped_mouse(); seladj_carrying = 1; } break; case 2: sel_unsaves(); break; case 3: if (enl_up) { enl_hide(); } else { enl_show(); } break; } } break; case IO_MOVE: { XY new; new.x = va_arg(ap,int); new.y = va_arg(ap,int); move_mouse_slide(new); if (seladj_carrying) { seladjust_carrymove(); } else { seladjust_choose_cursor(); } } break; case IO_KEY: beep(); break; case IO_DRAW: break; } va_end(ap); } static void seladjust(void) { sel_saves(); input_op = &seladjust_input_op; seladjust_choose_cursor(); seladj_carrying = 0; seladjp1 = selp1; seladjp2 = selp2; run = 0; } static void sel(void) { if (kbflags & KBF_SHIFT) { switch (selbox & (SB_HAVE|SB_SHOW)) { case SB_HAVE: selbox |= SB_SHOW; fulldisp = 1; break; case SB_HAVE|SB_SHOW: seladjust(); break; default: beep(); break; } return; } sel_saves(); input_op = select_input_op; use_cursor(&cursor_plus); selphase = 1; run = 0; } static void move_sel2(int op) { XY src; XY dst; XY d0; XY d1; XY s0; XY s1; XY ds; XY p; int s; int d; XY sp1; XY sp2; int ixf; switch (op) { case S2_DONE: src = xymin(selp1,selp2); dst = xymin(sel2p1,sel2p2); ds = xyadd(xyabsdiff(sel2p1,sel2p2),(XY){x:1,y:1}); d0 = (XY){x:0,y:0}; d1 = ds; if (dst.x < 0) d0.x = - dst.x; if (dst.x+d1.x > arena_x) d1.x = arena_x - dst.x; if (d1.x <= d0.x) return; if (dst.y < 0) d0.y = - dst.y; if (dst.y+d1.y > arena_y) d1.y = arena_y - dst.y; if (d1.y <= d0.y) return; ixf = xform_inverse(sel2xform); sp1 = xform_xy(ixf,d0,ds); sp2 = xform_xy(ixf,xysub(d1,(XY){x:1,y:1}),ds); s0 = xymin(sp1,sp2); s1 = xymax(sp1,sp2); if ( sel2xform && rects_overlap(xyadd(src,s0),xyadd(src,s1),xyadd(dst,d0),xyadd(dst,d1)) ) { char *t; char *tp; int w; int h; w = d1.x - d0.x; h = d1.y - d0.y; t = malloc(w*h); tp = t; for (p.y=d0.y;p.y=d0.y;p.y--) { for (p.x=d1.x-1;p.x>=d0.x;p.x--) { sp1 = xyadd(src,xform_xy(ixf,p,ds)); s = getpixel(sp1); d = getpixel(xyadd(dst,p)); if (s != d) setpixel(dst.x+p.x,dst.y+p.y,s); if (s) setpixel(sp1.x,sp1.y,0); } } } else if ((src.y > dst.y) || ((src.y == dst.y) && (src.x > dst.x))) { for (p.y=d0.y;p.y= dst.x+ds.x) || (p.y < dst.y) || (p.y >= dst.y+ds.y) ) return(0); lp = xform_xy(xform_inverse(sel2xform),xysub(p,dst),ds); return(load_bits[lp.x+(lp.y*load_bw)]); } static void move(void) { if ((selbox & (SB_SHOW|SB_HAVE)) != (SB_SHOW|SB_HAVE)) { beep(); return; } start_sel2(xysub(maybe_mapped_mouse(),selp1),xysub(selp2,selp1),&move_sel2,&get2_copy); } static void rndsel(void) { if ((selbox & (SB_SHOW|SB_HAVE)) != (SB_SHOW|SB_HAVE)) { beep(); return; } random_rectangle(selp1.x,selp1.y,selp2.x,selp2.y); fulldisp = 1; selbox &= ~SB_SHOW; } static void runrnd(void) { if (kbflags & KBF_SHIFT) { rndsel(); } else { run = RUN_DISP; } } static void set_fixed_text(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void set_fixed_text(const char *fmt, ...) { char *t; va_list ap; int l; va_start(ap,fmt); vasprintf(&t,fmt,ap); va_end(ap); l = strlen(t); ensure_text_buf(l); bcopy(t,textbuf,l); textlen = l; textdlen = l; textdoff = 0; textcurs = -1; free(t); } static void popdown_message(unsigned char ch __attribute__((__unused__)), int key __attribute__((__unused__))) { text_unsaves(); textlen = -1; } static void text_message(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void text_message(const char *fmt, ...) { va_list ap; char *s; va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); set_fixed_text("%s",s); free(s); text_saves(); input_op = text_input_op; text_keystroke = popdown_message; } static void file_save(const char *fn) { FILE *f; XY p0; int w; int h; XY p; if (! fn) { beep(); return; } f = fopen(fn,"w"); if (f == 0) { text_message("%s: %s",fn,strerror(errno)); beep(); return; } p0 = xymin(savep1,savep2); w = abs(savep1.x-savep2.x) + 1; h = abs(savep1.y-savep2.y) + 1; fprintf(f,"MLife-1\n%d %d\n",w,h); for (p.y=0;p.y arena_x) d1.x = arena_x - dst.x; if (d1.x <= d0.x) return; if (dst.y < 0) d0.y = - dst.y; if (dst.y+d1.y > arena_y) d1.y = arena_y - dst.y; if (d1.y <= d0.y) return; ixf = xform_inverse(sel2xform); sp1 = xform_xy(ixf,d0,ds); sp2 = xform_xy(ixf,xysub(d1,(XY){x:1,y:1}),ds); s0 = xymin(sp1,sp2); s1 = xymax(sp1,sp2); w = d1.x - d0.x; h = d1.y - d0.y; for (p.y=d0.y;p.y= w) { x = 0; y ++; } } load_bits = bits; load_bw = w; start_sel2((XY){x:w/2,y:h/2},(XY){x:w-1,y:h-1},&enter_load,&get2_load); break; default: text_message("%s: Unrecognized version number %d",fn,ver); fail(); break; } } #if 0 static void dprintf(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void dprintf(const char *fmt, ...) { va_list ap; static FILE *f = 0; if (f == 0) f = fopen("DEBUG","w"); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); } #endif static void file_load_lif(const char *fn, FILE *f, void (*pfail)(void)) { typedef struct line LINE; struct line { char *bits; int x0; int a; int w; } ; char ver[64]; int i; int c; LINE *v; int y0; int a; int h; XY bbmin; XY bbmax; static void freeup(void) { for (h--;h>=0;h--) free(v[h].bits); free(v); } static void fail(void) { freeup(); (*pfail)(); } static __inline__ int nlchar(int) __attribute__((__const__)); static __inline__ int nlchar(int c) { return((c=='\n')||(c=='\r')); } static void nlflush(FILE *f) { int c; do c = getc(f); while (nlchar(c)); if (c != EOF) ungetc(c,f); } static void eat_to_nl(const char *whatisit) { while (1) { int c; c = getc(f); if (c == EOF) { text_message("%s: EOF during %s",fn,whatisit); fail(); } if (nlchar(c)) { nlflush(f); break; } } } static void get_s_b(int *sp, int *bp) { int c; int n; int afterslash; afterslash = 0; *sp = 0; *bp = 0; while (1) { c = getc(f); if (c == EOF) { text_message("%s: EOF during #R line",fn); fail(); } switch (c) { case ' ': break; case '/': if (afterslash) { text_message("%s: multiple /s on #R line",fn); fail(); } afterslash = 1; break; case '0': n = 0; if (0) { case '1': n = 1; } else if (0) { case '2': n = 2; } else if (0) { case '3': n = 3; } else if (0) { case '4': n = 4; } else if (0) { case '5': n = 5; } else if (0) { case '6': n = 6; } else if (0) { case '7': n = 7; } else if (0) { case '8': n = 8; } *(afterslash?bp:sp) |= 1<= sizeof(ver)/sizeof(ver[0])) { text_message("%s: too-long version string",fn); fail(); } ver[i] = c; } if ((i == 4) && !bcmp(&ver[0],"1.05",4)) { while <"lineloop"> (1) { long int at; at = ftell(f); c = getc(f); if (c == EOF) break; if (nlchar(c)) continue; if (c != '#') { text_message("%s: unexpected marker character `%c' at %lu",fn,c,at); fail(); } c = getc(f); switch (c) { case 'D': eat_to_nl("a description line"); break; case 'N': eat_to_nl("#N trailer"); break; case 'R': { int s; int b; get_s_b(&s,&b); if ((s != ((1<<2)|(1<<3))) || (b != (1<<3))) { text_message("%s: for non-Conway rules",fn); fail(); } } break; case 'P': { int px; int py; int x; if (fscanf(f,"%d%d",&px,&py) != 2) { text_message("%s: can't scan position on #P line at %lu",fn,at); fail(); } eat_to_nl("#P trailer"); x = px; while (1) { c = getc(f); switch (c) { case '.': x ++; break; case '*': { LINE *l; if (h == 0) { y0 = py; bbmin = (XY){x:x,y:py}; bbmax = bbmin; a = 8; v = malloc(a*sizeof(*v)); for (i=a-1;i>=0;i--) v[i] = (LINE){bits:0,x0:0,a:0,w:0}; } if (x < bbmin.x) bbmin.x = x; if (x > bbmax.x) bbmax.x = x; if (py < bbmin.y) bbmin.y = py; if (py > bbmax.y) bbmax.y = py; if (py < y0) { i = y0 - py; v = realloc(v,(a+i)*sizeof(*v)); bcopy(v,v+i,a*sizeof(*v)); a += i; h += i; y0 = py; for (i--;i>=0;i--) v[i] = (LINE){bits:0,x0:0,a:0,w:0}; } else if (py >= y0+a) { i = 16 + py - (y0+a); a += i; v = realloc(v,a*sizeof(*v)); for (;i>0;i--) v[a-i] = (LINE){bits:0,x0:0,a:0,w:0}; } i = py + 1 - y0; if (i > h) h = i; l = &v[i-1]; if (l->w == 0) l->x0 = x; if (x < l->x0) { i = l->x0 - x; l->bits = realloc(l->bits,(l->a+i)*sizeof(*l->bits)); bcopy(l->bits,l->bits+i,l->a); bzero(l->bits,i); l->a += i; l->w += i; l->x0 -= i; } else if (x >= l->x0+l->a) { i = 16 + x - (l->x0+l->a); l->bits = realloc(l->bits,(l->a+i)*sizeof(*l->bits)); bzero(l->bits+l->a,i); l->a += i; } i = x + 1 - l->x0; if (i > l->w) l->w = i; l->bits[i-1] = 1; x ++; } break; case '#': ungetc(c,f); continue <"lineloop">; break; case EOF: if (x == px) break <"lineloop">; text_message("%s: EOF in bitmap",fn); fail(); break; default: if (nlchar(c)) { if (x > px) py ++; x = px; nlflush(f); break; } text_message("%s: bad character `%c' in bitmap at %lu",fn,c,ftell(f)-1); fail(); break; } } } break; } } load_bw = bbmax.x + 1 - bbmin.x; i = (bbmax.y + 1 - bbmin.y) * load_bw; load_bits = malloc(i); bzero(load_bits,i); for (i=0;ibits,load_bits+(l->x0-bbmin.x)+((y0+i-bbmin.y)*load_bw),l->w); } start_sel2((XY){x:-bbmin.x,y:-bbmin.y},(XY){x:load_bw-1,y:bbmax.y-bbmin.y},&enter_load,&get2_load); } else { text_message("%s: unrecognized version string `%.*s'",fn,i,&ver[0]); fail(); } freeup(); } static void file_load(const char *fn) { __label__ failed; FILE *f; char magic[6]; static void fail(void) { goto failed; } if (! fn) { beep(); return; } f = fopen(fn,"r"); if (f == 0) { text_message("%s: %s",fn,strerror(errno)); failed:; if (f) fclose(f); beep(); return; } if (fread(&magic[0],1,6,f) != 6) { text_message("%s: Can't read signature",fn); fail(); } if (! bcmp(&magic[0],"MLife-",6)) { file_load_mouse(fn,f,fail); } else if (! bcmp(&magic[0],"#Life ",6)) { file_load_lif(fn,f,fail); } else { text_message("%s: Invalid signature",fn); fail(); } fclose(f); } static void show_glob(void) { beep(); /* XXX textbuf_nulterm(); ... */ } static int accc_filename(unsigned char ch, int key) { if ((key == 109) && (kbflags & KBF_CTRL)) { show_glob(); return(TACC_DONE); } if ( ((ch >= 32) && (ch <= 126)) || ((ch >= 160) && (ch <= 255)) ) return(TACC_CHAR); return(TACC_ERR); } static void file_save_pick(unsigned char ch, int key __attribute__((__unused__))) { text_unsaves(); textlen = -1; switch (ch) { case 'e': case 'E': savep1.x = 0; savep1.y = 0; savep2.x = arena_x - 1; savep2.y = arena_y - 1; break; case 'r': case 'R': savep1 = selp1; savep2 = selp2; break; default: beep(); return; break; } text_getline(accc_filename,"File name: "); text_gotline = file_save; } static void file_pick_op(unsigned char ch, int key __attribute__((__unused__))) { text_unsaves(); textlen = -1; switch (ch) { case 's': case 'S': if ((selbox & (SB_HAVE|SB_SHOW)) == (SB_HAVE|SB_SHOW)) { set_fixed_text("Save ntire screen or just egion?"); text_saves(); input_op = text_input_op; text_keystroke = file_save_pick; } else { text_saves(); file_save_pick('e',0); } break; case 'l': case 'L': text_getline(accc_filename,"File name: "); text_gotline = file_load; break; default: beep(); break; } } static void file(void) { set_fixed_text("File: ave or oad?"); text_saves(); input_op = text_input_op; text_keystroke = file_pick_op; } static void start_stepping(const char *s) { if (s) { run = atoi(s); } else { run = 0; } } static void stepn(void) { text_getline(accc_digits,"Generations to step: "); text_gotline = start_stepping; } static void shapevar_setup(SHAPEVAR *v) { int i; char ch; int x; int y; int w; int h; int p; char *b; int o; int a; static void nonrectangular(void) { fprintf(stderr,"%s: non-rectangular shape\n",__progname); exit(1); } w = -1; y = 0; x = 0; b = 0; a = 0; o = 0; v->first1.x = -1; for <"charloop"> (i=0;;i++) { ch = v->text[i]; switch (ch) { case '\0': break <"charloop">; case ' ': continue <"charloop">; break; case '.': p = SVB_IGN; break; case 'x': p = SVB_ZERO; break; case '*': p = SVB_ONE; if (v->first1.x < 0) { v->first1.x = x; v->first1.y = y; v->ll1.x = x; v->ll1.y = y; v->ur1.x = x; v->ur1.y = y; } else { if (x < v->ll1.x) v->ll1.x = x; if (y < v->ll1.y) v->ll1.y = y; if (x > v->ur1.x) v->ur1.x = x; if (y > v->ur1.y) v->ur1.y = y; } break; case '/': if (w < 0) w = x; if (x != w) nonrectangular(); y ++; x = 0; continue <"charloop">; break; default: fprintf(stderr,"%s: bad char `%c' in shape bitmap\n",__progname,ch); exit(1); break; } if (o >= a) b = realloc(b,a=o+16); b[o++] = p; x ++; } if (v->first1.x < 0) { fprintf(stderr,"%s: blank shape\n",__progname); exit(1); } if (w < 0) { if (x == 0) { fprintf(stderr,"%s: empty shape\n",__progname); exit(1); } w = x; h = 1; } else { if (x == w) h = y + 1; else if (x == 0) h = y; else nonrectangular(); } v->size.x = w; v->size.y = h; v->bits = b; } static int variant_match_and_clear(SHAPEVAR *v, int x, int y, char *board) { int x1; int y1; int x2; int y2; int xx; int yy; int yo; int xo0; char *vbp; if ( (x+v->ll1.x-v->first1.x < 0) || (y+v->ll1.y-v->first1.y < 0) || (x+v->ur1.x-v->first1.x >= arena_x) || (y+v->ur1.y-v->first1.y >= arena_y) ) return(0); x1 = x - v->first1.x; x2 = x1 + v->size.x; y1 = y - v->first1.y; y2 = y1 + v->size.y; if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 > arena_x) x2 = arena_x; if (y2 > arena_y) y2 = arena_y; xo0 = x1 - (x - v->first1.x); for (yy=y1,yo=y1-(y-v->first1.y);yybits[xo0+(yo*v->size.x)]; for (xx=x1;xxfirst1.y);yybits[xo0+(yo*v->size.x)]; for (xx=x1;xx= 0) && (xx+disp.x < DISPLAY_X) && (yy+disp.y >= 0) && (yy+disp.y < DISPLAY_Y) ) vram[xx+disp.x+((yy+disp.y)*DISPLAY_X)] = COL_FOUND; } } } return(1); } static void inv(void) { char *board; int x; int y; char *bp; int sno; SHAPE *s; MSHAPE *m; int vno; SHAPEVAR *v; int any; printf("Inventory:\n"); board = malloc(arena_x*arena_y); bp = board; for (y=0;ycount = 0; for (vno=0;s->variants[vno];vno++) { v = s->variants[vno]; if (! v->bits) shapevar_setup(v); } } for (sno=0;mshapes[sno];sno++) { m = mshapes[sno]; for (vno=0;m->variants[vno];vno++) { v = m->variants[vno]; if (! v->bits) shapevar_setup(v); } } do { any = 0; bp = board; for (y=0;y (x=0;xvariants[vno];vno++) { v = s->variants[vno]; if (variant_match_and_clear(v,x,y,board)) { s->count ++; any = 1; continue <"nextxy">; } } } for (sno=0;mshapes[sno];sno++) { m = mshapes[sno]; for (vno=0;m->variants[vno];vno++) { v = m->variants[vno]; if (variant_match_and_clear(v,x,y,board)) { int i; for (i=0;m->pieces[i];i++) m->pieces[i]->count ++; any = 1; continue <"nextxy">; } } } } } } } while (any); for (sno=0;shapes[sno];sno++) { s = shapes[sno]; if (s->count) printf("%s: %d\n",s->name,s->count); } fulldisp = 1; } static void autorun(void) { int i; run = RUN_AUTO; autodisp = 0; for (i=0;ilear?",show_gen_counter?"ide":"how"); text_saves(); input_op = text_input_op; text_keystroke = gen_ctr_op; break; case 'b': case 'B': show_border_widths = ! show_border_widths; fulldisp = 1; break; default: beep(); break; } } static void dispcmds(void) { set_fixed_text("Display: eneration counter or order widths"); text_saves(); input_op = text_input_op; text_keystroke = dispcmd_pick; } static void setsize(int x, int y) { int i; WORD *buf; arena_xw = (x + WBITS - 3) / (WBITS - 2); arena_x = x; arena_y = y; edgemask = (~((WORD)0)) << ((WBITS-2) - ((x+(WBITS-3))%(WBITS-2))); disp.x = (DISPLAY_X - arena_x) / 2; disp.y = (DISPLAY_Y - arena_y) / 2; if (havemem) { for (i=0;i= 32) && (ch <= 126)) || ((ch >= 160) && (ch <= 255)) ) ? TACC_CHAR : TACC_ERR); } static void colon(void) { text_getline(accc_all,": "); text_gotline = colon_cmd; } static void default_input_op(int op, ...) { va_list ap; va_start(ap,op); switch (op) { case IO_CLICK: { int button; XY mapped; button = va_arg(ap,int); switch (button) { case 1: if (enl_up) { if (enl_xy_map(mouse,&mapped)) { setpixel(mapped.x,mapped.y,1); draw_pixel(mapped.x,mapped.y); } else { enl_hide(); } } else { enl_show(); dropmove |= buttonbit(1); } break; case 2: if (enl_up) { if (enl_xy_map(mouse,&mapped)) { setpixel(mapped.x,mapped.y,0); draw_pixel(mapped.x,mapped.y); } else { enl_hide(); } } else { enl_show(); dropmove |= buttonbit(2); } break; case 3: if (enl_up) { enl_hide(); } else { enl_show(); dropmove |= buttonbit(3); } break; } } break; case IO_MOVE: { XY new; XY omapped; XY nmapped; new.x = va_arg(ap,int); new.y = va_arg(ap,int); if (enl_up && enl_xy_map(mouse,&omapped) && enl_xy_map(new,&nmapped)) { switch (mousestate & 3) { case 1: pixel_line(omapped,nmapped,1); break; case 2: pixel_line(omapped,nmapped,0); break; } } move_mouse_slide(new); } break; case IO_KEY: { int key; key = va_arg(ap,int); switch (key) { case 57: runrnd(); break; /* R */ case 58: step(); break; /* T */ case 61: inv(); break; /* I */ case 77: autorun(); break; /* A */ case 78: sel(); break; /* S */ case 79: dispcmds(); break; /* D */ case 80: file(); break; /* F */ case 86: colon(); break; /* ;: */ case 100: clrsel(); break; /* Z */ case 102: copy(); break; /* C */ case 105: stepn(); break; /* N */ case 106: move(); break; /* M */ case 121: onegen(); break; /* space */ default: printf("%d\n",key); break; } } break; case IO_DRAW: break; } va_end(ap); } static void init(void) { sizeinit(1600,1024); input_op = default_input_op; textlen = -1; textbuf = 0; textballoc = 0; srandom(time(0)); show_border_widths = 0; show_border_widths_timer = 0; show_gen_counter = 0; gen_counter = 0; } static void draw_enl_selbox(XY p1, XY p2) { int x1; int x2; int y1; int y2; static int mapped_x(int x) { if (x <= enl_at.x) return(enl_disp.x); if (x >= enl_at.x+ENL_X) return(enl_disp.x+ENL_MX-1); return(enl_disp.x+((x-enl_at.x)*ENL_XMAG)); } static int mapped_y(int y) { if (y <= enl_at.y) return(enl_disp.y); if (y >= enl_at.y+ENL_Y) return(enl_disp.y+ENL_MY-1); return(enl_disp.y+((y-enl_at.y)*ENL_YMAG)); } static void xline(int x) { fbc->arecty = mapped_y(y1); fbc->arectx = x; fbc->arecty = mapped_y(y2); fbc->arectx = x+1; draw(); } static void yline(int y) { fbc->arecty = y; fbc->arectx = mapped_x(x1); fbc->arecty = y+1; fbc->arectx = mapped_x(x2); draw(); } fbc->fg = COL_SELBOX; x1 = imin(p1.x,p2.x); x2 = 1 + p1.x + p2.x - x1; y1 = imin(p1.y,p2.y); y2 = 1 + p1.y + p2.y - y1; if ((x1 >= enl_at.x) && (x1 <= enl_at.x+ENL_X)) xline(mapped_x(x1)); if ((x2 >= enl_at.x) && (x2 <= enl_at.x+ENL_X)) xline(mapped_x(x2)); if ((y1 >= enl_at.y) && (y1 <= enl_at.y+ENL_Y)) yline(mapped_y(y1)); if ((y2 >= enl_at.y) && (y2 <= enl_at.y+ENL_Y)) yline(mapped_y(y2)); } static void enl_draw_box(void) { int x; int y; fbc->alu = ALU_FG; fbc->fg = COL_BG; fbc->arecty = enl_disp.y; fbc->arectx = enl_disp.x; fbc->rrecty = ENL_MY - 1; fbc->rrectx = ENL_MX - 1; draw(); fbc->fg = COL_ENLGRID; for (y=0;y<=ENL_Y;y++) { fbc->arecty = enl_disp.y + (y * ENL_YMAG); fbc->arectx = enl_disp.x; fbc->rrecty = 1; fbc->rrectx = ENL_MX - 1; draw(); } for (x=0;x<=ENL_X;x++) { fbc->arecty = enl_disp.y; fbc->arectx = enl_disp.x + (x * ENL_XMAG); fbc->rrecty = ENL_MY - 1; fbc->rrectx = 1; draw(); } if (selbox & SB_SHOW) draw_enl_selbox(selp1,selp2); if (sel2box & SB_SHOW) draw_enl_selbox(sel2p1,sel2p2); fbc->fg = COL_ENLBOX; fbc->aliney = enl_disp.y; fbc->alinex = enl_disp.x; fbc->rliney = 0; fbc->rlinex = ENL_MX - 1; draw(); fbc->rliney = ENL_MY - 1; fbc->rlinex = 0; draw(); fbc->rliney = 0; fbc->rlinex = 1 - ENL_MX; draw(); fbc->rliney = 1 - ENL_MY; fbc->rlinex = 0; draw(); } static void draw_selbox(XY p1, XY p2) { fbc->fg = COL_SELBOX; fbc->alu = ALU_FG; fbc->aliney = p1.y + disp.y; fbc->alinex = p1.x + disp.x; fbc->aliney = p2.y + disp.y; fbc->alinex = p1.x + disp.x; draw(); fbc->aliney = p2.y + disp.y; fbc->alinex = p2.x + disp.x; draw(); fbc->aliney = p1.y + disp.y; fbc->alinex = p2.x + disp.x; draw(); fbc->aliney = p1.y + disp.y; fbc->alinex = p1.x + disp.x; draw(); } static void draw_gen_counter(void) { char buf[64]; sprintf(&buf[0],"%d",gen_counter); gen_counter_corner = place_gen_counter(); switch (gen_counter_corner) { case 0: draw_text_in_box(DTP_AFTER,0,DTP_AFTER,0,&buf[0],-1,-1,0); break; case 1: draw_text_in_box(DTP_BEFORE,DISPLAY_X,DTP_AFTER,0,&buf[0],-1,-1,0); break; case 2: draw_text_in_box(DTP_AFTER,0,DTP_BEFORE,DISPLAY_Y,&buf[0],-1,-1,0); break; case 3: draw_text_in_box(DTP_BEFORE,DISPLAY_X,DTP_BEFORE,DISPLAY_Y,&buf[0],-1,-1,0); break; default: abort(); break; } } static void draw_border_widths(void) { char buf[64]; if (disp.x < 0) { sprintf(&buf[0],"%d",-disp.x); draw_text_in_box(DTP_AFTER,0,DTP_CENTRE,DISPLAY_Y/2,&buf[0],-1,-1,DTP_OPEN_L); } if (disp.x+arena_x > DISPLAY_X) { sprintf(&buf[0],"%d",disp.x+arena_x-DISPLAY_X); draw_text_in_box(DTP_BEFORE,DISPLAY_X,DTP_CENTRE,DISPLAY_Y/2,&buf[0],-1,-1,DTP_OPEN_R); } if (disp.y < 0) { sprintf(&buf[0],"%d",-disp.y); draw_text_in_box(DTP_CENTRE,DISPLAY_X/2,DTP_AFTER,0,&buf[0],-1,-1,DTP_OPEN_U); } if (disp.y+arena_y > DISPLAY_Y) { sprintf(&buf[0],"%d",disp.y+arena_y-DISPLAY_Y); draw_text_in_box(DTP_CENTRE,DISPLAY_X/2,DTP_BEFORE,DISPLAY_Y,&buf[0],-1,-1,DTP_OPEN_D); } } static void vdraw(void) { int y; int x; fbc->fg = COL_OFFBOARD; fbc->alu = ALU_FG; fbc->arecty = 0; fbc->arectx = 0; fbc->rrecty = DISPLAY_Y - 1; fbc->rrectx = DISPLAY_X - 1; draw(); fbc->fg = COL_LIVE; fbc->bg = COL_BG; fbc->alu = ((ALU_FG & ALU_SRC) | (ALU_BG & ~ALU_SRC)) & 0xffff; fbc->mode = MODE_COLOR1; x = imin(disp.x+arena_x,DISPLAY_X) - imax(disp.x,0); y = imin(disp.y+arena_y,DISPLAY_Y) - imax(disp.y,0); if ((x > 0) && (y > 0)) font_30_to_32(mem,disp.x,disp.y,fbc,x,y); drain(); if (show_border_widths || show_border_widths_timer) draw_border_widths(); if (selbox & SB_SHOW) draw_selbox(selp1,selp2); if (sel2box & SB_SHOW) draw_selbox(sel2p1,sel2p2); if (enl_up) { int curfg; int c; enl_draw_box(); curfg = -1; for (y=0;yfg = c; curfg = c; } enl_cellbox(x,y); } } } } if (show_gen_counter) draw_gen_counter(); (*input_op)(IO_DRAW); drain(); } static void redisp(void) { fbc->pm = drawbit ? 0xf0 : 0x0f; vdraw(); setcmap(drawbit); drawbit = ! drawbit; } static void die(int exitcode) { thc->cursxy = 0xffc0ffc0; exit(exitcode); } static void selfsuspend(void) { drain(); close(msfd); while (1) { close(kbfd); while (1) { munmap(fbmap,fbmaplen); while (1) { kill(0,SIGTSTP); if (fbsetup()) continue; break; } if (kbsetup()) continue; break; } if (mssetup()) continue; break; } fulldisp = 1; cursor_move(); } static void keystroke(int key, int release) { switch (key) { int bit; case 1: die(0); break; case 23: if (! release) selfsuspend(); break; case 76: bit = KBF_CTRL; if (0) { case 99: bit = KBF_LSHIFT; } if (0) { case 110: bit = KBF_RSHIFT; } if (release) kbflags &= ~bit; else kbflags |= bit; return; break; } if (release) return; (*input_op)(IO_KEY,key); } static void kbread(void) { struct firm_event ev; int r; r = read(kbfd,&ev,sizeof(ev)); if (r < 0) { if (errno == EINTR) return; fprintf(stderr,"%s: keyboard read: %s\n",__progname,strerror(errno)); exit(1); } if (r != sizeof(ev)) { fprintf(stderr,"%s: keyboard read: size %d != %d\n",__progname,r,(int)sizeof(ev)); return; } keystroke(ev.id&0x7f,ev.value==VKEY_UP); } static void mouse_button(int button, int release) { int thisbit; thisbit = 1 << (button-1); if (release) { mousestate &= ~thisbit; dropmove &= ~thisbit; return; } mousestate |= thisbit; (*input_op)(IO_CLICK,button); } static void move_mouse(int dx, int dy) { XY new; new.x = mouse.x + dx; new.y = mouse.y + dy; if (! dropmove) (*input_op)(IO_MOVE,new.x,new.y); } static void msread(void) { struct firm_event ev[8]; int r; int v; int i; int dx; int dy; r = read(msfd,&ev[0],sizeof(ev)); if (r < 0) { if (errno == EINTR) return; fprintf(stderr,"%s: mouse read: %s\n",__progname,strerror(errno)); exit(1); } if (r % sizeof(ev[0])) { fprintf(stderr,"%s: mouse read: size %d not a multiple of %d\n",__progname,r,(int)sizeof(ev[0])); return; } if (ignore_mouse) return; r /= sizeof(ev[0]); dx = 0; dy = 0; for (i=0;i (p=1;p<(AUTOHIST_N/6);p++) { if (autohash[0] != autohash[p]) continue; if (! printed) { printf("%08x:",autohash[0]); printed = 1; } printf(" %d",p); for (c=1;c<6;c++) { if (bcmp(&autohash[0],&autohash[p*c],p*sizeof(autohash[0]))) { printf("[%d]",c); continue <"period">; } } printf("\n"); printf("period %d\n",p); run = 0; fulldisp = 1; break; } if (printed) printf("\n"); } static void tick(void) { struct pollfd pfd[2]; int rv; int ms; static void lower_timeout(int t) { if (t < 0) abort(); if ((ms == INFTIM) || (t < ms)) ms = t; } ms = INFTIM; pfd[0].fd = kbfd; pfd[0].events = POLLIN | POLLRDNORM; pfd[1].fd = msfd; pfd[1].events = POLLIN | POLLRDNORM; if (show_border_widths_timer) { struct timeval now; gettimeofday(&now,0); if ( (now.tv_sec > show_border_widths_expire.tv_sec) || ( (now.tv_sec == show_border_widths_expire.tv_sec) && (now.tv_usec >= show_border_widths_expire.tv_usec) ) ) { show_border_widths_timer = 0; fulldisp = 1; } else { lower_timeout( ((show_border_widths_expire.tv_sec-now.tv_sec)*1000) + (((int)show_border_widths_expire.tv_usec-(int)now.tv_usec)/1000) ); } } if (run || fulldisp) lower_timeout(0); rv = poll(&pfd[0],2,ms); if (rv < 0) { if (errno == EINTR) return; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } if (rv == 0) { if (fulldisp) { fulldisp = 0; redisp(); } switch (run) { case RUN_DISP: gen(); fulldisp = 1; break; case RUN_AUTO: gen(); autorun_hist(); if (autodisp < 1) { autodisp = 100; fulldisp = 1; } else { autodisp --; } break; case 0: break; default: if (run < 0) { run = 0; } else { gen(); fulldisp = 1; run --; } break; } return; } if ((rv > 0) && (pfd[0].revents & (POLLIN|POLLRDNORM))) kbread(); if ((rv > 0) && (pfd[1].revents & (POLLIN|POLLRDNORM))) msread(); } 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 (!strcmp(*av,"-mouse") || !strcmp(*av,"-ms") || !strcmp(*av,"-mspath")) { WANTARG(); mspath = 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); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); if (fbsetup() || kbsetup() || mssetup()) exit(1); msinit(); init(); redisp(); while (1) tick(); }