#include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "ch.h" #include "esch.h" #include "stdio-util.h" typedef enum { PT_UNKNOWN = 1, PT_NONE, PT_MOVE, PT_SETUP, PT_ROOT, PT_GAMEINFO, PT_INHERIT, } PROPTYPE; typedef enum { VT_UNSPEC = 1, VT_NONE, VT_NUMBER, VT_REAL, VT_DOUBLE, VT_COLOUR, VT_TEXT, VT_SIMPLETEXT, VT_POINT, // includes VT_MOVE VT_LIST_POINT, VT_ELIST_POINT, VT_LIST_COMPOSE_POINT_POINT, VT_LIST_COMPOSE_POINT_SIMPLETEXT, VT_COMPOSE_SIMPLETEXT_SIMPLETEXT, VT_NUMBER_OR_COMPOSE_NUMBER_NUMBER, VT_NONE_OR_COMPOSE_NUMBER_SIMPLETEXT, } VALTYPE; #define C_E '.' #define C_B 'B' #define C_W 'W' // Following values appear only in curboard #define C_BB 'C' #define C_WW 'X' #define C_EB 'b' #define C_EW 'w' typedef struct node NODE; typedef struct reader READER; typedef struct prop PROP; typedef struct value VALUE; typedef struct pkind PKIND; typedef struct xy XY; typedef struct ptst PTST; typedef struct text TEXT; typedef struct stst STST; typedef struct num12 NUM12; typedef struct tree TREE; typedef struct parserv PARSERV; typedef struct setup_visual_state SETUP_VISUAL_STATE; typedef struct colstate COLSTATE; typedef struct colsstate COLSSTATE; typedef struct col COL; typedef struct szconf SZCONF; typedef struct pos POS; typedef struct changes CHANGES; typedef struct chg CHG; typedef struct stargrid STARGRID; typedef struct capstate CAPSTATE; typedef struct pts_to_lines PTS_TO_LINES; typedef struct histwin HISTWIN; typedef struct postrace POSTRACE; typedef struct click_or_drag CLICK_OR_DRAG; typedef struct scroller SCROLLER; struct click_or_drag { int cur; int cur0; int down; int downx; int downy; LX_TIME downtime; int isdown; int canclick; int (*limit)(int); void (*drag)(int); void (*click)(int); } ; struct scroller { CLICK_OR_DRAG cod; int query; int qid; LX_XID qwin; LX_QUERYPOINTER_STATUS qps; int (*cur)(void); } ; struct postrace { int len; unsigned char *data; LX_XID pm; } ; struct histwin { LX_XID win; POS *pos; } ; struct pts_to_lines { LX_XID into; LX_XID gc; int phase; int e1x; int e1y; int e2x; int e2y; } ; struct capstate { unsigned char *flag; XY *pend; int npend; unsigned char col; POS *pos; } ; struct stargrid { unsigned char n; unsigned char o0; unsigned char oinc; unsigned char omax; } ; struct chg { unsigned char x; unsigned char y; unsigned char v; } ; struct changes { int nchgs; CHG *chgs; } ; struct pos { POS *up; CHANGES chg; int nkids; POS **kids; unsigned char *board; int bcap; int wcap; int curkid; unsigned char mover; int yinx; int ypix; LX_XID l_win; LX_XID r_win; } ; struct szconf { int value; unsigned int flags; #define SZF_PERCENT 0x00000001 int cur; } ; struct col { LX_RGB c; unsigned int pix; LX_RGB actc; } ; struct colsstate { COLSTATE *list; int id; union { int pending; COLSTATE *nexts; } u; } ; struct colstate { COLSTATE *link; COLSSTATE *ss; LX_OP *op; const char *str; COL *c; int success; const char *tag; } ; struct setup_visual_state { int (*match)(SETUP_VISUAL_STATE *, const LX_VISINFO *); const LX_VISINFO *best; const LX_VISINFO *best_onscreen; LX_XID id; LX_VISUALCLASS class; unsigned int defscr; } ; struct parserv { const char *err; int off; } ; #define PRV(e,o) ((PARSERV){.err=(e),.off=(o)}) #define PRV_OK ((PARSERV){.err=0,.off=-1}) struct tree { NODE **nodes; int nnodes; TREE **trees; int ntrees; } ; struct num12 { int n; int n2; // -1 if none } ; struct xy { unsigned char x; unsigned char y; } ; struct text { CH *s; int l; } ; struct ptst { XY pt; TEXT st; } ; struct stst { TEXT s1; TEXT s2; } ; struct pkind { TEXT name; PROPTYPE ptype; VALTYPE vtype; } ; struct reader { FILE *f; ESCH pushed; LOC at; // of next char to read PROP *prop; int rootnode; } ; struct node { PROP **props; int nprops; } ; struct value { VALTYPE t; union { struct { int n; CH *v; } unspec; // if VT_UNSPEC // nothing if VT_NONE int num; // if VT_NUMBER double real; // if VT_REAL int dbl; // if VT_DOUBLE char col; // if VT_COLOUR TEXT s; // if VT_TEXT or VT_SIMPLETEXT XY pt; // if VT_POINT struct { XY *v; int n; } ptlist; // if VT_LIST_POINT or VT_ELIST_POINT struct { XY (*v)[2]; int n; } ptptlist; // if VT_LIST_COMPOSE_POINT_POINT struct { PTST *v; int n; } ptstlist; // if VT_LIST_COMPOSE_POINT_SIMPLETEXT STST stst; // if VT_COMPOSE_SIMPLETEXT_SIMPLETEXT NUM12 num12; // if VT_NUMBER_OR_COMPOSE_NUMBER_NUMBER struct { int n; // -1 if NONE TEXT st; } nst; // if VT_NONE_OR_COMPOSE_NUMBER_SIMPLETEXT } u; } ; struct prop { TEXT name; PKIND *kind; TEXT valtext; VALUE val; } ; // *Strings*Space is used as both *Strings.Cap.Space and *Strings.Hist.Space static LX_DB *db; static const char *defaults_string = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *BorderMargin: 1\n\ *Colours*Board: #b80\n\ *Colours*Lines: #000\n\ *Colours*Black: #000\n\ *Colours*White: #fff\n\ *Colours*Mark: #f00\n\ *Colours*HistoryBG.Odd: #200\n\ *Colours*HistoryBG.Even: #004\n\ *Mag: 35\n\ *PieceSize: 80%\n\ *LineSize: 5%\n\ *StarSize: 15%\n\ *SmallPieceSize: 7\n\ *HistMarkSize: 3\n\ *ClickMaxTime: 750\n\ *ClickMinTime: 150\n\ *DragDelta: 10\n\ *Strings*Space: \\ \n\ *Strings*Cap*Label: Captured:\n\ *Strings*Cap*None: None\n\ *Strings*Cap*Times: ×\n\ *Strings*Hist*L: \\ <\\ \n\ *Strings*Hist*R: \\ >\\ \n\ *HistScrollWidth: 10\n\ "; static const char *filename; static int dumpfile; static char *displayname; static const char *geometryspec; static const char *fgcstr; static const char *bgcstr; static const char *bordercstr; static const char *borderwstr; static const char *bordermstr; static const char *hscrbgstr; static const char *hscrfgstr; static char *visualstr; static const char *boardcstr; static const char *linescstr; static const char *blackcstr; static const char *whitecstr; static const char *histbgcolstr_odd; static const char *histbgcolstr_even; static const char *magstr; static const char *piecesizestr; static const char *linesizestr; static const char *starsizestr; static const char *smallpiecesizestr; static const char *histmarksizestr; static const char *fontstr; static const char *clickmaxtimestr; static const char *clickmintimestr; static const char *dragdeltastr; static const char *str_captured; static const char *str_capnone; static const char *str_captimes; static const char *str_capspace; static const char *str_hist_l; static const char *str_hist_r; static const char *str_hist_space; static const char *histscrollwidthstr; static int argc; static char **argv; static char *rmstring; static LX_CONN *disp; static const LX_VISINFO *vis; static int scr; static int scrwidth; static int scrheight; static int depth; static LX_XID rootwin; static LX_SGC bitgc; static LX_SGC wingc; static LX_XID wincmap; static int defcmap; static COL fgcol; static COL bgcol; static COL bordercol; static COL boardcol; static COL linescol; static COL blackcol; static COL whitecol; static COL hbgcol_odd; static COL hbgcol_even; static COL hscr_bg; static COL hscr_fg; static int borderwidth; static int margin; static int mag; static SZCONF piecesize; static SZCONF linesize; static SZCONF starsize; static int histmarksize; static int smallpiecesize; static int histscrollwidth; static LX_XID topwin; static LX_XID boardbwin; static LX_XID boardwin; static LX_XID boardpm; static LX_XID starpm; static LX_XID boardbgpm; static LX_XID small_b_pm; static LX_XID small_w_pm; static LX_XID small_e_pm; static LX_XID small_mask_pm; static LX_XID piece_b_pm; static LX_XID piece_w_pm; static LX_XID piece_bb_pm; static LX_XID piece_ww_pm; static LX_XID capbwin; static LX_XID capwin; static LX_XID cappm; static LX_XID histbwin; static LX_XID histclipwin; static LX_XID histwin; static LX_XID histmarkwin; static LX_XID histiwin; static LX_XID histscrollwin; static LX_XID histthumbwin; static LX_XID histpm; static int starrad; static int boardsize; static int boardpoints; static CAPSTATE cs; static const STARGRID *starpattern; static int top_w; static int top_h; static int new_w; static int new_h; static int board_sz; static int need_layout; static int layout_id; static const LX_FONTINFO *fi; static int baselineskip; static int maxdw; static int cap_w; static int cap_h; static int cap_bl; static int cap_spy; static int captw; static int capnw; static int capxw; static int capsw; static int curwcap; static int curbcap; static int histmax; static int histcur; static int histcurpix; static int hist_th; static int hist_ty; static int hist_sw; static int hist_nw; static int hist_scrx; static int hist_scry; static int hist_scrw; static int hist_scrh; static int hist_scrih; static int hist_winw; static int hist_winh; static int hist_yoff; static int hist_rowh; static int hist_bl; static int hist_spy; static int hist_lt_w; static int hist_gt_w; static int hist_updated; static int hist_thumbmap; static AVL *hist_lr_wins; static AVL *hist_free_wins; static int clickmintime; static int clickmaxtime; static int dragdelta2; static SCROLLER scroll_hist; static SCROLLER scroll_hist_thumb; static unsigned char *curboard; static AVL *proptbl; static TREE **trees; static int ntrees; static POS *posroot; static POS *curpos; static AVL *postraces; #define MAXCOORD 52 static char coords[MAXCOORD] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const STARGRID stargrid[] = { { 5, 1, 1, 0 }, // . . . . . { 6, 1, 1, 0 }, // . . . . . . { 7, 1, 1, 0 }, // . . . . . . . { 8, 1, 1, 0 }, // . . . . . . . . { 9, 4, 1, 4 }, // . . . . * . . . . { 10, 3, 1, 3 }, // . . . * . . * . . . { 11, 3, 1, 3 }, // . . . * . . . * . . . { 12, 3, 1, 3 }, // . . . * . . . . * . . . { 13, 3, 1, 3 }, // . . . * . . . . . * . . . { 14, 3, 1, 3 }, // . . . * . . . . . . * . . . { 15, 3, 4, 7 }, // . . . * . . . * . . . * . . . { 16, 3, 3, 6 }, // . . . * . . * . . * . . * . . . { 17, 3, 5, 8 }, // . . . * . . . . * . . . . * . . . { 18, 3, 4, 7 }, // . . . * . . . * . . * . . . * . . . { 19, 3, 6, 9 }, // . . . * . . . . . * . . . . . * . . . { 20, 3, 4, 7 }, // . . . * . . . * . . . . * . . . * . . . { 21, 3, 7, 10 }, // . . . * . . . . . . * . . . . . . * . . . { 22, 3, 5, 8 }, // . . . * . . . . * . . . . * . . . . * . . . { 23, 3, 4, 11 }, // . . . * . . . * . . . * . . . * . . . * . . . { 24, 3, 5, 8 }, // . . . * . . . . * . . . . . . * . . . . * . . . { 25, 3, 6, 9 }, // . . . * . . . . . * . . . . . * . . . . . * . . . { 26, 3, 6, 9 }, // . . . * . . . . . * . . . . . . * . . . . . * . . . { 27, 3, 5, 13 }, // . . . * . . . . * . . . . * . . . . * . . . . * . . . { 28, 3, 7, 10 }, // . . . * . . . . . . * . . . . . . * . . . . . . * . . . { 29, 3, 7, 10 }, // . . . * . . . . . . * . . . . . . . * . . . . . . * . . . { 30, 3, 7, 10 }, // . . . * . . . . . . * . . . . . . . . * . . . . . . * . . . { 31, 3, 6, 15 }, // . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . { 32, 3, 5, 13 }, // . . . * . . . . * . . . . * . . . . * . . . . * . . . . * . . . { 33, 3, 5, 13 }, // . . . * . . . . * . . . . * . . . . . * . . . . * . . . . * . . . { 34, 3, 9, 12 }, // . . . * . . . . . * . . . . . * . . * . . . . . * . . . . . * . . . { 35, 3, 7, 17 }, // . . . * . . . . . . * . . . . . . * . . . . . . * . . . . . . * . . . { 36, 3, 6, 15 }, // . . . * . . . . . * . . . . . * . . . . * . . . . . * . . . . . * . . . { 37, 3, 6, 15 }, // . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . { 38, 3, 6, 15 }, // . . . * . . . . . * . . . . . * . . . . . . * . . . . . * . . . . . * . . . { 39, 3, 8, 19 }, // . . . * . . . . . . . * . . . . . . . * . . . . . . . * . . . . . . . * . . . { 40, 3, 6, 15 }, // . . . * . . . . . * . . . . . * . . . . . . . . * . . . . . * . . . . . * . . . { 41, 3, 7, 17 }, // . . . * . . . . . . * . . . . . . * . . . . . * . . . . . . * . . . . . . * . . . { 42, 3, 7, 17 }, // . . . * . . . . . . * . . . . . . * . . . . . . * . . . . . . * . . . . . . * . . . { 43, 3, 6, 21 }, // . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . { 44, 3, 5, 18 }, // . . . * . . . . * . . . . * . . . . * . . . . . . * . . . . * . . . . * . . . . * . . . { 45, 3, 5, 18 }, // . . . * . . . . * . . . . * . . . . * . . . . . . . * . . . . * . . . . * . . . . * . . . { 46, 3, 6, 21 }, // . . . * . . . . . * . . . . . * . . . . . * . . * . . . . . * . . . . . * . . . . . * . . . { 47, 3, 6, 21 }, // . . . * . . . . . * . . . . . * . . . . . * . . . * . . . . . * . . . . . * . . . . . * . . . { 48, 3, 6, 21 }, // . . . * . . . . . * . . . . . * . . . . . * . . . . * . . . . . * . . . . . * . . . . . * . . . { 49, 3, 6, 21 }, // . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . . . * . . . { 50, 3, 6, 21 }, // . . . * . . . . . * . . . . . * . . . . . * . . . . . . * . . . . . * . . . . . * . . . . . * . . . { 51, 3, 5, 23 }, // . . . * . . . . * . . . . * . . . . * . . . . * . . . * . . . . * . . . . * . . . . * . . . . * . . . { 52, 3, 5, 23 }, // . . . * . . . . * . . . . * . . . . * . . . . * . . . . * . . . . * . . . . * . . . . * . . . . * . . . }; #define NSTARGRID (sizeof(stargrid) / sizeof(stargrid[0])) #define CXY(x,y) ((x)+((y)*boardsize)) static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = (char **) malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i 0) { skip --; continue; } if (**av != '-') { if (! filename) { filename = *av; } else { fprintf(stderr,"%s: extra argument `%s'\n",__progname,*av); errs = 1; } continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-file")) { WANTARG(); filename = av[skip]; continue; } if (!strcmp(*av,"-dumpfile")) { dumpfile = 1; continue; } if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryspec = av[skip]; continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); fgcstr = av[skip]; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); bgcstr = av[skip]; continue; } if (!strcmp(*av,"-bordercolor") || !strcmp(*av,"-bd")) { WANTARG(); bordercstr = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); borderwstr = av[skip]; continue; } if (!strcmp(*av,"-bordermargin") || !strcmp(*av,"-bm")) { WANTARG(); bordermstr = av[skip]; continue; } if (!strcmp(*av,"-font")) { WANTARG(); fontstr = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&rmstring,"\n",av[skip],(char *)0); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (! filename) { fprintf(stderr,"%s: need SGF file name\n",__progname); errs = 1; } if (errs) exit(1); } #define NBUFS 8 static const char *text_str(const TEXT *t) { static char *bufs[NBUFS] = { 0 }; static int hand = 0; char *s; int i; s = malloc(t->l+1); for (i=t->l-1;i>=0;i--) s[i] = t->s[i].c; s[t->l] = '\0'; hand = (hand ? hand : NBUFS) - 1; free(bufs[hand]); bufs[hand] = s; return(s); } static int proptbl_compare(void *av, void *bv) { PKIND *a; PKIND *b; int i; a = av; b = bv; if (a->name.l != b->name.l) return(a->name.l-b->name.l); for (i=a->name.l-1;i>=0;i--) { if (a->name.s[i].c != b->name.s[i].c) return(a->name.s[i].c-b->name.s[i].c); } return(0); } static const AVL_OPS ao_proptbl = { .compare = proptbl_compare, .flags = AVL_F_NODUPS }; static void setup_prop(const char *name, PROPTYPE pt, VALTYPE vt) { int namelen; PKIND *pk; int i; namelen = strlen(name); pk = malloc(sizeof(PKIND)); pk->name.s = malloc(namelen*sizeof(CH)); for (i=namelen-1;i>=0;i--) pk->name.s[i] = (CH) { .c = (unsigned char)name[i], .flags = 0, .at = { .lno = -1, .cno = -1, .bno = -1 } }; pk->name.l = namelen; pk->ptype = pt; pk->vtype = vt; avl_insert(proptbl,pk); } static void setup_types(void) { proptbl = avl_new(&ao_proptbl); setup_prop("B",PT_MOVE,VT_POINT); setup_prop("KO",PT_NONE,VT_NONE); setup_prop("MN",PT_MOVE,VT_NUMBER); setup_prop("W",PT_MOVE,VT_POINT); setup_prop("AB",PT_SETUP,VT_LIST_POINT); setup_prop("AE",PT_SETUP,VT_LIST_POINT); setup_prop("AW",PT_SETUP,VT_LIST_POINT); setup_prop("PL",PT_SETUP,VT_COLOUR); setup_prop("C",PT_NONE,VT_TEXT); setup_prop("DM",PT_NONE,VT_DOUBLE); setup_prop("GB",PT_NONE,VT_DOUBLE); setup_prop("GW",PT_NONE,VT_DOUBLE); setup_prop("HO",PT_NONE,VT_DOUBLE); setup_prop("N",PT_NONE,VT_SIMPLETEXT); setup_prop("UC",PT_NONE,VT_DOUBLE); setup_prop("V",PT_NONE,VT_REAL); setup_prop("BM",PT_MOVE,VT_DOUBLE); setup_prop("DO",PT_MOVE,VT_DOUBLE); setup_prop("IT",PT_MOVE,VT_DOUBLE); setup_prop("TE",PT_MOVE,VT_DOUBLE); setup_prop("AR",PT_NONE,VT_LIST_COMPOSE_POINT_POINT); setup_prop("CR",PT_NONE,VT_LIST_POINT); setup_prop("DD",PT_INHERIT,VT_ELIST_POINT); setup_prop("LB",PT_NONE,VT_LIST_COMPOSE_POINT_SIMPLETEXT); setup_prop("LN",PT_NONE,VT_LIST_COMPOSE_POINT_POINT); setup_prop("MA",PT_NONE,VT_LIST_POINT); setup_prop("SL",PT_NONE,VT_LIST_POINT); setup_prop("SQ",PT_NONE,VT_LIST_POINT); setup_prop("TR",PT_NONE,VT_LIST_POINT); setup_prop("AP",PT_ROOT,VT_COMPOSE_SIMPLETEXT_SIMPLETEXT); setup_prop("CA",PT_ROOT,VT_SIMPLETEXT); setup_prop("FF",PT_ROOT,VT_NUMBER); setup_prop("GM",PT_ROOT,VT_NUMBER); setup_prop("ST",PT_ROOT,VT_NUMBER); setup_prop("SZ",PT_ROOT,VT_NUMBER_OR_COMPOSE_NUMBER_NUMBER); setup_prop("AN",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("BR",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("WR",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("BT",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("WT",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("CP",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("DT",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("EV",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("GN",PT_GAMEINFO,VT_TEXT); setup_prop("GC",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("ON",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("OT",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("PB",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("PC",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("PW",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("RE",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("RO",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("RU",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("SO",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("TM",PT_GAMEINFO,VT_REAL); setup_prop("US",PT_GAMEINFO,VT_SIMPLETEXT); setup_prop("BL",PT_MOVE,VT_REAL); setup_prop("WL",PT_MOVE,VT_REAL); setup_prop("OB",PT_MOVE,VT_NUMBER); setup_prop("OW",PT_MOVE,VT_NUMBER); setup_prop("FG",PT_NONE,VT_NONE_OR_COMPOSE_NUMBER_SIMPLETEXT); setup_prop("PM",PT_INHERIT,VT_NUMBER); setup_prop("VW",PT_INHERIT,VT_ELIST_POINT); setup_prop("HA",PT_GAMEINFO,VT_NUMBER); setup_prop("KM",PT_GAMEINFO,VT_REAL); setup_prop("TB",PT_NONE,VT_ELIST_POINT); setup_prop("TW",PT_NONE,VT_ELIST_POINT); } static void read_fail(CH, const char *, ...) __attribute__((__format__(__printf__,2,3),__noreturn__)); static void read_fail(CH c, const char *fmt, ...) { va_list ap; char *s; int l; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); fprintf(stderr,"%s: line %d, col %d (byte offset %d): %s\n",filename,c.at.lno,c.at.cno,c.at.bno,s); free(s); exit(1); } static CH getat(READER *r) { CH rv; if (esch_len(&r->pushed)) return(esch_pop(&r->pushed)); rv.c = getc(r->f); rv.flags = 0; rv.at = r->at; switch (rv.c) { case EOF: break; case '\n': r->at.lno ++; r->at.cno = 1; break; case '\t': r->at.cno = (r->at.cno + 8) & ~7; break; default: r->at.cno ++; break; } r->at.bno ++; return(rv); } static void unget(READER *r, CH ch) { esch_append_1(&r->pushed,ch); } static CH getsw(READER *r) { CH c; do c = getat(r); while ((c.c != EOF) && isspace(c.c)); return(c); } static PARSERV parse_none(const TEXT *s, int o) { int i; for (i=o;il;i++) if (! isspace(s->s[i].c)) return(PRV("value isn't empty",i)); return(PRV_OK); } static PARSERV parse_number(const TEXT *s, int o, int *vp) { int i; int v; int v2; int dv; for (i=o;il;i++) if (! isspace(s->s[i].c)) break; if (i >= s->l) return(PRV("value is empty",i)); v = 0; for <"num"> (;il;i++) { switch (s->s[i].c) { case '0': dv = 0; break; case '1': dv = 1; break; case '2': dv = 2; break; case '3': dv = 3; break; case '4': dv = 4; break; case '5': dv = 5; break; case '6': dv = 6; break; case '7': dv = 7; break; case '8': dv = 8; break; case '9': dv = 9; break; default: break <"num">; } v2 = (v * 10) + dv; if (v2/10 != v) return(PRV("value overflows",i)); v = v2; } *vp = v; for (i++;il;i++) if (! isspace(s->s[i].c)) return(PRV(0,i)); return(PRV_OK); } static PARSERV parse_real(const TEXT *s, int o, double *vp) { int i; double v; int dv; int gotdot; double m; int predig; int postdig; for (i=o;il;i++) if (! isspace(s->s[i].c)) break; if (i >= s->l) return(PRV("value is empty",i)); v = 0; predig = 0; gotdot = 0; for <"num"> (;il;i++) { switch (s->s[i].c) { case '0': dv = 0; break; case '1': dv = 1; break; case '2': dv = 2; break; case '3': dv = 3; break; case '4': dv = 4; break; case '5': dv = 5; break; case '6': dv = 6; break; case '7': dv = 7; break; case '8': dv = 8; break; case '9': dv = 9; break; case '.': if (gotdot) break <"num">; if (predig < 1) return(PRV("no digits before decimal point",i)); gotdot = 1; m = .1; postdig = 0; continue; break; default: break <"num">; } if (gotdot) { v += dv * m; m /= 10; postdig ++; } else { v = (v * 10) + dv; predig ++; } } if (gotdot) { if (postdig < 1) { return(PRV("no digits after decimal point",i)); } } else { if (predig < 1) { return(PRV("no digits before decimal point",i)); } } *vp = v; for (i++;il;i++) if (! isspace(s->s[i].c)) return(PRV(0,i)); return(PRV_OK); } /* * This does not parse C doubles (see parse_real for the closest thing * we have to that). This parses what the SGF spec calls a "Double", * which is either a 1 or a 2, represented as 0 and 1 in *vp. */ static PARSERV parse_double(const TEXT *s, int o, int *vp) { int i; int v; for (i=o;il;i++) if (! isspace(s->s[i].c)) break; if (i >= s->l) return(PRV("value is empty",i)); switch (s->s[i].c) { case '1': v = 0; break; case '2': v = 1; break; default: return(PRV("invalid value",i)); break; } *vp = v; for (i++;il;i++) if (! isspace(s->s[i].c)) return(PRV(0,i)); return(PRV_OK); } static PARSERV parse_colour(const TEXT *s, int o, char *vp) { int i; int v; for (i=o;il;i++) if (! isspace(s->s[i].c)) break; if (i >= s->l) return(PRV("value is empty",i)); switch (s->s[i].c) { case 'B': case 'W': v = s->s[i].c; break; default: return(PRV("invalid colour",i)); break; } *vp = v; for (i++;il;i++) if (! isspace(s->s[i].c)) return(PRV(0,i)); return(PRV_OK); } /* * The SGF spec's handling of text is a bit broken, in that it makes it * impossible to do get multiple consecutive spaces through to the * displayer, which makes it impossible to get either (a1) proper * two-space space after end-of-sentence punctuation or (a2) ASCII * graphics through to the displayer. It also is specified in a way * that is remarkably inconvenient to code; that's why the code below * instead of the usual "test quoting and switch on the character". * * The spec says * Soft line break: linebreaks preceded by a "\" (soft linebreaks * are converted to "", i.e. they are removed) * but it is not clear what happens if a line ends with an even number * of backslashes. A literal reading of the above says that the * linebreak is a soft linebreak and the first character of the next * line gets quoted, but that seems so weird I question whether that's * what's actually implemented. If nothing else, that makes it * completely impossible to represent a backslash immediately followed * by a linebreak. * * See also the comment on read_prop(). */ #define PTF_NOCOLON 0x00000001 // unescaped : is terminator (first part of Compose) #define PTF_SIMPLE 0x00000002 // SimpleText (if clear, Text) static PARSERV parse_text(const TEXT *str, int o, TEXT *vp, unsigned int flags) { ESCH b; int q; int s; int i; CH c; char lb1; esch_init(&b); q = 0; s = 0; lb1 = '\0'; for <"chars"> (i=o;il;i++) { c = str->s[i]; if ( ((lb1 == '\r') && (c.c == '\n')) || ((lb1 == '\n') && (c.c == '\r')) ) { continue; } else if (c.c == '\r') { lb1 = '\r'; c.c = '\n'; } else if (c.c == '\n') { lb1 = '\n'; } else { lb1 = '\0'; } if (q && (c.c == '\n')) { q = 0; continue; } if ((c.c == '\n') && (flags & PTF_SIMPLE)) c.c = ' '; if (isspace(c.c) && (c.c != '\n')) { if (s) continue; s = 1; c.c = ' '; } else { s = 0; } if (q) { esch_append_1(&b,str->s[i]); q = 0; } else { switch (c.c) { case '\\': q = 1; break; case ':': if (flags & PTF_NOCOLON) break <"chars">; // fall through default: esch_append_1(&b,str->s[i]); break; } } } vp->l = esch_len(&b); vp->s = esch_take(&b); if (i < str->l) return(PRV(0,i)); return(PRV_OK); } static PARSERV parse_point(const TEXT *s, int o, XY *vp) { XY p; char *cp; int i; for (i=o;il;i++) if (! isspace(s->s[i].c)) break; if (i >= s->l) return(PRV("value is empty",i)); cp = memchr(&coords[0],s->s[i].c,MAXCOORD); if (! cp) return(PRV("invalid X coordinate",i)); p.x = cp - &coords[0]; i ++; if (i >= s->l) return(PRV("missing Y coordinate",i+1)); cp = memchr(&coords[0],s->s[i].c,MAXCOORD); if (! cp) return(PRV("invalid Y coordinate",i)); p.y = cp - &coords[0]; *vp = p; for (i++;il;i++) if (! isspace(s->s[i].c)) return(PRV(0,i)); return(PRV_OK); } static void parse_prop_value(PROP *p, CH term) { PARSERV pr; const char *err; int o; switch (p->val.t) { case VT_NONE: pr = parse_none(&p->valtext,0); err = "nonempty value"; break; case VT_NUMBER: pr = parse_number(&p->valtext,0,&p->val.u.num); err = "invalid number"; break; case VT_REAL: pr = parse_real(&p->valtext,0,&p->val.u.real); err = "invalid value"; break; case VT_DOUBLE: pr = parse_double(&p->valtext,0,&p->val.u.dbl); err = "invalid value"; break; case VT_COLOUR: pr = parse_colour(&p->valtext,0,&p->val.u.col); err = "invalid colour"; break; case VT_TEXT: pr = parse_text(&p->valtext,0,&p->val.u.s,0); err = "invalid text"; break; case VT_SIMPLETEXT: pr = parse_text(&p->valtext,0,&p->val.u.s,PTF_SIMPLE); err = "invalid text"; break; case VT_ELIST_POINT: pr = parse_none(&p->valtext,0); if (! pr.err) { p->val.t = VT_NONE; break; } // fall through case VT_LIST_POINT: p->val.u.ptlist.n = 1; p->val.u.ptlist.v = malloc(sizeof(XY)); pr = parse_point(&p->valtext,0,&p->val.u.ptlist.v[0]); if (pr.off < 0) { p->val.t = VT_LIST_POINT; break; } err = "invalid point"; break; case VT_POINT: pr = parse_point(&p->valtext,0,&p->val.u.pt); err = "invalid point"; break; case VT_LIST_COMPOSE_POINT_POINT: p->val.u.ptptlist.n = 1; p->val.u.ptptlist.v = malloc(sizeof(XY [2])); pr = parse_point(&p->valtext,0,&p->val.u.ptptlist.v[0][0]); if (pr.err) { err = "invalid first point"; break; } if (pr.off < 0) { pr.off = p->valtext.l; pr.err = 0; err = "missing : delimiter"; break; } if (p->valtext.s[pr.off].c != ':') { pr.err = 0; err = "missing/incorrecet delimiter"; break; } o = pr.off + 1; pr = parse_point(&p->valtext,o,&p->val.u.ptptlist.v[0][1]); if (pr.err) { err = "invalid second point"; break; } if (pr.off >= 0) { err = "junk after second point"; break; } break; case VT_LIST_COMPOSE_POINT_SIMPLETEXT: p->val.u.ptstlist.n = 1; p->val.u.ptstlist.v = malloc(sizeof(PTST)); pr = parse_point(&p->valtext,0,&p->val.u.ptstlist.v[0].pt); if (pr.err) { err = "invalid point"; break; } if (pr.off < 0) { pr.off = p->valtext.l; pr.err = 0; err = "missing : delimiter"; break; } if (p->valtext.s[pr.off].c != ':') { pr.err = 0; err = "missing/incorrecet delimiter"; break; } pr = parse_text(&p->valtext,pr.off+1,&p->val.u.ptstlist.v[0].st,PTF_SIMPLE); if (pr.err) { err = "invalid text"; break; } if (pr.off >= 0) { err = "incorrectly closed"; break; } break; case VT_COMPOSE_SIMPLETEXT_SIMPLETEXT: pr = parse_text(&p->valtext,0,&p->val.u.stst.s1,PTF_SIMPLE|PTF_NOCOLON); if (pr.err) { err = "invalid texts"; break; } if (pr.off < 0) { pr.off = p->valtext.l; pr.err = 0; err = "missing : delimiter"; break; } if (p->valtext.s[pr.off].c != ':') { err = "invalid delimiter"; pr.err = 0; break; } pr = parse_text(&p->valtext,pr.off+1,&p->val.u.stst.s2,PTF_SIMPLE); if (pr.err) { err = "invalid text"; break; } if (pr.off >= 0) { err = "incorrectly closed"; break; } break; case VT_NUMBER_OR_COMPOSE_NUMBER_NUMBER: pr = parse_number(&p->valtext,0,&p->val.u.num12.n); if (pr.err) { err = "invalid number"; break; } if (pr.off < 0) { p->val.u.num12.n2 = -1; break; } if (p->valtext.s[pr.off].c != ':') { err = "invalid delimiter"; pr.err = 0; break; } pr = parse_number(&p->valtext,pr.off+1,&p->val.u.num12.n2); if (pr.err) { err = "invalid number"; break; } if (pr.off >= 0) { err = "junk after number"; break; } break; case VT_NONE_OR_COMPOSE_NUMBER_SIMPLETEXT: pr = parse_none(&p->valtext,0); if (! pr.err) { p->val.u.nst.n = -1; break; } pr = parse_number(&p->valtext,0,&p->val.u.nst.n); if (pr.err) { err = "invalid number"; break; } if (pr.off < 0) { pr.off = p->valtext.l; pr.err = 0; err = "missing : delimiter"; break; } if (p->valtext.s[pr.off].c != ':') { pr.err = 0; err = "missing/incorrecet delimiter"; break; } pr = parse_text(&p->valtext,pr.off+1,&p->val.u.nst.st,PTF_SIMPLE); if (pr.err) { err = "invalid text"; break; } if (pr.off >= 0) { err = "incorrectly closed"; break; } break; default: abort(); break; } if (pr.off < 0) return; read_fail((pr.offvaltext.l)?p->valtext.s[pr.off]:term,"%s in %s property%s%s%s",err,text_str(&p->name),pr.err?" (":"",pr.err?pr.err:"",pr.err?")":""); } static void parse_prop_value_append(PROP *p, ESCH *text, CH term) { PARSERV pr; const char *err; int o; TEXT tt; tt.l = esch_len(text); tt.s = esch_buf(text); switch (p->val.t) { case VT_LIST_POINT: p->val.u.ptlist.v = realloc(p->val.u.ptlist.v,(p->val.u.ptlist.n+1)*sizeof(XY)); pr = parse_point(&tt,0,&p->val.u.ptlist.v[p->val.u.ptlist.n]); if (pr.off < 0) { p->val.u.ptlist.n ++; break; } err = "invalid point"; break; case VT_LIST_COMPOSE_POINT_POINT: p->val.u.ptptlist.v = realloc(p->val.u.ptptlist.v,(p->val.u.ptptlist.n+1)*sizeof(XY [2])); pr = parse_point(&tt,0,&p->val.u.ptptlist.v[p->val.u.ptptlist.n][0]); if (pr.err) { err = "invalid first point"; break; } if (pr.off < 0) { pr.off = tt.l; pr.err = 0; err = "missing : delimiter"; break; } if (tt.s[pr.off].c != ':') { pr.err = 0; err = "missing/incorrecet delimiter"; break; } o = pr.off + 1; pr = parse_point(&tt,o,&p->val.u.ptptlist.v[p->val.u.ptptlist.n][1]); if (pr.err) { err = "invalid second point"; break; } if (pr.off >= 0) { err = "junk after second point"; break; } p->val.u.ptptlist.n ++; break; case VT_LIST_COMPOSE_POINT_SIMPLETEXT: p->val.u.ptstlist.v = realloc(p->val.u.ptstlist.v,(p->val.u.ptstlist.n+1)*sizeof(PTST)); pr = parse_point(&tt,0,&p->val.u.ptstlist.v[p->val.u.ptstlist.n].pt); if (pr.err) { err = "invalid point"; break; } if (pr.off < 0) { pr.off = tt.l; pr.err = 0; err = "missing : delimiter"; break; } if (tt.s[pr.off].c != ':') { pr.err = 0; err = "missing/incorrecet delimiter"; break; } pr = parse_text(&tt,pr.off+1,&p->val.u.ptstlist.v[p->val.u.ptstlist.n].st,PTF_SIMPLE); if (pr.err) { err = "invalid text"; break; } if (pr.off >= 0) { err = "incorrectly closed"; break; } p->val.u.ptstlist.n ++; break; default: abort(); break; } if (pr.off < 0) return; read_fail((pr.offname),pr.err?" (":"",pr.err?pr.err:"",pr.err?")":""); } // ELIST_POINT is omitted because parse_elist_point sets val.t to // either VT_NONE or LIST_POINT, whichever is appropriate. static int list_more(PROP *p) { switch (p->val.t) { case VT_LIST_POINT: case VT_LIST_COMPOSE_POINT_POINT: case VT_LIST_COMPOSE_POINT_SIMPLETEXT: return(1); break; default: return(0); break; } } /* * The handling of backslashes is ambiguous in the spec. A literal * reading of the rules makes it impossible to represent a backslash * followed by a hard linebreak in a Text or SimpleText value; it also * is not clear how backslashes should be handled in values specified * for unrecognized properties. For the moment, you get "what this * code does". */ static PROP *read_prop(READER *r) { CH c; PROP *p; static ESCH pname = ESCH_STATIC_INIT; static ESCH pval = ESCH_STATIC_INIT; int q; PKIND cpk; esch_clear(&pname); while (1) { c = esch_len(&pname) ? getat(r) : getsw(r); if (! isupper(c.c)) break; esch_append_1(&pname,c); } if (! esch_len(&pname)) { unget(r,c); return(0); } if ((c.c != '[') && !isspace(c.c)) read_fail(c,"invalid property name character %c",c.c); if (isspace(c.c)) c = getsw(r); if (c.c != '[') read_fail(c,"invalid opening value delimiter %c",c.c); p = 0; while (1) { esch_clear(&pval); q = 0; while (1) { c = getat(r); if (c.c == EOF) read_fail(c,"EOF while reading property value"); if (!q && (c.c == ']')) break; esch_append_1(&pval,c); if (q) q = 0; else if (c.c == '\\') q = 1; } if (! p) { p = malloc(sizeof(PROP)); p->name.l = esch_len(&pname); p->name.s = esch_take(&pname); cpk.name = p->name; p->kind = avl_find(proptbl,&cpk); if (p->kind) { p->val.t = p->kind->vtype; p->valtext.l = esch_len(&pval); p->valtext.s = esch_take(&pval); parse_prop_value(p,c); } else { p->val.t = VT_UNSPEC; p->val.u.unspec.n = esch_len(&pval); p->val.u.unspec.v = esch_take(&pval); } if (! list_more(p)) return(p); } else { parse_prop_value_append(p,&pval,c); } c = getsw(r); if (c.c != '[') { unget(r,c); return(p); } } } static NODE *read_node(READER *r) { CH c; PROP **propv; int propa; int propn; PROP *p; NODE *n; c = getsw(r); if (c.c != ';') { unget(r,c); return(0); } n = malloc(sizeof(NODE)); propv = 0; propa = 0; propn = 0; while (1) { p = read_prop(r); if (! p) break; if (propn >= propa) { propa += 8; propv = realloc(propv,propa*sizeof(PROP *)); } propv[propn++] = p; } n = malloc(sizeof(NODE)); n->props = propv; n->nprops = propn; return(n); } static TREE *read_tree(READER *r) { CH c; NODE **nodev; int nodea; int noden; NODE *n; TREE **treev; int treea; int treen; TREE *t; c = getsw(r); if (c.c != '(') { unget(r,c); return(0); } nodev = 0; nodea = 0; noden = 0; while (1) { n = read_node(r); if (! n) break; if (noden >= nodea) { nodea += 8; nodev = realloc(nodev,nodea*sizeof(NODE *)); } nodev[noden++] = n; } r->rootnode = 0; treev = 0; treea = 0; treen = 0; while (1) { t = read_tree(r); if (! t) break; if (treen >= treea) { treea += 8; treev = realloc(treev,treea*sizeof(TREE *)); } treev[treen++] = t; } c = getsw(r); if (c.c != ')') read_fail(c,"improperly closed tree"); t = malloc(sizeof(TREE)); t->nodes = nodev; t->nnodes = noden; t->trees = treev; t->ntrees = treen; return(t); } static void load_sgf(void) { READER r; TREE *t; setup_types(); r.f = fopen(filename,"r"); if (! r.f) { fprintf(stderr,"%s: %s: %s\n",__progname,filename,strerror(errno)); exit(1); } esch_init(&r.pushed); r.at.lno = 1; r.at.cno = 1; r.at.bno = 0; r.prop = 0; trees = 0; ntrees = 0; while (1) { r.rootnode = 1; t = read_tree(&r); if (! t) break; ntrees ++; trees = realloc(trees,ntrees*sizeof(TREE *)); trees[ntrees-1] = t; } } static const char *indent_gametree(void *fp) { char f; f = *(char *)fp; *(char *)fp = 0; return(f?"( ":" "); } static const char *indent_node(void *fp) { char f; f = *(char *)fp; *(char *)fp = 0; return(f?";":" "); } static void dump_text(TEXT *t, FILE *to) { int i; int c; for (i=0;il;i++) { c = t->s[i].c; switch (c) { case '\\': case ']': case ':': putc('\\',to); break; } putc(c,to); } } static void dump_value(VALUE *v, FILE *to) { int i; putc('[',to); switch (v->t) { case VT_UNSPEC: for (i=0;iu.unspec.n;i++) putc(v->u.unspec.v[i].c,to); break; case VT_NONE: break; case VT_NUMBER: fprintf(to,"%d",v->u.num); break; case VT_REAL: fprintf(to,"%f",v->u.real); break; case VT_DOUBLE: fprintf(to,"%c",v->u.dbl?'2':'1'); break; case VT_COLOUR: fprintf(to,"%c",v->u.col); break; case VT_TEXT: case VT_SIMPLETEXT: dump_text(&v->u.s,to); break; case VT_POINT: fprintf(to,"%c%c",coords[v->u.pt.x],coords[v->u.pt.y]); break; case VT_LIST_POINT: for (i=0;iu.ptlist.n;i++) fprintf(to,"%s%c%c",i?"][":"",coords[v->u.ptlist.v[i].x],coords[v->u.ptlist.v[i].y]); break; case VT_LIST_COMPOSE_POINT_POINT: for (i=0;iu.ptptlist.n;i++) { fprintf(to,"%s%c%c",i?"][":"",coords[v->u.ptptlist.v[i][0].x],coords[v->u.ptptlist.v[i][0].y]); if ((v->u.ptptlist.v[i][1].x != v->u.ptptlist.v[i][0].x) || (v->u.ptptlist.v[i][1].y != v->u.ptptlist.v[i][0].y)) { fprintf(to,":%c%c",coords[v->u.ptptlist.v[i][1].x],coords[v->u.ptptlist.v[i][1].y]); } } break; case VT_LIST_COMPOSE_POINT_SIMPLETEXT: for (i=0;iu.ptstlist.n;i++) { fprintf(to,"%s%c%c:",i?"][":"",coords[v->u.ptstlist.v[i].pt.x],coords[v->u.ptstlist.v[i].pt.y]); dump_text(&v->u.ptstlist.v[i].st,to); } break; case VT_COMPOSE_SIMPLETEXT_SIMPLETEXT: dump_text(&v->u.stst.s1,to); putc(';',to); dump_text(&v->u.stst.s2,to); break; case VT_NUMBER_OR_COMPOSE_NUMBER_NUMBER: fprintf(to,"%d",v->u.num12.n); if (v->u.num12.n2 >= 0) fprintf(to,":%d",v->u.num12.n2); break; case VT_NONE_OR_COMPOSE_NUMBER_SIMPLETEXT: if (v->u.nst.n >= 0) { fprintf(to,"%d:",v->u.nst.n); dump_text(&v->u.nst.st,to); } break; default: abort(); break; } putc(']',to); } static void dump_prop(PROP *p, FILE *to) { dump_text(&p->name,to); dump_value(&p->val,to); } static void dump_node(NODE *n, FILE *to) { FILE *f; char first; int x; first = 1; f = indent_wrap(to,&indent_node,&first); for (x=0;xnprops;x++) { if (x) putc('\n',f); dump_prop(n->props[x],f); } fclose(f); } static void dump_gametree(TREE *t, FILE *to) { FILE *f; int x; char first; first = 1; f = indent_wrap(to,&indent_gametree,&first); for (x=0;xnnodes;x++) dump_node(t->nodes[x],f); for (x=0;xntrees;x++) dump_gametree(t->trees[x],f); fprintf(f,")\n"); fclose(f); } static void dump_trees(void) { int i; for (i=0;itype) { case LX_XE_Request: printf("Request"); break; case LX_XE_Value: printf("Value"); break; case LX_XE_Window: printf("Window"); break; case LX_XE_Pixmap: printf("Pixmap"); break; case LX_XE_Atom: printf("Atom"); break; case LX_XE_Cursor: printf("Cursor"); break; case LX_XE_Font: printf("Font"); break; case LX_XE_Match: printf("Match"); break; case LX_XE_Drawable: printf("Drawable"); break; case LX_XE_Access: printf("Access"); break; case LX_XE_Alloc: printf("Alloc"); break; case LX_XE_Colormap: printf("Colormap"); break; case LX_XE_GContext: printf("GContext"); break; case LX_XE_IDChoice: printf("IDChoice"); break; case LX_XE_Name: printf("Name"); break; case LX_XE_Length: printf("Length"); break; case LX_XE_Implementation: printf("Implementation"); break; case LX_XE_Other: printf("Other (%u)",err->u.other.type); break; default: printf("?%d",(int)err->type); break; } if (conn != disp) printf(" on unexpected LX_CONN %p",(void *)conn); printf("\n"); return(LX_X_ERR_CRASH); } static void err_lib(LX_CONN *conn, const LX_LIB_ERR *err) { printf("Library error: "); switch (err->type) { case LX_LE_NOMEM: printf("NOMEM"); break; case LX_LE_SYSERR: printf("SYSERR"); break; case LX_LE_OSLIBERR: printf("OSLIBERR"); break; case LX_LE_BAD_CALL: printf("BAD_CALL"); break; case LX_LE_NO_DISPLAY: printf("NO_DISPLAY"); break; case LX_LE_BAD_DISPLAY: printf("BAD_DISPLAY"); break; case LX_LE_NO_SCREEN: printf("NO_SCREEN"); break; case LX_LE_UNX_EOF: printf("UNX_EOF"); break; case LX_LE_ALL_FAIL: printf("ALL_FAIL"); break; case LX_LE_PROTO_ERR: printf("PROTO_ERR"); break; case LX_LE_REJECTED: printf("REJECTED"); break; default: printf("?%d",(int)err->type); break; } printf("\n"); lx_default_lib_err(conn,err); } // return true if a is a better visual than b static int better_visual(const LX_VISINFO *a, const LX_VISINFO *b) { return( (a->depth > b->depth) || ( (a->depth == b->depth) && (a->id == lx_root_visual(disp,a->screen)) ) ); } static int setup_visual_map(void *sv, const LX_VISINFO *vi) { SETUP_VISUAL_STATE *s; s = sv; if ((*s->match)(s,vi)) { if (!s->best || better_visual(vi,s->best)) s->best = vi; if (vi->screen == s->defscr) { if (!s->best_onscreen || better_visual(vi,s->best_onscreen)) s->best_onscreen = vi; } } return(0); } static int match_visual_id(SETUP_VISUAL_STATE *s, const LX_VISINFO *vi) { return(vi->id==s->id); } static int match_visual_class(SETUP_VISUAL_STATE *s, const LX_VISINFO *vi) { return(vi->class==s->class); } static void setup_visual(void) { SETUP_VISUAL_STATE s; const char *classname; if (! visualstr) { s.match = &match_visual_id; s.id = lx_root_visual(disp,lx_default_screen(disp)); } else { s.match = &match_visual_class; #define FOO(c) if (!strcasecmp(visualstr,#c)) { s.class = LX_VISUALCLASS_##c; classname = #c; } else FOO(StaticGray) FOO(StaticColor) FOO(TrueColor) FOO(GrayScale) FOO(PseudoColor) FOO(DirectColor) #undef FOO { unsigned long long int id; char *cp; s.match = &match_visual_id; id = strtoll(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } s.id = (LX_XID) id; if (s.id != id) { fprintf(stderr,"%s: %s: out-of-range visual option\n",__progname,visualstr); exit(1); } } } s.best = 0; s.best_onscreen = 0; s.defscr = lx_default_screen(disp); lx_map_visuals(disp,&setup_visual_map,&s); if (! s.best) { fprintf(stderr,"%s: no such visual available\n",__progname); exit(1); } if (! s.best_onscreen) { fprintf(stderr,"%s: no matching visual on screen %u, using screen %u\n",__progname,s.defscr,s.best->screen); vis = s.best; } else { vis = s.best_onscreen; } } static void setup_db(void) { LX_DB *db2; int ec; char *str; char *home; char hostname[256]; int i; ec = 0; db = lx_db_from_string(defaults_string,-1,LX_DB_IGNORE_ERR|LX_DB_ERR_COUNT,&ec); if (!db || ec) { fprintf(stderr,"%s: invalid defaults string (error count = %d)\n",__progname,ec); exit(1); } home = getenv("HOME"); if (home) { for (i=1;i>=0;i--) { switch (i) { case 1: str = malloc(strlen(home)+1+10+1); sprintf(str,"%s/.Xdefaults",home); break; case 0: gethostname(&hostname[0],(sizeof(hostname)/sizeof(hostname[0]))-1); hostname[(sizeof(hostname)/sizeof(hostname[0]))-1] = '\0'; str = malloc(strlen(home)+1+11+strlen(&hostname[0])+1); sprintf(str,"%s/.Xdefaults-%s",home,&hostname[0]); break; default: abort(); break; } ec = 0; db2 = lx_db_from_file(str,LX_DB_IGNORE_ERR|LX_DB_ERR_COUNT,&ec); if (db2) { if (ec) { fprintf(stderr,"%s: %s: invalid defaults file (error count = %d)\n",__progname,str,ec); } else { lx_db_merge(db,db2); } lx_db_done(db2); } free(str); } } if (rmstring) { ec = 0; db2 = lx_db_from_string(rmstring,-1,LX_DB_IGNORE_ERR|LX_DB_ERR_COUNT,&ec); if (!db || ec) { fprintf(stderr,"%s: invalid resource string (error count = %d)\n",__progname,ec); exit(1); } lx_db_merge(db,db2); lx_db_done(db2); } } static void maybeset(const char **strp, const char *str) { if (str && !*strp) *strp = str; } static const char *get_default_value(const char *name, const char *class) { static LX_QUARK *nv = 0; static LX_QUARK *cv = 0; static int a = 0; int i; int n; LX_DBVAL v; n = lx_rm_q_string(name,-1,nv,a); if (n < 0) abort(); if (n > a) { free(nv); free(cv); a = n; nv = malloc(a*sizeof(LX_QUARK)); cv = malloc(a*sizeof(LX_QUARK)); i = lx_rm_q_string(name,-1,nv,a); if (i != n) abort(); } i = lx_rm_q_string(class,-1,cv,a); if (i != n) abort(); v = lx_db_query(db,nv,cv,n); return((v.l<0)?0:v.s); } static void load_db(void) { maybeset(&geometryspec,get_default_value("xsgf.geometry","Game.Geometry")); maybeset(&fgcstr,get_default_value("xsgf.foreground","Game.Foreground")); maybeset(&bgcstr,get_default_value("xsgf.background","Game.Background")); maybeset(&bordercstr,get_default_value("xsgf.borderColor","Game.BorderColor")); maybeset(&borderwstr,get_default_value("xsgf.borderWidth","Game.BorderWidth")); maybeset(&bordermstr,get_default_value("xsgf.borderMargin","Game.BorderMargin")); maybeset(&boardcstr,get_default_value("xsgf.colours.board","Game.Colours.Board")); maybeset(&linescstr,get_default_value("xsgf.colours.lines","Game.Colours.Lines")); maybeset(&whitecstr,get_default_value("xsgf.colours.white","Game.Colours.White")); maybeset(&blackcstr,get_default_value("xsgf.colours.black","Game.Colours.Black")); maybeset(&histbgcolstr_odd,get_default_value("xsgf.colours.historybg.odd","Game.Colours.HistoryBG.Odd")); maybeset(&histbgcolstr_even,get_default_value("xsgf.colours.historybg.even","Game.Colours.HistoryBG.Even")); maybeset(&hscrbgstr,get_default_value("xsgf.colours.history.scroll.background","Game.Colours.History.Scroll.Background")); maybeset(&hscrfgstr,get_default_value("xsgf.colours.history.scroll.foreground","Game.Colours.History.Scroll.Foreground")); maybeset(&magstr,get_default_value("xsgf.mag","Game.Mag")); maybeset(&piecesizestr,get_default_value("xsgf.pieceSize","Game.PieceSize")); maybeset(&linesizestr,get_default_value("xsgf.lineSize","Game.LineSize")); maybeset(&starsizestr,get_default_value("xsgf.starSize","Game.StarSize")); maybeset(&smallpiecesizestr,get_default_value("xsgf.smallPieceSize","Game.SmallPieceSize")); maybeset(&histscrollwidthstr,get_default_value("xsgf.histScrollWidth","Game.HistScrollWidth")); maybeset(&histmarksizestr,get_default_value("xsgf.histMarkSize","Game.HistMarkSize")); maybeset(&fontstr,get_default_value("xsgf.font","Game.Font")); maybeset(&clickmaxtimestr,get_default_value("xsgf.clickMaxTime","Game.ClickMaxTime")); maybeset(&clickmintimestr,get_default_value("xsgf.clickMinTime","Game.ClickMinTime")); maybeset(&dragdeltastr,get_default_value("xsgf.dragDelta","Game.DragDelta")); maybeset(&str_captured,get_default_value("xsgf.strings.cap.label","Game.Strings.Cap.Label")); maybeset(&str_capnone,get_default_value("xsgf.strings.cap.none","Game.Strings.Cap.None")); maybeset(&str_captimes,get_default_value("xsgf.strings.cap.times","Game.Strings.Cap.Times")); maybeset(&str_capspace,get_default_value("xsgf.strings.cap.space","Game.Strings.Cap.Space")); maybeset(&str_hist_l,get_default_value("xsgf.strings.hist.l","Game.Strings.Hist.L")); maybeset(&str_hist_r,get_default_value("xsgf.strings.hist.r","Game.Strings.Hist.R")); maybeset(&str_hist_space,get_default_value("xsgf.strings.hist.space","Game.Strings.Hist.Space")); } static LX_SGC setup_gc(int depth) { LX_XID pm; LX_SGC gc; pm = lx_CreatePixmap(disp,rootwin,depth,1,1); gc = lx_CreateSGC_va(disp,pm,LX_GCV_END); lx_SGC_GC(disp,gc); lx_FreePixmap(disp,pm); return(gc); } static void setup_cmap(void) { if (vis->id == lx_root_visual(disp,scr)) { wincmap = lx_root_colormap(disp,scr); defcmap = 1; } else { wincmap = lx_CreateColormap(disp,vis->id,rootwin,LX_AllocNone); defcmap = 0; } } static void reset_szconf(SZCONF *c) { double d; if (c->flags & SZF_PERCENT) { d = (mag * c->value) / 100; } else { d = c->value; } if (d < 1) d = 1; c->cur = (rint((d-1)*.5) * 2) + 1; } static void reset_szconfs(void) { reset_szconf(&piecesize); reset_szconf(&linesize); reset_szconf(&starsize); } static void p2l_init(PTS_TO_LINES *pl, LX_XID into, LX_XID gc) { pl->into = into; pl->gc = gc; pl->phase = 0; } static void p2l_flush(PTS_TO_LINES *pl) { switch (pl->phase) { case 0: break; case 1: lx_fill_rectangle(disp,pl->into,pl->gc,pl->e1x,pl->e1y,1,1); break; case 2: lx_fill_rectangle(disp,pl->into,pl->gc,pl->e1x,pl->e1y,pl->e2x+1-pl->e1x,pl->e2y+1-pl->e1y); break; default: abort(); break; } pl->phase = 0; } static void p2l_point(PTS_TO_LINES *pl, int x, int y) { do <"newline"> { switch (pl->phase) { case 0: pl->e1x = x; pl->e1y = y; pl->phase = 1; break; case 1: if ((x == pl->e1x) && (y == pl->e1y)) return; if ( ((x == pl->e1x+1) && (y == pl->e1y)) || ((y == pl->e1y+1) && (x == pl->e1x)) ) { pl->e2x = x; pl->e2y = y; pl->phase = 2; } else if ( ((x == pl->e1x-1) && (y == pl->e1y)) || ((y == pl->e1y-1) && (x == pl->e1x)) ) { pl->e2x = pl->e1x; pl->e2y = pl->e1y; pl->e1x = x; pl->e1y = y; pl->phase = 2; } else { break <"newline">; } break; case 2: if (pl->e1x == pl->e2x) { if (x != pl->e1x) break <"newline">; if ((y >= pl->e1y) && (y <= pl->e2y)) return; if (y == pl->e1y-1) { pl->e1y = y; } else if (y == pl->e2y+1) { pl->e2y = y; } else { break <"newline">; } } else if (pl->e1y == pl->e2y) { if (y != pl->e1y) break <"newline">; if ((x >= pl->e1x) && (x <= pl->e2x)) return; if (x == pl->e1x-1) { pl->e1x = x; } else if (x == pl->e2x+1) { pl->e2x = x; } else { break <"newline">; } } else { abort(); } break; default: abort(); break; } return; } while (0); p2l_flush(pl); p2l_point(pl,x,y); } static void draw_circle(LX_XID into, LX_SGC sgc, int diam, int xo, int yo) { double r; int x; int y; double c; PTS_TO_LINES pl; c = (diam - 1) * .5; r = (diam * .5) - .001; r = r * r; p2l_init(&pl,into,lx_SGC_GC(disp,sgc)); for (x=diam-1;x>=0;x--) for (y=diam-1;y>=0;y--) { if (((x-c)*(x-c))+((y-c)*(y-c)) < r) { p2l_point(&pl,x+xo,y+yo); } } p2l_flush(&pl); } static void update_captures(POS *p) { char *bt; int bl; char *wt; int wl; int bw; int ww; int xo; LX_XID gcx; int tw; if ((p->bcap == curbcap) && (p->wcap == curwcap)) return; lx_fill_rectangle(disp,cappm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(bgcol.pix),LX_GCV_PUSH),0,0,cap_w,cap_h); tw = captw; if (p->bcap) { bl = asprintf(&bt,"%d",p->bcap); bw = lx_text_extents_8(fi,bt,bl).width; } if (p->wcap) { wl = asprintf(&wt,"%d",p->wcap); ww = lx_text_extents_8(fi,wt,wl).width; } gcx = lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(fgcol.pix),LX_GCV_Background(bgcol.pix),LX_GCV_PUSH); tw = captw; if (p->bcap) tw += capsw + 1 + smallpiecesize + 1 + capxw + bw; if (p->wcap) tw += capsw + 1 + smallpiecesize + 1 + capxw + ww; if (!p->bcap && !p->wcap) tw += capsw + capnw; xo = (cap_w - tw) / 2; lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_captured,-1); xo += captw; if (p->bcap) { lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_capspace,-1); xo += capsw + 1; lx_CopyArea(disp,small_b_pm,cappm,gcx,0,0,xo,cap_spy,smallpiecesize,smallpiecesize); xo += smallpiecesize + 1; lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_captimes,-1); xo += capxw; lx_ImageText8(disp,cappm,gcx,xo,cap_bl,bt,bl); xo += bw; free(bt); } if (p->wcap) { lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_capspace,-1); xo += capsw + 1; lx_CopyArea(disp,small_w_pm,cappm,gcx,0,0,xo,cap_spy,smallpiecesize,smallpiecesize); xo += smallpiecesize + 1; lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_captimes,-1); xo += capxw; lx_ImageText8(disp,cappm,gcx,xo,cap_bl,wt,wl); xo += ww; free(wt); } if (!p->bcap && !p->wcap) { lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_capspace,-1); xo += capsw; lx_ImageText8(disp,cappm,gcx,xo,cap_bl,str_capnone,-1); } lx_ChangeWindowAttributes_va(disp,capwin,LX_CWV_BackPixmap(cappm),LX_CWV_END); lx_ClearArea(disp,capwin,0,0,0,0,0); curbcap = p->bcap; curwcap = p->wcap; } static void draw_board(POS *p) { int i; int j; int hpiece; int hsmall; LX_XID gcx; int bx; unsigned char pbv; unsigned char cbv; int x; int y; hsmall = smallpiecesize >> 1; hpiece = mag >> 1; bx = boardpoints; gcx = lx_SGC_GC(disp,wingc); for (i=boardsize-1;i>=0;i--) { y = (i + 1) * mag; for (j=boardsize-1;j>=0;j--) { x = (j + 1) * mag; bx --; pbv = p->up ? p->up->board[bx] : C_E; cbv = p->board[bx]; switch (cbv) { case C_E: case C_B: case C_W: break; default: abort(); break; } switch (pbv) { case C_E: case C_B: case C_W: break; default: abort(); break; } switch (cbv) { case C_E: switch (pbv) { case C_E: break; case C_B: cbv = C_EB; break; case C_W: cbv = C_EW; break; } break; case C_B: switch (pbv) { case C_E: cbv = C_BB; break; case C_B: break; case C_W: abort(); break; } break; case C_W: switch (pbv) { case C_E: cbv = C_WW; break; case C_B: abort(); break; case C_W: break; } break; default: abort(); break; } if (curboard[bx] != cbv) { curboard[bx] = cbv; switch (cbv) { case C_E: lx_CopyArea(disp,boardbgpm,boardpm,gcx,x-hpiece,y-hpiece,x-hpiece,y-hpiece,mag,mag); break; case C_B: lx_CopyArea(disp,piece_b_pm,boardpm,gcx,0,0,x-hpiece,y-hpiece,mag,mag); break; case C_W: lx_CopyArea(disp,piece_w_pm,boardpm,gcx,0,0,x-hpiece,y-hpiece,mag,mag); break; case C_BB: lx_CopyArea(disp,piece_bb_pm,boardpm,gcx,0,0,x-hpiece,y-hpiece,mag,mag); break; case C_WW: lx_CopyArea(disp,piece_ww_pm,boardpm,gcx,0,0,x-hpiece,y-hpiece,mag,mag); break; case C_EB: lx_CopyArea(disp,boardbgpm,boardpm,gcx,x-hpiece,y-hpiece,x-hpiece,y-hpiece,mag,mag); gcx = lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(small_mask_pm),LX_GCV_ClipXOrigin(x-hsmall),LX_GCV_ClipYOrigin(y-hsmall),LX_GCV_PUSH); lx_CopyArea(disp,small_b_pm,boardpm,gcx,0,0,x-hsmall,y-hsmall,smallpiecesize,smallpiecesize); gcx = lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(LX_GCCLIPMASK_None),LX_GCV_PUSH); break; case C_EW: lx_CopyArea(disp,boardbgpm,boardpm,gcx,x-hpiece,y-hpiece,x-hpiece,y-hpiece,mag,mag); gcx = lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(small_mask_pm),LX_GCV_ClipXOrigin(x-hsmall),LX_GCV_ClipYOrigin(y-hsmall),LX_GCV_PUSH); lx_CopyArea(disp,small_w_pm,boardpm,gcx,0,0,x-hsmall,y-hsmall,smallpiecesize,smallpiecesize); gcx = lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(LX_GCCLIPMASK_None),LX_GCV_PUSH); break; default: abort(); break; } } } } } static void update_board(POS *p) { draw_board(p); lx_ChangeWindowAttributes_va(disp,boardwin,LX_CWV_BackPixmap(boardpm),LX_CWV_END); lx_ClearArea(disp,boardwin,0,0,0,0,0); } static void reset_histmark(void) { lx_ConfigureWindow_va(disp,histmarkwin,LX_CWV_Y(curpos->ypix),LX_CWV_END); } static LX_XID setup_piece(unsigned int pixel) { LX_XID pm; pm = lx_CreatePixmap(disp,rootwin,depth,mag,mag); lx_fill_rectangle(disp,pm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(boardcol.pix),LX_GCV_PUSH),0,0,mag,mag); lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(pixel),LX_GCV_END); draw_circle(pm,wingc,mag-2,1,1); return(pm); } static LX_XID setup_overlaid(LX_XID base, LX_XID small) { LX_XID pm; pm = lx_CreatePixmap(disp,rootwin,depth,mag,mag); lx_CopyArea(disp,base,pm,lx_SGC_GC(disp,wingc),0,0,0,0,mag,mag); lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(small_mask_pm),LX_GCV_ClipXOrigin((mag-smallpiecesize)/2),LX_GCV_ClipYOrigin((mag-smallpiecesize)/2),LX_GCV_END); lx_CopyArea(disp,small,pm,lx_SGC_GC(disp,wingc),0,0,(mag-smallpiecesize)/2,(mag-smallpiecesize)/2,smallpiecesize,smallpiecesize); lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(LX_GCCLIPMASK_None),LX_GCV_END); return(pm); } static void free_hist_lr_wins(void) { HISTWIN *w; while ((w = avl_delete_first(hist_lr_wins))) { if (w->pos->l_win == w->win) w->pos->l_win = LX_WINDOW_None; if (w->pos->r_win == w->win) w->pos->r_win = LX_WINDOW_None; lx_UnmapWindow(disp,w->win); if (avl_insert(hist_free_wins,w)) abort(); } } static LX_XID add_hist_lr_win(POS *p, int x, int w) { HISTWIN *hw; hw = avl_delete_first(hist_free_wins); if (! hw) { hw = malloc(sizeof(HISTWIN)); hw->win = lx_CreateWindow_va(disp,histiwin,-1,-1,1,hist_rowh,0,0,LX_WCLASS_InputOnly,LX_VISUAL_CopyFromParent,LX_CWV_EventMask(LX_EM_ButtonPress),LX_CWV_END); } hw->pos = p; lx_ConfigureWindow_va(disp,hw->win,LX_CWV_X(x),LX_CWV_Y(p->ypix+hist_rowh),LX_CWV_W(w),LX_CWV_H(hist_rowh),LX_CWV_END); lx_MapWindow(disp,hw->win); if (avl_insert(hist_lr_wins,hw)) abort(); return(hw->win); } static int trace_len(void) { POS *p; int n; n = 1; for (p=posroot;p->nkids;p=p->kids[p->curkid]) n ++; return(n); } static void gen_trace(unsigned char *into) { POS *p; int i; i = 0; for (p=posroot;p->nkids;p=p->kids[p->curkid]) into[i++] = p->curkid; } static void maybe_redraw_hist(void) { POS *p; LX_XID pm; char ystr[8]; int yn; int yw; int x; static unsigned char *tracetmp = 0; static int ttsize = 0; int tl; POSTRACE ptc; POSTRACE *pt; if (hist_updated) return; tl = trace_len(); if (tl > ttsize) { free(tracetmp); ttsize = tl; tracetmp = malloc(tl); } gen_trace(tracetmp); histcur = tl; histcurpix = histcur * hist_rowh; free_hist_lr_wins(); ptc.len = tl - 1; ptc.data = tracetmp; pt = avl_find(postraces,&ptc); if (pt) { lx_CopyArea(disp,pt->pm,histpm,lx_SGC_GC(disp,wingc),0,0,0,0,hist_winw,tl*hist_rowh); if (tl < histmax) lx_fill_rectangle(disp,histpm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(bgcol.pix),LX_GCV_PUSH),0,tl*hist_rowh,hist_winw,hist_winh); } else { lx_fill_rectangle(disp,histpm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(bgcol.pix),LX_GCV_PUSH),0,0,hist_winw,hist_winh); p = posroot; while (p) { switch (p->mover) { case C_E: pm = small_e_pm; break; case C_B: pm = small_b_pm; break; case C_W: pm = small_w_pm; break; default: abort(); break; } x = margin + histmarksize + margin; if (p->yinx) { unsigned int bgpix; bgpix = (p->yinx & 1) ? hbgcol_odd.pix : hbgcol_even.pix; lx_fill_rectangle(disp,histpm, lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(bgpix),LX_GCV_PUSH), 0,p->ypix,hist_winw,hist_rowh); yn = snprintf(&ystr[0],sizeof(ystr),"%d",p->yinx); if (yn >= sizeof(ystr)) abort(); yw = lx_text_extents_8(fi,&ystr[0],yn).width; lx_ImageText8(disp,histpm, lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(fgcol.pix),LX_GCV_Background(bgpix),LX_GCV_PUSH), x+hist_nw-yw,p->ypix+hist_bl,&ystr[0],yn); x += hist_nw; lx_ImageText8(disp,histpm,lx_SGC_GC(disp,wingc), x,p->ypix+hist_bl,str_hist_space,-1); x += hist_sw; if (p->up->curkid > 0) { lx_ImageText8(disp,histpm, lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(fgcol.pix),LX_GCV_Background(bgpix),LX_GCV_PUSH), x,p->ypix+hist_bl,str_hist_l,-1); x += hist_lt_w; lx_CopyArea(disp,pm,histpm, lx_ChangeSGC_va(disp,wingc, LX_GCV_ClipMask(small_mask_pm), LX_GCV_ClipXOrigin(x), LX_GCV_ClipYOrigin(p->ypix+hist_spy), LX_GCV_PUSH), 0,0,x,p->ypix+hist_spy,smallpiecesize,smallpiecesize); } else { x += hist_lt_w; } x += 2 * smallpiecesize; lx_CopyArea(disp,pm,histpm, lx_ChangeSGC_va(disp,wingc, LX_GCV_ClipMask(small_mask_pm), LX_GCV_ClipXOrigin(x), LX_GCV_ClipYOrigin(p->ypix+hist_spy), LX_GCV_PUSH), 0,0,x,p->ypix+hist_spy,smallpiecesize,smallpiecesize); x += 2 * smallpiecesize; if (p->up->curkid < p->up->nkids-1) { lx_CopyArea(disp,pm,histpm, lx_ChangeSGC_va(disp,wingc, LX_GCV_ClipMask(small_mask_pm), LX_GCV_ClipXOrigin(x), LX_GCV_ClipYOrigin(p->ypix+hist_spy), LX_GCV_PUSH), 0,0,x,p->ypix+hist_spy,smallpiecesize,smallpiecesize); x += smallpiecesize; lx_ImageText8(disp,histpm, lx_ChangeSGC_va(disp,wingc, LX_GCV_Foreground(fgcol.pix), LX_GCV_Background(bgpix), LX_GCV_ClipMask(LX_GCCLIPMASK_None), LX_GCV_PUSH), x,p->ypix+hist_bl,str_hist_r,-1); } } else { x += hist_nw + hist_sw + hist_lt_w + (2 * smallpiecesize); lx_CopyArea(disp,pm,histpm, lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(LX_GCCLIPMASK_None),LX_GCV_PUSH), 0,0,x,p->ypix+hist_spy,smallpiecesize,smallpiecesize); } lx_ChangeSGC_va(disp,wingc,LX_GCV_ClipMask(LX_GCCLIPMASK_None),LX_GCV_END); p = p->nkids ? p->kids[p->curkid] : 0; } pt = malloc(sizeof(POSTRACE)); pt->len = tl - 1; pt->data = malloc(tl); bcopy(tracetmp,pt->data,tl); pt->pm = lx_CreatePixmap(disp,rootwin,depth,hist_winw,tl*hist_rowh); lx_CopyArea(disp,histpm,pt->pm,lx_SGC_GC(disp,wingc),0,0,0,0,hist_winw,hist_winh); if (avl_insert(postraces,pt)) abort(); } p = posroot; while (p) { if (p->yinx) { x = margin + histmarksize + margin + hist_nw + hist_sw; if (p->up->curkid > 0) { if (p->up->l_win != LX_WINDOW_None) abort(); p->up->l_win = add_hist_lr_win(p->up,x,hist_lt_w+smallpiecesize); } if (p->up->curkid < p->up->nkids-1) { x += hist_lt_w + (4 * smallpiecesize); if (p->up->r_win != LX_WINDOW_None) abort(); p->up->r_win = add_hist_lr_win(p->up,x,smallpiecesize+hist_gt_w); } } p = p->nkids ? p->kids[p->curkid] : 0; } lx_ChangeWindowAttributes_va(disp,histwin,LX_CWV_BackPixmap(histpm),LX_CWV_END); lx_ClearArea(disp,histwin,0,0,0,0,0); hist_updated = 1; } static void scroll_histwin(void) { int ty; int th; int m; if (hist_yoff+hist_scrih > hist_winh) hist_yoff = hist_winh - hist_scrih; if (hist_yoff < 0) hist_yoff = 0; reset_histmark(); if (histcurpix <= hist_scrih) { m = 0; } else { th = ((hist_scrih * hist_scrih) + histcurpix - 1) / histcurpix; if (th >= hist_scrih) { m = 0; } else { m = 1; ty = (hist_yoff * hist_scrih) / histcurpix; } } if (m) { if (! hist_thumbmap) { lx_MapWindow(disp,histthumbwin); hist_thumbmap = 1; } if ((ty != hist_ty) || (th != hist_th)) { hist_ty = ty; hist_th = th; lx_ConfigureWindow_va(disp,histthumbwin,LX_CWV_Y(ty),LX_CWV_H(th),LX_CWV_END); } } else { if (hist_thumbmap) { lx_UnmapWindow(disp,histthumbwin); hist_thumbmap = 0; } } lx_ConfigureWindow_va(disp,histwin,LX_CWV_Y(-hist_yoff),LX_CWV_END); } static void draw_boardbg(void) { LX_XID gcx; int i; int j; int oinc; int omax; int hline; int hstar; hline = linesize.cur >> 1; hstar = starsize.cur >> 1; if (hstar != starrad) { if (starpm != LX_PIXMAP_None) lx_FreePixmap(disp,starpm); starpm = lx_CreatePixmap(disp,rootwin,depth,(2*hstar)+1,(2*hstar)+1); starrad = hstar; } gcx = lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(boardcol.pix),LX_GCV_PUSH); lx_fill_rectangle(disp,boardpm,gcx,0,0,board_sz,board_sz); lx_fill_rectangle(disp,starpm,gcx,0,0,starsize.cur,starsize.cur); lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(linescol.pix),LX_GCV_END); draw_circle(starpm,wingc,starsize.cur,0,0); gcx = lx_SGC_GC(disp,wingc); oinc = starpattern->oinc; omax = starpattern->omax; for (i=boardsize-1;i>=0;i--) { lx_fill_rectangle(disp,boardpm,gcx,mag-hline,((i+1)*mag)-hline,((boardsize-1)*mag)+linesize.cur,linesize.cur); lx_fill_rectangle(disp,boardpm,gcx,((i+1)*mag)-hline,mag-hline,linesize.cur,((boardsize-1)*mag)+linesize.cur); } for (i=starpattern->o0;i<=omax;i+=oinc) { for (j=starpattern->o0;j<=omax;j+=oinc) { lx_CopyArea(disp,starpm,boardpm,gcx,0,0,((i+1)*mag)-hstar,((j+1)*mag)-hstar,starsize.cur,starsize.cur); lx_CopyArea(disp,starpm,boardpm,gcx,0,0,((boardsize-i)*mag)-hstar,((j+1)*mag)-hstar,starsize.cur,starsize.cur); lx_CopyArea(disp,starpm,boardpm,gcx,0,0,((i+1)*mag)-hstar,((boardsize-j)*mag)-hstar,starsize.cur,starsize.cur); lx_CopyArea(disp,starpm,boardpm,gcx,0,0,((boardsize-i)*mag)-hstar,((boardsize-j)*mag)-hstar,starsize.cur,starsize.cur); } } lx_CopyArea(disp,boardpm,boardbgpm,gcx,0,0,0,0,board_sz,board_sz); } static int maybe_relayout(void *arg __attribute__((__unused__))) { int bx; int by; int cx; int cy; int i; int rcw; int rcx; int bbsz; int cbw; int cbh; if (! need_layout) return(AIO_BLOCK_NIL); need_layout = 0; if ((new_w == top_w) && (new_h == top_h)) return(AIO_BLOCK_NIL); top_w = new_w; top_h = new_h; rcw = cap_w + (2 * borderwidth); hist_scrw = hist_winw + histscrollwidth + (3 * borderwidth); if (hist_scrw > rcw) rcw = hist_scrw; board_sz = top_w - rcw - (3 * margin) - (2 * borderwidth); i = top_h - (2 * margin) - (2 * borderwidth); if (i < board_sz) board_sz = i; mag = (board_sz - 1) / ((boardsize - 1) + 2); if (! (mag & 1)) mag --; if (mag < 1) mag = 1; board_sz = (mag * ((boardsize - 1) + 2)) + 1; bbsz = board_sz + (2 * borderwidth); bx = (top_w - bbsz - rcw) / 3; by = (top_h - bbsz) / 2; rcx = bx + bbsz + ((top_w - (bx + bbsz + rcw)) / 2); cbw = cap_w + (2 * borderwidth); cbh = cap_h + (2 * borderwidth); cx = rcx + ((rcw - cbw) / 2); cy = margin; hist_scrh = top_h - cbh - (3 * margin); hist_scrih = hist_scrh - (2 * borderwidth); hist_scrx = rcx + ((rcw - hist_scrw) / 2); hist_scry = cy + cbh + margin; lx_ConfigureWindow_va(disp,boardwin,LX_CWV_W(board_sz),LX_CWV_H(board_sz),LX_CWV_END); i = borderwidth + board_sz + borderwidth; lx_ConfigureWindow_va(disp,boardbwin,LX_CWV_X(bx),LX_CWV_Y(by),LX_CWV_W(i),LX_CWV_H(i),LX_CWV_END); lx_ConfigureWindow_va(disp,capwin,LX_CWV_W(cap_w),LX_CWV_H(cap_h),LX_CWV_END); lx_ConfigureWindow_va(disp,capbwin,LX_CWV_X(cx),LX_CWV_Y(cy),LX_CWV_W(cbw),LX_CWV_H((2*borderwidth)+cap_h),LX_CWV_END); lx_ConfigureWindow_va(disp,histclipwin,LX_CWV_H(hist_scrh-(2*borderwidth)),LX_CWV_END); lx_ConfigureWindow_va(disp,histbwin,LX_CWV_X(hist_scrx),LX_CWV_Y(hist_scry),LX_CWV_W(hist_scrw),LX_CWV_H(hist_scrh),LX_CWV_END); lx_ConfigureWindow_va(disp,histscrollwin,LX_CWV_X(borderwidth+hist_winw+borderwidth),LX_CWV_H(hist_scrih),LX_CWV_END); reset_szconfs(); if (boardpm != LX_PIXMAP_None) { lx_FreePixmap(disp,boardpm); lx_FreePixmap(disp,boardbgpm); } boardpm = lx_CreatePixmap(disp,rootwin,depth,board_sz,board_sz); boardbgpm = lx_CreatePixmap(disp,rootwin,depth,board_sz,board_sz); draw_boardbg(); if (piece_b_pm != LX_PIXMAP_None) { lx_FreePixmap(disp,piece_b_pm); lx_FreePixmap(disp,piece_w_pm); lx_FreePixmap(disp,piece_bb_pm); lx_FreePixmap(disp,piece_ww_pm); } piece_b_pm = setup_piece(blackcol.pix); piece_w_pm = setup_piece(whitecol.pix); piece_bb_pm = setup_overlaid(piece_b_pm,small_b_pm); piece_ww_pm = setup_overlaid(piece_w_pm,small_w_pm); update_board(curpos); update_captures(curpos); maybe_redraw_hist(); scroll_histwin(); return(AIO_BLOCK_LOOP); } static LX_XID setup_small_pm(unsigned int body, unsigned int border) { LX_XID pm; pm = lx_CreatePixmap(disp,rootwin,depth,smallpiecesize,smallpiecesize); lx_fill_rectangle(disp,pm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(bgcol.pix),LX_GCV_PUSH),0,0,smallpiecesize,smallpiecesize); lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(border),LX_GCV_END); draw_circle(pm,wingc,smallpiecesize,0,0); lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(body),LX_GCV_END); draw_circle(pm,wingc,smallpiecesize-2,1,1); return(pm); } static int cmp_hist_wins(void *av, void *bv) { HISTWIN *a; HISTWIN *b; a = av; b = bv; if (a->win < b->win) return(-1); if (a->win > b->win) return(1); return(0); } static const AVL_OPS ao_hist_wins = { .compare = &cmp_hist_wins, .flags = AVL_F_NODUPS }; static int cmp_postraces(void *av, void *bv) { POSTRACE *a; POSTRACE *b; a = av; b = bv; if (a->len != b->len) return(a->len-b->len); return(memcmp(a->data,b->data,a->len)); } static const AVL_OPS ao_postraces = { .compare = &cmp_postraces, .flags = AVL_F_NODUPS }; static void cod_init(CLICK_OR_DRAG *cod, int (*limit)(int), void (*drag)(int), void (*click)(int)) { cod->isdown = 0; cod->limit = limit; cod->drag = drag; cod->click = click; } static void cod_press(CLICK_OR_DRAG *cod, int cur, int v, int x, int y, LX_TIME time) { cod->cur = cur; cod->cur0 = cur; cod->down = v; cod->downx = x; cod->downy = y; cod->downtime = time; cod->isdown = 1; cod->canclick = 1; } static void cod_check_delta(CLICK_OR_DRAG *cod, int x, int y) { int d; d = ((x - cod->downx) * (x - cod->downx)) + ((y - cod->downy) * (y - cod->downy)); if (d > dragdelta2) cod->canclick = 0; } static void cod_moved_to(CLICK_OR_DRAG *cod, int v, int x, int y) { int nv; if (! cod->isdown) return; cod_check_delta(cod,x,y); nv = (*cod->limit)(cod->cur+v-cod->down); if (nv == cod->cur) return; (*cod->drag)(nv); } static void cod_scrolled_to(CLICK_OR_DRAG *cod, int v) { if (! cod->isdown) return; cod->cur = v; } static void cod_release(CLICK_OR_DRAG *cod, int v, int x, int y, LX_TIME time) { LX_TIME dt; cod_check_delta(cod,x,y); dt = time - cod->downtime; if ( (dt < clickmintime) || ((dt < clickmaxtime) && cod->canclick) ) { (*cod->drag)(cod->cur0); (*cod->click)(v); } else { cod_moved_to(cod,v,x,y); } cod->isdown = 0; } static void scroller_qp_done(void *sv) { SCROLLER *s; s = sv; cod_moved_to(&s->cod,s->qps.winy,s->qps.winx,s->qps.winy); } static int scroller_query(void *sv) { SCROLLER *s; s = sv; if (! s->query) return(AIO_BLOCK_NIL); s->query = 0; lx_op_callback(lx_QueryPointer_status(disp,s->qwin,&s->qps),&scroller_qp_done,s,0); return(AIO_BLOCK_LOOP); } static void scroller_init(SCROLLER *s, LX_XID qpwin, int (*limit)(int), void (*drag)(int), void (*click)(int), int (*cury)(void)) { cod_init(&s->cod,limit,drag,click); s->query = 0; s->qid = aio_add_block(&scroller_query,s); s->qwin = qpwin; s->cur = cury; } static void scroller_press(SCROLLER *s, const LX_EVENT_ButtonPress *e) { cod_press(&s->cod,(*s->cur)(),e->eventy,e->eventx,e->eventy,e->time); } static void scroller_release(SCROLLER *s, const LX_EVENT_ButtonRelease *e) { cod_release(&s->cod,e->eventy,e->eventx,e->eventy,e->time); } static void scroller_motion(SCROLLER *s) { s->query = 1; } static void set_curpos(POS *p) { curpos = p; reset_histmark(); update_board(curpos); update_captures(curpos); } static void hist_click(int y) { int inx; POS *p; inx = y / hist_rowh; if ((inx < 0) || (inx >= histmax)) return; do <"found"> { p = posroot; while (p) { if (p->yinx == inx) break <"found">; p = (p->nkids > 0) ? p->kids[p->curkid] : 0; } return; } while (0); if (p == curpos) return; set_curpos(p); } static int scroll_limit_hist(int v) { if (v < -histcurpix) v = - histcurpix; if (v > 0) v = 0; return(v); } static void scroll_drag_hist(int v) { hist_yoff = - v; scroll_histwin(); } static void scroll_click_hist(int v) { hist_click(v); } static int scroll_cury_hist(void) { return(-hist_yoff); } static int scroll_limit_thumb(int v) { if (! hist_thumbmap) return(v); if (v+hist_th > hist_scrih) v = hist_scrih - hist_th; if (v < 0) v = 0; return(v); } static void scroll_drag_thumb(int v) { if (! hist_thumbmap) return; if (hist_scrih == hist_th) return; // can this happen? hist_yoff = (v * (histcurpix - hist_scrih)) / (hist_scrih - hist_th); if (hist_yoff < 0) hist_yoff = 0; scroll_histwin(); } static void scroll_click_thumb(int v) { if (! hist_thumbmap) return; scroll_drag_thumb(v-(hist_th/2)); } static int scroll_cury_thumb(void) { if (! hist_thumbmap) return(0); return(hist_ty); } static void scroll_curpos_visible(void) { if (curpos->ypix < hist_yoff) { hist_yoff = curpos->ypix; hist_updated = 0; } else if (curpos->ypix > hist_yoff+hist_scrih-hist_rowh) { hist_yoff = curpos->ypix + hist_rowh - hist_scrih; hist_updated = 0; } } static void keyboard_curpos_move(void) { scroll_curpos_visible(); hist_updated = 0; update_board(curpos); update_captures(curpos); maybe_redraw_hist(); scroll_histwin(); } static int initial_scroll(void *idpv) { if (need_layout) return(AIO_BLOCK_NIL); aio_remove_block(*(int *)idpv); free(idpv); keyboard_curpos_move(); return(AIO_BLOCK_LOOP); } static void setup_windows(void) { int x; int y; int w; int h; LX_GEOMETRY g; int e; int *idtmp; e = lx_parse_geometry(geometryspec,&g); if (e < 0) { fprintf(stderr,"%s: %s: invalid geometry spec\n",__progname,geometryspec); exit(1); } w = (scrwidth * 3) / 4; h = (scrheight * 3) / 4; x = (scrwidth - w - (2 * borderwidth)) / 2; y = (scrheight - h - (2 * borderwidth)) / 2; e = lx_apply_geometry(&g,scrwidth,scrheight,borderwidth,&x,&y,&w,&h); if (e < 0) { fprintf(stderr,"%s: %s: window geometry out of range\n",__progname,geometryspec); exit(1); } topwin = lx_CreateWindow_va(disp,rootwin,x,y,w,h,borderwidth,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bgcol.pix), LX_CWV_BorderPixel(bordercol.pix), LX_CWV_EventMask(LX_EM_KeyPress|LX_EM_StructureNotify), LX_CWV_Colormap(wincmap), LX_CWV_Cursor(LX_CURSOR_None), LX_CWV_END); boardbwin = lx_CreateWindow_va(disp,topwin,-1,-1,1,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bordercol.pix), LX_CWV_BorderPixel(0), LX_CWV_END); boardwin = lx_CreateWindow_va(disp,boardbwin,borderwidth,borderwidth,1,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(0), LX_CWV_BorderPixel(0), LX_CWV_EventMask( LX_EM_ButtonPress | LX_EM_ButtonRelease | LX_EM_PointerMotion | LX_EM_EnterWindow | LX_EM_LeaveWindow ), LX_CWV_END); lx_MapSubwindows(disp,boardbwin); capbwin = lx_CreateWindow_va(disp,topwin,-1,-1,1,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bordercol.pix), LX_CWV_BorderPixel(0), LX_CWV_END); capwin = lx_CreateWindow_va(disp,capbwin,borderwidth,borderwidth,1,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bgcol.pix), LX_CWV_BorderPixel(0), LX_CWV_END); cappm = lx_CreatePixmap(disp,rootwin,depth,cap_w,cap_h); lx_MapSubwindows(disp,capbwin); histbwin = lx_CreateWindow_va(disp,topwin,-1,-1,1,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bordercol.pix), LX_CWV_BorderPixel(0), LX_CWV_END); histclipwin = lx_CreateWindow_va(disp,histbwin,borderwidth,borderwidth,hist_winw,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bgcol.pix), LX_CWV_END); histwin = lx_CreateWindow_va(disp,histclipwin,0,0,hist_winw,hist_winh,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(bgcol.pix), LX_CWV_EventMask(LX_EM_StructureNotify), LX_CWV_END); histmarkwin = lx_CreateWindow_va(disp,histwin,margin,-hist_rowh,histmarksize,hist_rowh,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(fgcol.pix), LX_CWV_END); histiwin = lx_CreateWindow_va(disp,histwin,0,0,hist_winw,hist_winh,0,0,LX_WCLASS_InputOnly,vis->id, LX_CWV_EventMask( LX_EM_ButtonPress | LX_EM_ButtonRelease | LX_EM_ButtonMotion | LX_EM_PointerMotionHint ), LX_CWV_END); histscrollwin = lx_CreateWindow_va(disp,histbwin,-histscrollwidth,borderwidth,histscrollwidth,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(hscr_bg.pix), LX_CWV_EventMask(LX_EM_ButtonPress|LX_EM_ButtonRelease|LX_EM_ButtonMotion), LX_CWV_END); histthumbwin = lx_CreateWindow_va(disp,histscrollwin,0,-1,histscrollwidth,1,0,depth,LX_WCLASS_InputOutput,vis->id, LX_CWV_BackPixel(hscr_fg.pix), LX_CWV_END); histpm = lx_CreatePixmap(disp,rootwin,depth,hist_winw,hist_winh); hist_thumbmap = 0; lx_MapSubwindows(disp,histwin); lx_MapSubwindows(disp,histclipwin); lx_MapSubwindows(disp,histbwin); lx_MapSubwindows(disp,topwin); curwcap = 0; curbcap = 0; boardpm = LX_PIXMAP_None; board_sz = 1; starpm = LX_PIXMAP_None; starrad = -1; piece_b_pm = LX_PIXMAP_None; top_w = -1; top_h = -1; new_w = w; new_h = h; memset(curboard,C_E,boardpoints); lx_MapWindow(disp,topwin); need_layout = 1; layout_id = aio_add_block(&maybe_relayout,0); small_b_pm = setup_small_pm(blackcol.pix,whitecol.pix); small_w_pm = setup_small_pm(whitecol.pix,blackcol.pix); small_e_pm = lx_CreatePixmap(disp,rootwin,depth,smallpiecesize,smallpiecesize); lx_fill_rectangle(disp,small_e_pm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(bgcol.pix),LX_GCV_PUSH),0,0,smallpiecesize,smallpiecesize); lx_fill_rectangle(disp,small_e_pm,lx_ChangeSGC_va(disp,wingc,LX_GCV_Foreground(fgcol.pix),LX_GCV_PUSH),0,smallpiecesize/2,smallpiecesize,1); small_mask_pm = lx_CreatePixmap(disp,rootwin,1,smallpiecesize,smallpiecesize); lx_fill_rectangle(disp,small_mask_pm,lx_ChangeSGC_va(disp,bitgc,LX_GCV_Foreground(0),LX_GCV_PUSH),0,0,smallpiecesize,smallpiecesize); lx_ChangeSGC_va(disp,bitgc,LX_GCV_Foreground(1),LX_GCV_END); draw_circle(small_mask_pm,bitgc,smallpiecesize,0,0); hist_lr_wins = avl_new(&ao_hist_wins); hist_free_wins = avl_new(&ao_hist_wins); postraces = avl_new(&ao_postraces); scroller_init(&scroll_hist,histiwin,&scroll_limit_hist,&scroll_drag_hist,&scroll_click_hist,&scroll_cury_hist); scroller_init(&scroll_hist_thumb,histscrollwin,&scroll_limit_thumb,&scroll_drag_thumb,&scroll_click_thumb,&scroll_cury_thumb); idtmp = malloc(sizeof(int)); *idtmp = aio_add_block(&initial_scroll,idtmp); hist_ty = -1; hist_th = 1; } static void init_szconf(SZCONF *c, const char *s, const char *tag) { int v; int n1; int n2; const char *why; #define FAIL(msg) do { why = (msg); break <"bad">; } while (0) if (! s) abort(); do <"bad"> { n1 = -1; n2 = -1; sscanf(s,"%d%% %n%*c%n",&v,&n1,&n2); if ((n1 >= 0) && (n2 < 0)) { if ((v < 0) || (v > 100)) FAIL("percentage out of range"); c->flags = SZF_PERCENT; c->value = v; return; } n1 = -1; n2 = -1; sscanf(s,"%d %n%*c%n",&v,&n1,&n2); if ((n1 >= 0) && (n2 < 0)) { if (v < 0) FAIL("size out of range"); c->flags = 0; c->value = v; return; } FAIL("can't parse string"); } while (0); fprintf(stderr,"%s: bad %s size `%s' (%s)\n",__progname,tag,s,why); exit(1); } static void set_pos_y(POS *p, int yi, int yp) { int i; while (1) { p->yinx = yi++; p->ypix = yp; yp += hist_rowh; if (p->nkids != 1) break; p = p->kids[0]; } for (i=p->nkids-1;i>=0;i--) set_pos_y(p->kids[i],yi,yp); } static void setup_numbers(void) { int dw; int i; init_szconf(&piecesize,piecesizestr,"piece"); init_szconf(&linesize,linesizestr,"line"); init_szconf(&starsize,starsizestr,"star point"); smallpiecesize = (atoi(smallpiecesizestr) * 2) + 1; histscrollwidth = atoi(histscrollwidthstr); histmarksize = atoi(histmarksizestr); if (borderwstr) borderwidth = atoi(borderwstr); if (bordermstr) margin = atoi(bordermstr); // mag can't be determined until our window size is known mag = -1; // actual values for size have to wait until mag is known baselineskip = fi->font_ascent + fi->font_descent; for (i=0;i<10;i++) { dw = lx_text_extents_8(fi,"0123456789"+i,1).width; if ((i == 0) || (dw > maxdw)) maxdw = dw; } captw = lx_text_extents_8(fi,str_captured,-1).width; capnw = lx_text_extents_8(fi,str_capnone,-1).width; capxw = lx_text_extents_8(fi,str_captimes,-1).width; capsw = lx_text_extents_8(fi,str_capspace,-1).width; cap_w = capsw + (2 * (1 + smallpiecesize + 1 + capxw + (3 * maxdw))); if (capnw > cap_w) cap_w = capnw; cap_w += margin + captw + capsw + margin; cap_h += margin + ((baselineskip > smallpiecesize) ? baselineskip : smallpiecesize) + margin; cap_w = margin + captw + (2 * (capsw + 1 + smallpiecesize + 1 + capxw + (3 * maxdw))) + margin; cap_bl = margin + ((cap_h - baselineskip) / 2) + fi->font_ascent; cap_spy = margin + ((cap_h - smallpiecesize) / 2); hist_sw = lx_text_extents_8(fi,str_hist_space,-1).width; hist_nw = maxdw * 3; hist_lt_w = lx_text_extents_8(fi,str_hist_l,-1).width; hist_gt_w = lx_text_extents_8(fi,str_hist_r,-1).width; hist_rowh = baselineskip; i = margin + smallpiecesize + margin; if (i > hist_rowh) hist_rowh = i; hist_rowh += (hist_rowh + 1) >> 1; hist_winw = margin + histmarksize + margin + hist_nw + hist_sw + hist_lt_w + (5 * smallpiecesize) + hist_gt_w + margin; hist_winh = histmax * hist_rowh; hist_bl = ((hist_rowh - baselineskip) / 2) + fi->font_ascent; hist_spy = (hist_rowh - smallpiecesize) / 2; hist_yoff = 0; hist_updated = 0; set_pos_y(posroot,0,0); clickmaxtime = atoi(clickmaxtimestr); clickmintime = atoi(clickmintimestr); dragdelta2 = atoi(dragdeltastr); dragdelta2 *= dragdelta2; curboard = malloc(boardpoints); } static void have_font(void *fitmpv) { fi = *(LX_FONTINFO **)fitmpv; free(fitmpv); setup_numbers(); setup_windows(); } static void colour_setup_step3(COLSSTATE *); // forward static void colour_alloc_cb(void *sv) { COLSTATE *s; COLSSTATE *ss; LX_XID fid; LX_FONTINFO **fitmp; s = sv; ss = s->ss; if (s != ss->u.nexts) abort(); if (! s->success) { if (! defcmap) abort(); wincmap = lx_CopyColormapAndFree(disp,wincmap); defcmap = 0; } else { ss->u.nexts = s->link; } if (ss->u.nexts) { colour_setup_step3(ss); return; } while ((s = ss->list)) { ss->list = s->link; free(s); } free(ss); fid = fontstr ? lx_OpenFont(disp,fontstr) : lx_SGC_GC(disp,wingc); fitmp = malloc(sizeof(LX_FONTINFO *)); lx_op_callback(lx_QueryFont(disp,lx_SGC_GC(disp,wingc),fitmp),&have_font,fitmp,0); } static int colour_alloc_err(LX_CONN *xc, LX_OP *op, const LX_X_ERR *e, void *sv) { COLSTATE *s; (void)op; if (xc != disp) abort(); if (! defcmap) return(0); s = sv; if (e->type != LX_XE_Alloc) { printf("colour_alloc_err for %s, unexpected\n",s->tag); return(0); } printf("colour_alloc_err for %s, expected\n",s->tag); s->success = 0; return(1); } static void colour_setup_step3(COLSSTATE *ss) { COLSTATE *s; s = ss->u.nexts; s->op = lx_AllocColor(disp,wincmap,s->c->c.r,s->c->c.g,s->c->c.b,&s->c->pix,&s->c->actc.r,&s->c->actc.g,&s->c->actc.b); // XXX something with CopyColormapAndFree s->success = 1; lx_op_callback(s->op,&colour_alloc_cb,s,0); lx_op_err(s->op,&colour_alloc_err,s); lx_op_set_udata(s->op,s); } static int colour_setup_step2(void *ssv) { COLSSTATE *ss; ss = ssv; if (ss->u.pending) return(AIO_BLOCK_NIL); aio_remove_block(ss->id); ss->id = AIO_NOID; ss->u.nexts = ss->list; colour_setup_step3(ss); return(AIO_BLOCK_LOOP); } static void colour_parse_cb(void *sv) { COLSTATE *s; s = sv; if (! s->success) { fprintf(stderr,"%s: %s: bad colour name\n",__progname,s->str); exit(1); } s->ss->u.pending --; } static void setup_colour(const char *str, COL *c, COLSSTATE *ss, const char *tag) { COLSTATE *s; s = malloc(sizeof(COLSTATE)); s->tag = tag; s->link = ss->list; ss->list = s; s->ss = ss; s->str = str; s->c = c; ss->u.pending ++; s->op = lx_lookup_color_rgb(disp,wincmap,str,-1,&c->c,&s->success); lx_op_callback(s->op,&colour_parse_cb,s,0); } static void setup_colours(void) { COLSSTATE *s; s = malloc(sizeof(COLSSTATE)); s->list = 0; s->u.pending = 0; setup_colour(fgcstr,&fgcol,s,"foreground"); setup_colour(bgcstr,&bgcol,s,"background"); setup_colour(bordercstr,&bordercol,s,"border"); setup_colour(boardcstr,&boardcol,s,"board"); setup_colour(linescstr,&linescol,s,"lines"); setup_colour(blackcstr,&blackcol,s,"black pieces"); setup_colour(whitecstr,&whitecol,s,"white pieces"); setup_colour(histbgcolstr_odd,&hbgcol_odd,s,"odd history background"); setup_colour(histbgcolstr_even,&hbgcol_even,s,"even history background"); setup_colour(hscrbgstr,&hscr_bg,s,"history scrollbar background"); setup_colour(hscrfgstr,&hscr_fg,s,"history scrollbar foreground"); s->id = aio_add_block(&colour_setup_step2,s); } static void X_step_2(void *arg __attribute__((__unused__))) { setup_visual(); scr = vis->screen; scrwidth = lx_root_width(disp,scr); scrheight = lx_root_height(disp,scr); depth = vis->depth; rootwin = lx_root(disp,scr); setup_db(); load_db(); bitgc = setup_gc(1); wingc = setup_gc(depth); setup_cmap(); setup_colours(); } static void hist_lr_press(HISTWIN *hw) { POS *p; if (hw->win == hw->pos->l_win) { if (hw->pos->curkid < 1) abort(); hw->pos->curkid --; } else if (hw->win == hw->pos->r_win) { if (hw->pos->curkid >= hw->pos->nkids-1) abort(); hw->pos->curkid ++; } else { abort(); } p = hw->pos->kids[hw->pos->curkid]; if (curpos != p) set_curpos(p); hist_updated = 0; maybe_redraw_hist(); scroll_histwin(); } static void button_press(const LX_EVENT_ButtonPress *e) { HISTWIN c; HISTWIN *hw; if (e->eventw == histiwin) { scroller_press(&scroll_hist,e); return; } if (e->eventw == histscrollwin) { scroller_press(&scroll_hist_thumb,e); return; } c.win = e->eventw; hw = avl_find(hist_lr_wins,&c); if (hw) { hist_lr_press(hw); return; } } static void button_release(const LX_EVENT_ButtonRelease *e) { if (e->eventw == histiwin) { scroller_release(&scroll_hist,e); return; } if (e->eventw == histscrollwin) { scroller_release(&scroll_hist_thumb,e); return; } } static void motion_notify(const LX_EVENT_MotionNotify *e) { if (e->eventw == histiwin) { scroller_motion(&scroll_hist); return; } if (e->eventw == histscrollwin) { scroller_motion(&scroll_hist_thumb); return; } } static void configure_notify(const LX_EVENT_ConfigureNotify *e) { if (e->window == topwin) { if ((e->w != top_w) || (e->h != top_h)) { new_w = e->w; new_h = e->h; need_layout = 1; } } else if (e->window == histwin) { cod_scrolled_to(&scroll_hist.cod,e->y); } } static void key_press(const LX_EVENT_KeyPress *e) { LX_KEYSYM ks; int n; POS *p; ks = lx_kbmap_keysym(disp,e->keycode,e->state); n = (e->state & LX_EVS_Shift) ? 10 : 1; switch (ks) { case LX_KEYSYM_Up: if (curpos->up) { p = curpos; for (;(n>0)&&p->up;n--) p = p->up; curpos = p; keyboard_curpos_move(); } break; case LX_KEYSYM_Down: if (curpos->nkids > 0) { p = curpos; for (;(n>0)&&(p->nkids>0);n--) p = p->kids[curpos->curkid]; curpos = p; keyboard_curpos_move(); } break; case LX_KEYSYM_Left: if (curpos->up && (curpos->up->curkid > 0)) { curpos->up->curkid -= n; if (curpos->up->curkid < 0) curpos->up->curkid = 0; set_curpos(curpos->up->kids[curpos->up->curkid]); keyboard_curpos_move(); } break; case LX_KEYSYM_Right: if (curpos->up && (curpos->up->curkid < curpos->up->nkids-1)) { curpos->up->curkid += n; if (curpos->up->curkid >= curpos->up->nkids) curpos->up->curkid = curpos->up->nkids - 1; set_curpos(curpos->up->kids[curpos->up->curkid]); keyboard_curpos_move(); } break; } } static void handle_xevent(LX_CONN *d, const LX_EVENT *e) { if (d != disp) abort(); switch (e->type) { case LX_EV_KeyPress: key_press(&e->u.KeyPress); break; case LX_EV_KeyRelease: break; case LX_EV_ButtonPress: button_press(&e->u.ButtonPress); break; case LX_EV_ButtonRelease: button_release(&e->u.ButtonRelease); break; case LX_EV_MotionNotify: motion_notify(&e->u.MotionNotify); break; case LX_EV_EnterNotify: printf("Got EnterNotify: %d,%d (root %d,%d) mode ", e->u.EnterNotify.eventx,e->u.EnterNotify.eventy, e->u.EnterNotify.rootx,e->u.EnterNotify.rooty); switch (e->u.EnterNotify.detail) { case LX_ENTERLEAVEDETAIL_Ancestor: printf("Ancestor"); break; case LX_ENTERLEAVEDETAIL_Virtual: printf("Virtual"); break; case LX_ENTERLEAVEDETAIL_Inferior: printf("Inferior"); break; case LX_ENTERLEAVEDETAIL_Nonlinear: printf("Nonlinear"); break; case LX_ENTERLEAVEDETAIL_NonlinearVirtual: printf("NonlinearVirtual"); break; default: printf("?%d",(int)e->u.EnterNotify.detail); break; } printf("\n"); break; case LX_EV_LeaveNotify: printf("Got LeaveNotify: %d,%d (root %d,%d) mode ", e->u.LeaveNotify.eventx,e->u.LeaveNotify.eventy, e->u.LeaveNotify.rootx,e->u.LeaveNotify.rooty); switch (e->u.LeaveNotify.detail) { case LX_ENTERLEAVEDETAIL_Ancestor: printf("Ancestor"); break; case LX_ENTERLEAVEDETAIL_Virtual: printf("Virtual"); break; case LX_ENTERLEAVEDETAIL_Inferior: printf("Inferior"); break; case LX_ENTERLEAVEDETAIL_Nonlinear: printf("Nonlinear"); break; case LX_ENTERLEAVEDETAIL_NonlinearVirtual: printf("NonlinearVirtual"); break; default: printf("?%d",(int)e->u.LeaveNotify.detail); break; } printf("\n"); break; case LX_EV_Expose: // if (e->u.Expose.count == 0) expose(e->u.Expose.eventw); break; case LX_EV_NoExposure: break; case LX_EV_MapNotify: break; case LX_EV_ConfigureNotify: configure_notify(&e->u.ConfigureNotify); break; case LX_EV_MappingNotify: lx_kbmap_update(disp,&e->u.MappingNotify); break; default: printf("Got event type %d\n",e->type); break; } } static void X_open_done(LX_CONN *conn, void *arg __attribute__((__unused__))) { disp = conn; lx_err_set_X(disp,&err_X); lx_err_set_lib(disp,&err_lib); lx_set_event_handler(disp,&handle_xevent); lx_op_drop(lx_kbmap_setup(disp)); aio_call_once(&X_step_2,0); } static void start_X(void) { lx_open(displayname,0,&X_open_done,0,0,/*LX_OPENF_DEBUG*/0); } #include static void trace_setup(void) { int fd; if (&aio__trace_setup) { fd = open("z.trace",O_RDWR,0); if (fd < 0) return; aio__trace_setup(fd); close(fd); } } static int equal_ch_c(const CH *ch, const char *c, int l) { for (;l>0;l--,ch++,c++) if (ch->c != *c) return(0); return(1); } static const VALUE *get_prop(const NODE *n, const char *name) { int nl; int px; PROP *p; nl = strlen(name); for (px=n->nprops-1;px>=0;px--) { p = n->props[px]; if ((p->name.l == nl) && equal_ch_c(p->name.s,name,nl)) { return(&p->val); } } return(0); } static void add_pt_chg(POS *p, XY pt, unsigned char bv, const char *col, const char *what, const char *prop) { if ((pt.x >= boardsize) || (pt.y >= boardsize)) { printf("Off-board point for %s %s (%s)\n",col,what,prop); exit(1); } p->chg.chgs = realloc(p->chg.chgs,(p->chg.nchgs+1)*sizeof(CHG)); p->chg.chgs[p->chg.nchgs++] = (CHG){.x=pt.x,.y=pt.y,.v=bv}; } static void apply_setup(POS *p, const VALUE *v, const char *prop, const char *col, unsigned char bv) { int i; if (v->t != VT_LIST_POINT) { printf("Unexpected data type for %s setup placement (%s)\n",col,prop); exit(1); } for (i=v->u.ptlist.n-1;i>=0;i--) { add_pt_chg(p,v->u.ptlist.v[i],bv,col,"setup placement",prop); } } static void make_move(POS *p, const VALUE *v, const char *prop, const char *col, unsigned char bv) { if (v->t != VT_POINT) { printf("Unexpected data type for %s move (%s)\n",col,prop); exit(1); } if ((p->mover != C_E) && (p->mover != bv)) abort(); p->mover = bv; add_pt_chg(p,v->u.pt,bv,col,"move",prop); } static POS *new_pos(void) { POS *p; p = malloc(sizeof(POS)); p->up = 0; p->chg.nchgs = 0; p->chg.chgs = 0; p->nkids = 0; p->kids = 0; p->board = 0; p->bcap = 0; p->wcap = 0; p->mover = C_E; p->yinx = -1; p->ypix = -1; p->l_win = LX_WINDOW_None; p->r_win = LX_WINDOW_None; return(p); } static POS *pos_for(TREE *t) { int x; POS *p; POS *q; const VALUE *v[4]; p = new_pos(); p->nkids = t->ntrees; p->kids = p->nkids ? malloc(p->nkids*sizeof(POS *)) : 0; for (x=0;xntrees;x++) { q = pos_for(t->trees[x]); q->up = p; p->kids[x] = q; } for (x=t->nnodes-1;x>=0;x--) { v[0] = get_prop(t->nodes[x],"B"); v[1] = get_prop(t->nodes[x],"W"); v[2] = get_prop(t->nodes[x],"AB"); v[3] = get_prop(t->nodes[x],"AW"); if ((v[2] || v[3]) && (v[0] || v[1])) { printf("Node has both setup (AB/AW) and move (B/W) properties\n"); exit(1); } if (v[0] && v[1]) { printf("Node has both B and W moves\n"); } if (v[0]) make_move(p,v[0],"B","black",C_B); if (v[1]) make_move(p,v[1],"W","white",C_W); if (v[2]) apply_setup(p,v[2],"AB","black",C_B); if (v[3]) apply_setup(p,v[3],"AW","white",C_W); if (x) { q = new_pos(); p->up = q; q->nkids = 1; q->kids = malloc(sizeof(POS *)); q->kids[0] = p; p = q; } } return(p); } static unsigned char *copy_board(const unsigned char *b) { unsigned char *s; s = malloc(boardpoints); bcopy(b,s,boardpoints); return(s); } static int capture_fill(void) { static const int ddx[4] = { -1, 1, 0, 0 }; static const int ddy[4] = { 0, 0, -1, 1 }; int d; XY p; int nx; int ny; int c; while (cs.npend > 0) { p = cs.pend[--cs.npend]; c = CXY(p.x,p.y); if (cs.pos->board[c] != cs.col) return(0); for (d=4-1;d>=0;d--) { nx = ddx[d] + (int)p.x; ny = ddy[d] + (int)p.y; if ((nx < 0) | (ny < 0) || (nx >= boardsize) || (ny >= boardsize)) continue; c = CXY(nx,ny); if (cs.pos->board[c] == C_E) return(1); if ((cs.pos->board[c] == cs.col) && !cs.flag[c]) { cs.flag[c] = 1; cs.pend[cs.npend++] = (XY){.x=nx,.y=ny}; } } } return(0); } static void maybe_capture(POS *p, int x, int y) { int i; int nc; bzero(cs.flag,boardpoints); cs.flag[CXY(x,y)] = 1; cs.pend[0] = (XY){.x=x,.y=y}; cs.npend = 1; cs.col = p->board[CXY(x,y)]; cs.pos = p; if (capture_fill()) return; nc = 0; for (i=boardpoints-1;i>=0;i--) { if (cs.flag[i]) { p->board[i] = C_E; nc ++; } } switch (cs.col) { case C_B: p->bcap += nc; break; case C_W: p->wcap += nc; break; default: abort(); break; } } static void apply_changes(POS *p, const CHANGES *chg) { int i; CHG *c; unsigned char other; for (i=chg->nchgs-1;i>=0;i--) { c = chg->chgs + i; p->board[CXY(c->x,c->y)] = c->v; } if (chg->nchgs == 1) { switch (c->v) { case C_W: other = C_B; break; case C_B: other = C_W; break; default: return; break; } if ((c->x > 0) && (p->board[CXY(c->x-1,c->y)] == other)) maybe_capture(p,c->x-1,c->y); if ((c->y > 0) && (p->board[CXY(c->x,c->y-1)] == other)) maybe_capture(p,c->x,c->y-1); if ((c->x < boardsize-1) && (p->board[CXY(c->x+1,c->y)] == other)) maybe_capture(p,c->x+1,c->y); if ((c->y < boardsize-1) && (p->board[CXY(c->x,c->y+1)] == other)) maybe_capture(p,c->x,c->y+1); maybe_capture(p,c->x,c->y); } } static void build_boards_(POS *p) { int x; p->board = copy_board(p->up->board); p->bcap = p->up->bcap; p->wcap = p->up->wcap; apply_changes(p,&p->chg); for (x=p->nkids-1;x>=0;x--) { build_boards_(p->kids[x]); } } static void build_boards(POS *p) { unsigned char *board; int x; board = malloc(boardpoints); memset(board,C_E,boardpoints); p->board = board; p->bcap = 0; p->wcap = 0; apply_changes(p,&p->chg); for (x=p->nkids-1;x>=0;x--) { build_boards_(p->kids[x]); } } static int pos_tree_depth(POS *p) { int n; int m; int i; int d; n = 1; while (p->nkids == 1) { n ++; p = p->kids[0]; } m = 0; for (i=p->nkids-1;i>=0;i--) { d = pos_tree_depth(p->kids[i]); if (d > m) m = d; } return(m+n); } static void init_curkids(POS *p) { int i; while (1) { p->curkid = 0; if (p->nkids != 1) break; p = p->kids[0]; } for (i=p->nkids-1;i>=0;i--) init_curkids(p->kids[i]); } static void load_tree(TREE *t) { NODE *n0; const VALUE *v; int i; if (t->nnodes < 1) { printf("Root of game tree has no nodes!\n"); exit(1); } n0 = t->nodes[0]; v = get_prop(n0,"FF"); if (!v || (v->t != VT_NUMBER) || (v->u.num != 4)) { printf("Unsupported file formatll\n"); exit(1); } v = get_prop(n0,"GM"); if (!v || (v->t != VT_NUMBER) || (v->u.num != 1)) { printf("Game isn't go\n"); exit(1); } v = get_prop(n0,"SZ"); if (! v) { printf("No board size\n"); exit(1); } if (v->t != VT_NUMBER_OR_COMPOSE_NUMBER_NUMBER) { printf("Unexpected data type for board size\n"); exit(1); } if (v->u.num12.n2 >= 0) { printf("Can't handle non-square boards at present\n"); exit(1); } do <"found"> { for (i=NSTARGRID-1;i>=0;i--) { if (v->u.num12.n == stargrid[i].n) break <"found">; } printf("Board size %d not supported\n",v->u.num12.n); exit(1); } while (0); boardsize = v->u.num12.n; boardpoints = boardsize * boardsize; cs.flag = malloc(boardpoints); cs.pend = malloc(boardpoints*sizeof(XY)); starpattern = &stargrid[i]; posroot = pos_for(t); posroot->up = 0; build_boards(posroot); init_curkids(posroot); histmax = pos_tree_depth(posroot); histcur = trace_len(); histcurpix = histcur * hist_rowh; curpos = posroot; while (curpos->nkids > 0) curpos = curpos->kids[0]; } int main(int ac, char **av) { trace_setup(); aio_poll_init(); saveargv(ac,av); handleargs(ac,av); load_sgf(); if (dumpfile) { dump_trees(); return(0); } if (ntrees < 1) { printf("No games in SGF file\n"); exit(1); } load_tree(trees[0]); start_X(); aio_event_loop(); return(1); }