#include #include #include #include #include #include extern const char *__progname; #include #include #include #include #include #include "mjp.h" #include "tiles.h" #include "client.h" #include "strings.h" #include "pollloop.h" #define DISCARD_X 7 #define DISCARD_Y 3 #define PADSPACE (TILE_XSIZE/2) #define WALLWIDTH 6 static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColour: white\n\ *BorderWidth: 1\n\ *Font: fixed\n\ *Name: xmahjongg\n\ *IconName: xmahjongg\n\ *TilesPath: /home/mouse/games/mahjongg/.tiles\n\ *TableColour: #642\n\ *VacantColour: #444\n\ *StackShift: 3\n\ "; #if NWIND != 4 #error Code needs updating #endif #define NPLAYERS 4 #define PL_B 0 #define PL_L 1 #define PL_R 2 #define PL_T 3 #if NPLAYERS != NWIND #error code needs updating #endif static char *xrmstring; static const char *displayname; static const char *geometryspec; static const char *visualstr; static const char *fontname; static const char *name; static const char *iconname; static const char *tilespath; static const char *tablecstr; static const char *vacantcstr; static const char *foreground; static const char *background; static const char *bordercstr; static const char *borderwstr; static const char *stackshiftstr; static int synch = -1; static int backnum = -1; #if 0 /* XXX */ static unsigned char ccc_wallpresent[WALLSIZE]; static unsigned char ccc_replpresent[REPLSIZE]; static TILE ccc_repltile[REPLSIZE]; static unsigned char ccc_dorapresent[DORASIZE]; static TILE ccc_doratile[DORASIZE]; static char *ccc_windnames[NWIND]; static int ccc_mywind; static int ccc_nhand[NWIND]; static TILE ccc_hand[NWIND][MAXHAND]; static TILE sorted_hand[NWIND][MAXHAND]; static int ccc_nmeld[NWIND]; static MELD ccc_melds[NWIND][MAXMELDS]; static int ccc_ndiscard[NWIND]; static TILE ccc_discard[NWIND][DECKSIZE]; static int ccc_nfs[NWIND]; static TILE ccc_fs[NWIND][MAXFS]; static int ccc_riichi_at[NWIND]; #endif static Display *disp; static Screen *scr; static int width; static int height; static int defdepth; static int depth; static Window rootwin; static Colormap defcmap; static Visual *visual; static XFontStruct *font; static int deffont; static int baselineskip; typedef enum { CLFA_PRESENCE = 1, CLFA_STRING, CLFA_CALLBACK } CLFATYPE; typedef struct clflag CLFLAG; typedef struct clfarg CLFARG; typedef struct pendcall PENDCALL; struct pendcall { int *idp; void (*fn)(void); } ; struct clfarg { CLFATYPE type; union { int *presence; const char **string; void (*fn)(const char *); } u; } ; #define CLFARG_PRESENCE(p) { .type = CLFA_PRESENCE, .u = { .presence = (p) } } #define CLFARG_STRING(sp) { .type = CLFA_STRING, .u = { .string = (sp) } } #define CLFARG_CALLBACK(cb) { .type = CLFA_CALLBACK, .u = { .fn = (cb) } } struct clflag { const char *flag; int nargs; CLFARG *args; } ; static XColor *tiles_colours; static Pixmap tile_face_pm[TILE__N][NPLAYERS]; static Pixmap (*tile_back_pm)[NPLAYERS]; static XColor table_colour; static XColor vacant_colour; static Colormap wincmap; static int borderwidth; static XColor fgcolour; static XColor bgcolour; static XColor bdcolour; static CLFLAG clflags[]; /* fowrard */ static int nclflags; /* forward */ static CLFLAG *curflag; static int stackshift; static Window gametopwin; static Window tablewin; static Window wallwin; static Window meldwin[NPLAYERS]; static Window handwin[NPLAYERS]; static Window discwin[NPLAYERS]; static Window dorawin; static Window replwin; static int meldwidth; static int tablew; static int tableh; static int curtopw; static int curtoph; static int xblockid; static int xreadid; static GC defgc; static GC wingc; static TILE walldisp[WALLWIDTH][2]; static int walltiles; static char walldamaged[WALLWIDTH+1]; static char wallNdamaged; static int wallupdid; static int wallNx; static int wallNw; static char *wallNtxt; static int wallNtxtlen; static XCharStruct wallNtxtext; #if DORASIZE % 1 #error Code assumes DORASIZE is even #endif #define DORAWIDTH (DORASIZE/2) static TILE doradisp[DORAWIDTH][2]; static char doradamaged[DORAWIDTH+1]; static int doraupdid; #if REPLSIZE % 1 #error Code assumes REPLSIZE is even #endif #define REPLWIDTH (REPLSIZE/2) static TILE repldisp[REPLWIDTH][2]; static char repldamaged[REPLWIDTH+1]; static int replupdid; #define XTE_JUNK &xte_junk_dir,&xte_junk_asc,&xte_junk_dsc static int xte_junk_dir; static int xte_junk_asc; static int xte_junk_dsc; static int argc; static const char * const *argv; static int (*prev_error)(Display *, XErrorEvent *); static int (*prev_io_error)(Display *); static void *dequal(const volatile void *p) { return((((const volatile char *)p)-(const volatile char *)0)+(char *)0); } static void append_string(char **strp, ...) { va_list ap; const char *str; int oldl; int newl; char *new; char *np; va_start(ap,strp); if (! *strp) { *strp = malloc(1); **strp = '\0'; } oldl = strlen(*strp); newl = 0; while (1) { str = va_arg(ap,const char *); if (str == 0) break; newl += strlen(str); } va_end(ap); new = malloc(oldl+newl+1); np = new; strcpy(np,*strp); np += strlen(np); va_start(ap,strp); while (1) { str = va_arg(ap,const char *); if (str == 0) break; strcpy(np,str); np += strlen(np); } va_end(ap); free(*strp); *strp = new; } void client_original_args(int ac, const char * const *av) { argc = ac; argv = av; } int client_flag(const char *flag) { int i; for (i=nclflags-1;i>=0;i--) { if (!strcmp(flag,clflags[i].flag)) { curflag = &clflags[i]; return(clflags[i].nargs); } } return(CLIENT_FLAG_UNKNOWN); } void client_args(const char * const *args) { int i; int j; CLFARG *a; for (i=0,j=0;inargs;i++) { a = &curflag->args[i]; switch (a->type) { case CLFA_PRESENCE: *a->u.presence = 1; break; case CLFA_STRING: *a->u.string = args[j++]; break; case CLFA_CALLBACK: (*a->u.fn)(args[j++]); break; default: abort(); break; } } } static void xrm_arg(const char *arg) { append_string(&xrmstring,"\n",arg,(char *)0); } static CLFLAG clflags[] = { { "-display", 1, (CLFARG[1]){ CLFARG_STRING(&displayname) } }, { "-geometry", 1, (CLFARG[1]){ CLFARG_STRING(&geometryspec) } }, { "-visual", 1, (CLFARG[1]){ CLFARG_STRING(&visualstr) } }, { "-xrm", 1, (CLFARG[1]){ CLFARG_CALLBACK(&xrm_arg) } }, { "-font", 1, (CLFARG[1]){ CLFARG_STRING(&fontname) } }, { "-fn", 1, (CLFARG[1]){ CLFARG_STRING(&fontname) } }, { "-name", 1, (CLFARG[1]){ CLFARG_STRING(&name) } }, { "-iconname", 1, (CLFARG[1]){ CLFARG_STRING(&iconname) } }, { "-tiles", 1, (CLFARG[1]){ CLFARG_STRING(&tilespath) } }, { "-table-colour", 1, (CLFARG[1]){ CLFARG_STRING(&tablecstr) } }, { "-table-color", 1, (CLFARG[1]){ CLFARG_STRING(&tablecstr) } }, { "-vacant-colour", 1, (CLFARG[1]){ CLFARG_STRING(&vacantcstr) } }, { "-vacant-color", 1, (CLFARG[1]){ CLFARG_STRING(&vacantcstr) } }, { "-sync", 1, (CLFARG[1]){ CLFARG_PRESENCE(&synch) } }, { "-fg", 1, (CLFARG[1]){ CLFARG_STRING(&foreground) } }, { "-foreground", 1, (CLFARG[1]){ CLFARG_STRING(&foreground) } }, { "-bg", 1, (CLFARG[1]){ CLFARG_STRING(&background) } }, { "-background", 1, (CLFARG[1]){ CLFARG_STRING(&background) } }, { "-bordercolour", 1, (CLFARG[1]){ CLFARG_STRING(&bordercstr) } }, { "-bordercolor", 1, (CLFARG[1]){ CLFARG_STRING(&bordercstr) } }, { "-bc", 1, (CLFARG[1]){ CLFARG_STRING(&bordercstr) } }, { "-borderwidth", 1, (CLFARG[1]){ CLFARG_STRING(&borderwstr) } }, { "-bw", 1, (CLFARG[1]){ CLFARG_STRING(&borderwstr) } }, { "-stackshift", 1, (CLFARG[1]){ CLFARG_STRING(&stackshiftstr) } } }; static int nclflags = sizeof(clflags) / sizeof(clflags[0]); static void setup_db(void) { char *str; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } if (xrmstring) { db2 = XrmGetStringDatabase(xrmstring); XrmMergeDatabases(db2,&db); } } static void maybeset(const char **strp, const char *str) { if (str && !*strp) *strp = str; } static void maybeseti3(int *ip, int i) { if ((i >= 0) && (*ip < 0)) *ip = i; } static const char *resname(void) { return("xmahjongg"); } static const char *classname(void) { return("Game"); } static char *get_default_value(const char *n, const char *c) { char *type; XrmValue value; char *nt; char *ct; char *rv; asprintf(&nt,"%s.%s",resname(),n); asprintf(&ct,"%s.%s",classname(),c); rv = (XrmGetResource(db,nt,ct,&type,&value) == False) ? 0 : value.addr; free(nt); free(ct); return(rv); } static int yn3(char *str) { if (str == 0) return(-1); if ( !strcasecmp(str,"true") || !strcasecmp(str,"t") || !strcasecmp(str,"vrai") || !strcasecmp(str,"v") || !strcasecmp(str,"yes") || !strcasecmp(str,"y") || !strcasecmp(str,"oui") || !strcasecmp(str,"o") || !strcasecmp(str,"ja") || !strcasecmp(str,"j") || !strcasecmp(str,"1") ) { return(1); } if ( !strcasecmp(str,"false") || !strcasecmp(str,"f") || !strcasecmp(str,"faux") || !strcasecmp(str,"no") || !strcasecmp(str,"n") || !strcasecmp(str,"non") || !strcasecmp(str,"nil") || !strcasecmp(str,"nei") || !strcasecmp(str,"0") ) { return(0); } return(-1); } static void read_db(void) { maybeset(&geometryspec,get_default_value("geometry","Geometry")); maybeset(&name,get_default_value("name","Name")); maybeset(&iconname,get_default_value("iconName","IconName")); maybeseti3(&synch,yn3(get_default_value("sync","Sync"))); maybeset(&visualstr,get_default_value("visual","Visual")); maybeseti3(&backnum,yn3(get_default_value("backNumber","BackNumber"))); maybeset(&tilespath,get_default_value("tilesPath","TilesPath")); maybeset(&tablecstr,get_default_value("tableColour","TableColour")); maybeset(&vacantcstr,get_default_value("vacantColour","VacantColour")); maybeset(&foreground,get_default_value("foreground","Foreground")); maybeset(&background,get_default_value("background","Background")); maybeset(&bordercstr,get_default_value("borderColour","BorderColour")); maybeset(&borderwstr,get_default_value("borderWidth","BorderWidth")); maybeset(&stackshiftstr,get_default_value("stackShift","StackShift")); if (synch < 0) synch = 0; if (backnum < 0) backnum = 0; } static Display *open_display(const char *disp) { Display *rv; rv = XOpenDisplay(disp); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(disp)); exit(1); } return(rv); } static void setup_visual(void) { char *cp; XVisualInfo *xvi; int nvi; XVisualInfo template; long int mask; int i; int vpref; if (visualstr == 0) return; template.screen = XScreenNumberOfScreen(scr); mask = VisualScreenMask | VisualClassMask; if (!strcasecmp(visualstr,"staticgray")) template.class = StaticGray; else if (!strcasecmp(visualstr,"grayscale")) template.class = GrayScale; else if (!strcasecmp(visualstr,"staticcolor")) template.class = StaticColor; else if (!strcasecmp(visualstr,"pseudocolor")) template.class = PseudoColor; else if (!strcasecmp(visualstr,"directcolor")) template.class = DirectColor; else if (!strcasecmp(visualstr,"truecolor")) template.class = TrueColor; else { unsigned long int id; id = strtol(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option, using default visual\n",__progname,visualstr); return; } template.visualid = (VisualID) id; mask |= VisualIDMask; mask &= ~VisualClassMask; } xvi = XGetVisualInfo(disp,mask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: %s: no matching visual found, using default visual\n",__progname,visualstr); return; } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } vpref = 0; for (i=1;i xvi[vpref].depth) ) ) vpref = i; } visual = xvi[vpref].visual; depth = xvi[vpref].depth; XFree((char *)xvi); if (visual != XDefaultVisualOfScreen(scr)) defcmap = None; } static void setup_xcolour(XColor *xc, const char *what) { while (XAllocColor(disp,wincmap,xc) == 0) { if (wincmap != defcmap) { fprintf(stderr,"%s: can't allocate colourmap cell for colour %s\n",__progname,what); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); } } static void setup_colour(const char *str, XColor *xc) { if (XParseColor(disp,wincmap,str,xc) == 0) { fprintf(stderr,"%s: bad colour `%s'\n",__progname,str); exit(1); } setup_xcolour(xc,str); } static void setup_colours(void) { int i; wincmap = defcmap; if (wincmap == None) wincmap = XCreateColormap(disp,rootwin,visual,AllocNone); setup_colour(tablecstr,&table_colour); setup_colour(vacantcstr,&vacant_colour); setup_colour(foreground,&fgcolour); setup_colour(background,&bgcolour); setup_colour(bordercstr,&bdcolour); for (i=tile_ncol-1;i>=0;i--) { tiles_colours[i].red = tile_cmap[i][0]; tiles_colours[i].green = tile_cmap[i][1]; tiles_colours[i].blue = tile_cmap[i][2]; setup_xcolour(&tiles_colours[i],"for tiles"); } } static void setup_numbers(void) { borderwidth = borderwstr ? atoi(borderwstr) : 1; stackshift = stackshiftstr ? atoi(stackshiftstr) : 1; } static void setup_tiles(void) { int i; void clr_pmv(Pixmap *v) { int j; for (j=NPLAYERS-1;j>=0;j--) v[j] = None; } if (! tilespath) { fprintf(stderr,"%s: need a tiles path spec\n",__progname); exit(1); } if (tile_loaddeck(tilespath) < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,tilespath,strerror(errno)); exit(1); } if (tile_nbacks < 1) { fprintf(stderr,"%s: %s: tile set has no backs\n",__progname,tilespath); exit(1); } tiles_colours = malloc(tile_ncol*sizeof(XColor)); for (i=TILE__N-1;i>=0;i--) clr_pmv(&tile_face_pm[i][0]); tile_back_pm = malloc(tile_nbacks*sizeof(Pixmap)); for (i=tile_nbacks-1;i>=0;i--) clr_pmv(&tile_back_pm[i][0]); if (backnum >= tile_nbacks) backnum = 0; } static int ioerror_handler(Display *disp) { return((*prev_io_error)(disp)); } static int error_handler(Display *disp, XErrorEvent *e) { (*prev_error)(disp,e); exit(1); } static void setup_error(void) { prev_error = XSetErrorHandler(error_handler); prev_io_error = XSetIOErrorHandler(ioerror_handler); } static void setup_gc(void) { Pixmap pm; pm = XCreatePixmap(disp,rootwin,1,1,depth); wingc = XCreateGC(disp,pm,0,0); if (deffont) { if (depth != defdepth) { fprintf(stderr,"%s: can't use server default font - visual depth isn't the default\n",__progname); fprintf(stderr,"%s: specify a font, or choose a different visual\n",__progname); exit(1); } XCopyGC(disp,defgc,GCFont,wingc); } else { XSetFont(disp,wingc,font->fid); } } static int rot_w(int p, int w, int h) { switch (p) { case PL_B: case PL_T: return(w); break; case PL_L: case PL_R: return(h); break; } abort(); } static int rot_h(int p, int w, int h) { switch (p) { case PL_B: case PL_T: return(h); break; case PL_L: case PL_R: return(w); break; } abort(); } static int rot_x(int p, int x, int y, int w, int h) { switch (p) { case PL_B: return(x); break; case PL_L: return(h-1-y); break; case PL_T: return(w-1-x); break; case PL_R: return(y); break; } abort(); } static int rot_y(int p, int x, int y, int w, int h) { switch (p) { case PL_B: return(y); break; case PL_L: return(x); break; case PL_T: return(h-1-y); break; case PL_R: return(w-1-x); break; } abort(); } static void rot_winpos( int p, int *xp, int *yp, int *wp, int *hp, int x, int y, int w, int h, int sz ) { switch (p) { case PL_B: *xp = x; *yp = y; *wp = w; *hp = h; break; case PL_L: *xp = sz - (y + h); *yp = x; *wp = h; *hp = w; break; case PL_T: *xp = sz - (x + w); *yp = sz - (y + h); *wp = w; *hp = h; break; case PL_R: *xp = y; *yp = sz - (x + w); *wp = h; *hp = w; break; default: abort(); break; } } static void setup_windows(void) { int i; int x; int y; int w; int h; int bits; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; meldwidth = (8 * TILE_XSIZE) + (4 * TILE_YSIZE) + (3 * PADSPACE); i = 14 * TILE_XSIZE; if (meldwidth > i) i = meldwidth; x = (2 * DISCARD_Y * TILE_YSIZE) + (6 * TILE_XSIZE) + (4 * PADSPACE); if (x > i) i = x; x = (2 * DISCARD_Y * TILE_YSIZE) + (3 * TILE_YSIZE) + (6 * PADSPACE); if (x > i) i = x; i += (2 * PADSPACE) + (4 * TILE_YSIZE); x = (DISCARD_X*TILE_XSIZE) + (2 * (((2+DISCARD_Y)*TILE_YSIZE) + (2*PADSPACE))); if (x > i) i = x; tablew = i; tableh = i; w = i + (2 * borderwidth); h = i + (2 * borderwidth); x = (width - w) / 2; y = (height - h) / 2; bits = XParseGeometry(geometryspec,&x,&y,&w,&h); if (bits & XNegative) x = width + x - w; if (bits & YNegative) y = height + y - h; attr.background_pixel = vacant_colour.pixel; attr.border_pixel = bdcolour.pixel; attr.event_mask = StructureNotifyMask; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask | KeyPressMask | KeyReleaseMask; attr.colormap = wincmap; w -= 2 * borderwidth; h -= 2 * borderwidth; curtopw = w; curtoph = h; gametopwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,visual,CWBackPixel|CWBorderPixel|CWEventMask|CWDontPropagate|CWColormap,&attr); wn_prop.value = (unsigned char *) dequal(name); wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = (unsigned char *) dequal(iconname); in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); normal_hints.flags = PMinSize | PResizeInc; normal_hints.x = x; normal_hints.y = y; normal_hints.flags |= (bits & (XValue|YValue)) ? USPosition : 0; normal_hints.width = w; normal_hints.height = h; normal_hints.flags |= (bits & (WidthValue|HeightValue)) ? USSize : PSize; normal_hints.min_width = w; normal_hints.min_height = h; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = True; class_hints.res_name = dequal(resname()); class_hints.res_class = dequal(classname()); XSetWMProperties(disp,gametopwin,&wn_prop,&in_prop,dequal(argv),argc,&normal_hints,&wm_hints,&class_hints); XSetWindowColormap(disp,gametopwin,wincmap); attr.background_pixel = table_colour.pixel; tablewin = XCreateWindow(disp,gametopwin,0,0,i,i,0,depth,InputOutput,CopyFromParent,CWBackPixel,&attr); XMapRaised(disp,tablewin); attr.background_pixel = table_colour.pixel;; attr.event_mask = ExposureMask; x = (tablew - (WALLWIDTH * TILE_XSIZE)) / 2; y = ((tableh - TILE_YSIZE) / 2) - TILE_YSIZE - PADSPACE - stackshift; wallwin = XCreateWindow(disp,tablewin,x,y,WALLWIDTH*TILE_XSIZE,TILE_YSIZE+stackshift,0,depth,InputOutput,CopyFromParent,CWBackPixel|CWEventMask,&attr); XMapRaised(disp,wallwin); attr.background_pixel = table_colour.pixel;; attr.event_mask = ExposureMask; x = (tablew - (6 * TILE_XSIZE)) / 2; y = ((tableh - TILE_YSIZE) / 2) - stackshift; replwin = XCreateWindow(disp,tablewin,x,y,6*TILE_XSIZE,TILE_YSIZE+stackshift,0,depth,InputOutput,CopyFromParent,CWBackPixel|CWEventMask,&attr); XMapRaised(disp,replwin); attr.background_pixel = table_colour.pixel;; attr.event_mask = ExposureMask; x = (tablew - (5 * TILE_XSIZE)) / 2; y = ((tableh - TILE_YSIZE) / 2) + TILE_YSIZE + PADSPACE - stackshift; dorawin = XCreateWindow(disp,tablewin,x,y,5*TILE_XSIZE,TILE_YSIZE+stackshift,0,depth,InputOutput,CopyFromParent,CWBackPixel|CWEventMask,&attr); XMapRaised(disp,dorawin); for (i=NPLAYERS-1;i>=0;i--) { attr.background_pixel = vacant_colour.pixel;; attr.event_mask = ExposureMask; rot_winpos( i, &x, &y, &w, &h, (tablew - meldwidth) / 2, tableh - (2 * TILE_YSIZE) - PADSPACE, meldwidth, TILE_YSIZE, tablew ); meldwin[i] = XCreateWindow(disp,tablewin,x,y-stackshift,w,h+stackshift,0,depth,InputOutput,CopyFromParent,CWBackPixel|CWEventMask,&attr); XMapRaised(disp,meldwin[i]); attr.background_pixel = vacant_colour.pixel;; attr.event_mask = ExposureMask; rot_winpos( i, &x, &y, &w, &h, (tablew - (14 * TILE_XSIZE)) / 2, tableh - TILE_YSIZE, 14 * TILE_XSIZE, TILE_YSIZE, tablew ); handwin[PL_B] = XCreateWindow(disp,tablewin,x,y,w,h,0,depth,InputOutput,CopyFromParent,CWBackPixel|CWEventMask,&attr); XMapRaised(disp,handwin[PL_B]); attr.background_pixel = vacant_colour.pixel;; attr.event_mask = ExposureMask; rot_winpos( i, &x, &y, &w, &h, (tablew - (DISCARD_X * TILE_XSIZE)) / 2, tableh - ((DISCARD_Y+2) * TILE_YSIZE) - (2 * PADSPACE), DISCARD_X * TILE_XSIZE, DISCARD_Y * TILE_YSIZE, tablew ); discwin[PL_B] = XCreateWindow(disp,tablewin,x,y,w,h,0,depth,InputOutput,CopyFromParent,CWBackPixel|CWEventMask,&attr); XMapRaised(disp,discwin[PL_B]); } XMapRaised(disp,gametopwin); } static void setup_font(void) { if (fontname) { font = XLoadQueryFont(disp,fontname); if (font) { deffont = 0; } else { fprintf(stderr,"%s: can't load font %s, using server default\n",__progname,fontname); deffont = 1; } } else { deffont = 1; } if (deffont) font = XQueryFont(disp,XGContextFromGC(defgc)); baselineskip = font->ascent + font->descent; } static void resize(int neww, int newh) { if ((neww == curtopw) && (newh == curtoph)) return; curtopw = neww; curtoph = newh; XMoveWindow(disp,tablewin,(neww-tablew)/2,(newh-tableh)/2); } static int rectangle_intersection( int xa, int ya, int wa, int ha, int xb, int yb, int wb, int hb, int *xp, int *yp, int *wp, int *hp ) { if ((xa+wa <= xb) || (xb+wb <= xa) || (ya+ha <= yb) || (yb+hb <= ya)) return(0); if (xb > xa) { wa -= xb - xa; xa = xb; } else { wb -= xa - xb; } if (yb > ya) { ha -= yb - ya; ya = yb; } else { hb -= ya - yb; } if (wb < wa) wa = wb; if (hb < ha) ha = hb; *xp = xa; *yp = ya; *wp = wa; *hp = ha; return(1); } static int call_pending_call(void *pcv) { PENDCALL *pc; pc = pcv; remove_block_id(*pc->idp); *pc->idp = PL_NOID; (*pc->fn)(); free(pc); return(BLOCK_LOOP); } static void pending_call(void (*fn)(void), int *idp) { PENDCALL *pc; if (*idp != PL_NOID) return; pc = malloc(sizeof(PENDCALL)); pc->idp = idp; pc->fn = fn; *idp = add_block_fn(&call_pending_call,pc); } static Pixmap tile_pm(TILE t, int pl) { Pixmap *pmp; unsigned char (*pic)[TILE_XSIZE]; if ((pl < 0) || (pl >= NPLAYERS)) abort(); switch (t) { case TILE_UNKTILE: pmp = &tile_back_pm[backnum][pl]; pic = &tile_backs[backnum][0]; break; default: if ((t < 0) || (t >= TILE__N)) abort(); pmp = &tile_face_pm[t][pl]; pic = &tile_pics[t][0]; break; } if (*pmp == None) { Pixmap p; int hist[tile_ncol]; int x; int y; int i; p = XCreatePixmap(disp,rootwin,rot_w(pl,TILE_XSIZE,TILE_YSIZE),rot_h(pl,TILE_XSIZE,TILE_YSIZE),depth); for (i=tile_ncol-1;i>=0;i--) hist[i] = 0; for (y=TILE_YSIZE-1;y>=0;y--) { for (x=TILE_XSIZE-1;x>=0;x--) { i = pic[y][x]; if ((i < 0) || (i >= tile_ncol)) abort(); hist[i] ++; } } x = tile_ncol - 1; for (i=x-1;i>=0;i--) if (hist[i] > hist[x]) x = i; XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,tiles_colours[x].pixel); XFillRectangle(disp,p,wingc,0,0,rot_w(pl,TILE_XSIZE,TILE_YSIZE),rot_h(pl,TILE_XSIZE,TILE_YSIZE)); hist[x] = 0; for (i=tile_ncol-1;i>=0;i--) { if (hist[i]) { XSetForeground(disp,wingc,tiles_colours[i].pixel); for (y=TILE_YSIZE-1;y>=0;y--) { for (x=TILE_XSIZE-1;x>=0;x--) { if (pic[y][x] == i) { XDrawPoint(disp,p,wingc,rot_x(pl,x,y,TILE_XSIZE,TILE_YSIZE),rot_y(pl,x,y,TILE_XSIZE,TILE_YSIZE)); } } } } } *pmp = p; } return(*pmp); } static void fix_tilevec2_damage(char *dmgvec, TILE (*tiles)[2], int nx, Window w) { int i; int j; void fix_damaged_range(int x1, int x2) { int x; for (x=x1;x= 0) { fix_damaged_range(j,i); j = -1; } } } if (j >= 0) fix_damaged_range(j,WALLWIDTH); } static int expose_tilevec(char *dmgvec, int nx, int x, int w) { int x2; x2 = (x+w-1) / TILE_XSIZE; if (x2 >= nx) x2 = nx - 1; if (x < 0) { w -= x; x = 0; } for (x/=TILE_XSIZE;x<=x2;x++) dmgvec[x] = 1; return(1); } static void update_wall(void) { XRectangle r[2]; if (walldamaged[0]) { walldamaged[0] = 0; if (wallNtxt) { r[0].x = 0; r[0].y = 0; r[0].width = wallNx; r[0].height = TILE_YSIZE + stackshift; r[1].x = wallNx + wallNw; r[1].y = 0; r[1].width = (WALLWIDTH * TILE_XSIZE) - r[1].x; r[1].height = r[0].height; XSetClipRectangles(disp,wingc,0,0,&r[0],2,YXBanded); fix_tilevec2_damage(&walldamaged[1],&walldisp[0],WALLWIDTH,wallwin); XSetClipMask(disp,wingc,None); } else { fix_tilevec2_damage(&walldamaged[1],&walldisp[0],WALLWIDTH,wallwin); } } if (wallNdamaged) { wallNdamaged = 0; XClearArea(disp,wallwin,wallNx,0,wallNw,TILE_YSIZE+stackshift,False); XSetForeground(disp,wingc,fgcolour.pixel); XSetBackground(disp,wingc,table_colour.pixel); XDrawImageString(disp,wallwin,wingc,wallNx,((TILE_YSIZE+stackshift-baselineskip)/2)+font->ascent,wallNtxt,wallNtxtlen); } } static void expose_wall(int x, int y, int w, int h) { int xx; int yy; int ww; int hh; void dotiles(int x, int w) { if (expose_tilevec(&walldamaged[1],WALLWIDTH,x,w)) { walldamaged[0] = 1; pending_call(&update_wall,&wallupdid); } } if (walltiles > WALLWIDTH*2) { if (rectangle_intersection(x,y,w,h,wallNx,0,wallNw,TILE_YSIZE+stackshift,&xx,&yy,&ww,&hh)) { wallNdamaged = 1; pending_call(&update_wall,&wallupdid); } if (rectangle_intersection(x,y,w,h,0,0,wallNx,TILE_YSIZE+stackshift,&xx,&yy,&ww,&hh)) { dotiles(xx,ww); } if (rectangle_intersection(x,y,w,h,wallNx+wallNw,0,(WALLWIDTH*TILE_XSIZE)-(wallNx+wallNw),TILE_YSIZE+stackshift,&xx,&yy,&ww,&hh)) { dotiles(xx,ww); } } else { dotiles(x,w); } } static void update_dora(void) { if (doradamaged[0]) { doradamaged[0] = 0; fix_tilevec2_damage(&doradamaged[1],&doradisp[0],DORAWIDTH,dorawin); } } static void expose_dora(int x, int w) { if (expose_tilevec(&doradamaged[1],DORAWIDTH,x,w)) { doradamaged[0] = 1; pending_call(&update_dora,&doraupdid); } } static void update_repl(void) { if (repldamaged[0]) { repldamaged[0] = 0; fix_tilevec2_damage(&repldamaged[1],&repldisp[0],REPLWIDTH,replwin); } } static void expose_repl(int x, int w) { if (expose_tilevec(&repldamaged[1],REPLWIDTH,x,w)) { repldamaged[0] = 1; pending_call(&update_repl,&replupdid); } } static void doexpose(Window win, int x, int y, int w, int h, int c) { if (win == wallwin) { expose_wall(x,y,w,h); } else if (win == dorawin) { expose_dora(x,w); } else if (win == replwin) { expose_repl(x,w); } c=c; } static void handle_event(XEvent *e) { switch (e->type) { case ConfigureNotify: /* XConfigureEvent - xconfigure */ resize(e->xconfigure.width,e->xconfigure.height); break; case Expose: /* XExposeEvent - xexpose */ doexpose(e->xexpose.window,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); break; #if 0 case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ gotbuttonpress = 1; buttonpress_win = e->xbutton.window; buttonpress_x = e->xbutton.x; buttonpress_y = e->xbutton.y; if (animating) return; buttonevent(e->xbutton.window,e->xbutton.x,e->xbutton.y,e->xbutton.button,e->xbutton.time,1); break; case ButtonRelease: /* XButtonPressedEvent - XButtonEvent - xbutton */ if (animating) return; buttonevent(e->xbutton.window,e->xmotion.x,e->xmotion.y,e->xbutton.button,e->xbutton.time,0); break; case MotionNotify: /* XMotionEvent - xmotion */ gotmotion ++; break; case EnterNotify: /* XEnterWindowEvent - XCrossingEvent - xcrossing */ enterleave(e->xcrossing.window,e->xcrossing.x,e->xcrossing.y,1); break; case LeaveNotify: /* XLeaveWindowEvent - XCrossingEvent - xcrossing */ enterleave(e->xcrossing.window,e->xcrossing.x,e->xcrossing.y,0); break; #endif } } static int check_x(void) { int n; int rv; XEvent ev; rv = 0; while (1) { n = XEventsQueued(disp,QueuedAfterFlush); if (n == 0) break; rv = 1; for (;n>0;n--) { XNextEvent(disp,&ev); handle_event(&ev); } } return(rv); } static int x_block(void *arg __attribute__((__unused__))) { return(check_x()?BLOCK_LOOP:BLOCK_NIL); } static void rd_x(void *arg __attribute__((__unused__))) { check_x(); } static void set_wall_tiles(int newtiles) { int i; int newx; int neww; char *newtxt; int newtxtlen; XCharStruct newtxtext; int oldtiles; int oldx; int oldw; char *oldtxt; int oldtxtlen; XCharStruct oldtxtext; if (newtiles < 0) abort(); if (newtiles == walltiles) return; if (walltiles < 0) walltiles = 0; oldtiles = walltiles; oldx = wallNx; oldw = wallNw; oldtxt = wallNtxt; oldtxtlen = wallNtxtlen; oldtxtext = wallNtxtext; if (newtiles > WALLWIDTH*2) { newtxtlen = asprintf(&newtxt,"%d",newtiles); XTextExtents(font,newtxt,newtxtlen,XTE_JUNK,&newtxtext); neww = newtxtext.width; newx = ((WALLWIDTH * TILE_XSIZE) - neww) / 2; } else { newtxt = 0; newtxtlen = 0; neww = 0; newx = 0; } walltiles = newtiles; wallNx = newx; wallNw = neww; wallNtxt = newtxt; wallNtxtlen = newtxtlen; wallNtxtext = newtxtext; if (newtxt) { for (i=WALLWIDTH-1;i>=0;i--) { walldisp[i][0] = TILE_UNKTILE; walldisp[i][1] = TILE_UNKTILE; } if (newtiles & 1) walldisp[WALLWIDTH-1][0] = TILE_NOTILE; if (oldtxt) { if (neww > oldw) { expose_wall(newx,0,neww,TILE_YSIZE+stackshift); } else { expose_wall(oldx,0,oldw,TILE_YSIZE+stackshift); } if ((newtiles^oldtiles) & 1) expose_wall((WALLWIDTH-1)*TILE_XSIZE,0,TILE_XSIZE,TILE_YSIZE+stackshift); } else { expose_wall(newx,0,neww,TILE_YSIZE+stackshift); expose_wall((oldtiles/2)*TILE_XSIZE,0,(WALLWIDTH-(oldtiles/2))*TILE_XSIZE,TILE_YSIZE+stackshift); } } else { for (i=(WALLWIDTH*2)-1;i>=newtiles;i--) walldisp[i/2][1^(i&1)] = TILE_NOTILE; for (i=newtiles-1;i>=0;i--) walldisp[i/2][1^(i&1)] = TILE_UNKTILE; if (oldtxt) { expose_wall(oldx,0,oldw,TILE_YSIZE+stackshift); oldtiles = (WALLWIDTH * 2) - (oldtiles & 1); } if (newtiles < oldtiles) { expose_wall((newtiles/2)*TILE_XSIZE,0,(((oldtiles+1)/2)-(newtiles/2))*TILE_XSIZE,TILE_YSIZE+stackshift); } else { expose_wall((oldtiles/2)*TILE_XSIZE,0,(((newtiles+1)/2)-(oldtiles/2))*TILE_XSIZE,TILE_YSIZE+stackshift); } } } static void stdin_line(const char *l) { l=l; } static void rd_stdin(void *arg __attribute__((__unused__))) { char rb[512]; int rn; int i; static char *lb = 0; static int lba = 0; static int lbn = 0; void lbsave(char c) { if (lbn >= lba) lb = realloc(lb,lba=lbn+8); lb[lbn++] = c; } rn = read(0,&rb[0],sizeof(rb)); if (rn < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: read from stdin: %s\n",__progname,strerror(errno)); exit(1); } if (rn == 0) { fprintf(stderr,"%s: EOF on stdin\n",__progname); exit(1); } for (i=0;i=0;i--) { doradisp[i][0] = TILE_NOTILE; doradisp[i][1] = TILE_NOTILE; doradamaged[i+1] = 1; } doradamaged[0] = 1; } static void repl_init(void) { int i; replupdid = PL_NOID; for (i=REPLWIDTH-1;i>=0;i--) { repldisp[i][0] = TILE_NOTILE; repldisp[i][1] = TILE_NOTILE; repldamaged[i+1] = 1; } repldamaged[0] = 1; } void client_startup(void) { disp = open_display(displayname); setup_error(); scr = XDefaultScreenOfDisplay(disp); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); defdepth = XDefaultDepthOfScreen(scr); depth = defdepth; rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); defgc = XDefaultGCOfScreen(scr); setup_db(); read_db(); if (synch) XSynchronize(disp,True); setup_font(); setup_numbers(); setup_tiles(); setup_visual(); setup_colours(); setup_gc(); setup_windows(); xblockid = add_block_fn(&x_block,0); xreadid = add_poll_fd(XConnectionNumber(disp),&rwtest_always,&rwtest_never,&rd_x,0,0); wall_init(); dora_init(); repl_init(); add_poll_fd(0,&rwtest_always,&rwtest_never,&rd_stdin,0,0); } const char *client_name(void) { return("X client"); } void client_change_notify(void) { int i; int j; int n; TILE t; n = 0; for (i=WALLSIZE-1;i>=0;i--) if (wallpresent[i]) n ++; set_wall_tiles(n); for (i=REPLSIZE-1;i>=0;i--) { t = replpresent[i] ? TILE_UNKTILE : TILE_NOTILE; if (repldisp[i/2][i&1] != t) { repldisp[i/2][i&1] = t; repldamaged[0] = 1; repldamaged[1+(i/2)] = 1; pending_call(&update_repl,&replupdid); } } for (i=DORASIZE-1;i>=0;i--) { t = dorapresent[i] ? doratile[i] : TILE_NOTILE; j = DORAWIDTH - 1 - (i/2); if (doradisp[j][i&1] != t) { doradisp[j][i&1] = t; doradamaged[0] = 1; doradamaged[1+j] = 1; pending_call(&update_dora,&doraupdid); } } } void client_chat(int from, unsigned int otherdest, const char *text, int len) { from=from;otherdest=otherdest;text=text;len=len; } void client_play(void) { } void client_claim(void) { } void client_reject(const char *msg, int len) { msg=msg;len=len; }