/* * Program to let people manipulate an X representation of a Rush Hour * puzzle. See puzzle-fmt.txt for the puzzle file format. * * The puzzle is taken from stdin. The goal position for piece a is * marked by its pseudo-raised bumps being piece a's colour. */ #include #include #include #include #include #include #include #include #define SIZE 6 #define MAXPCS 16 extern const char *__progname; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColour: white\n\ *BorderWidth: 1\n\ *Piece.a.Colour: #911530\n\ *Piece.b.Colour: #1e2c47\n\ *Piece.c.Colour: #85a170\n\ *Piece.d.Colour: #4f5ee7\n\ *Piece.e.Colour: #c58a62\n\ *Piece.f.Colour: #6f7a85\n\ *Piece.g.Colour: #625060\n\ *Piece.h.Colour: #2f4826\n\ *Piece.i.Colour: #8f71a6\n\ *Piece.j.Colour: #67c6bc\n\ *Piece.k.Colour: #0d7762\n\ *Piece.l.Colour: #2d7ec0\n\ *Piece.m.Colour: #b5a32a\n\ *Piece.n.Colour: #185fe7\n\ *Piece.o.Colour: #2aa7b9\n\ *Piece.p.Colour: #838ae7\n\ *Board.Bulk: #646464\n\ *Board.Bright: #c8c8c8\n\ *Board.Dark: #000000\n\ *Board.Mark: #ffffff\n\ *Board.BumpPercent: 60\n\ *Board.PiecePercent: 80\n\ *Board.CellSize: 80\n\ "; static char *displayname; static char *visualstr; static char *geometryspec; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *boardcstr_bulk; static char *boardcstr_bright; static char *boardcstr_dark; static char *boardcstr_mark; static char *bump_pct_str; static char *pc_pct_str; static char *cellsize_str; static char *selkey; static int xsync; static int debugging; static const char *resname = "xrushhour"; static const char *resclass = "Game"; static char *xrmstr = 0; static int argc; static char **argv; typedef struct piece PIECE; typedef struct xy XY; struct xy { int x; int y; } ; struct piece { XY size; XY initloc; XY loc; char letter; Window win; } ; static XrmDatabase db; static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static XColor fgcolour; static XColor bgcolour; static XColor bdcolour; static Colormap wincmap; static int defcmap; static XVisualInfo visinfo; static Window topwin; static Window iowin; static Window boardwin; static Window markwin; static Window inputwin; static Window goalwin[3]; static int npcs; static PIECE pcs[MAXPCS]; static PIECE *apc; static GC gc; static int borderwidth; static PIECE *board[SIZE][SIZE]; static XColor bc_bulk; static XColor bc_bright; static XColor bc_dark; static XColor bc_mark; static int cellsize; static int bumpsize; static int piecesize; static int pieceoff; static int marked; static XY markpt; static void *deconst(const void *cvp) { void *vp; bcopy(&cvp,&vp,sizeof(void *)); return(vp); } static void strappend(char **strp, const char *str) { char *new; int oldl; int sl; if (! *strp) { *strp = malloc(1); **strp = '\0'; } oldl = strlen(*strp); sl = strlen(str); new = malloc(oldl+sl+1); bcopy(*strp,new,oldl); bcopy(str,new+oldl,sl); new[oldl+sl] = '\0'; free(*strp); *strp = new; } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = 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") || !strcmp(*av,"-geom")) { 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,"-selkey")) { WANTARG(); selkey = av[skip]; continue; } if (!strcmp(*av,"-debug")) { debugging = 1; continue; } if (!strcmp(*av,"-sync")) { xsync = 1; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-resname")) { WANTARG(); resname = av[skip]; continue; } if (!strcmp(*av,"-resclass")) { WANTARG(); resclass = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&xrmstr,"\n"); strappend(&xrmstr,av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static void read_puzzle(void) { int fsz; int x; int y; int c; char data[SIZE][SIZE]; char chs[MAXPCS]; char *chp; XY min[MAXPCS]; XY max[MAXPCS]; int cx; int n; auto void badpiece(char, const char *, ...) __attribute__((__format__(__printf__,2,3))); void badpiece(char ch, const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: bad piece %c in puzzle (",__progname,ch); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,")\n"); exit(1); } if (scanf("%d",&fsz) != 1) { fprintf(stderr,"%s: can't read puzzle size\n",__progname); exit(1); } if (fsz != SIZE) { fprintf(stderr,"%s: wrong size puzzle for this build (file has %d, want %d)\n",__progname,fsz,SIZE); exit(1); } npcs = 0; for (y=0;y (1) { c = getchar(); if (c == EOF) { fprintf(stderr,"%s: EOF while reading puzzle\n",__progname); exit(1); } switch (c) { case '.': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': break <"skip">; default: continue; break; } } data[x][y] = c; if (c != '.') { if ((npcs < 1) || !(chp=memchr(&chs[0],c,npcs))) { cx = npcs ++; chs[cx] = c; min[cx].x = x; min[cx].y = y; max[cx] = min[cx]; } else { cx = chp - &chs[0]; if (x < min[cx].x) min[cx].x = x; if (x > max[cx].x) max[cx].x = x; if (y < min[cx].y) min[cx].y = y; if (y > max[cx].y) max[cx].y = y; } } } chp = memchr(&chs[0],'a',npcs); if (! chp) { fprintf(stderr,"%s: no piece `a' in puzzle\n",__progname); exit(1); } cx = chp - &chs[0]; apc = &pcs[cx]; for (cx=npcs-1;cx>=0;cx--) { if (min[cx].x == max[cx].x) { n = 1 + max[cx].y - min[cx].y; if ((n < 2) || (n > 3)) badpiece(chs[cx],"V, n=%d",n); if ((n == 3) && (data[min[cx].x][min[cx].y+1] != chs[cx])) badpiece(chs[cx],"V, hole"); pcs[cx].size.x = 1; pcs[cx].size.y = n; } else if (min[cx].y == max[cx].y) { n = 1 + max[cx].x - min[cx].x; if ((n < 2) || (n > 3)) badpiece(chs[cx],"H, n=%d",n); if ((n == 3) && (data[min[cx].x+1][min[cx].y] != chs[cx])) badpiece(chs[cx],"H, hole"); pcs[cx].size.x = n; pcs[cx].size.y = 1; } else { badpiece(chs[cx],"dx=%d dy=%d",max[cx].x-min[cx].x,max[cx].y-min[cx].y); } pcs[cx].initloc = (XY){.x=min[cx].x,.y=min[cx].y}; pcs[cx].letter = chs[cx]; } if ( (apc->size.y != 1) || ! ((apc->size.x == 2) || (apc->size.x == 3)) ) { fprintf(stderr,"%s: piece `a' is not horizontal 2- or 3-cell piece\n",__progname); exit(1); } } static Display *open_display(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) { XVisualInfo *xvi; int nvi; XVisualInfo template; int class; const char *classname; int best_onscr; int best; int i; if (! visualstr) { template.visualid = XVisualIDFromVisual(XDefaultVisual(disp,XDefaultScreen(disp))); xvi = XGetVisualInfo(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); } visinfo = xvi[0]; } else { do <"vis"> { #define FOO(c) if (!strcasecmp(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(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; xvi = XGetVisualInfo(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); } visinfo = xvi[0]; if (visinfo.screen != XDefaultScreen(disp)) { fprintf(stderr,"%s: warning: visual %s is on screen %d\n",__progname,visualstr,(int)visinfo.screen); } break <"vis">; } template.class = class; xvi = XGetVisualInfo(disp,VisualClassMask,&template,&nvi); best = -1; best_onscr = -1; for (i=0;i xvi[best_onscr].depth) || ( (xvi[i].depth == xvi[best_onscr].depth) && (xvi[i].visual == XDefaultVisual(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(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(disp),xvi[best].screen); } else { best = best_onscr; } visinfo = xvi[best]; } while (0); } } static void setup_db(void) { int i; char *str; char *home; XrmDatabase db2; home = getenv("HOME"); db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } else if (home && (i == 0)) { str = malloc(strlen(home)+1+10+1); sprintf(str,"%s/.Xdefaults",home); db2 = XrmGetFileDatabase(str); if (db2) XrmMergeDatabases(db2,&db); free(str); } if (xrmstr) { db2 = XrmGetStringDatabase(xrmstr); if (db2) XrmMergeDatabases(db2,&db); } } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static char *get_default_value(const char *name, const char *class) { char *type; XrmValue value; if (XrmGetResource(db,name,class,&type,&value) == False) return(0); return(value.addr); } static void setup_cmap(void) { if (visinfo.visual == XDefaultVisualOfScreen(scr)) { wincmap = XDefaultColormapOfScreen(scr); defcmap = 1; } else { wincmap = XCreateColormap(disp,rootwin,visinfo.visual,AllocNone); defcmap = 0; } } static void setup_colour(char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad colour `%s'\n",__progname,str); exit(1); } if (XAllocColor(disp,wincmap,col) == 0) { if (! defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for colour `%s'\n",__progname,str); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); defcmap = 0; if (XAllocColor(disp,wincmap,col) == 0) { fprintf(stderr,"%s: can't allocate colormap cell for colour `%s'\n",__progname,str); exit(1); } } } static void setup_colours(void) { setup_colour(foreground,&fgcolour); setup_colour(background,&bgcolour); setup_colour(bordercstr,&bdcolour); setup_colour(boardcstr_bulk,&bc_bulk); setup_colour(boardcstr_bright,&bc_bright); setup_colour(boardcstr_dark,&bc_dark); setup_colour(boardcstr_mark,&bc_mark); } static void setup_numbers(void) { int pct; borderwidth = atoi(borderwstr); cellsize = atoi(cellsize_str); if (cellsize < 5) cellsize = 5; pct = atoi(bump_pct_str); if (pct < 0) pct = 0; if (pct > 100) pct = 100; bumpsize = (cellsize * pct) / 100; pct = atoi(pc_pct_str); if (pct < 0) pct = 0; if (pct > 100) pct = 100; piecesize = (cellsize * pct) / 100; pieceoff = (cellsize - piecesize) / 2; } static void resize_top(int w, int h) { XMoveWindow(disp,iowin,(w-(SIZE*cellsize))/2,(h-(SIZE*cellsize))/2); } static void setup_windows(void) { int x; int y; int w; int h; int bits; unsigned long int attrmask; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; w = (SIZE * cellsize) + (borderwidth * 2); h = (SIZE * cellsize) + (borderwidth * 2); bits = XParseGeometry(geometryspec,&x,&y,&w,&h); if (! (bits & XValue)) x = (width - w) / 2; if (! (bits & YValue)) y = (height - h) / 2; /* ghaa. If x-spec has - rather than +, x value is nonetheless negative! (Similarly y.) */ if (bits & XNegative) x = width + x - w; if (bits & YNegative) y = height + y - h; attrmask = 0; attr.background_pixel = bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolour.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = StructureNotifyMask; attrmask |= CWEventMask; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; attrmask |= CWDontPropagate; w -= 2 * borderwidth; h -= 2 * borderwidth; topwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,visinfo.visual,attrmask,&attr); wn_prop.value = (unsigned char *) deconst("xrushhour"); wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = (unsigned char *) deconst("xrushhour"); in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); normal_hints.flags = PMinSize | PBaseSize | PResizeInc; normal_hints.x = x; normal_hints.y = y; normal_hints.flags |= (bits & (XValue|YValue)) ? USPosition : PPosition; normal_hints.width = w; normal_hints.height = h; normal_hints.flags |= (bits & (WidthValue|HeightValue)) ? USSize : PSize; normal_hints.min_width = SIZE * cellsize; normal_hints.min_height = SIZE * cellsize; normal_hints.base_width = SIZE * cellsize; normal_hints.base_height = SIZE * cellsize; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = False; class_hints.res_name = deconst(resname); class_hints.res_class = deconst(resclass); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); if (selkey) XChangeProperty(disp,topwin,XInternAtom(disp,"wm_selkey",False),XA_STRING,8,PropModeReplace,selkey,strlen(selkey)); attrmask = 0; attr.background_pixmap = None; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; iowin = XCreateWindow(disp,topwin,0,0,SIZE*cellsize,SIZE*cellsize,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,iowin); resize_top(w,h); attrmask = 0; attr.background_pixmap = None; attrmask |= CWBackPixmap; attr.backing_store = NotUseful; attrmask |= CWBackingStore; boardwin = XCreateWindow(disp,iowin,0,0,SIZE*cellsize,SIZE*cellsize,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,boardwin); attrmask = 0; attr.background_pixel = bc_mark.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; markwin = XCreateWindow(disp,boardwin,-cellsize,-cellsize,cellsize,cellsize,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,markwin); x = (cellsize - bumpsize) / 2; w = cellsize - (x * 2) - 2; attrmask = 0; attr.background_pixel = bgcolour.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; goalwin[0] = XCreateWindow(disp,boardwin,-w,-w,w,w,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,goalwin[0]); /* grr, attr isn't consted, so we can't count on it being unchanged */ attr.background_pixel = bgcolour.pixel; attr.backing_store = NotUseful; goalwin[1] = XCreateWindow(disp,boardwin,-w,-w,w,w,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,goalwin[1]); attr.background_pixel = bgcolour.pixel; attr.backing_store = NotUseful; goalwin[2] = XCreateWindow(disp,boardwin,-w,-w,w,w,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,goalwin[2]); attrmask = 0; attr.event_mask = ButtonPressMask | PointerMotionMask | PointerMotionHintMask | EnterWindowMask | LeaveWindowMask; attrmask |= CWEventMask; inputwin = XCreateWindow(disp,iowin,0,0,SIZE*cellsize,SIZE*cellsize,0,0,InputOnly,CopyFromParent,attrmask,&attr); XMapWindow(disp,inputwin); XMapWindow(disp,topwin); gc = XCreateGC(disp,topwin,0L,(XGCValues *)0); } static void setup_board(void) { Pixmap pm; int x; pm = XCreatePixmap(disp,topwin,cellsize,cellsize,depth); XSetForeground(disp,gc,bc_bulk.pixel); XFillRectangle(disp,pm,gc,0,0,cellsize,cellsize); x = (cellsize - bumpsize) / 2; XSetForeground(disp,gc,bc_bright.pixel); XFillRectangle(disp,pm,gc,x,x,cellsize-(2*x),1); XFillRectangle(disp,pm,gc,x,x,1,cellsize-(2*x)-1); XSetForeground(disp,gc,bc_dark.pixel); XFillRectangle(disp,pm,gc,x,cellsize-1-x,cellsize-(2*x),1); XFillRectangle(disp,pm,gc,cellsize-1-x,x+1,1,cellsize-(2*x)-1); XSetWindowBackgroundPixmap(disp,boardwin,pm); XFreePixmap(disp,pm); XClearWindow(disp,boardwin); XMoveWindow(disp,goalwin[0],((SIZE-2)*cellsize)+x+1,(apc->initloc.y*cellsize)+x+1); XMoveWindow(disp,goalwin[1],((SIZE-1)*cellsize)+x+1,(apc->initloc.y*cellsize)+x+1); if (apc->size.x > 2) { XMoveWindow(disp,goalwin[2],((SIZE-3)*cellsize)+x+1,(apc->initloc.y*cellsize)+x+1); } else { XMoveWindow(disp,goalwin[2],((SIZE-1)*cellsize)+x+1,(apc->initloc.y*cellsize)+x+1); } } static void setup_pieces(void) { int x; PIECE *p; unsigned long int attrmask; XSetWindowAttributes attr; XColor c; char *rn; char *rc; int w; int h; for (x=npcs-1;x>=0;x--) { p = &pcs[x]; asprintf(&rn,"xrushhour.piece.%c.colour",p->letter); asprintf(&rc,"Game.Piece.%c.Colour",p->letter); setup_colour(get_default_value(rn,rc),&c); free(rn); free(rc); if (p == apc) { XSetWindowBackground(disp,goalwin[0],c.pixel); XClearWindow(disp,goalwin[0]); XSetWindowBackground(disp,goalwin[1],c.pixel); XClearWindow(disp,goalwin[1]); XSetWindowBackground(disp,goalwin[2],c.pixel); XClearWindow(disp,goalwin[2]); } attrmask = 0; attr.background_pixel = c.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; w = piecesize + (cellsize * (p->size.x - 1)); h = piecesize + (cellsize * (p->size.y - 1)); p->win = XCreateWindow(disp,boardwin,-w,-h,w,h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,p->win); } marked = 0; } static void move_pc_window(PIECE *p) { XMoveWindow(disp,p->win,(p->loc.x*cellsize)+pieceoff,(p->loc.y*cellsize)+pieceoff); } static void move_mark(void) { if (marked) { XMoveWindow(disp,markwin,markpt.x*cellsize,markpt.y*cellsize); } else { XMoveWindow(disp,markwin,-cellsize,-cellsize); } } static void reset_board(void) { int x; int y; int px; PIECE *p; for (x=SIZE-1;x>=0;x--) for (y=SIZE-1;y>=0;y--) board[x][y] = 0; for (px=npcs-1;px>=0;px--) { p = &pcs[px]; p->loc = p->initloc; move_pc_window(p); for (x=p->size.x-1;x>=0;x--) for (y=p->size.y-1;y>=0;y--) { board[p->loc.x+x][p->loc.y+y] = p; } } move_mark(); } static void beep(void) { XBell(disp,0); } static void button_press(Window w, int x, int y) { if (w != inputwin) return; if ( (x < 0) || (x >= (SIZE*cellsize)) || (y < 0) || (y >= (SIZE*cellsize)) ) { beep(); return; } x /= cellsize; y /= cellsize; if (marked) { marked = 0; move_mark(); } else { if (! board[x][y]) { beep(); return; } marked = 1; markpt = (XY){.x=x,.y=y}; move_mark(); } } static void maybe_move_piece(int x, int y) { PIECE *p; int lim; int chg; if ((x < 0) || (x >= SIZE) || (y < 0) || (y >= SIZE)) abort(); if (! marked) return; p = board[markpt.x][markpt.y]; if (! p) abort(); chg = 0; if (p->size.x == 1) { while ((y < markpt.y) && (p->loc.y > 0) && !board[p->loc.x][p->loc.y-1]) { board[p->loc.x][p->loc.y+p->size.y-1] = 0; p->loc.y --; markpt.y --; board[p->loc.x][p->loc.y] = p; chg = 1; } lim = SIZE - p->size.y; while ((y > markpt.y) && (p->loc.y < lim) && !board[p->loc.x][p->loc.y+p->size.y]) { board[p->loc.x][p->loc.y] = 0; p->loc.y ++; markpt.y ++; board[p->loc.x][p->loc.y+p->size.y-1] = p; chg = 1; } if (chg) { move_pc_window(p); move_mark(); } } else if (p->size.y == 1) { while ((x < markpt.x) && (p->loc.x > 0) && !board[p->loc.x-1][p->loc.y]) { board[p->loc.x+p->size.x-1][p->loc.y] = 0; p->loc.x --; markpt.x --; board[p->loc.x][p->loc.y] = p; chg = 1; } lim = SIZE - p->size.x; while ((x > markpt.x) && (p->loc.x < lim) && !board[p->loc.x+p->size.x][p->loc.y]) { board[p->loc.x][p->loc.y] = 0; p->loc.x ++; markpt.x ++; board[p->loc.x+p->size.x-1][p->loc.y] = p; chg = 1; } if (chg) { move_pc_window(p); move_mark(); } } else { abort(); } } static void pointer_motion(void) { XEvent e; Window wroot; Window wchild; int xroot; int yroot; int xwin; int ywin; unsigned int mask; if (! marked) return; while (XCheckTypedEvent(disp,MotionNotify,&e)) ; if (! XQueryPointer(disp,inputwin,&wroot,&wchild,&xroot,&yroot,&xwin,&ywin,&mask)) return; if ( (xwin < 0) || (xwin >= (SIZE*cellsize)) || (ywin < 0) || (ywin >= (SIZE*cellsize)) ) return; maybe_move_piece(xwin/cellsize,ywin/cellsize); } static void handle_event(XEvent *e) { switch (e->type) { default: break; case ConfigureNotify: if (e->xconfigure.window == topwin) { resize_top(e->xconfigure.width,e->xconfigure.height); } break; case ButtonPress: if (e->xbutton.same_screen) button_press(e->xbutton.window,e->xbutton.x,e->xbutton.y); break; case MotionNotify: pointer_motion(); break; } } static void run(void) __attribute__((__noreturn__)); static void run(void) { XEvent e; while (1) { XNextEvent(disp,&e); handle_event(&e); } } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); read_puzzle(); disp = open_display(displayname); if (disp == 0) { fprintf(stderr,"%s: can't open display\n",__progname); exit(1); } if (xsync) XSynchronize(disp,True); setup_visual(); scr = XScreenOfDisplay(disp,visinfo.screen); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); depth = visinfo.depth; rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); setup_db(); maybeset(&geometryspec,get_default_value("xrushhour.board.geometry","Game.Geometry")); maybeset(&foreground,get_default_value("xrushhour.foreground","Game.Foreground")); maybeset(&background,get_default_value("xrushhour.background","Game.Background")); maybeset(&bordercstr,get_default_value("xrushhour.borderColor","Game.BorderColor")); maybeset(&borderwstr,get_default_value("xrushhour.borderWidth","Game.BorderWidth")); maybeset(&boardcstr_bulk,get_default_value("xrushhour.board.bulk","Game.Board.Bulk")); maybeset(&boardcstr_bright,get_default_value("xrushhour.board.bright","Game.Board.Bright")); maybeset(&boardcstr_dark,get_default_value("xrushhour.board.dark","Game.Board.Dark")); maybeset(&boardcstr_mark,get_default_value("xrushhour.board.mark","Game.Board.Mark")); maybeset(&bump_pct_str,get_default_value("xrushhour.board.bumpPercent","Game.Board.BumpPercent")); maybeset(&pc_pct_str,get_default_value("xrushhour.board.piecePercent","Game.Board.PiecePercent")); maybeset(&cellsize_str,get_default_value("xrushhour.board.cellSize","Game.Board.CellSize")); maybeset(&selkey,get_default_value("xrushhour.selectKey","Game.SelectKey")); setup_cmap(); setup_colours(); setup_numbers(); setup_windows(); setup_board(); setup_pieces(); reset_board(); run(); }