#include #include #include #include #include extern const char *__progname; #include #include #include #include #include #include #include static const char *defaults = "\ *Background: black\n\ *Foreground: white\n\ *Player.1.Colour: #ff0000\n\ *Player.2.Colour: #00c800\n\ *Player.3.Colour: #c8c800\n\ *Player.4.Colour: #0000ff\n\ *Player.1.Background: #800000\n\ *Player.2.Background: #006400\n\ *Player.3.Background: #646400\n\ *Player.4.Background: #000080\n\ *Player.1.Name: red\n\ *Player.2.Name: green\n\ *Player.3.Name: yellow\n\ *Player.4.Name: blue\n\ *Board.Colour.1: #707070\n\ *Board.Colour.2: #808080\n\ *Size.1.Board.Mag: 30\n\ *Size.1.Hand.Mag: 6\n\ *Size.2.Board.Mag: 30\n\ *Size.2.Hand.Mag: 6\n\ *Size.3.Board.Mag: 30\n\ *Size.3.Hand.Mag: 6\n\ *Size.4.Board.Mag: 30\n\ *Size.4.Hand.Mag: 6\n\ *Size.5.Board.Mag: 30\n\ *Size.5.Hand.Mag: 6\n\ *Size.6.Board.Mag: 15\n\ *Size.6.Hand.Mag: 4\n\ *Size.7.Board.Mag: 8\n\ *Size.7.Hand.Mag: 3\n\ *Size.8.Board.Mag: 6\n\ *Size.8.Hand.Mag: 1\n\ *Piece.Mine.Colour: #fff\n\ *Piece.Other.Colour: #888\n\ *Border.Width: 1\n\ "; typedef struct shape SHAPE; typedef struct gplayer GPLAYER; typedef struct iplayer IPLAYER; typedef unsigned char XFORM; #define XF_NEGX 1 #define XF_NEGY 2 #define XF_SWAP 4 #define XF___ 0 #define XF__X (XF_NEGX) #define XF_Y_ (XF_NEGY) #define XF_YX (XF_NEGY|XF_NEGX) #define XFS__ (XF_SWAP) #define XFS_X (XF_SWAP|XF_NEGX) #define XFSY_ (XF_SWAP|XF_NEGY) #define XFSYX (XF_SWAP|XF_NEGY|XF_NEGX) #define XF_R180 XF_YX /* rotate 180 */ #define XF_CW XFS_X /* rotate CW */ #define XF_CCW XFSY_ /* rotate CCW */ #define XF_OSWAP XFSYX /* swap about / */ #define XF_MAX 7 typedef struct pcplace PCPLACE; struct pcplace { int n; int x; int y; XFORM xf; Window win; } ; struct iplayer { int n; const char *display; int synch; char *xrmstr; char *background; char *foreground; char *pcolstr[4]; char *pbgcolstr[4]; char *bdcolstr[2]; char *borderwstr; char *bordercstr; char *boardmagstr; char *handmagstr; char *pccolstr_mine; char *pccolstr_other; char *visualstr; XrmDatabase db; Display *disp; XVisualInfo visinfo; Screen *scr; int root_w; int root_h; Window rootwin; int handmag; int boardmag; int borderwidth; XColor bordercolour; int defcmap; Colormap wincmap; XColor bgcolour; XColor fgcolour; XColor pcolours[4]; XColor pbgcolours[4]; XColor bdcolour[2]; XColor pccol_mine; XColor pccol_other; Window topwin; Window boardwin; Window boardinput; Window boxwin; Window boardghost; int ghostox; int ghostoy; SHAPE *bdamage; Window playerwin[4]; Window turnwin[4]; GC wingc; GC bitgc; PCPLACE *places[4]; char *pname[4]; Cursor arrow_small; Cursor arrow_large; Cursor (*pccurs)[8]; } ; struct gplayer { int n; char *pcavail; int ipx; const char *colour; } ; struct shape { SHAPE *link; int x; int y; char *map; } ; #define MAP(sh,ix,iy) ((sh)->map[((iy)*(sh)->x)+(ix)]) static int argc; static char **argv; static int (*oldioerr)(Display *); static int (*olderr)(Display *, XErrorEvent *); /* xf_compose[A][B] is result for A then B */ static const XFORM xf_compose[8][8] /* ___ __X _Y_ _YX S__ S_X SY_ SYX < 2nd 1st v */ = { { XF___, XF__X, XF_Y_, XF_YX, XFS__, XFS_X, XFSY_, XFSYX }, /* ___ */ { XF__X, XF___, XF_YX, XF_Y_, XFSY_, XFSYX, XFS__, XFS_X }, /* __X */ { XF_Y_, XF_YX, XF___, XF__X, XFS_X, XFS__, XFSYX, XFSY_ }, /* _Y_ */ { XF_YX, XF_Y_, XF__X, XF___, XFSYX, XFSY_, XFS_X, XFS__ }, /* _YX */ { XFS__, XFS_X, XFSY_, XFSYX, XF___, XF__X, XF_Y_, XF_YX }, /* S__ */ { XFS_X, XFS__, XFSYX, XFSY_, XF_Y_, XF_YX, XF___, XF__X }, /* S_X */ { XFSY_, XFSYX, XFS__, XFS_X, XF__X, XF___, XF_YX, XF_Y_ }, /* SY_ */ { XFSYX, XFSY_, XFS_X, XFS__, XF_YX, XF_Y_, XF__X, XF___ }, /* SYX */ }; static int size = 5; static int nplayers = 1; static IPLAYER iplayers[4]; static GPLAYER gplayers[4]; static const char *resname = "blokus"; static const char *resclass = "Game"; static SHAPE **shapes; static SHAPE *cur; static SHAPE *next; static int totalsquares; static int totalshapes; static SHAPE *board; #define B_EMPTY 6 static int boardpc_x; static int boardpc_y; static int turn; static int turn3; static int carry; static int firstturn; static void *deconst(const volatile void *in) { return((((const volatile char *)in)-(const volatile char *)0)+(char *)0); } static SHAPE *newshape(int x, int y) { SHAPE *p; p = malloc(sizeof(SHAPE)+(x*y)); p->map = (void *) (p+1); p->x = x; p->y = y; return(p); } static void copymap(SHAPE *f, SHAPE *t, int x1, int x2, int y1, int y2, int xo, int yo) { int x; int y; for (y=y1;yy;y>0;y--) { for (x=n->x;x>0;x--) { if (n->map[no] != p->map[o]) return(0); no ++; o += xi; } o += yi; } return(1); } static void save_or_free(SHAPE *n) { SHAPE *p; do <"free"> { for (p=next;p;p=p->link) { if ( ( (p->x == n->x) && (p->y == n->y) && ( mapmatch(n,p,0,1,0) || mapmatch(n,p,p->x-1,-1,p->x*2) || mapmatch(n,p,(p->y-1)*p->x,1,-p->x*2) || mapmatch(n,p,(p->x*p->y)-1,-1,0) ) ) || ( (p->x == n->y) && (p->y == n->x) && ( mapmatch(n,p,0,p->x,1-(p->x*p->y)) || mapmatch(n,p,p->x-1,p->x,-(p->x*p->y)-1) || mapmatch(n,p,(p->y-1)*p->x,-p->x,(p->x*p->y)+1) || mapmatch(n,p,(p->y*p->x)-1,-p->x,(p->x*p->y)-1) ) ) ) { break <"free">; } } n->link = next; next = n; return; } while (0); free(n); } static void add1square(SHAPE *p) { SHAPE *n; int i; int j; for (i=p->y-1;i>=0;i--) { if (MAP(p,0,i)) { n = newshape(p->x+1,p->y); copymap(p,n,0,p->x,0,p->y,1,0); for (j=n->y-1;j>=0;j--) MAP(n,0,j) = (i==j); save_or_free(n); } if (MAP(p,p->x-1,i)) { n = newshape(p->x+1,p->y); copymap(p,n,0,p->x,0,p->y,0,0); for (j=n->y-1;j>=0;j--) MAP(n,p->x,j) = (i==j); save_or_free(n); } } for (i=p->x-1;i>=0;i--) { if (MAP(p,i,0)) { n = newshape(p->x,p->y+1); copymap(p,n,0,p->x,0,p->y,0,1); for (j=n->x-1;j>=0;j--) MAP(n,j,0) = (i==j); save_or_free(n); } if (MAP(p,i,p->y-1)) { n = newshape(p->x,p->y+1); copymap(p,n,0,p->x,0,p->y,0,0); for (j=n->x-1;j>=0;j--) MAP(n,j,p->y) = (i==j); save_or_free(n); } } for (i=p->y-1;i>=0;i--) { for (j=p->x-1;j>=0;j--) { if (! MAP(p,j,i)) { if ( ((j > 0) && MAP(p,j-1,i)) || ((i > 0) && MAP(p,j,i-1)) || ((j < p->x-1) && MAP(p,j+1,i)) || ((i < p->y-1) && MAP(p,j,i+1)) ) { n = newshape(p->x,p->y); copymap(p,n,0,p->x,0,p->y,0,0); MAP(n,j,i) = 1; save_or_free(n); } } } } } static void init_preargs(void) { int pno; IPLAYER *p; int i; for (pno=4-1;pno>=0;pno--) { p = &iplayers[pno]; p->n = pno; p->display = 0; p->synch = 0; p->xrmstr = 0; p->background = 0; p->foreground = 0; for (i=0;i<4;i++) { p->pcolstr[i] = 0; p->pbgcolstr[i] = 0; } p->bdcolstr[0] = 0; p->bdcolstr[1] = 0; p->borderwstr = 0; p->bordercstr = 0; p->boardmagstr = 0; p->handmagstr = 0; p->pccolstr_mine = 0; p->pccolstr_other = 0; p->visualstr = 0; gplayers[pno].n = pno; } } static int handleargs(int ac, char **av) { int skip; int errs; IPLAYER *p; argc = ac; argv = av; skip = 0; errs = 0; p = &iplayers[0]; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: stray argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-n")) { int n; WANTARG(); n = atoi(av[skip]); if ((n < 1) || (n > 4)) { fprintf(stderr,"%s: -n argument must be 1..4\n",__progname); errs ++; } else { nplayers = n; } continue; } if (!strcmp(*av,"-size")) { WANTARG(); size = atoi(av[skip]); continue; } if (!strcmp(*av,"-resname")) { WANTARG(); resname = av[skip]; continue; } if (!strcmp(*av,"-resclass")) { WANTARG(); resclass = av[skip]; continue; } if (!strcmp(*av,"-display1") || !strcmp(*av,"-display")) { WANTARG(); p = &iplayers[0]; p->display = av[skip]; continue; } if (!strcmp(*av,"-display2")) { WANTARG(); p = &iplayers[1]; p->display = av[skip]; continue; } if (!strcmp(*av,"-display3")) { WANTARG(); p = &iplayers[2]; p->display = av[skip]; continue; } if (!strcmp(*av,"-display4")) { WANTARG(); p = &iplayers[3]; p->display = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); p->visualstr = av[skip]; continue; } if (!strcmp(*av,"-sync") || !strcmp(*av,"-synch")) { p->synch = 1; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); p->background = av[skip]; continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); p->foreground = av[skip]; continue; } if (!strcmp(*av,"-pcolour1") || !strcmp(*av,"-pc1") || !strcmp(*av,"-pcolor1")) { WANTARG(); p->pcolstr[0] = av[skip]; continue; } if (!strcmp(*av,"-pcolour2") || !strcmp(*av,"-pc2") || !strcmp(*av,"-pcolor2")) { WANTARG(); p->pcolstr[1] = av[skip]; continue; } if (!strcmp(*av,"-pcolour3") || !strcmp(*av,"-pc3") || !strcmp(*av,"-pcolor3")) { WANTARG(); p->pcolstr[2] = av[skip]; continue; } if (!strcmp(*av,"-pcolour4") || !strcmp(*av,"-pc4") || !strcmp(*av,"-pcolor4")) { WANTARG(); p->pcolstr[3] = av[skip]; continue; } if (!strcmp(*av,"-pbgcolour1") || !strcmp(*av,"-pbg1") || !strcmp(*av,"-pbgcolor1")) { WANTARG(); p->pbgcolstr[0] = av[skip]; continue; } if (!strcmp(*av,"-pbgcolour2") || !strcmp(*av,"-pbg2") || !strcmp(*av,"-pbgcolor2")) { WANTARG(); p->pbgcolstr[1] = av[skip]; continue; } if (!strcmp(*av,"-pbgcolour3") || !strcmp(*av,"-pbg3") || !strcmp(*av,"-pbgcolor3")) { WANTARG(); p->pbgcolstr[2] = av[skip]; continue; } if (!strcmp(*av,"-pbgcolour4") || !strcmp(*av,"-pbg4") || !strcmp(*av,"-pbgcolor4")) { WANTARG(); p->pbgcolstr[3] = av[skip]; continue; } if (!strcmp(*av,"-name1")) { WANTARG(); p->pname[0] = av[skip]; continue; } if (!strcmp(*av,"-name2")) { WANTARG(); p->pname[1] = av[skip]; continue; } if (!strcmp(*av,"-name3")) { WANTARG(); p->pname[2] = av[skip]; continue; } if (!strcmp(*av,"-name4")) { WANTARG(); p->pname[3] = av[skip]; continue; } if (!strcmp(*av,"-boardcolour1") || !strcmp(*av,"-bc1") || !strcmp(*av,"-boardcolor1")) { WANTARG(); p->bdcolstr[0] = av[skip]; continue; } if (!strcmp(*av,"-boardcolour2") || !strcmp(*av,"-bc2") || !strcmp(*av,"-boardcolor2")) { WANTARG(); p->bdcolstr[1] = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); p->borderwstr = av[skip]; continue; } if (!strcmp(*av,"-bordercolour") || !strcmp(*av,"-bc") || !strcmp(*av,"-bordercolor")) { WANTARG(); p->bordercstr = av[skip]; continue; } if ( !strcmp(*av,"-pc-colour-mine") || !strcmp(*av,"-pc-colour-my") || !strcmp(*av,"-pc-color-mine") || !strcmp(*av,"-pc-color-my") ) { WANTARG(); p->pccolstr_mine = av[skip]; continue; } if ( !strcmp(*av,"-pc-colour-other") || !strcmp(*av,"-pc-colour-other") ) { WANTARG(); p->pccolstr_other = av[skip]; continue; } if (!strcmp(*av,"-boardmag")) { WANTARG(); p->boardmagstr = av[skip]; continue; } if (!strcmp(*av,"-handmag")) { WANTARG(); p->handmagstr = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } return(errs); } static int isqrt(int v) { int i; int j; i = v; j = 1; while ((i != j) && (i+1 != j)) { i = j; j = (i + (v/i)) / 2; } return(i); } static void gen_shapes(void) { SHAPE *s; SHAPE *all; int curn; int i; if (size < 1) { fprintf(stderr,"%s: size must be > 0\n",__progname); exit(1); } cur = newshape(1,1); cur->link = 0; cur->map[0] = 1; all = 0; totalsquares = 0; totalshapes = 0; for (curn=1;curnlink; add1square(s); s->link = all; all = s; totalsquares += curn; totalshapes ++; } cur = next; } while (cur) { s = cur; cur = s->link; s->link = all; all = s; totalsquares += curn; totalshapes ++; } shapes = malloc(totalshapes*sizeof(SHAPE *)); for (i=0;all;i++) { shapes[i] = all; all = all->link; } if (i != totalshapes) abort(); i = isqrt((totalsquares*4)-1); board = newshape(i+2,i+2); memset(board->map,B_EMPTY,board->x*board->y); } static char *get_default_value(IPLAYER *p, const char *name, const char *class) { char *type; XrmValue value; int nl; int cl; static int nblen = 0; static char *nbuf = 0; static int cblen = 0; static char *cbuf = 0; static int rnlen = -1; static int rclen; if (rnlen < 0) { rnlen = strlen(resname); rclen = strlen(resclass); } nl = strlen(name); cl = strlen(class); if (nl+1+rnlen+1 > nblen) { free(nbuf); nblen = nl + 1 + rnlen + 1; nbuf = malloc(nblen); } if (cl+1+rclen+1 > cblen) { free(cbuf); cblen = cl + 1 + rclen + 1; cbuf = malloc(cblen); } sprintf(nbuf,"%s.%s",resname,name); sprintf(cbuf,"%s.%s",resclass,class); if (XrmGetResource(p->db,nbuf,cbuf,&type,&value) == False) return(0); return(value.addr); } static void resource_string(IPLAYER *p, char **sp, const char *nm, const char *cl) { if (*sp) return; *sp = get_default_value(p,nm,cl); } static void setup_colour(IPLAYER *p, XColor *c, char *s) { if (XParseColor(p->disp,p->wincmap,s,c) == 0) { fprintf(stderr,"%s: bad colour `%s'\n",__progname,s); exit(1); } while (1) { if (XAllocColor(p->disp,p->wincmap,c) == 0) { if (! p->defcmap) { fprintf(stderr,"%s: can't allocate colourmap cell for colour `%s'\n",__progname,s); exit(1); } p->wincmap = XCopyColormapAndFree(p->disp,p->wincmap); p->defcmap = 0; continue; } break; } } static void setup_db(IPLAYER *p) { XrmDatabase db; XrmDatabase db2; char *str; char *home; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(p->disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } else if (p->n == 0) { home = getenv("HOME"); if (home) { asprintf(&str,"%s/.Xdefaults",home); db2 = XrmGetFileDatabase(str); if (db2) XrmMergeDatabases(db2,&db); free(str); } } if (p->xrmstr) { db2 = XrmGetStringDatabase(p->xrmstr); XrmMergeDatabases(db2,&db); } p->db = db; } static void setup_colours(IPLAYER *p) { int i; if (p->visinfo.visual == XDefaultVisualOfScreen(p->scr)) { p->wincmap = XDefaultColormapOfScreen(p->scr); p->defcmap = 1; } else { p->wincmap = XCreateColormap(p->disp,p->rootwin,p->visinfo.visual,AllocNone); p->defcmap = 0; } setup_colour(p,&p->bgcolour,p->background); setup_colour(p,&p->fgcolour,p->foreground); for (i=0;i<4;i++) { setup_colour(p,&p->pcolours[i],p->pcolstr[i]); setup_colour(p,&p->pbgcolours[i],p->pbgcolstr[i]); } setup_colour(p,&p->bdcolour[0],p->bdcolstr[0]); setup_colour(p,&p->bdcolour[1],p->bdcolstr[1]); setup_colour(p,&p->bordercolour,p->bordercstr); setup_colour(p,&p->pccol_mine,p->pccolstr_mine); setup_colour(p,&p->pccol_other,p->pccolstr_other); } static void setup_windows(IPLAYER *p) { int pcrows; int pccols; int x; int y; int w; int h; int i; int pcw; int pch; int pcsz; unsigned long int attrmask; unsigned long int gcvalmask; XGCValues gcval; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints *normal_hints; XWMHints *wm_hints; XClassHint *class_hints; Pixmap pm; XPoint pts[4][6]; PCPLACE *pl; struct { int x0; int y0; int rincx; int rincy; int cincx; int cincy; } pclayout[4]; pcsz = (size+1) * p->handmag; pccols = (board->x * p->boardmag) / pcsz; i = (isqrt(((pccols-1)*(pccols-1))+(4*totalshapes)) + 1 - pccols) / 2; while ((i * (i + pccols - 1)) < totalshapes) i ++; pcrows = i; if (pcrows == 1) pccols = totalshapes + i - 1; pcw = pccols * pcsz; pch = pcrows * pcsz; w = ((board->x+2) * p->boardmag) + (2 * pch); h = w; x = (p->root_w - w) / 2; y = (p->root_h - h) / 2; attrmask = 0; attr.background_pixel = p->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = p->bordercolour.pixel; attrmask |= CWBorderPixel; attr.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | ButtonPressMask; attrmask |= CWEventMask; attr.do_not_propagate_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask; attrmask |= CWDontPropagate; attr.colormap = p->wincmap; attrmask |= CWColormap; p->topwin = XCreateWindow(p->disp,p->rootwin,x,y,w,h,p->borderwidth,p->visinfo.depth,InputOutput,p->visinfo.visual,attrmask,&attr); wn_prop.value = (void *) deconst("blokus"); wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen(wn_prop.value); in_prop.value = (void *) deconst("blokus"); in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen(in_prop.value); normal_hints = XAllocSizeHints(); normal_hints->flags = PMinSize | PResizeInc; normal_hints->x = x; normal_hints->y = y; normal_hints->width = w; normal_hints->height = h; normal_hints->flags = PPosition | PSize; normal_hints->min_width = w; normal_hints->min_height = h; normal_hints->width_inc = 1; normal_hints->height_inc = 1; wm_hints = XAllocWMHints(); wm_hints->flags = InputHint; wm_hints->input = True; class_hints = XAllocClassHint(); class_hints->res_name = deconst(resname); class_hints->res_class = deconst(resclass); XSetWMProperties(p->disp,p->topwin,&wn_prop,&in_prop,argv,argc,normal_hints,wm_hints,class_hints); XFree(normal_hints); XFree(wm_hints); XFree(class_hints); gcvalmask = 0; p->wingc = XCreateGC(p->disp,p->topwin,gcvalmask,&gcval); pm = XCreatePixmap(p->disp,p->rootwin,1,1,1); p->bitgc = XCreateGC(p->disp,pm,0,0); XFreePixmap(p->disp,pm); /* non-horizontal boundaries belong to the part to their right horizontal bondaries belong to the part below them . a a a a a a a a . 0 b . a a [2] a a . c 1 b b . a a a a . c c 2 b b b . . . . c c c 3 b[1]b . . . . c[3]c 4 b b b . . . . c c c 5 b b b . . . . c c c 6 b b . d d d d . c c 7 b . d d [0] d d . c 8 . d d d d d d d d . 9 0 1 2 3 4 5 6 7 8 9 */ pts[0][0].x = w; pts[0][0].y = h; pts[0][1].x = 0; pts[0][1].y = h; pts[0][2].x = pch; pts[0][2].y = h - pch; pts[0][3].x = w - pch; pts[0][3].y = h - pch; pts[0][4].x = w - pch - p->boardmag; pts[0][4].y = h - pch - p->boardmag; pts[0][5].x = pch + p->boardmag; pts[0][5].y = h - pch - p->boardmag; pclayout[0].x0 = (w - pcw) / 2; pclayout[0].y0 = h - pch; pclayout[0].rincx = 1; pclayout[0].rincy = 0; pclayout[0].cincx = 0; pclayout[0].cincy = 1; pts[1][0].x = 0; pts[1][0].y = h - 1; pts[1][1].x = 0; pts[1][1].y = 0; pts[1][2].x = pch; pts[1][2].y = pch; pts[1][3].x = pch; pts[1][3].y = h - 1 - pch; pts[1][4].x = pch + p->boardmag; pts[1][4].y = h - 1 - pch - p->boardmag; pts[1][5].x = pch + p->boardmag; pts[1][5].y = pch + p->boardmag; pclayout[1].x0 = pch - pcsz; pclayout[1].y0 = (h - pcw) / 2; pclayout[1].rincx = 0; pclayout[1].rincy = 1; pclayout[1].cincx = -1; pclayout[1].cincy = 0; pts[2][0].x = 1; pts[2][0].y = 0; pts[2][1].x = w - 1; pts[2][1].y = 0; pts[2][2].x = w - 1 - pch; pts[2][2].y = pch; pts[2][3].x = pch + 1; pts[2][3].y = pch; pts[2][4].x = pch + 1 + p->boardmag; pts[2][4].y = pch + p->boardmag; pts[2][5].x = w - 1 - pch - p->boardmag; pts[2][5].y = pch + p->boardmag; pclayout[2].x0 = ((w - pcw) / 2) + pcw - pcsz; pclayout[2].y0 = pch - pcsz; pclayout[2].rincx = -1; pclayout[2].rincy = 0; pclayout[2].cincx = 0; pclayout[2].cincy = -1; pts[3][0].x = w; pts[3][0].y = 0; pts[3][1].x = w; pts[3][1].y = h - 1; pts[3][2].x = w - pch; pts[3][2].y = h - 1 - pch; pts[3][3].x = w - pch; pts[3][3].y = pch; pts[3][4].x = w - pch - p->boardmag; pts[3][4].y = pch + p->boardmag; pts[3][5].x = w - pch - p->boardmag; pts[3][5].y = h - 1 - pch - p->boardmag; pclayout[3].x0 = w - pch; pclayout[3].y0 = ((h - pcw) / 2) + pcw - pcsz; pclayout[3].rincx = 0; pclayout[3].rincy = -1; pclayout[3].cincx = 1; pclayout[3].cincy = 0; pm = XCreatePixmap(p->disp,p->rootwin,w,h,1); for (i=4-1;i>=0;i--) { int r; int c; int atx; int aty; int sx; int rn; attrmask = 0; attr.background_pixel = p->pbgcolours[i].pixel; attrmask |= CWBackPixel; p->playerwin[i] = XCreateWindow(p->disp,p->topwin,0,0,w,h,0,p->visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XSetForeground(p->disp,p->bitgc,0); XFillRectangle(p->disp,pm,p->bitgc,0,0,w,h); XSetForeground(p->disp,p->bitgc,1); XFillPolygon(p->disp,pm,p->bitgc,&pts[i][0],4,Convex,CoordModeOrigin); XShapeCombineMask(p->disp,p->playerwin[i],ShapeBounding,0,0,pm,ShapeSet); attrmask = 0; attr.background_pixel = p->pbgcolours[i].pixel; attrmask |= CWBackPixel; p->turnwin[i] = XCreateWindow(p->disp,p->topwin,0,0,w,h,0,p->visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XSetForeground(p->disp,p->bitgc,0); XFillRectangle(p->disp,pm,p->bitgc,0,0,w,h); XSetForeground(p->disp,p->bitgc,1); XFillPolygon(p->disp,pm,p->bitgc,&pts[i][2],4,Convex,CoordModeOrigin); XShapeCombineMask(p->disp,p->turnwin[i],ShapeBounding,0,0,pm,ShapeSet); atx = pclayout[i].x0; aty = pclayout[i].y0; r = 0; c = 0; rn = pccols; for (sx=totalshapes-1;sx>=0;sx--) { attrmask = 0; attr.background_pixel = p->bgcolour.pixel; attrmask |= CWBackPixel; attr.event_mask = ButtonPressMask | ButtonReleaseMask | ExposureMask; attrmask |= CWEventMask; pl = &p->places[i][sx]; pl->n = sx; pl->x = atx; pl->y = aty; pl->xf = XF___; pl->win = XCreateWindow(p->disp,p->playerwin[i], atx + (p->handmag/2), aty + (p->handmag/2), pcsz - p->handmag, pcsz - p->handmag, 0,p->visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(p->disp,p->places[i][sx].win); c ++; atx += pclayout[i].rincx * pcsz; aty += pclayout[i].rincy * pcsz; if (c >= rn) { atx -= rn * pclayout[i].rincx * pcsz; aty -= rn * pclayout[i].rincy * pcsz; r ++; c = - r; rn ++; if (sx < rn+r) { atx += ((pccols-sx) * pclayout[i].rincx * pcsz) / 2; aty += ((pccols-sx) * pclayout[i].rincy * pcsz) / 2; } else { atx -= r * pclayout[i].rincx * pcsz; aty -= r * pclayout[i].rincy * pcsz; } atx += pclayout[i].cincx * pcsz; aty += pclayout[i].cincy * pcsz; } } XMapWindow(p->disp,p->playerwin[i]); } XFreePixmap(p->disp,pm); attrmask = 0; attr.background_pixel = p->pccol_mine.pixel; attrmask |= CWBackPixel; p->boxwin = XCreateWindow(p->disp,p->playerwin[p->n],0,0,pcsz,pcsz,0,p->visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); pm = XCreatePixmap(p->disp,p->rootwin,pcsz,pcsz,1); XSetForeground(p->disp,p->bitgc,1); XFillRectangle(p->disp,pm,p->bitgc,0,0,pcsz,pcsz); XSetForeground(p->disp,p->bitgc,0); XFillRectangle(p->disp,pm,p->bitgc,p->handmag/2,p->handmag/2,pcsz-p->handmag,pcsz-p->handmag); XShapeCombineMask(p->disp,p->boxwin,ShapeBounding,0,0,pm,ShapeSet); XFreePixmap(p->disp,pm); XRaiseWindow(p->disp,p->boxwin); pm = XCreatePixmap(p->disp,p->rootwin,p->boardmag*2,p->boardmag*2,p->visinfo.depth); XSetForeground(p->disp,p->wingc,p->bdcolour[0].pixel); XFillRectangle(p->disp,pm,p->wingc,0,0,p->boardmag,p->boardmag); XFillRectangle(p->disp,pm,p->wingc,p->boardmag,p->boardmag,p->boardmag,p->boardmag); XSetForeground(p->disp,p->wingc,p->bdcolour[1].pixel); XFillRectangle(p->disp,pm,p->wingc,p->boardmag,0,p->boardmag,p->boardmag); XFillRectangle(p->disp,pm,p->wingc,0,p->boardmag,p->boardmag,p->boardmag); attrmask = 0; attr.background_pixmap = pm; attrmask |= CWBackPixmap; attr.event_mask = ExposureMask; attrmask |= CWEventMask; p->boardwin = XCreateWindow( p->disp, p->topwin, (w-(board->x*p->boardmag))/2, (h-(board->y*p->boardmag))/2, board->x*p->boardmag, board->y*p->boardmag, 0, p->visinfo.depth, InputOutput, CopyFromParent, attrmask, &attr ); XFreePixmap(p->disp,pm); attrmask = 0; attr.event_mask = ButtonPressMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | PointerMotionHintMask; attrmask |= CWEventMask; p->boardinput = XCreateWindow( p->disp, p->topwin, (w-(board->x*p->boardmag))/2, (h-(board->y*p->boardmag))/2, board->x*p->boardmag, board->y*p->boardmag, 0, 0, InputOnly, CopyFromParent, attrmask, &attr ); XMapRaised(p->disp,p->boardwin); XMapRaised(p->disp,p->boardinput); attrmask = 0; attr.save_under = True; attrmask |= CWSaveUnder; p->boardghost = XCreateWindow(p->disp,p->boardwin,1,1,1,1,0,p->visinfo.depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(p->disp,p->topwin); } static void setup_visual(IPLAYER *p) { XVisualInfo *xvi; int nvi; XVisualInfo template; int class; const char *classname; int best_onscr; int best; int i; if (! p->visualstr) { template.visualid = XVisualIDFromVisual(XDefaultVisual(p->disp,XDefaultScreen(p->disp))); xvi = XGetVisualInfo(p->disp,VisualIDMask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: default visual not found?!\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo found nonexistent default visual?\n",__progname); exit(1); } p->visinfo = xvi[0]; } else { do <"vis"> { #define FOO(c) if (!strcasecmp(p->visualstr,#c)) { class = c; classname = #c; } else FOO(StaticGray) FOO(StaticColor) FOO(TrueColor) FOO(GrayScale) FOO(PseudoColor) FOO(DirectColor) #undef FOO { unsigned long int id; char *cp; id = strtol(p->visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,p->visualstr); exit(1); } template.visualid = (VisualID) id; xvi = XGetVisualInfo(p->disp,VisualIDMask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: no such visual found\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } p->visinfo = xvi[0]; if (p->visinfo.screen != XDefaultScreen(p->disp)) { fprintf(stderr,"%s: warning: visual %s is on screen %d\n",__progname,p->visualstr,(int)p->visinfo.screen); } break <"vis">; } template.class = class; xvi = XGetVisualInfo(p->disp,VisualClassMask,&template,&nvi); best = -1; best_onscr = -1; for (i=0;idisp)) { if ( (best_onscr < 0) || (xvi[i].depth > xvi[best_onscr].depth) || ( (xvi[i].depth == xvi[best_onscr].depth) && (xvi[i].visual == XDefaultVisual(p->disp,xvi[i].screen)) ) ) { best_onscr = i; } } if ( (best < 0) || (xvi[i].depth > xvi[best].depth) || ( (xvi[i].depth == xvi[best].depth) && (xvi[i].visual == XDefaultVisual(p->disp,xvi[i].screen)) ) ) { best = i; } } if (best < 0) { fprintf(stderr,"%s: no %s visual available\n",__progname,classname); exit(1); } if (best_onscr < 0) { fprintf(stderr,"%s: no %s visual on screen %d, using screen %d\n",__progname,classname,XDefaultScreen(p->disp),xvi[best].screen); } else { best = best_onscr; } p->visinfo = xvi[best]; } while (0); } } static int xform_x(XFORM xf, int x, int y) { return((xf&XF_SWAP)?y:x); } static int xform_y(XFORM xf, int x, int y) { return((xf&XF_SWAP)?x:y); } static int xform_map(SHAPE *s, XFORM xf, int x, int y) { int sx; int sy; sx = s->x; sy = s->y; if (xf & XF_SWAP) { sx = s->y; sy = s->x; } if (xf & XF_NEGX) x = sx - 1 - x; if (xf & XF_NEGY) y = sy - 1 - y; if (xf & XF_SWAP) { int t; t = x; x = y; y = t; } return(MAP(s,x,y)); } static void build_pccurs(IPLAYER *p, int shapex, XFORM xf) { SHAPE *s; int sx; int sy; Pixmap img; Pixmap msk; int x; int y; XColor f; XColor b; s = shapes[shapex]; sx = xform_x(xf,s->x,s->y); sy = xform_y(xf,s->x,s->y); if (p->handmag < 3) { img = XCreatePixmap(p->disp,p->rootwin,(p->handmag*sx)+2,(p->handmag*sy)+2,1); msk = XCreatePixmap(p->disp,p->rootwin,(p->handmag*sx)+2,(p->handmag*sy)+2,1); XSetForeground(p->disp,p->bitgc,0); XFillRectangle(p->disp,img,p->bitgc,0,0,(p->handmag*sx)+2,(p->handmag*sy)+2); XFillRectangle(p->disp,msk,p->bitgc,0,0,(p->handmag*sx)+2,(p->handmag*sy)+2); } else { img = XCreatePixmap(p->disp,p->rootwin,p->handmag*sx,p->handmag*sy,1); msk = XCreatePixmap(p->disp,p->rootwin,p->handmag*sx,p->handmag*sy,1); XSetForeground(p->disp,p->bitgc,0); XFillRectangle(p->disp,img,p->bitgc,0,0,p->handmag*sx,p->handmag*sy); XFillRectangle(p->disp,msk,p->bitgc,0,0,p->handmag*sx,p->handmag*sy); } XSetForeground(p->disp,p->bitgc,1); for (y=sy-1;y>=0;y--) { for (x=sx-1;x>=0;x--) { if (xform_map(s,xf,x,y)) { if (p->handmag < 3) { XFillRectangle( p->disp, img, p->bitgc, (x*p->handmag)+1, (y*p->handmag)+1, p->handmag, p->handmag ); XFillRectangle( p->disp, msk, p->bitgc, x*p->handmag, y*p->handmag, p->handmag+2, p->handmag+2 ); } else { XFillRectangle( p->disp, img, p->bitgc, (x*p->handmag)+1, (y*p->handmag)+1, p->handmag-2, p->handmag-2 ); XFillRectangle( p->disp, msk, p->bitgc, x*p->handmag, y*p->handmag, p->handmag, p->handmag ); } } } } f = p->fgcolour; b = p->bgcolour; if (p->handmag < 3) { p->pccurs[shapex][xf] = XCreatePixmapCursor(p->disp,img,msk,&f,&b,((p->handmag*sx)/2)+1,((p->handmag*sy)/2)+1); } else { p->pccurs[shapex][xf] = XCreatePixmapCursor(p->disp,img,msk,&f,&b,(p->handmag*sx)/2,(p->handmag*sy)/2); } XFreePixmap(p->disp,img); XFreePixmap(p->disp,msk); } static void build_ghost(IPLAYER *p, int shapex, XFORM xf) { SHAPE *s; int sx; int sy; int i; XRectangle rects[size]; int x; int y; s = shapes[shapex]; sx = xform_x(xf,s->x,s->y); sy = xform_y(xf,s->x,s->y); i = 0; for (y=sy-1;y>=0;y--) { for (x=sx-1;x>=0;x--) { if (xform_map(s,xf,x,y)) { if (i >= size) abort(); rects[i++] = (XRectangle){ .x=x*p->boardmag, .y=y*p->boardmag, .width=p->boardmag, .height=p->boardmag }; } } } XSetWindowBackground(p->disp,p->boardghost,p->pbgcolours[turn].pixel); XResizeWindow(p->disp,p->boardghost,sx*p->boardmag,sy*p->boardmag); XShapeCombineRectangles(p->disp,p->boardghost,ShapeBounding, 0,0,&rects[0],size,ShapeSet,Unsorted); XClearWindow(p->disp,p->boardghost); p->ghostox = (sx * p->boardmag) / 2; p->ghostoy = (sy * p->boardmag) / 2; } static void setcarry(int cyx) { IPLAYER *p; PCPLACE *pl; p = &iplayers[(gplayers[turn].ipx<0)?turn3:gplayers[turn].ipx]; if (cyx >= 0) { pl = &p->places[turn][cyx]; if (p->pccurs[cyx][pl->xf] == None) build_pccurs(p,cyx,pl->xf); XDefineCursor(p->disp,p->turnwin[turn],p->pccurs[cyx][pl->xf]); XDefineCursor(p->disp,p->boardinput,p->pccurs[cyx][pl->xf]); XMoveWindow(p->disp,p->boxwin,pl->x,pl->y); XMapWindow(p->disp,p->boxwin); build_ghost(p,cyx,pl->xf); } else { XUnmapWindow(p->disp,p->boxwin); XUnmapWindow(p->disp,p->boardghost); XUndefineCursor(p->disp,p->boardinput); } carry = cyx; } static void setturn(int tx) { int pno; IPLAYER *p; int gno; GPLAYER *g; int sx; Cursor c; if (carry >= 0) setcarry(-1); for (pno=nplayers-1;pno>=0;pno--) { p = &iplayers[pno]; XUnmapWindow(p->disp,p->turnwin[turn]); if (tx >= 0) XMapWindow(p->disp,p->turnwin[tx]); for (gno=4-1;gno>=0;gno--) { g = &gplayers[gno]; c = ( (gno == tx) && ( (g->ipx == pno) || ((g->ipx < 0) && (turn3 == pno)) ) ) ? p->arrow_small : None; XDefineCursor(p->disp,p->playerwin[gno],c); XDefineCursor(p->disp,p->turnwin[gno],c); if ((g->ipx >= 0) && (g->ipx != pno)) continue; for (sx=totalshapes-1;sx>=0;sx--) { c = ( g->pcavail[sx] && (gno == tx) && ( (g->ipx == pno) || ((g->ipx < 0) && (turn3 == pno)) ) ) ? p->arrow_large : None; XDefineCursor(p->disp,p->places[gno][sx].win,c); } } } turn = tx; } static void redraw_placewin(IPLAYER *p, int plx, int pcx) { PCPLACE *pl; int xo; int yo; int sx; int sy; int x; int y; unsigned long int pix; pl = &p->places[plx][pcx]; if (gplayers[plx].pcavail[pcx]) { SHAPE *s; s = shapes[pcx]; sx = xform_x(pl->xf,s->x,s->y); sy = xform_y(pl->xf,s->x,s->y); xo = ((size - sx) * p->handmag) / 2; yo = ((size - sy) * p->handmag) / 2; if (gplayers[plx].ipx < 0) { pix = p->pcolours[turn3].pixel; } else if (gplayers[plx].ipx == p->n) { pix = p->pccol_mine.pixel; } else { pix = p->pccol_other.pixel; } XSetForeground(p->disp,p->wingc,pix); for (y=sy-1;y>=0;y--) { for (x=sx-1;x>=0;x--) { if (xform_map(s,pl->xf,x,y)) { if (p->handmag < 3) { XFillRectangle( p->disp, p->places[plx][pcx].win, p->wingc, xo+(x*p->handmag), yo+(y*p->handmag), p->handmag, p->handmag ); } else { XFillRectangle( p->disp, p->places[plx][pcx].win, p->wingc, xo+(x*p->handmag)+1, yo+(y*p->handmag)+1, p->handmag-2, p->handmag-2 ); } } } } } } static void setturn3(int tx) { int pno; int sx; turn3 = tx; for (pno=nplayers-1;pno>=0;pno--) { for (sx=totalshapes-1;sx>=0;sx--) redraw_placewin(&iplayers[pno],3,sx); } } static void setup_cursors(IPLAYER *p) { XColor f; XColor b; p->arrow_small = XCreateFontCursor(p->disp,XC_draft_small); f = p->fgcolour; b = p->bgcolour; XRecolorCursor(p->disp,p->arrow_small,&f,&b); p->arrow_large = XCreateFontCursor(p->disp,XC_draft_large); f = p->fgcolour; b = p->bgcolour; XRecolorCursor(p->disp,p->arrow_large,&f,&b); } static int ioerr(Display *d) { return((*oldioerr)(d)); } static int err(Display *d, XErrorEvent *e) { return((*olderr)(d,e)); } static void setup(void) { int pno; IPLAYER *p; GPLAYER *g; int i; int j; char *ntmp; char *ctmp; oldioerr = XSetIOErrorHandler(ioerr); olderr = XSetErrorHandler(err); for (pno=4-1;pno>=0;pno--) { g = &gplayers[pno]; g->pcavail = malloc(totalshapes); memset(g->pcavail,1,totalshapes); } gplayers[0].ipx = 0; gplayers[1].ipx = 1; switch (nplayers) { case 1: gplayers[1].ipx = 0; gplayers[2].ipx = 0; gplayers[3].ipx = 0; break; case 2: gplayers[2].ipx = 0; gplayers[3].ipx = 1; break; case 3: gplayers[2].ipx = 2; gplayers[3].ipx = -1; break; case 4: gplayers[2].ipx = 2; gplayers[3].ipx = 3; break; default: abort(); break; } for (pno=nplayers-1;pno>=0;pno--) { p = &iplayers[pno]; if ((pno > 0) && !p->display) { fprintf(stderr,"%s: need -display%d when using -n %d\n",__progname,pno+1,nplayers); exit(1); } p->pccurs = malloc(totalshapes*sizeof(*p->pccurs)); for (i=totalshapes-1;i>=0;i--) for (j=8-1;j>=0;j--) p->pccurs[i][j] = None; p->bdamage = newshape(board->x,board->y); memset(p->bdamage->map,0,board->x*board->y); p->places[0] = malloc(4*totalshapes*sizeof(PCPLACE)); p->places[1] = p->places[0] + totalshapes; p->places[2] = p->places[1] + totalshapes; p->places[3] = p->places[2] + totalshapes; p->disp = XOpenDisplay(p->display); if (! p->disp) { fprintf(stderr,"%s: can't open player #%d display\n",__progname,pno+1); exit(1); } if (p->synch) XSynchronize(p->disp,True); setup_db(p); resource_string(p,&p->background,"background","Background"); resource_string(p,&p->foreground,"foreground","Foreground"); resource_string(p,&p->pcolstr[0],"player.1.colour","Player.1.Colour"); resource_string(p,&p->pcolstr[1],"player.2.colour","Player.2.Colour"); resource_string(p,&p->pcolstr[2],"player.3.colour","Player.3.Colour"); resource_string(p,&p->pcolstr[3],"player.4.colour","Player.4.Colour"); resource_string(p,&p->pbgcolstr[0],"player.1.bgcolour","Player.1.Background"); resource_string(p,&p->pbgcolstr[1],"player.2.bgcolour","Player.2.Background"); resource_string(p,&p->pbgcolstr[2],"player.3.bgcolour","Player.3.Background"); resource_string(p,&p->pbgcolstr[3],"player.4.bgcolour","Player.4.Background"); resource_string(p,&p->pname[0],"player.1.name","Player.1.Name"); resource_string(p,&p->pname[1],"player.2.name","Player.2.Name"); resource_string(p,&p->pname[2],"player.3.name","Player.3.Name"); resource_string(p,&p->pname[3],"player.4.name","Player.4.Name"); resource_string(p,&p->bdcolstr[0],"board.colour.1","Board.Colour.1"); resource_string(p,&p->bdcolstr[1],"board.colour.2","Board.Colour.2"); resource_string(p,&p->borderwstr,"border.width","Border.Width"); resource_string(p,&p->bordercstr,"border.colour","Border.Foreground"); resource_string(p,&p->pccolstr_mine,"piece.mine.colour","Piece.Mine.Colour"); resource_string(p,&p->pccolstr_other,"piece.other.colour","Piece.Other.Colour"); resource_string(p,&p->bordercstr,"border.colour","Border.Foreground"); asprintf(&ntmp,"size.%d.board.mag",size); asprintf(&ctmp,"Size.%d.Board.Mag",size); resource_string(p,&p->boardmagstr,ntmp,ctmp); free(ntmp); free(ctmp); asprintf(&ntmp,"size.%d.hand.mag",size); asprintf(&ctmp,"Size.%d.Hand.Mag",size); resource_string(p,&p->handmagstr,ntmp,ctmp); free(ntmp); free(ctmp); if (!p->boardmagstr || !p->handmagstr) { fprintf(stderr,"%s: need board and hand mag factors for size %d\n",__progname,size); exit(1); } resource_string(p,&p->visualstr,"visual","Visual"); setup_visual(p); p->scr = XScreenOfDisplay(p->disp,p->visinfo.screen); p->root_w = XWidthOfScreen(p->scr); p->root_h = XHeightOfScreen(p->scr); p->rootwin = XRootWindowOfScreen(p->scr); p->boardmag = atoi(p->boardmagstr); p->handmag = atoi(p->handmagstr); p->borderwidth = atoi(p->borderwstr); setup_colours(p); setup_cursors(p); setup_windows(p); } if (nplayers == 3) { turn3 = 1; setturn3(0); } turn = 1; setturn(0); firstturn = 1; } static void expose_board(IPLAYER *p, int x, int y, int w, int h, int count) { int x0; int y0; int x1; int y1; int s; x0 = x / p->boardmag; y0 = y / p->boardmag; x1 = (x + w - 1) / p->boardmag; y1 = (y + h - 1) / p->boardmag; for (y=y0;y<=y1;y++) { for (x=x0;x<=x1;x++) { MAP(p->bdamage,x,y) = 1; } } if (count) return; for (y=board->y-1;y>=0;y--) { for (x=board->x-1;x>=0;x--) { if (MAP(p->bdamage,x,y)) { MAP(p->bdamage,x,y) = 0; s = MAP(board,x,y); if (s != B_EMPTY) { XSetForeground(p->disp,p->wingc,p->pcolours[s].pixel); XFillRectangle( p->disp, p->boardwin, p->wingc, x*p->boardmag, y*p->boardmag, p->boardmag, p->boardmag ); } } } } } static void do_expose(IPLAYER *p, Window win, int x, int y, int w, int h, int count) { int i; int j; if (win == p->boardwin) { expose_board(p,x,y,w,h,count); return; } for (j=3;j>=0;j--) { for (i=totalshapes-1;i>=0;i--) { if (win == p->places[j][i].win) { if (count == 0) redraw_placewin(p,j,i); return; } } } } static void compose_pcplace(IPLAYER *p, PCPLACE *pl, XFORM xf) { pl->xf = xf_compose[pl->xf][xf]; XClearWindow(p->disp,pl->win); redraw_placewin(p,turn,pl->n); } static int board_ptr(IPLAYER *p, int x, int y) { SHAPE *s; int sx; int sy; PCPLACE *pl; if (carry < 0) return(1); if (turn < 0) return(1); if (!( (gplayers[turn].ipx == p->n) || ( (gplayers[turn].ipx < 0) && (turn3 == p->n) ) )) return(1); s = shapes[carry]; pl = &p->places[turn][carry]; sx = xform_x(pl->xf,s->x,s->y); sy = xform_y(pl->xf,s->x,s->y); if ( (x < 0) || (y < 0) || (x >= board->x*p->boardmag) || (y >= board->y*p->boardmag) ) return(1); x -= p->ghostox; y -= p->ghostoy; x = (x + (p->boardmag/2)) / p->boardmag; y = (y + (p->boardmag/2)) / p->boardmag; if (x < 0) x = 0; if (y < 0) y = 0; if (x+sx > board->x) x = board->x - sx; if (y+sy > board->y) y = board->y - sy; if ((x < 0) || (y < 0)) abort(); XMoveWindow(p->disp,p->boardghost,x*p->boardmag,y*p->boardmag); boardpc_x = x; boardpc_y = y; return(0); } static int bad_placement(int who, int shx, XFORM xf, int atx, int aty) { SHAPE *s; int sx; int sy; int x; int y; int bx; int by; int ok; s = shapes[shx]; sx = xform_x(xf,s->x,s->y); sy = xform_y(xf,s->x,s->y); if (firstturn) { int cx; int cy; switch (who) { case 0: cx = 0; cy = board->y-1; break; case 1: cx = 0; cy = 0; break; case 2: cx = board->x-1; cy = 0; break; case 3: cx = board->x-1; cy = board->y-1; break; default: abort(); } return( (atx > cx) || (atx+sx <= cx) || (aty > cy) || (aty+sy <= cy) || !xform_map(s,xf,cx-atx,cy-aty) ); } else { if ( (atx < 0) || (atx+sx > board->x) || (atx < 0) || (aty+sy > board->y) ) return(1); ok = 0; for (y=sy-1;y>=0;y--) for (x=sx-1;x>=0;x--) { if (xform_map(s,xf,x,y)) { bx = atx + x; by = aty + y; if (MAP(board,bx,by) != B_EMPTY) return(1); if ((bx > 0) && (MAP(board,bx-1,by) == who)) return(1); if ((by > 0) && (MAP(board,bx,by-1) == who)) return(1); if ((bx < board->x-1) && (MAP(board,bx+1,by) == who)) return(1); if ((by < board->y-1) && (MAP(board,bx,by+1) == who)) return(1); if (!ok && (bx > 0) && (by > 0) && (MAP(board,bx-1,by-1) == who)) ok = 1; if (!ok && (bx < board->x-1) && (by > 0) && (MAP(board,bx+1,by-1) == who)) ok = 1; if (!ok && (bx > 0) && (by < board->y-1) && (MAP(board,bx-1,by+1) == who)) ok = 1; if (!ok && (bx < board->x-1) && (by < board->y-1) && (MAP(board,bx+1,by+1) == who)) ok = 1; } } return(!ok); } } static void advance_turn(void) { if ((nplayers == 3) && (turn == 3)) setturn3((turn3+1)%3); setturn((turn+1)&3); if (turn == 0) firstturn = 0; } static int canplay(void) { int pc; SHAPE *s; int xf; int sx; int sy; int x; int y; for (pc=totalshapes-1;pc>=0;pc--) { if (! gplayers[turn].pcavail[pc]) continue; s = shapes[pc]; for (xf=XF_MAX;xf>=0;xf--) { sx = xform_x(xf,s->x,s->y); sy = xform_y(xf,s->x,s->y); for (y=board->y-sy;y>=0;y--) { for (x=board->x-sx;x>=0;x--) { if (! bad_placement(turn,pc,xf,x,y)) return(1); } } } } return(0); } static void showscore(void) { int pl; int px; int score; for (pl=nplayers-1;pl>=0;pl--) { score = 0; for (px=4-1;px>=0;px--) { if (gplayers[px].ipx == pl) { } } } } static void boardpress(IPLAYER *p, int x, int y) { SHAPE *s; int sx; int sy; PCPLACE *pl; int dx; IPLAYER *pd; int i; if (board_ptr(p,x,y)) return; if ((carry < 0) || (turn < 0)) abort(); pl = &p->places[turn][carry]; if (bad_placement(turn,carry,pl->xf,boardpc_x,boardpc_y)) return; s = shapes[carry]; sx = xform_x(pl->xf,s->x,s->y); sy = xform_y(pl->xf,s->x,s->y); for (y=sy-1;y>=0;y--) { for (x=sx-1;x>=0;x--) { if (xform_map(s,pl->xf,x,y)) { MAP(board,x+boardpc_x,y+boardpc_y) = turn; for (dx=nplayers-1;dx>=0;dx--) { pd = &iplayers[dx]; expose_board(pd,(x+boardpc_x)*pd->boardmag,(y+boardpc_y)*pd->boardmag,pd->boardmag,pd->boardmag,0); } } } } gplayers[turn].pcavail[carry] = 0; for (dx=nplayers-1;dx>=0;dx--) { pd = &iplayers[dx]; XClearWindow(pd->disp,pd->places[turn][carry].win); } setcarry(-1); for (i=4;i>0;i--) { advance_turn(); if (canplay()) return; } setturn(-1); showscore(); } static void buttonpress(IPLAYER *p, Window win, int button, int x, int y) { int i; PCPLACE *pl; if (turn < 0) return; if (!( (gplayers[turn].ipx == p->n) || ((gplayers[turn].ipx < 0) && (turn3 == p->n)) )) { XBell(p->disp,0); return; } if (win == p->boardinput) { switch (button) { case Button1: boardpress(p,x,y); break; case Button2: if (carry >= 0) { pl = &p->places[turn][carry]; compose_pcplace(p,pl,XF_CW); setcarry(carry); } break; case Button3: if (carry >= 0) { pl = &p->places[turn][carry]; compose_pcplace(p,pl,XF_SWAP); setcarry(carry); } break; } return; } for (i=totalshapes-1;i>=0;i--) { pl = &p->places[turn][i]; if (win == pl->win) { if (gplayers[turn].pcavail[i]) { switch (button) { case Button1: setcarry(i); break; case Button2: compose_pcplace(p,pl,XF_CW); setcarry(i); break; case Button3: compose_pcplace(p,pl,XF_SWAP); setcarry(i); break; } } return; } } } static void motion(IPLAYER *p) { Window root; Window child; int root_x; int root_y; int win_x; int win_y; unsigned int mask; if (XQueryPointer(p->disp,p->boardinput,&root,&child,&root_x,&root_y,&win_x,&win_y,&mask) == True) { board_ptr(p,win_x,win_y); } } static void enter_window(IPLAYER *p, Window win, int x, int y) { if ( (win == p->boardinput) && (turn >= 0) && ( (gplayers[turn].ipx == p->n) || ( (gplayers[turn].ipx < 0) && (turn3 == p->n) ) ) ) { board_ptr(p,x,y); if (carry >= 0) XMapWindow(p->disp,p->boardghost); } } static void leave_window(IPLAYER *p, Window win) { if ( (win == p->boardinput) && (turn >= 0) && ( (gplayers[turn].ipx == p->n) || ( (gplayers[turn].ipx < 0) && (turn3 == p->n) ) ) && (carry >= 0) ) { XUnmapWindow(p->disp,p->boardghost); } } static void handle_event(IPLAYER *p, XEvent *e) { switch (e->type) { default: break; case Expose: /* XExposeEvent - xexpose */ do_expose( p, e->xexpose.window, e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height, e->xexpose.count ); break; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ buttonpress(p,e->xbutton.window,e->xbutton.button,e->xbutton.x,e->xbutton.y); break; case MotionNotify: /* XPointerMovedEvent - XMotionEvent - xmotion */ motion(p); break; case EnterNotify: /* XEnterWindowEvent - XCrossingEvent - xcrossing */ enter_window(p,e->xcrossing.window,e->xcrossing.x,e->xcrossing.y); break; case LeaveNotify: /* XLeaveWindowEvent - XCrossingEvent - xcrossing */ leave_window(p,e->xcrossing.window); break; } } static void run(void) { int pno; int loop; int flush; IPLAYER *p; struct pollfd pfds[4]; int prv; XEvent e; flush = 1; while (1) { loop = 0; for (pno=0;pnodisp) > 0) { do { XNextEvent(p->disp,&e); handle_event(p,&e); flush = 1; } while (XQLength(p->disp) > 0); loop = 1; } pfds[pno].fd = XConnectionNumber(p->disp); pfds[pno].events = POLLIN | POLLRDNORM; } if (loop) continue; prv = poll(&pfds[0],nplayers,flush?0:60000); if (prv == 0) { for (pno=nplayers-1;pno>=0;pno--) XFlush(iplayers[pno].disp); flush = 0; continue; } for (pno=nplayers-1;pno>=0;pno--) XEventsQueued(iplayers[pno].disp,QueuedAfterReading); } } int main(int, char **); int main(int ac, char **av) { init_preargs(); if (handleargs(ac,av)) exit(1); gen_shapes(); setup(); run(); #if 0 { SHAPE *s; int x; int y; int n; printf("pieces=%d squares=%d\n",totalshapes,totalsquares); for (n=0;ny;y++) { for (x=0;xx;x++) { printf(" %c",MAP(s,x,y)?'*':'-'); } printf("\n"); } } } #endif return(0); }