#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNUSED_ARG(x) x __attribute__((__unused__)) #include "cards.h" #if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 5) #define HAVE_R5 #endif #define FULL_WIDTH_GRAY /* kludge around bug in some R4 servers */ #define CARD_MARGIN_X 20 /* pixels used as spacing around cards, horizontally */ #define CARD_MARGIN_Y 45 /* pixels used as spacing around cards, horizontally */ #define CARD_UP_OVL_Y 25 /* pixels to show of face-up card underneath another, vertically */ #define ANIM_TICK 50000 /* usec per tick when moving things automatically */ #define ANIM_SPEED 25 /* pixels per tick */ #define NPILE 10 #define PILEMAX 17 #define NWORK 4 #define NHOME 4 #define SAVEFILE_FMTVER 2 #define MAX(a,b) (((a)>(b)) ? (a) : (b)) #define MIN(a,b) (((a)<(b)) ? (a) : (b)) #define SECUSEC 1000000 #ifdef __GNUC__ #define flowsink __attribute__ ((noreturn)) #endif #if !defined(flowsink) #define flowsink #endif extern const char *__progname; static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *BorderMargin: 0\n\ *Gamma: 1.0\n\ *Font: fixed\n\ *Name: xseahaven\n\ *IconName: xseahaven\n\ *DeckPath: deck\n\ *BackNumber: 0\n\ *DragPixels: 10\n\ *IgnoreDragTime: 125\n\ *MaxClickTime: 300\n\ *DoubleClickIdle: 500\n\ *SaveFile: ~/.seahaven\n\ *SaveFileMode: 0600\n\ "; static char *xrmstring; static char *displayname; static char *geometryspec; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *bordermstr; static char *margincstr; static char *fontname; static char *name; static char *iconname; static char *deckpath; static char *dragpixstr; static char *igndragstr; static char *maxclickstr; static char *dblclickstr; static char *visualstr; static char *gammastr; static int runsync; static int invalid_save_exit; static int breaklock; static char *savefile; static char nosavefile_; #define nosavefile (&nosavefile_) static char *savefilemodestr; static char text_init_state[256]; static int argc; static char **argv; static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static Colormap defcmap; static Visual *visual; static int (*oldioerr)(Display *); static int (*olderr)(Display *, XErrorEvent *); static XColor fgcolor; static XColor bgcolor; static XColor bdcolor; static XColor mgcolor; typedef struct button BUTTON; struct button { const char *label; void (*clickfn)(BUTTON *); BUTTON *flink; BUTTON *blink; char *private; Window win; int textw; int winw; XCharStruct textsize; int pressed; Time pushtime; int disabled; } ; typedef struct whereis WHEREIS; struct whereis { unsigned char where; #define WHERE_NONE 0 #define WHERE_PILE 1 #define WHERE_HOME 2 #define WHERE_WORK 3 #define WHERE_TEMP 4 unsigned char pile; unsigned char cwp; } ; typedef struct undo UNDO; struct undo { UNDO *link; unsigned int boundary : 1; unsigned int fromfile : 1; unsigned int from : 3; unsigned int to : 3; unsigned int card : 6; unsigned char frompile; unsigned char topile; } ; typedef struct atom ATOM; struct atom { const char *name; Atom atom; } ; static GC drawgc; static GC xorgc; static Window topwin; static int desw; static int desh; static Window iwin; static Window cwin; static Window linewin; static Window gamewin; static Window menuwin; static Window scorewin; static int gotbuttonpress; static Window buttonpress_win; static int buttonpress_x; static int buttonpress_y; static Colormap wincmap; static int margin; static int borderwidth; static XFontStruct *font; static int deffont; static int baselineskip; static int drag_pixels; static int ignore_drag_time; static int max_click_time; static int dbl_click_time; static double gammafactor; static int autocheck; static int savefd = -1; static int savefilemode = 0666; static int games_won; static int games_lost; static int games_winnable; static int cur_winnable; static int scorex; static int scorey; static int scorew; static int scoreh; static Time seltime; static ATOM atoms[] = { { "PRIMARY" }, #define AX_PRIMARY 0 { "TEXT" }, #define AX_TEXT 1 { "STRING" }, #define AX_STRING 2 { "MULTIPLE" }, #define AX_MULTIPLE 3 { "TARGETS" }, #define AX_TARGETS 4 { "TIMESTAMP" }, #define AX_TIMESTAMP 5 { "ATOM_PAIR" }, #define AX_ATOM_PAIR 6 { "INTEGER" }, #define AX_INTEGER 7 { "ATOM" }, #define AX_ATOM 8 { 0 } }; static void newgame_click(BUTTON *); static void select_click(BUTTON *); static void undo_click(BUTTON *); static void save_click(BUTTON *); static void quit_click(BUTTON *); static BUTTON newgamebutton = { "New game", newgame_click }; static BUTTON selectbutton = { "Select", select_click }; static BUTTON undobutton = { "Undo", undo_click }; static BUTTON savebutton = { "Save", save_click }; static BUTTON quitbutton = { "Quit", quit_click }; static BUTTON *buttons[] = { &newgamebutton, &selectbutton, &undobutton, &savebutton, &quitbutton }; #define NBUTTONS (sizeof(buttons)/sizeof(buttons[0])) static int button_w; static Window deck_wins[52]; static XColor *deck_colors; static Pixmap deck_face[52]; static int c_x[52]; static int c_y[52]; static UNDO *undochain = 0; static WHEREIS whereis[52]; static int pile_x[NPILE]; static int pile_y[NPILE]; static int home_x[NHOME]; static int home_y[NHOME]; static int work_x[NWORK]; static int work_y[NWORK]; static int pile_n[NPILE]; static int pile_c[NPILE][PILEMAX]; static int work_c[NWORK]; #if NHOME != 4 #error NHOME wrong for suit vectors #endif static const int homesuit[NHOME] = { CARD_C, CARD_D, CARD_H, CARD_S }; static const int savesuit[NHOME] = { CARD_C, CARD_D, CARD_H, CARD_S }; static int homeheight[NHOME]; static Window carrywin; static Pixmap carrybg; static int carrybgn; static int carrywhole; static int carryseq; static int carryheight; static int *carrycards; static int carryx; static int carryy; static int carryoffx; static int carryoffy; static int carrystartt; static int carrystartx; static int carrystarty; static int carryclick; static int gotmotion; static BUTTON *allbuttons; static Pixmap gray_pm; static Pixmap outline_pm; typedef struct spaceloc SPACELOC; struct spaceloc { char flags; #define SL_PTR_X 0x01 #define SL_PTR_Y 0x02 int x; int y; int *xp; int *yp; #define SL_FIXED(x,y) { 0, x, y, 0, 0 } #define SL_PTRS(x,y) { SL_PTR_X|SL_PTR_Y, 0, 0, x, y, } } ; static SPACELOC spaceloc[] #if NHOME != 4 #error NHOME wrong for spaceloc[] #endif = { SL_PTRS(&home_x[0],&home_y[0]), SL_PTRS(&home_x[1],&home_y[1]), SL_PTRS(&home_x[2],&home_y[2]), SL_PTRS(&home_x[3],&home_y[3]), #if NWORK != 4 #error NWORK wrong for spaceloc[] #endif SL_PTRS(&work_x[0],&work_y[0]), SL_PTRS(&work_x[1],&work_y[1]), SL_PTRS(&work_x[2],&work_y[2]), SL_PTRS(&work_x[3],&work_y[3]), #if NPILE != 10 #error NPILE wrong for spaceloc[] #endif SL_PTRS(&pile_x[0],&pile_y[0]), SL_PTRS(&pile_x[1],&pile_y[1]), SL_PTRS(&pile_x[2],&pile_y[2]), SL_PTRS(&pile_x[3],&pile_y[3]), SL_PTRS(&pile_x[4],&pile_y[4]), SL_PTRS(&pile_x[5],&pile_y[5]), SL_PTRS(&pile_x[6],&pile_y[6]), SL_PTRS(&pile_x[7],&pile_y[7]), SL_PTRS(&pile_x[8],&pile_y[8]), SL_PTRS(&pile_x[9],&pile_y[9]), }; #define NSPACE (sizeof(spaceloc)/sizeof(spaceloc[0])) static Window spacewins[NSPACE]; static int junk_direction; static int junk_font_ascent; static int junk_font_descent; #define XTE_JUNK &junk_direction,&junk_font_ascent,&junk_font_descent 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 != '-') { fprintf(stderr,"%s: unrecognized 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,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryspec = av[skip]; continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); foreground = av[skip]; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); background = 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,"-margincolor") || !strcmp(*av,"-mc")) { WANTARG(); margincstr = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-gamma")) { WANTARG(); gammastr = av[skip]; continue; } if (!strcmp(*av,"-savefile")) { WANTARG(); savefile = av[skip]; continue; } if (!strcmp(*av,"-nosavefile")) { savefile = nosavefile; continue; } if (!strcmp(*av,"-savefilemode")) { WANTARG(); savefilemodestr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); name = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-font") || !strcmp(*av,"-fn")) { WANTARG(); fontname = av[skip]; continue; } if (!strcmp(*av,"-deck")) { WANTARG(); deckpath = av[skip]; continue; } if (!strcmp(*av,"-sync")) { runsync = 1; continue; } if (!strcmp(*av,"-breaklock")) { breaklock = 1; continue; } if (!strcmp(*av,"-ivs-debug")) { invalid_save_exit = 1; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&xrmstring,"\n",av[skip],(char *)0); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) { exit(1); } } static Display *open_display(char *d) { Display *rv; rv = XOpenDisplay(d); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(d)); exit(1); } return(rv); } static void setup_db(void) { char *str; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #ifdef HAVE_R5 str = XScreenResourceString(scr); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #endif if (xrmstring) { db2 = XrmGetStringDatabase(xrmstring); XrmMergeDatabases(db2,&db); } } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static char *get_default_value(const char *n, const char *c) { char *type; XrmValue value; if (XrmGetResource(db,n,c,&type,&value) == False) return(0); return(value.addr); } static Pixmap pmsetup(unsigned char bmc[CARD_YSIZE][CARD_XSIZE], unsigned char bmbw[CARD_YSIZE][CARD_BW_XBYTES]) { Pixmap rv; if (depth == 1) { static XImage *i = 0; static unsigned char buf[((CARD_XSIZE+7)>>3)*CARD_YSIZE]; #if CARD_BW_XBYTES != ((CARD_XSIZE+7)>>3) #error CARD_BW_XBYTES and CARD_XSIZE do not agree #endif static GC gc; int x; int y; int n; int acc; unsigned char *bp; if (i == 0) { i = XCreateImage(disp,visual,1,XYBitmap,0,(char *)&buf[0],CARD_XSIZE,CARD_YSIZE,8,(CARD_XSIZE+7)>>3); i->bitmap_unit = 8; i->bitmap_bit_order = MSBFirst; rv = XCreatePixmap(disp,rootwin,1,1,1); gc = XCreateGC(disp,rv,0L,(XGCValues *)0); XSetBackground(disp,gc,0L); XSetForeground(disp,gc,1L); XFreePixmap(disp,rv); } rv = XCreatePixmap(disp,rootwin,CARD_XSIZE,CARD_YSIZE,1); if (bmbw) { bcopy((char *)&bmbw[0][0],(char *)&buf[0],CARD_YSIZE*CARD_BW_XBYTES); } else { acc = 0; bp = &buf[0]; for (y=0;y 7) { *bp++ = acc; n = 0; } acc <<= 1; if (deck_colors[bmc[y][x]].pixel) acc |= 1; n ++; } if (n > 0) { *bp++ = acc << (8 - n); } } } XPutImage(disp,rv,gc,i,0,0,0,0,CARD_XSIZE,CARD_YSIZE); } else if (depth == 8) { static XImage *i = 0; static unsigned char buf[CARD_YSIZE*CARD_XSIZE]; static GC gc; int x; int y; unsigned char *bp; if (i == 0) { i = XCreateImage(disp,visual,8,ZPixmap,0,(char *)&buf[0],CARD_XSIZE,CARD_YSIZE,8,CARD_XSIZE); i->bitmap_unit = 8; rv = XCreatePixmap(disp,rootwin,1,1,8); gc = XCreateGC(disp,rv,0L,(XGCValues *)0); XFreePixmap(disp,rv); } bp = &buf[0]; for (y=0;y>3)*CARD_YSIZE]; static GC gc1; static GC gcn; static Pixmap tmp; int x; int y; int n; int acc; int m; int c; unsigned char *bp; if (i == 0) { i = XCreateImage(disp,visual,1,XYBitmap,0,(char *)&buf[0],CARD_XSIZE,CARD_YSIZE,8,(CARD_XSIZE+7)>>3); i->bitmap_unit = 8; i->bitmap_bit_order = MSBFirst; tmp = XCreatePixmap(disp,rootwin,CARD_XSIZE,CARD_YSIZE,1); gc1 = XCreateGC(disp,tmp,0L,(XGCValues *)0); XSetBackground(disp,gc1,0L); XSetForeground(disp,gc1,1L); rv = XCreatePixmap(disp,rootwin,1,1,depth); gcn = XCreateGC(disp,rv,0L,(XGCValues *)0); XSetBackground(disp,gcn,0L); XFreePixmap(disp,rv); } rv = XCreatePixmap(disp,rootwin,CARD_XSIZE,CARD_YSIZE,depth); XSetForeground(disp,gcn,0L); XSetFunction(disp,gcn,GXcopy); XFillRectangle(disp,rv,gcn,0,0,CARD_XSIZE,CARD_YSIZE); XSetFunction(disp,gcn,GXor); for (c=0;c 7) { *bp++ = acc; n = 0; } acc <<= 1; if (bmc[y][x] == c) { acc |= 1; m ++; } n ++; } if (n > 0) { *bp++ = acc << (8 - n); } } if (m > 0) { XPutImage(disp,tmp,gc1,i,0,0,0,0,CARD_XSIZE,CARD_YSIZE); XSetForeground(disp,gcn,deck_colors[c].pixel); XCopyPlane(disp,tmp,rv,gcn,0,0,CARD_XSIZE,CARD_YSIZE,0,0,1L); } } } return(rv); } static void setup_atoms(void) { int i; for (i=0;atoms[i].name;i++) { atoms[i].atom = XInternAtom(disp,atoms[i].name,False); } } static void setup_deck(void) { if (card_loaddeck(deckpath) < 0) { perror(deckpath); exit(1); } deck_colors = (XColor *) malloc(card_ncol*sizeof(XColor)); } static void setup_font(void) { deffont = 0; font = fontname ? XLoadQueryFont(disp,fontname) : 0; if (fontname && !font) { fprintf(stderr,"%s: can't load font %s, using server default\n",__progname,fontname); deffont = 1; } baselineskip = font->ascent + font->descent; } static void setup_numbers(void) { if (bordermstr) margin = atoi(bordermstr); if (borderwstr) borderwidth = atoi(borderwstr); if (dragpixstr) drag_pixels = atoi(dragpixstr); if (igndragstr) ignore_drag_time = atoi(igndragstr); if (maxclickstr) max_click_time = atoi(maxclickstr); if (dblclickstr) dbl_click_time = atoi(dblclickstr); if (gammastr) gammafactor = 1 / atof(gammastr); else gammafactor = 1; if (savefilemodestr) savefilemode = strtol(savefilemodestr,0,8); seltime = CurrentTime; } static void setup_xcolor(XColor *col, const char *what) { while (1) { if (XAllocColor(disp,wincmap,col) == 0) { if (wincmap != defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for color %s\n",__progname,what); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); continue; } break; } } static void setup_color(char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } setup_xcolor(col,str); } static unsigned short int gammamap(unsigned short int x) { signed long int v; if (gammafactor == 1) return(x); v = 65535.9999 * pow(x/65535.0,gammafactor); if (v < 0) v = 0; if (v > 65535) v = 65535; return(v); } static void setup_colors(void) { int i; if (defcmap == None) { wincmap = XCreateColormap(disp,rootwin,visual,AllocNone); } else { wincmap = defcmap; } setup_color(foreground,&fgcolor); setup_color(background,&bgcolor); setup_color(bordercstr,&bdcolor); setup_color(margincstr,&mgcolor); for (i=0;ilabel,strlen(b->label),XTE_JUNK,&b->textsize); b->textw = b->textsize.rbearing - b->textsize.lbearing; b->winw = b->textw; b->pressed = 0; b->flink = allbuttons; b->blink = 0; if (b->flink) b->flink->blink = b; allbuttons = b; } static void setup_buttons(void) { int i; button_w = 0; for (i=0;itextw; } } static char *deconst(const char *s) { char *rv; bcopy(&s,&rv,sizeof(char *)); return(rv); } static void makebuttonwindow(BUTTON *b, Window parent) { unsigned long int attrmask; XSetWindowAttributes attr; attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask; attrmask |= CWEventMask; b->win = XCreateWindow(disp,parent,0,0,1,1,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,b->win); } static void reset_scorewin(void) { XMoveResizeWindow(disp,scorewin,scorex,scorey,scorew,scoreh); } static void redraw_score(int clearfirst) { int w; int x; XCharStruct size; char sbuf[128]; if (games_won+games_lost == 0) { sprintf(&sbuf[0]," No games finished "); } else { sprintf(&sbuf[0]," Played %d, won %d, lost %d (%d%%) ",games_won+games_lost,games_won,games_lost,(games_won*100)/(games_won+games_lost)); } XTextExtents(font,&sbuf[0],strlen(&sbuf[0]),XTE_JUNK,&size); x = (size.lbearing < 0) ? - size.lbearing : 0; w = x + size.rbearing; if (w < x+size.width) w = x + size.width; if (w != scorew) { scorex -= w - scorew; scorew = w; reset_scorewin(); /* let the resulting Expose trigger real redisplay (it will anyway) */ } else { if (clearfirst) XClearWindow(disp,scorewin); XDrawImageString(disp,scorewin,drawgc,x,font->ascent,&sbuf[0],strlen(&sbuf[0])); } } static void resize(int topw, int toph) { int x; int y; int hm; int dx; int i; y = 0; hm = topw - button_w; XMoveResizeWindow(disp,menuwin,0,y,topw,baselineskip); x = 0; for (i=0;iwin,x,0,buttons[i]->winw,baselineskip); x += buttons[i]->winw; } y += baselineskip; XMoveResizeWindow(disp,linewin,0,y,topw,borderwidth); scorex = topw - borderwidth - 1; scorew = 1; scorey = y; scoreh = baselineskip + (2 * borderwidth); reset_scorewin(); redraw_score(1); y += borderwidth; x = (topw - desw) / 2; XResizeWindow(disp,cwin,desw,toph-y); XMoveResizeWindow(disp,gamewin,x,y,desw,toph-y); } 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; desw = MAX( CARD_MARGIN_X + (NPILE * (CARD_XSIZE+CARD_MARGIN_X)) , (CARD_MARGIN_X*3) + ((NHOME+NWORK) * (CARD_XSIZE+CARD_MARGIN_X)) ); desh = (CARD_YSIZE * 2) + (CARD_MARGIN_Y * 3) + (CARD_UP_OVL_Y * (PILEMAX-1)); w = borderwidth + margin + desw + margin + borderwidth; h = borderwidth + margin + baselineskip + margin + borderwidth + margin + desh + margin + 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 = mgcolor.pixel; attr.border_pixel = bdcolor.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; topwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,visual,CWBackPixel|CWBorderPixel|CWEventMask|CWDontPropagate|CWColormap,&attr); wn_prop.value = (unsigned char *) name; wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = (unsigned char *) 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 = 1; normal_hints.min_height = 1; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = True; class_hints.res_name = deconst("xseahaven"); class_hints.res_class = deconst("XCardGame"); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); gamewin = XCreateWindow(disp,topwin,0,0,desw,desh,0,depth,InputOutput,CopyFromParent,0UL,&attr); XMapWindow(disp,gamewin); attr.background_pixel = bgcolor.pixel; menuwin = XCreateWindow(disp,topwin,0,0,1,1,0,depth,InputOutput,CopyFromParent,CWBackPixel,&attr); XMapWindow(disp,menuwin); for (i=0;ifid); } } XSetForeground(disp,drawgc,fgcolor.pixel); XSetBackground(disp,drawgc,bgcolor.pixel); xorgc = XCreateGC(disp,topwin,0L,(XGCValues *)0); XSetFunction(disp,xorgc,GXxor); XSetForeground(disp,xorgc,fgcolor.pixel^bgcolor.pixel); XRaiseWindow(disp,iwin); XMapRaised(disp,topwin); resize(w,h); } #define RND_N 63 static unsigned long int rnd_state[RND_N]; static int rnd_h1 = -1; static int rnd_h2; static int rnd_th; static void rnd_init(void) { struct timeval tv; struct stat stb; int i; if (rnd_h1 >= 0) return; stat(".",&stb); gettimeofday(&tv,0); bcopy(&stb,&rnd_state[0],MIN(sizeof(rnd_state),sizeof(stb))); for (i=0;i0;i--) { if (++rnd_h1 >= RND_N) rnd_h1 = 0; if (++rnd_h2 >= RND_N) rnd_h2 = 0; rnd_state[rnd_h2] += rnd_state[rnd_h1]; } rnd_th = 0; } static unsigned long int rnd(void) { rnd_init(); if (++rnd_h1 >= RND_N) rnd_h1 = 0; if (++rnd_h2 >= RND_N) rnd_h2 = 0; return(rnd_state[rnd_h2]+=rnd_state[rnd_h1]); } static void rnd_tweak(const void *data, int nb) { const unsigned char *bp; rnd_init(); bp = data; for (;nb>0;nb--) { if (++rnd_th >= RND_N) rnd_th = 0; rnd_state[rnd_th] ^= *bp++; rnd_state[rnd_th] = (rnd_state[rnd_th] >> 1) ^ ((rnd_state[rnd_th] & 1) ? 0xedb88320 : 0); } } static void movecard(int c, int x, int y) { XMoveWindow(disp,deck_wins[c],x,y); c_x[c] = x; c_y[c] = y; } static void reset_homecards(int p) { Window wins[13]; int wincnt; int i; if (homeheight[p] > 1) { wincnt = 0; for (i=homeheight[p]-1;i>=0;i--) wins[wincnt++] = deck_wins[CARD_MAKE(homesuit[p],CARD_A+i)]; XRestackWindows(disp,&wins[0],wincnt); } for (i=0;i 1) { Window wins[PILEMAX]; int wincnt; wincnt = 0; for (i=pile_n[p]-1;i>=0;i--) wins[wincnt++] = deck_wins[pile_c[p][i]]; XRestackWindows(disp,&wins[0],wincnt); } for (i=pile_n[p]-1;i>=0;i--) { c = pile_c[p][i]; movecard(c,pile_x[p],pile_y[p]+(i*CARD_UP_OVL_Y)); } } static UNDO *pop_undochain(void) { UNDO *u; u = undochain; undochain = u->link; return(u); } static void playvar_init(void) { carrycards = 0; autocheck = 1; } static void setup_locs(void) { int i; #if NHOME != 4 #error NHOME wrong for setup_locs() #endif home_x[0] = CARD_MARGIN_X; home_x[1] = CARD_MARGIN_X + CARD_XSIZE + CARD_MARGIN_X; home_x[2] = desw - (CARD_MARGIN_X + CARD_XSIZE + CARD_MARGIN_X) - CARD_XSIZE; home_x[3] = desw - CARD_MARGIN_X - CARD_XSIZE; for (i=0;i= 52)) { *cp++ = '?'; *cp++ = '?'; } else { switch (CARD_VALUE(c)) { case CARD_A: *cp++ = 'A'; break; case CARD_2: *cp++ = '2'; break; case CARD_3: *cp++ = '3'; break; case CARD_4: *cp++ = '4'; break; case CARD_5: *cp++ = '5'; break; case CARD_6: *cp++ = '6'; break; case CARD_7: *cp++ = '7'; break; case CARD_8: *cp++ = '8'; break; case CARD_9: *cp++ = '9'; break; case CARD_T: *cp++ = 'T'; break; case CARD_J: *cp++ = 'J'; break; case CARD_Q: *cp++ = 'Q'; break; case CARD_K: *cp++ = 'K'; break; default: *cp++ = '~'; break; } switch (CARD_SUIT(c)) { case CARD_C: *cp++ = 'c'; break; case CARD_D: *cp++ = 'd'; break; case CARD_H: *cp++ = 'h'; break; case CARD_S: *cp++ = 's'; break; default: *cp++ = '~'; break; } } *cpp = cp; } static void set_text_init_state(void) { char *cp; int i; int j; cp = &text_init_state[0]; addcard(&cp,work_c[1]); addcard(&cp,work_c[2]); *cp++ = ' '; for (i=0;i sizeof(text_init_state)) abort(); } static void setup_game(void) { int i; int j; int c; int sptr; char shuf[52]; for (i=0;i0;i--) { j = rnd() % (i+1); if (j != i) { c = shuf[i]; shuf[i] = shuf[j]; shuf[j] = c; } } sptr = 51; #define GETCARD() (shuf[sptr--]) #define PEEKCARD() (shuf[sptr]) work_c[0] = -1; work_c[3] = -1; for (i=0;i<2;i++) { c = GETCARD(); work_c[i+1] = c; whereis[c].where = WHERE_WORK; whereis[c].pile = i + 1; movecard(c,work_x[i+1],work_y[i+1]); } for (i=0;i<10;i++) { pile_n[i] = 5; for (j=0;j<5;j++) { c = GETCARD(); pile_c[i][j] = c; whereis[c].where = WHERE_PILE; whereis[c].pile = i; whereis[c].cwp = j; } reset_pilecards(i); } #undef GETCARD #undef PEEKCARD playvar_init(); while (undochain) free(pop_undochain()); cur_winnable = game_is_winnable(); set_text_init_state(); } static char *expand_tilde(const char *path) { const char *pp; char *tstr; char *rv; if (path[0] != '~') return(deconst(path)); for (pp=path+1;(*pp)&&(*pp!='/');pp++) ; if (pp == &path[1]) { tstr = getenv("HOME"); if (tstr == 0) { fprintf(stderr,"%s: no $HOME to expand %s\n",__progname,path); exit(1); } } else { char *ntmp; struct passwd *pw; ntmp = malloc((pp-(path+1))+1); bcopy(path+1,ntmp,pp-(path+1)); ntmp[pp-(path+1)] = '\0'; pw = getpwnam(ntmp); if (pw == 0) { fprintf(stderr,"%s: no user `%s' to expand %s\n",__progname,ntmp,path); exit(1); } free(ntmp); tstr = pw->pw_dir; } rv = malloc(strlen(tstr)+strlen(pp)+1); if (rv) sprintf(rv,"%s%s",tstr,pp); return(rv); } #define SAVESIZE 256 #define CRCPOLY 0xedb88320 static unsigned long int crctable[256] = { 0, 0 }; /* xdigs[] and xval() must agree! */ static const char xdigs[16] = "0123456789abcdef"; static int xval(char c) { switch (c) { case '0': return(0); break; case '1': return(1); break; case '2': return(2); break; case '3': return(3); break; case '4': return(4); break; case '5': return(5); break; case '6': return(6); break; case '7': return(7); break; case '8': return(8); break; case '9': return(9); break; case 'a': return(10); break; case 'b': return(11); break; case 'c': return(12); break; case 'd': return(13); break; case 'e': return(14); break; case 'f': return(15); break; } return(-1); } static void initcrc(void) { int i; unsigned long int crc; int j; if (crctable[1] != 0) return; for (i=0;i<256;i++) { crc = 0; for (j=0;j<8;j++) crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0); crctable[i] = crc; } } static void savefile_write(const void *buf) { static char tmp[(SAVESIZE*2)+8]; unsigned char c; unsigned long int crc; int i; char *tp; initcrc(); crc = 0; tp = &tmp[0]; for (i=0;i>4]; crc = (crc >> 8) ^ crctable[(crc^c)&0xff]; } for (i=0;i<8;i++) { *tp++ = xdigs[crc&0xf]; crc >>= 4; } write(savefd,tmp,(SAVESIZE*2)+8); } static int savefile_read(void *buf) { static char tmp[(SAVESIZE*2)+8]; char *tp; unsigned long int crc; int i; int vh; int v; initcrc(); if (read(savefd,tmp,(SAVESIZE*2)+8) != (SAVESIZE*2)+8) return(1); crc = 0; tp = &tmp[0]; for (i=0;i> 8) ^ crctable[(crc^v)&0xff]; } for (i=0;i<8;i++) { if (*tp++ != xdigs[crc&0xf]) return(1); crc >>= 4; } return(0); } static void write_savefile(void) { unsigned char savebuf[SAVESIZE]; unsigned char keybuf[SAVESIZE]; int keylen; unsigned char *dbeg; unsigned char *dend; unsigned char *keyend; unsigned char *key; unsigned char *sbp; UNDO *u; int i; int j; int sbx; static unsigned char keybyte(void) { if (key >= keyend) key = &keybuf[0]; return(*key++); } static void obyte(unsigned char c) { savebuf[sbx++] = keybyte() ^ c; if (sbx >= SAVESIZE) { savefile_write(&savebuf[0]); sbx = 0; } } static void put_uint(unsigned int v) { while (v > 127) { *sbp++ = (v & 0x7f) | 0x80; v >>= 7; } *sbp++ = v; } if (savefd < 0) return; lseek(savefd,0,SEEK_SET); sbp = &savebuf[0]; *sbp++ = SAVEFILE_FMTVER & 0xff; *sbp++ = SAVEFILE_FMTVER >> 8; dbeg = sbp; *sbp++ = NPILE; *sbp++ = NWORK; *sbp++ = NHOME; *sbp++ = 52; put_uint(5); for (u=undochain,i=0;u;u=u->link,i++) ; put_uint(i); put_uint(games_won); put_uint(games_lost); put_uint((games_winnable<<1)+!!cur_winnable); put_uint(strlen(&text_init_state[0])); for (i=0;i> 16) & 0xff; bcopy(&keybuf[0],dbeg,keylen); sbp = &savebuf[SAVESIZE-i]; for (j=0;jlink) { if (u->boundary) { obyte(0); } else { obyte(u->card+1); obyte((u->from<<4)+u->to); obyte(u->frompile); obyte(u->topile); } } while (sbx > 0) obyte(rnd()); ftruncate(savefd,lseek(savefd,0,SEEK_CUR)); } static int ioerr(Display *d) { write_savefile(); return((*oldioerr)(d)); } #include static int err(Display *d, XErrorEvent *e) { write_savefile(); return((*olderr)(d,e)); } static int badundo(UNDO *u) { static int chk(int wh, int p) { switch (wh) { case WHERE_PILE: if (p > NPILE) return(1); break; case WHERE_HOME: if (p > NHOME) return(1); break; case WHERE_WORK: if (p > NWORK) return(1); break; case WHERE_TEMP: break; default: return(1); break; } return(0); } return(!u->boundary&&(chk(u->from,u->frompile)||chk(u->to,u->topile))); } static int read_savefile(void) { __label__ fail; unsigned char savebuf[SAVESIZE]; unsigned char keybuf[SAVESIZE]; int keylen; unsigned char *keyend; unsigned char *key; int fmtver; int npile; int nwork; int nhome; int ncards; int nundo; UNDO *undolist; UNDO **undotail; UNDO *u; UNDO ut; int i; int j; int k; WHEREIS cwh[52]; int hh[NHOME]; int wc[NWORK]; int pn[NPILE]; int pc[NPILE][PILEMAX]; int tislen; char tis[sizeof(text_init_state)]; int gw; int gl; int chw; int c; int sbx; static unsigned char keybyte(void) { if (key >= keyend) key = &keybuf[0]; return(*key++); } static void fail(const char *, ...) __attribute__((format(printf,1,2))); static void fail(const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: invalid savefile %s: ",__progname,savefile); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); if (invalid_save_exit) exit(1); goto fail; } static unsigned char ibyte(void) { if (sbx >= SAVESIZE) { if (savefile_read(&savebuf[0])) fail("file read failed"); sbx = 0; } return(savebuf[sbx++]^keybyte()); } static unsigned int get_uint(void) { unsigned int v; unsigned int s; unsigned int c; v = 0; s = 0; do { c = ibyte(); v |= (c & 0x7f) << s; s += 7; } while (c & 0x80); return(v); } if (savefile_read(&savebuf[0])) return(1); fmtver = savebuf[0] + (savebuf[1] << 8); if (fmtver != SAVEFILE_FMTVER) return(1); i = savebuf[2]; keylen = &savebuf[SAVESIZE-i] - &savebuf[3]; if (keylen < 1) return(1); bcopy(&savebuf[3],&keybuf[0],keylen); keyend = &keybuf[keylen]; key = keyend; undotail = &undolist; sbx = SAVESIZE - i; npile = ibyte(); nwork = ibyte(); nhome = ibyte(); ncards = ibyte(); if ( (npile != NPILE) || (nwork != NWORK) || (nhome != NHOME) || (ncards != 52) ) return(1); c = get_uint(); nundo = (c >= 1) ? get_uint() : 0; if (nundo < 0) fail("nundo < 0"); gw = (c >= 2) ? get_uint() : 0; if (gw < 0) fail("gw < 0"); gl = (c >= 3) ? get_uint() : 0; if (gl < 0) fail("gl < 0"); chw = (c >= 4) ? get_uint() : 0; if (chw < 0) fail("chw < 0"); tislen = (c >= 5) ? get_uint() : 0; if (tislen < 0) fail("tislen < 0"); if (tislen >= sizeof(text_init_state)) fail("tislen too large"); for (i=0;i<52;i++) cwh[i].where = WHERE_NONE; for (i=0;i 13) fail("invalid homeheight[%d]=%d",j,hh[j]); for (k=0;k 52) fail("invalid work[%d]=%d",i,((int)c)-1); c --; if (c >= 0) { if (cwh[c].where != WHERE_NONE) fail("card %d appears twice",c); cwh[c].where = WHERE_WORK; cwh[c].pile = i; } wc[i] = c; } for (i=0;i PILEMAX) fail("invalid pile_n[%d]=%d",i,pn[i]); for (j=0;j= 52) fail("invalid pile_c[%d][%d]=%d",i,j,c); if (cwh[c].where != WHERE_NONE) fail("card %d appears twice",c); pc[i][j] = c; cwh[c].where = WHERE_PILE; cwh[c].pile = i; cwh[c].cwp = j; } } for (i=0;i<52;i++) if (cwh[i].where == WHERE_NONE) fail("card %d doesn't appear",i); for (i=0;i0;nundo--) { i = ibyte(); if (i > 52) fail("invalid undo card %d",((int)i)-1); if (i == 0) { ut.boundary = 1; } else { ut.boundary = 0; ut.card = i - 1; i = ibyte(); ut.from = i >> 4; ut.to = i; ut.frompile = ibyte(); ut.topile = ibyte(); } if (badundo(&ut)) fail("invalid undo, badundo() true"); u = malloc(sizeof(UNDO)); *u = ut; *undotail = u; undotail = &u->link; } for (i=0;i> 1; cur_winnable = chw & 1; bcopy(&tis[0],&text_init_state[0],tislen); text_init_state[tislen] = '\0'; playvar_init(); redraw_score(1); return(0); fail:; *undotail = 0; while (undolist) { u = undolist; undolist = u->link; free(u); } return(1); } static void break_save_lock(void) { } static void lock_savefile(void) { } static void unlock_savefile(void) { } static void new_session(void) { setup_game(); games_won = 0; games_lost = 0; games_winnable = 0; redraw_score(1); } static void setup_save(void) { if (!savefile || (savefile == nosavefile)) { new_session(); return; } savefile = expand_tilde(savefile); while (1) { savefd = open(savefile,O_RDWR,0); if (savefd >= 0) break; savefd = open(savefile,O_RDWR|O_CREAT|O_EXCL,savefilemode); if (savefd >= 0) { new_session(); write_savefile(); return; } if (errno != EEXIST) { fprintf(stderr,"%s: can't open %s (%s), switching to -nosavefile\n",__progname,savefile,strerror(errno)); savefile = nosavefile; return; } } lock_savefile(); if (read_savefile()) { new_session(); write_savefile(); } unlock_savefile(); } static void setup_spaces(void) { int i; int x; int y; for (i=0;i= desw) x = desw - 1; if (y < 0) y = 0; else if (y >= desh) y = desh - 1; carryx = x - carryoffx; carryy = y - carryoffy; if ( (abs(x-carrystartx) > drag_pixels) || (abs(y-carrystarty) > drag_pixels) ) carryclick = 0; XMoveWindow(disp,carrywin,carryx,carryy); } static void redrawbutton(BUTTON *b) { XClearArea(disp,b->win,0,0,b->winw,baselineskip,False); XDrawString(disp,(Drawable)b->win,drawgc,((b->winw-b->textw)/2)-b->textsize.lbearing,font->ascent,b->label,strlen(b->label)); if (b->pressed) XFillRectangle(disp,(Drawable)b->win,xorgc,0,0,b->winw,baselineskip); } static void buttonleave(BUTTON *b) { if (b->pressed) { b->pressed = 0; redrawbutton(b); } } static void enterleave(Window win, UNUSED_ARG(int x), UNUSED_ARG(int y), int enterp) { BUTTON *b; if (enterp) return; for (b=allbuttons;b;b=b->flink) { if (win == b->win) { buttonleave(b); return; } } } static void doexpose(Window win, UNUSED_ARG(int x), UNUSED_ARG(int y), UNUSED_ARG(int w), UNUSED_ARG(int h), int count) { BUTTON *b; if (count != 0) return; if (win == scorewin) { redraw_score(0); return; } for (b=allbuttons;b;b=b->flink) { if (win == b->win) { redrawbutton(b); return; } } } static void buttonbutton(BUTTON *b, UNUSED_ARG(int bno), Time t, int downp) { if (b->disabled) return; if (downp) { b->pressed = 1; b->pushtime = t; redrawbutton(b); } else { if (b->pressed) { b->pressed = 0; (*b->clickfn)(b); redrawbutton(b); } } } static int xyincardat(int x, int y, int cx, int cy) { return((x >= cx) && (y >= cy) && (x < cx+CARD_XSIZE) && (y < cy+CARD_YSIZE)); } static void resize_carry(int ncards) { int y; if (carrybgn == ncards) return; carrybgn = ncards; y = CARD_YSIZE + ((ncards - 1) * CARD_UP_OVL_Y); XFreePixmap(disp,carrybg); carrybg = XCreatePixmap(disp,carrywin,CARD_XSIZE,y,depth); XResizeWindow(disp,carrywin,CARD_XSIZE,y); } static void startcarry1(Time when, int x, int y, int cx, int cy, int ncards) { carrystartt = when; carrystartx = x; carrystarty = y; carryoffx = x - cx; carryoffy = y - cy; carryx = x; carryy = y; carryclick = 1; resize_carry(ncards); XMoveWindow(disp,carrywin,cx,cy); carryheight = ncards; carrycards = malloc(ncards*sizeof(int)); } static void startcarry2(void) { int i; XCopyArea(disp,deck_face[carrycards[carryheight-1]],carrybg,drawgc,0,0,CARD_XSIZE,CARD_YSIZE,0,(carryheight-1)*CARD_UP_OVL_Y); for (i=carryheight-2;i>=0;i--) { XCopyArea(disp,deck_face[carrycards[i]],carrybg,drawgc,0,0,CARD_XSIZE,CARD_UP_OVL_Y,0,i*CARD_UP_OVL_Y); } XSetWindowBackgroundPixmap(disp,carrywin,carrybg); XMapRaised(disp,carrywin); for (i=carryheight-1;i>=0;i--) XUnmapWindow(disp,deck_wins[carrycards[i]]); } static void pickup_card(int c, Time when, int x, int y) { startcarry1(when,x,y,c_x[c],c_y[c],1); carrycards[0] = c; carrywhole = (whereis[c].where == WHERE_PILE) && (pile_n[whereis[c].pile] == 1); startcarry2(); } static void pickup_sequence(int p, int n, Time when, int x, int y) { int i; startcarry1(when,x,y,pile_x[p],c_y[pile_c[p][pile_n[p]-n]],n); for (i=0;i 0) { XEvent e; do { XNextEvent(disp,&e); handle_event(&e,1); if (gotbuttonpress) { XPutBackEvent(disp,&e); return(1); } } while (XEventsQueued(disp,QueuedAfterReading) > 0); } return(0); } static int anim_move(Window win, int x1, int y1, int x2, int y2) { int nticks; int tickno; struct timeval nexttick; struct timeval now; int xfd; nticks = (hypot((double)(x2-x1),(double)(y2-y1)) / ANIM_SPEED) + 1; tickno = 0; gotbuttonpress = 0; xfd = XConnectionNumber(disp); gettimeofday(&nexttick,(struct timezone *)0); while (tickno < nticks) { if (anim_event_loop()) break; gettimeofday(&now,(struct timezone *)0); if ( (now.tv_sec < nexttick.tv_sec) || ( (now.tv_sec == nexttick.tv_sec) && (now.tv_usec < nexttick.tv_usec) ) ) { struct timeval delta; fd_set fds; XFlush(disp); delta.tv_sec = nexttick.tv_sec - now.tv_sec; delta.tv_usec = nexttick.tv_usec - now.tv_usec; if (delta.tv_usec < 0) { delta.tv_usec += SECUSEC; delta.tv_sec --; } FD_ZERO(&fds); FD_SET(xfd,&fds); select(FD_SETSIZE,&fds,(fd_set *)0,(fd_set *)0,&delta); continue; } XMoveWindow(disp,win,((x1*(nticks-tickno))+(x2*tickno))/nticks,((y1*(nticks-tickno))+(y2*tickno))/nticks); tickno ++; nexttick.tv_usec += ANIM_TICK; while (nexttick.tv_usec >= SECUSEC) { nexttick.tv_usec -= SECUSEC; nexttick.tv_sec ++; } } XMoveWindow(disp,win,x2,y2); return(gotbuttonpress); } static void raisecard(int c) { XRaiseWindow(disp,deck_wins[c]); } static void endcarry(void) { int i; for (i=0;i= PILEMAX) goto lose; whereis[c].cwp = pile_n[pile]; pile_c[pile][pile_n[pile]] = c; x = pile_x[pile]; y = pile_y[pile] + (pile_n[pile] * CARD_UP_OVL_Y); pile_n[pile] ++; break; case WHERE_TEMP: x = - CARD_XSIZE; y = - CARD_YSIZE; break; default: goto lose; break; } whereis[c].where = where; whereis[c].pile = pile; raisecard(c); movecard(c,x,y); return(0); } static void replace_card(int c) { place_card(c,whereis[c].where,whereis[c].pile,1); } static void undo_record(int c, int where, int pile) { UNDO *u; u = malloc(sizeof(UNDO)); if (c < 0) { u->boundary = 1; } else { u->boundary = 0; u->card = c; u->from = whereis[c].where; u->frompile = whereis[c].pile; u->to = where; u->topile = pile; } u->fromfile = 0; u->link = undochain; undochain = u; } static int freeworkspace(void) { int i; for (i=0;i= 0) && xyincardat(x,y,work_x[i],work_y[i])) { pickup_card(work_c[i],when,x,y); return; } } for (i=0;i= 0) && (pile_n[i]-j <= fhc+1) && (CARD_SUIT(pile_c[i][j]) == CARD_SUIT(pile_c[i][j+1])) && (CARD_VALUE(pile_c[i][j]) == CARD_VALUE(pile_c[i][j+1])+1); j -- ) { if (xyincardat(x,y,pile_x[i],pile_y[i]+(j*CARD_UP_OVL_Y))) { pickup_sequence(i,pile_n[i]-j,when,x,y); return; } } if ( (fpc > 0) && xyincardat(x,y,pile_x[i],pile_y[i]) && ( (pile_n[i] == 1) || !xyincardat(x,y,pile_x[i],pile_y[i]+CARD_UP_OVL_Y) ) ) { pickup_entire(i,when,x,y); return; } } } else { int dx; int dy; int d; int bestd; int i; int ox; int oy; WHEREIS where; WHEREIS twhere; if (! downp) { if ( (when-carrystartt < ignore_drag_time) || ( carryclick && (when-carrystartt < max_click_time) ) ) return; } x -= carryoffx; y -= carryoffy; ox = x; oy = y; #if 0 if (downp && (when-carrystartt < dbl_click_time)) { if ( (CARD_VALUE(carrycard) == CARD_A) && (whereis[carrycard].where != WHERE_HOME) ) { for (i=0;i 0) && (carrycard == CARD_MAKE(homesuit[i],CARD_A+homeheight[i])) ) { x = home_x[i]; y = home_y[i]; break; } } } } #endif if (carryheight > 1) { bestd = desw + desh; bestd *= bestd; where.where = WHERE_NONE; if (carryseq) { for (i=0;i CARD_XSIZE) || (abs(dy) > CARD_YSIZE)) continue; d = (dx * dx) + (dy * dy); if (d < bestd) { bestd = d; where.where = WHERE_PILE; where.pile = i; } } switch (where.where) { case WHERE_PILE: for (i=carryheight-1;i>0;i--) { int j; j = freeworkspace(); undo_record(carrycards[i],WHERE_WORK,j); unplace_card(carrycards[i]); place_card(carrycards[i],WHERE_WORK,j,1); } anim_move(carrywin,ox,oy,pile_x[where.pile],pile_y[where.pile]+(pile_n[where.pile]*CARD_UP_OVL_Y)); undo_record(carrycards[0],WHERE_PILE,where.pile); unplace_card(carrycards[0]); place_card(carrycards[0],WHERE_PILE,where.pile,1); for (i=1;i CARD_XSIZE) || (abs(dy) > CARD_YSIZE)) continue; d = (dx * dx) + (dy * dy); if (d < bestd) { bestd = d; where.where = WHERE_PILE; where.pile = i; } } switch (where.where) { case WHERE_PILE: for (i=carryheight-1;i>=0;i--) { undo_record(carrycards[i],WHERE_TEMP,0); unplace_card(carrycards[i]); place_card(carrycards[i],WHERE_TEMP,0,1); } anim_move(carrywin,ox,oy,pile_x[where.pile],pile_y[where.pile]); for (i=0;i CARD_XSIZE) || (abs(dy) > CARD_YSIZE)) continue; d = (dx * dx) + (dy * dy); if (d < bestd) { bestd = d; where = twhere; } } i = where.pile; switch (where.where) { case WHERE_WORK: undo_record(carrycards[0],WHERE_WORK,i); unplace_card(carrycards[0]); anim_move(carrywin,ox,oy,work_x[i],work_y[i]); place_card(carrycards[0],WHERE_WORK,i,1); autocheck = 1; break; case WHERE_PILE: undo_record(carrycards[0],WHERE_PILE,i); unplace_card(carrycards[0]); anim_move(carrywin,ox,oy,pile_x[i],pile_y[i]+(pile_n[i]*CARD_UP_OVL_Y)); place_card(carrycards[0],WHERE_PILE,i,1); autocheck = 1; break; default: anim_move(carrywin,ox,oy,carrystartx-carryoffx,carrystarty-carryoffy); break; } endcarry(); } } } static void buttonevent(Window win, int x, int y, int button, Time t, int downp) { BUTTON *b; if (win == iwin) { push(x,y,t,downp); return; } for (b=allbuttons;b;b=b->flink) { if (win == b->win) { buttonbutton(b,button,t,downp); return; } } } static int cvt_selection(Window requestor, Atom selection, Atom target, Atom property, Time time) { if (property == None) { if (target == atoms[AX_MULTIPLE].atom) return(0); property = target; } if (target == atoms[AX_TEXT].atom) target = atoms[AX_STRING].atom; if (target == atoms[AX_STRING].atom) { XChangeProperty(disp,requestor,property,atoms[AX_STRING].atom,8,PropModeReplace,&text_init_state[0],strlen(&text_init_state[0])); return(1); } else if (target == atoms[AX_TARGETS].atom) { static int alist[] = { AX_TARGETS, AX_MULTIPLE, AX_TIMESTAMP, AX_STRING, AX_TEXT }; #define ALIST_SIZE (sizeof(alist)/sizeof(alist[0])) int i; Atom t[ALIST_SIZE]; for (i=0;i 1) { if (XGetWindowProperty(disp,requestor,property,0L,nitems,False,atoms[AX_ATOM_PAIR].atom,&type,&format,&nitems,&bytes_after,&data) != Success) return(0); atomlist = (Atom *) data; } must_replace = 0; for (i=1;itype) { default: break; 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; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ gotbuttonpress ++; 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; case SelectionRequest: /* XSelectionRequestEvent - xselectionrequest */ if (e->xselectionrequest.owner == topwin) { selection_request(e->xselectionrequest.requestor,e->xselectionrequest.selection,e->xselectionrequest.target,e->xselectionrequest.property,e->xselectionrequest.time); } break; } } static void run(void) flowsink; static void run(void) { XEvent e; gotmotion = 0; while (1) { if (XEventsQueued(disp,QueuedAfterReading) == 0) { if (gotmotion) { gotmotion = 0; move(); continue; } if (autocheck) { int h; int i; int c; for (h=0;h<13;h++) { for (i=0;iboundary) undo_record(-1,WHERE_NONE,0); autocheck = 0; if (0) { gotcard:; if (carrycards) endcarry(); raisecard(c); undo_record(c,WHERE_HOME,i); unplace_card(c); anim_move(deck_wins[c],c_x[c],c_y[c],home_x[i],home_y[i]); place_card(c,WHERE_HOME,i,1); continue; } } } XNextEvent(disp,&e); handle_event(&e,0); } } static void newgame_click(UNUSED_ARG(BUTTON *b)) { int i; for (i=0;ipushtime); seltime = b->pushtime; } static void undo_click(UNUSED_ARG(BUTTON *b)) { UNDO *u; if (undochain && undochain->boundary) free(pop_undochain()); while (undochain && !undochain->boundary) { u = pop_undochain(); if ( (whereis[u->card].where != u->to) || (whereis[u->card].pile != u->topile) ) { if (! u->fromfile) { abort(); } } else { unplace_card(u->card); if (place_card(u->card,u->from,u->frompile,!u->fromfile)) { replace_card(u->card); } } free(u); } if (! undochain) autocheck = 1; } static void save_click(UNUSED_ARG(BUTTON *b)) { lock_savefile(); write_savefile(); unlock_savefile(); } static void quit_click(UNUSED_ARG(BUTTON *b)) { lock_savefile(); write_savefile(); unlock_savefile(); exit(0); } static void setup_visual(void) { char *cp; XVisualInfo *xvi; int nvi; XVisualInfo template; long int mask; int i; int vpref; defcmap = XDefaultColormapOfScreen(scr); visual = XDefaultVisualOfScreen(scr); if (! visualstr) 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) || ( (xvi[i].depth == xvi[vpref].depth) && (xvi[i].visual == visual) ) ) { vpref = i; } } if (xvi[vpref].visual == visual) return; depth = xvi[vpref].depth; visual = xvi[vpref].visual; defcmap = None; XFree((char *)xvi); } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); disp = open_display(displayname); if (runsync) XSynchronize(disp,True); oldioerr = XSetIOErrorHandler(ioerr); olderr = XSetErrorHandler(err); scr = XDefaultScreenOfDisplay(disp); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); depth = XDefaultDepthOfScreen(scr); rootwin = XRootWindowOfScreen(scr); setup_visual(); setup_db(); maybeset(&geometryspec,get_default_value("xseahaven.geometry","XCardGame.Geometry")); maybeset(&fontname,get_default_value("xseahaven.font","XCardGame.Font")); maybeset(&foreground,get_default_value("xseahaven.foreground","XCardGame.Foreground")); maybeset(&background,get_default_value("xseahaven.background","XCardGame.Background")); maybeset(&bordercstr,get_default_value("xseahaven.borderColor","XCardGame.BorderColor")); maybeset(&borderwstr,get_default_value("xseahaven.borderWidth","XCardGame.BorderWidth")); maybeset(&bordermstr,get_default_value("xseahaven.borderMargin","XCardGame.BorderMargin")); maybeset(&margincstr,get_default_value("xseahaven.marginColor","XCardGame.Background")); maybeset(&name,get_default_value("xseahaven.name","XCardGame.Name")); maybeset(&iconname,get_default_value("xseahaven.iconName","XCardGame.IconName")); maybeset(&gammastr,get_default_value("xseahaven.gamma","XCardGame.Gamma")); maybeset(&deckpath,get_default_value("xseahaven.deckPath","XCardGame.DeckPath")); maybeset(&dragpixstr,get_default_value("xseahaven.dragPixels","XCardGame.DragPixels")); maybeset(&igndragstr,get_default_value("xseahaven.ignoreDragTime","XCardGame.IgnoreDragTime")); maybeset(&maxclickstr,get_default_value("xseahaven.maxClickTime","XCardGame.MaxClickTime")); maybeset(&dblclickstr,get_default_value("xseahaven.doubleClickIdle","XCardGame.DoubleClickIdle")); maybeset(&savefile,get_default_value("xseahaven.saveFile","XCardGame.SaveFile")); maybeset(&savefilemodestr,get_default_value("xseahaven.saveFileMode","XCardGame.SaveFileMode")); if (breaklock) { break_save_lock(); exit(0); } setup_atoms(); setup_deck(); setup_font(); setup_numbers(); setup_colors(); setup_buttons(); setup_windows(); setup_locs(); setup_save(); setup_spaces(); run(); return(0); }