#include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; /* assume no more than 16 buttons on the mouse - probably safe */ /* this number cannot be more than the number of bits in an unsigned int */ #define MAXPTRMAP 16 /* the X protocol does not permit more than 5 virtual buttons */ #define MAXBUTTONS 5 #define SQRT_3_4 0.8660254037844386 /* sqrt(3/4) */ #define SECUSEC 1000000 typedef struct menu_item MENU_ITEM; typedef struct node NODE; typedef struct pgm PGM; typedef struct repgm_col REPGM_COL; typedef struct repgm_item REPGM_ITEM; typedef struct row ROW; typedef union freenode FREENODE; typedef union freerow FREEROW; struct menu_item { const char *str; int index; XCharStruct item_extent; int width; Window texttop; Window textwin; Window acceltop; Window accelwin; int ptr_accel; unsigned int mouse_bits; } ; struct node { short int x; short int y; short int bits; } ; struct pgm { char leave; #define LEAVE_DIE 6 #define LEAVE_BAD 7 int *used; } ; struct repgm_col { const char *title; int *var; int max; Window titlewin; XCharStruct extent; int textx; int texty; int xoff; } ; struct repgm_item { int column; int value; Window topwin; Window letterwin; XCharStruct extent; int textx; int texty; } ; struct row { NODE **nodes; int xmin; int xmax; } ; union freenode { FREENODE *link; NODE node; } ; union freerow { FREEROW *link; ROW node; } ; static MENU_ITEM menu[] = { { "Quit" }, #define MENU_QUIT 0 { "Find Start" }, #define MENU_FIND_START 1 { "Find End" }, #define MENU_FIND_END 2 { "Clear" }, #define MENU_CLEAR 3 { "Step" }, #define MENU_STEP 4 { "Run/Stop" }, #define MENU_RUN_STOP 5 { "Reprogram" }, #define MENU_REPROGRAM 6 { "Pan" }, #define MENU_PAN 7 { "Speed up" }, #define MENU_SPEED_UP 8 { "Slow down" }, #define MENU_SLOW_DOWN 9 { "Zoom in" }, #define MENU_ZOOM_IN 10 { "Zoom out" }, #define MENU_ZOOM_OUT 11 }; #define MENUSIZE (sizeof(menu)/sizeof(menu[0])) static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static Colormap defcmap; static GC defgc; static Visual *visual; static int nptr; static unsigned char ptr_map[MAXPTRMAP]; static MENU_ITEM *ptr_accel[MAXBUTTONS]; static int menu_ascent; static int menu_descent; static int menu_mousew; static MENU_ITEM *menu_active; static int active_x; static int active_y; static double active_scale; static XColor fgcolor; static XColor bgcolor; static XColor bdcolor; static GC wingc; static GC revgc; static GC xorgc; static Window topwin; static Window mainwin; static Window labelwin; static Window repgmwin; static Colormap wincmap; static int topwinw; static int topwinh; static int mainw; static int mainh; static int repgmw; static int repgmh; static int borderwidth; static XFontStruct *font; static int default_font; static XCharStruct label_extent; static double scale; static int cx; static int cy; static int speedp2; static struct timeval speed; static struct timeval tickspeed; static int ticker_running; static int flat_out; static int flat_count; static char pgmstr[256]; #define PGM_A 0 #define PGM_B 1 #define PGM_C 2 #define PGM_D 3 static int pgm_1; static int pgm_2; static int pgm_3[4]; static int pgm_4; static int used_1; static int used_2; static int used_3[4]; static int used_4; static int used_other; static REPGM_COL repgm_cols[] = { { "1", &pgm_1, 2 }, { "2", &pgm_2, 4 }, { "3.1", &pgm_3[0], 3 }, { "3.2", &pgm_3[1], 3 }, { "3.3", &pgm_3[2], 3 }, { "3.4", &pgm_3[3], 3 }, { "4", &pgm_4, 2 } }; #define REPGM_NCOLS (sizeof(repgm_cols)/sizeof(repgm_cols[0])) static REPGM_ITEM *repgm_items; static int repgm_nitems; static int running; static struct timeval curtime; static int timechanged; static int skipticks; static PGM program[32]; static ROW **nodey; static int nodeymin; static int nodeymax; static NODE *at; static int dir; static int length; static int xyinc[2][6] = { { -1, -1, 0, 1, 1, 0 }, { 0, 1, 1, 0, -1, -1 } }; static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *BorderColor: white\n\ *BorderWidth: 1\n\ *Name: xtrace\n\ *IconName: xtrace\n\ "; static char *displayname; static char *geometryspec; static char *foreground; static char *background; static char *bordercstr; static char *borderwstr; static char *name; static char *iconname; static char *fontname; static int argc; static char **argv; static void *deconst_(int dummy, ...) { va_list ap; va_start(ap,dummy); return(va_arg(ap,void *)); va_end(ap); } static void *deconst(const void *vp) { return(deconst_(0,vp)); } static unsigned int gcd(unsigned int, unsigned int) __attribute__((__const__)); static unsigned int gcd(unsigned int a, unsigned int b) { unsigned int r; while (b != 0) { r = a % b; a = b; b = r; } return(a); } static __inline__ unsigned int lcm(unsigned int, unsigned int) __attribute__((__const__)); static __inline__ unsigned int lcm(unsigned int a, unsigned int b) { return(a*(b/gcd(a,b))); } /* smallest n s.t. n*a >= b; assumes b>0 */ static __inline__ unsigned int min_mult(unsigned int, unsigned int) __attribute__((__const__)); static __inline__ unsigned int min_mult(unsigned int a, unsigned int b) { return(((b-1)/a)+1); } #define NEWPKG(type) \ static char *newblk_##type = 0; \ static int blkn_##type = 0; \ static int newn_##type = 0; \ static type *new_##type(void) \ { \ char *rv; \ \ if (newn_##type < 1) \ { if (blkn_##type == 0) \ { blkn_##type = min_mult(lcm(getpagesize(),sizeof(type)),0x10000); \ } \ newblk_##type = sbrk(blkn_##type*sizeof(type)); \ newn_##type = blkn_##type; \ } \ rv = newblk_##type; \ newblk_##type += sizeof(type); \ newn_##type --; \ return((void *)rv); \ } NEWPKG(FREEROW) NEWPKG(FREENODE) static FREEROW *freerows = 0; static FREENODE *freenodes = 0; static ROW *newrow(void) { ROW *rv; if (freerows) { rv = (ROW *) freerows; freerows = freerows->link; } else { rv = (ROW *) new_FREEROW(); } return(rv); } static void freerow(ROW *row) { FREEROW *frow; frow = (FREEROW *) row; frow->link = freerows; freerows = frow; } static NODE *newnode(void) { NODE *rv; if (freenodes) { rv = (NODE *) freenodes; freenodes = freenodes->link; } else { rv = (NODE *) new_FREENODE(); } return(rv); } static void freenode(NODE *node) { FREENODE *fnode; fnode = (FREENODE *) node; fnode->link = freenodes; freenodes = fnode; } static NODE *getnode(int x, int y) { ROW *r; int i; if (y > nodeymax) { int newmax; ROW **rp; NODE *n; newmax = y + 10; nodey = realloc(nodey,(newmax+1-nodeymin)*sizeof(ROW *)); rp = nodey + (nodeymax + 1 - nodeymin); for (i=nodeymax+1;i<=newmax;i++) { r = newrow(); r->nodes = malloc(sizeof(NODE *)); r->xmin = x; r->xmax = x; n = newnode(); n->x = x; n->y = i; n->bits = 0; r->nodes[0] = n; *rp++ = r; } nodeymax = newmax; } else if (y < nodeymin) { int newmin; ROW **rp; NODE *n; newmin = y - 20; nodey = realloc(nodey,(nodeymax+1-newmin)*sizeof(ROW *)); bcopy(&nodey[0],&nodey[nodeymin-newmin],(nodeymax+1-nodeymin)*sizeof(ROW *)); rp = nodey + (nodeymin - newmin); for (i=nodeymin-1;i>=newmin;i--) { r = newrow(); r->nodes = malloc(sizeof(NODE *)); r->xmin = x; r->xmax = x; n = newnode(); n->x = x; n->y = i; n->bits = 0; r->nodes[0] = n; *--rp = r; } nodeymin = newmin; } r = nodey[y-nodeymin]; if (x > r->xmax) { int newmax; NODE **np; NODE *n; newmax = x + 10; r->nodes = realloc(r->nodes,(newmax+1-r->xmin)*sizeof(NODE *)); np = r->nodes + (r->xmax + 1 - r->xmin); for (i=r->xmax+1;i<=newmax;i++) { n = newnode(); n->x = i; n->y = y; n->bits = 0; *np++ = n; } r->xmax = newmax; return(r->nodes[x-r->xmin]); } else if (x < r->xmin) { int newmin; NODE **np; NODE *n; newmin = x - 20; r->nodes = realloc(r->nodes,(r->xmax+1-newmin)*sizeof(NODE *)); bcopy(&r->nodes[0],&r->nodes[r->xmin-newmin],(r->xmax+1-r->xmin)*sizeof(NODE *)); np = r->nodes + (r->xmin - newmin); for (i=r->xmin-1;i>=newmin;i--) { n = newnode(); n->x = i; n->y = y; n->bits = 0; *--np = n; } r->xmin = newmin; } return(r->nodes[x-r->xmin]); } 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) #define FOO(str,var) \ if (!strcmp(*av,str)) \ { WANTARG(); \ var = av[skip]; \ continue; \ } FOO("-display",displayname) FOO("-geometry",geometryspec) FOO("-fg",foreground) FOO("-bg",background) FOO("-bordercolor",bordercstr) FOO("-borderwidth",borderwstr) FOO("-name",name) FOO("-iconname",iconname) FOO("-font",fontname) #undef FOO #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) { exit(1); } } static void maybeset(char **strp,char *str) { if (str && !*strp) *strp = str; } static void check_ptr_map(void) { int i; for (i=0;i MAXBUTTONS)) { fprintf(stderr,"%s: program was built for an X protocol having mouse buttons 1 through %d;\n",__progname,MAXBUTTONS); fprintf(stderr,"%s: this server claims the existence of a button #%d\n",__progname,ptr_map[i]); exit(1); } } } static void setup_db(void) { char *str; char *home; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } else { home = getenv("HOME"); if (home) { str = malloc(strlen(home)+1+10+1); sprintf(str,"%s/.Xdefaults",home); db2 = XrmGetFileDatabase(str); if (db2) { XrmMergeDatabases(db2,&db); } free(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_color(char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } while (1) { if (XAllocColor(disp,wincmap,col) == 0) { if (wincmap != defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for color `%s'\n",__progname,str); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); continue; } break; } } static void setup_font(void) { default_font = 0; font = fontname ? XLoadQueryFont(disp,fontname) : 0; if (fontname && !font) { fprintf(stderr,"%s: can't load font %s, using server default\n",__progname,fontname); } if (! font) { default_font = 1; font = XQueryFont(disp,XGContextFromGC(defgc)); if (! font) { fprintf(stderr,"%s: can't query the default font?!\n",__progname); exit(1); } } } static void setup_colors(void) { wincmap = defcmap; setup_color(foreground,&fgcolor); setup_color(background,&bgcolor); setup_color(bordercstr,&bdcolor); } static void setup_numbers(void) { if (borderwstr) borderwidth = atoi(borderwstr); } static void reset_menu_numbers(void) { int i; MENU_ITEM *m; int dirhint; int fontasc; int fontdsc; for (i=0;istr,strlen(m->str),&dirhint,&fontasc,&fontdsc,&m->item_extent); m->width = m->item_extent.rbearing - m->item_extent.lbearing; m->ptr_accel = 0; } for (i=0;imax; XTextExtents(font,col->title,strlen(col->title),&dirhint,&fontasc,&fontdsc,&col->extent); } repgm_items = (REPGM_ITEM *) malloc(repgm_nitems*sizeof(REPGM_ITEM)); x = 0; for (i=0;icolumn = i; item->value = j; XTextExtents(font,"abcd"+j,1,&dirhint,&fontasc,&fontdsc,&item->extent); } } } static void layout_repgm(void) { int i; int maxw; int maxmax; REPGM_COL *col; REPGM_ITEM *item; int h; int cellsize; h = 2 * (font->ascent + font->descent); maxw = h; maxmax = 0; for (i=0;iextent.width > maxw) maxw = col->extent.width; if (col->max > maxmax) maxmax = col->max; } for (i=0;iextent.width > maxw) maxw = item->extent.width; } maxw += 10; cellsize = maxw + 20; repgmw = cellsize * REPGM_NCOLS; repgmh = cellsize * (maxmax+1); XResizeWindow(disp,repgmwin,repgmw,repgmh); for (i=0;ixoff = cellsize * i; XMoveResizeWindow(disp,col->titlewin,col->xoff,0,cellsize,cellsize); col->textx = (cellsize - col->extent.width) / 2; col->texty = ((cellsize - (col->extent.ascent+col->extent.descent)) / 2) + col->extent.ascent; } for (i=0;itopwin,(item->column*cellsize)+5,((item->value+1)*cellsize)+5,maxw-2,maxw-2); XMoveResizeWindow(disp,item->letterwin,0,0,maxw-6,maxw-6); item->textx = (maxw-6 - item->extent.width) / 2; item->texty = ((maxw-6 - (item->extent.ascent+item->extent.descent)) / 2) + item->extent.ascent; } } static void position_windows(void) { int menuh; int winy; int i; int totalw; int x; int w; int dw; int accelw; int toph; menuh = menu_ascent + menu_descent; winy = (2 * menuh) + 2; toph = topwinh - winy; XMoveResizeWindow(disp,mainwin,0,winy,topwinw,toph); if ((topwinw != mainw) || (toph != mainh)) { XClearArea(disp,mainwin,0,0,0,0,True); cx += (topwinw - mainw) / 2; cy += (toph - mainh) / 2; mainw = topwinw; mainh = toph; } accelw = (menu_mousew * nptr) + nptr + 1; totalw = 0; for (i=0;itexttop,x,0,m->width+dw,menuh); XMoveResizeWindow(disp,m->textwin,dw/2,0,m->width,menuh); XMoveResizeWindow(disp,m->acceltop,x,menuh+1,m->width+dw,menuh); XMoveResizeWindow(disp,m->accelwin,(m->width+dw-accelw)/2,0,accelw,menuh); x += m->width + dw + 1; } XMoveWindow(disp,repgmwin,(topwinw-repgmw)/2,(topwinh-repgmh)/2); } static void resize(int topw, int toph) { if ((topw == topwinw) && (toph == topwinh)) return; topwinw = topw; topwinh = toph; position_windows(); } static void setup_windows(void) { int x; int y; int w; int h; int bits; int i; unsigned long int attrmask; unsigned long int gcvalmask; XGCValues gcval; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; w = (width * 2) / 3; h = (height * 2) / 3; x = width / 6; y = height / 6; bits = XParseGeometry(geometryspec,&x,&y,&w,&h); if (bits & XNegative) x = width + x - w; if (bits & YNegative) y = height + y - h; attrmask = 0; attr.background_pixel = bdcolor.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolor.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,CopyFromParent,attrmask,&attr); wn_prop.value = deconst("XTrace"); wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = deconst("xtrace"); 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 : PPosition; 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 = False; class_hints.res_name = deconst("xtrace"); class_hints.res_class = deconst("XTrace"); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); XSetWindowColormap(disp,topwin,wincmap); attrmask = 0; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask | ButtonPressMask | PointerMotionMask; /* XXX PointerMotionHintMask */ attrmask |= CWEventMask; mainwin = XCreateWindow(disp,topwin,0,0,1,1,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,mainwin); attrmask = 0; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; labelwin = XCreateWindow(disp,mainwin,0,0,1,1,1,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapRaised(disp,labelwin); attrmask = 0; attr.border_pixel = bdcolor.pixel; attrmask |= CWBorderPixel; attr.background_pixel = bgcolor.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; repgmwin = XCreateWindow(disp,topwin,0,0,1,1,1,depth,InputOutput,CopyFromParent,attrmask,&attr); for (i=0;ifid; gcvalmask |= GCFont; } gcval.foreground = fgcolor.pixel; gcvalmask |= GCForeground; gcval.background = bgcolor.pixel; gcvalmask |= GCBackground; wingc = XCreateGC(disp,mainwin,gcvalmask,&gcval); gcvalmask = 0; if (! default_font) { gcval.font = font->fid; gcvalmask |= GCFont; } gcval.foreground = bgcolor.pixel; gcvalmask |= GCForeground; gcval.background = fgcolor.pixel; gcvalmask |= GCBackground; revgc = XCreateGC(disp,mainwin,gcvalmask,&gcval); gcvalmask = 0; if (! default_font) { gcval.font = font->fid; gcvalmask |= GCFont; } gcval.function = GXxor; gcvalmask |= GCFunction; gcval.foreground = fgcolor.pixel ^ bgcolor.pixel; gcvalmask |= GCForeground; gcval.background = 0; gcvalmask |= GCBackground; xorgc = XCreateGC(disp,mainwin,gcvalmask,&gcval); topwinw = -1; topwinh = -1; mainw = 0; mainh = 0; cx = 0; cy = 0; resize(w,h); XMapRaised(disp,topwin); } static void sigalrm_handler(int sig __attribute__((__unused__))) { timechanged = 1; } static void startticker(void) { struct itimerval itv; itv.it_interval = tickspeed; itv.it_value = tickspeed; setitimer(ITIMER_REAL,&itv,(struct itimerval *)0); ticker_running = 1; } static void setspeed(void) { /* 10 is a more or less random value - one segment every twenty minutes or so -19 is ceil(lg(1000000)) */ if (speedp2 > 10) speedp2 = 10; else if (speedp2 < -19) speedp2 = -19; if (speedp2 >= 0) { speed.tv_sec = 1 << speedp2; speed.tv_usec = 0; } else { speed.tv_sec = 0; speed.tv_usec = 1000000 >> -speedp2; } tickspeed = speed; if (ticker_running) startticker(); flat_out = 0; } static void stopticker(void) { struct itimerval itv; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0; itv.it_interval = itv.it_value; setitimer(ITIMER_REAL,&itv,(struct itimerval *)0); ticker_running = 0; } static void redraw_label(void) { XDrawImageString(disp,(Drawable)labelwin,wingc,2-label_extent.lbearing,2+label_extent.ascent,&pgmstr[0],strlen(&pgmstr[0])); } static void reset_labelwin(void) { int dirhint; int fontasc; int fontdsc; XTextExtents(font,&pgmstr[0],strlen(&pgmstr[0]),&dirhint,&fontasc,&fontdsc,&label_extent); if (label_extent.ascent < fontasc) label_extent.ascent = fontasc; if (label_extent.descent < fontdsc) label_extent.descent = fontdsc; XMoveResizeWindow(disp,labelwin,-1,-1,4+label_extent.rbearing-label_extent.lbearing,4+label_extent.ascent+label_extent.descent); XClearArea(disp,labelwin,0,0,0,0,False); redraw_label(); } static void update_label(void) { static const char *used = "abcd"; static const char *unused = "ABCD"; char *psp; psp = &pgmstr[0]; sprintf(psp,"1%c 2%c 3%c%c%c%c 4%c (length %d) [", (used_1?used:unused)[pgm_1], (used_2?used:unused)[pgm_2], (used_3[0]?used:unused)[pgm_3[0]], (used_3[1]?used:unused)[pgm_3[1]], (used_3[2]?used:unused)[pgm_3[2]], (used_3[3]?used:unused)[pgm_3[3]], (used_4?used:unused)[pgm_4], length ); psp += strlen(psp); if (running) { strcpy(psp,"running, "); psp += 9; } else { strcpy(psp,"stopped ("); psp += 9; } if (flat_out) { strcpy(psp,"flat out"); } else { if (speedp2 >= 0) { sprintf(psp,"%d-second ticks",1<x+(n->y*.5))); *yp = cy - nint(scale*SQRT_3_4*n->y); } static void draw_line(NODE *n1, NODE *n2) { int x1; int y1; int x2; int y2; nodexy(n1,&x1,&y1); nodexy(n2,&x2,&y2); XDrawLine(disp,mainwin,wingc,x1,y1,x2,y2); } static void tick(void) { int pgmx; int outdir; int indir; int leave; NODE *newat; pgmx = (at->bits >> (dir + 1)) & 31; leave = program[pgmx].leave; switch (leave) { case 0: case 1: case 2: case 3: case 4: case 5: break; case LEAVE_DIE: running = 0; update_label(); return; break; case LEAVE_BAD: fprintf(stderr,"BAD pgmx VALUE %02o IN step()\n",pgmx); abort(); break; default: fprintf(stderr,"BAD LEAVE VALUE %d IN step()\n",leave); abort(); break; } (*program[pgmx].used) ++; outdir = dir + leave; if (outdir >= 6) outdir -= 6; indir = outdir + 3; if (indir >= 6) indir -= 6; newat = getnode(at->x+xyinc[0][indir],at->y+xyinc[1][indir]); at->bits |= 010101 << outdir; newat->bits |= 010101 << indir; switch (indir) { case 0: case 4: case 5: draw_line(newat,at); break; default: draw_line(at,newat); break; } at = newat; dir = indir; length ++; } static void update_mainwin(int x, int y, int w, int h) { int y1; int y2; int yv; int x1; int x2; int xv; NODE *n; int b; int d; y1 = floor((cy-(y+h))/(scale*SQRT_3_4)); y2 = floor((cy-y)/(scale*SQRT_3_4)); for (yv=y1;yv<=y2;yv++) { x1 = floor(((x-cx)/scale)-(yv*.5)); x2 = floor(((x+w-cx)/scale)-(yv*.5)); for (xv=x1;xv<=x2;xv++) { n = getnode(xv,yv); if (n->bits) { for (b=0;b<3;b++) { d = "\0\5\4"[b]; if (n->bits & (1 << d)) { draw_line(n,getnode(xv-xyinc[0][d],yv-xyinc[1][d])); } } } } } } static void draw_active(void) { switch (menu_active->index) { case MENU_PAN: XDrawRectangle(disp,(Drawable)mainwin,xorgc,active_x-(mainw/2),active_y-(mainh/2),mainw-1,mainh-1); break; case MENU_ZOOM_IN: XDrawRectangle(disp,(Drawable)mainwin,xorgc,nint((mainw/2.0)-(active_scale*(mainw/2.0))),nint((mainh/2.0)-(active_scale*(mainh/2.0))),nint(mainw*active_scale)-1,nint(mainh*active_scale)-1); break; case MENU_ZOOM_OUT: XDrawRectangle(disp,(Drawable)mainwin,xorgc,nint((mainw/2.0)-(active_scale*(mainw/2.0))),nint((mainh/2.0)-(active_scale*(mainh/2.0))),nint(mainw*active_scale)-1,nint(mainh*active_scale)-1); break; } } static void draw_active_clipped(int x, int y, int w, int h) { XRectangle r; r.x = x; r.y = y; r.width = w; r.height = h; XSetClipRectangles(disp,xorgc,0,0,&r,1,YXBanded); draw_active(); XSetClipMask(disp,xorgc,None); } static void redisplay(Window win, int x, int y, int w, int h, int count) { int i; if (win == mainwin) { update_mainwin(x,y,w,h); if (menu_active) draw_active_clipped(x,y,w,h); skipticks = 1; return; } if (win == labelwin) { redraw_label(); return; } if (count != 0) return; for (i=0;imouse_bits != 0) { int j; for (j=0;jmouse_bits & (1 << j)) { XFillRectangle(disp,(Drawable)m->accelwin,wingc,j*(menu_mousew+1),0,menu_mousew,menu_ascent+menu_descent); } else { XDrawRectangle(disp,(Drawable)m->accelwin,wingc,j*(menu_mousew+1),0,menu_mousew-1,menu_ascent+menu_descent-1); } } } return; } } for (i=0;ititlewin) { XDrawImageString(disp,(Drawable)win,wingc,col->textx,col->texty,col->title,strlen(col->title)); return; } } for (i=0;iletterwin) { XDrawImageString(disp,(Drawable)win,wingc,item->textx,item->texty,"abcd"+item->value,1); return; } } } static void erase_active(void) { draw_active(); } static void load_program(void) { int i; static char tbl2[5][5] = { { 1, 2, 3, 4, 020 }, { 1, 2, 3, 5, 010 }, { 1, 2, 4, 5, 004 }, { 1, 3, 4, 5, 002 }, { 2, 3, 4, 5, 001 } }; static char tbl3[2][4][4] = { { { 5, 2, 4, 005 }, { 3, 2, 4, 021 }, { 3, 1, 5, 012 }, { 1, 2, 4, 024 } }, { { 3, 4, 5, 003 }, { 1, 4, 5, 006 }, { 1, 2, 5, 014 }, { 1, 2, 3, 030 } } }; static char tbl4[3][10] = { { 1, 1, 1, 1, 2, 2, 2, 3, 3, 4 }, { 2, 3, 4, 5, 3, 4, 5, 4, 5, 5 }, { 034, 032, 026, 016, 031, 025, 015, 023, 013, 007 } }; static char tblforce[2][5] = { { 1, 2, 3, 4, 5 }, { 036, 035, 033, 027, 017 } }; for (i=0;i<32;i++) { program[i].leave = LEAVE_BAD; program[i].used = 0; } program[0].leave = "\4\5"[pgm_1]; program[0].used = &used_1; for (i=0;i<5;i++) { program[(int)tbl2[i][4]].leave = tbl2[i][pgm_2]; program[(int)tbl2[i][4]].used = &used_2; } for (i=0;i<4;i++) { program[(int)tbl3[pgm_1][i][3]].leave = tbl3[pgm_1][i][pgm_3[i]]; program[(int)tbl3[pgm_1][i][3]].used = &used_3[i]; } for (i=0;i<10;i++) { program[(int)tbl4[2][i]].leave = tbl4[pgm_4][i]; program[(int)tbl4[2][i]].used = &used_4; } for (i=0;i<5;i++) { program[(int)tblforce[1][i]].leave = tblforce[0][i]; program[(int)tblforce[1][i]].used = &used_other; } program[31].leave = LEAVE_DIE; program[31].used = &used_other; update_label(); } static void reset_repgm_border(REPGM_ITEM *item) { if (*repgm_cols[item->column].var == item->value) { XSetWindowBorder(disp,item->letterwin,bdcolor.pixel); } else { XSetWindowBorder(disp,item->letterwin,bgcolor.pixel); } } static void update_repgmwin(void) { int i; for (i=0;itextwin,0,0,0,0,True); if ((menu_active->index == MENU_REPROGRAM) && (m != menu_active)) { XUnmapWindow(disp,repgmwin); load_program(); } } menu_active = m; active_x = mainw / 2; active_y = mainh / 2; if (menu_active) { draw_active(); XClearArea(disp,menu_active->textwin,0,0,0,0,True); if (menu_active->index == MENU_REPROGRAM) { update_repgmwin(); XMapRaised(disp,repgmwin); } } update_label(); } static int button_number(int but) { switch (but) { case Button1: return(0); break; case Button2: return(1); break; case Button3: return(2); break; case Button4: return(3); break; case Button5: return(4); break; } return(-1); } static void recenter(int onlive) { if (onlive) { int x; int y; nodexy(at,&x,&y); cx += (mainw / 2) - x; cy += (mainh / 2) - y; } else { cx = mainw / 2; cy = mainh / 2; } XClearArea(disp,mainwin,0,0,0,0,True); } static void alloc_nodes(void) { nodeymin = 0; nodeymax = 0; nodey = malloc(sizeof(ROW *)); nodey[0] = newrow(); nodey[0]->nodes = malloc(sizeof(NODE *)); nodey[0]->xmin = 0; nodey[0]->xmax = 0; nodey[0]->nodes[0] = newnode(); nodey[0]->nodes[0]->x = 0; nodey[0]->nodes[0]->y = 0; nodey[0]->nodes[0]->bits = 0; } static void clear_all_nodes(void) { int x; int y; ROW *r; for (y=nodeymin;y<=nodeymax;y++) { r = nodey[y-nodeymin]; for (x=r->xmin;x<=r->xmax;x++) freenode(r->nodes[x-r->xmin]); free(r->nodes); freerow(r); } free(nodey); alloc_nodes(); length = 0; XClearArea(disp,mainwin,0,0,0,0,True); used_1 = 0; used_2 = 0; used_3[0] = 0; used_3[1] = 0; used_3[2] = 0; used_3[3] = 0; used_4 = 0; update_label(); } static void home(void) { at = getnode(0,0); dir = 0; XClearArea(disp,mainwin,0,0,0,0,True); } static void menu_choice(MENU_ITEM *m) { if (menu_active) { erase_active(); set_menu_active((MENU_ITEM *)0); } switch(m->index) { case MENU_QUIT: exit(0); break; case MENU_FIND_START: recenter(0); break; case MENU_FIND_END: recenter(1); break; case MENU_CLEAR: clear_all_nodes(); home(); break; case MENU_STEP: running = 0; tick(); update_label(); break; case MENU_RUN_STOP: running = ! running; update_label(); break; case MENU_REPROGRAM: set_menu_active(m); break; case MENU_PAN: set_menu_active(m); break; case MENU_SPEED_UP: speedp2 --; setspeed(); update_label(); break; case MENU_SLOW_DOWN: speedp2 ++; setspeed(); update_label(); break; case MENU_ZOOM_IN: set_menu_active(m); break; case MENU_ZOOM_OUT: set_menu_active(m); break; } } static void set_accel(MENU_ITEM *m, int bno) { int i; unsigned int abits; if (bno < 0) return; for (i=0;imouse_bits = abits; m->ptr_accel = bno; ptr_accel[bno] = m; XClearArea(disp,m->accelwin,0,0,0,0,True); } static void repgm_choose(REPGM_ITEM *item) { REPGM_COL *col; REPGM_ITEM *olditem; REPGM_ITEM *item2; int i; col = &repgm_cols[item->column]; olditem = 0; for (i=0;icolumn == item->column) && (item2->value == *col->var)) { olditem = item2; break; } } *col->var = item->value; if (olditem) reset_repgm_border(olditem); reset_repgm_border(item); update_label(); } static void button_pressed(Window win, int x, int y, int but) { int i; if (win == mainwin) { if (menu_active) { MENU_ITEM *m; erase_active(); m = menu_active; set_menu_active((MENU_ITEM *)0); switch (m->index) { case MENU_REPROGRAM: break; case MENU_PAN: { int dx; int dy; dx = (mainw/2) - x; dy = (mainh/2) - y; if (dx < 0) { if (dy < 0) { XCopyArea(disp,mainwin,mainwin,wingc,-dx,-dy,mainw+dx,mainh+dy,0,0); XClearArea(disp,mainwin,0,mainh+dy,0,-dy,True); XClearArea(disp,mainwin,mainw+dx,0,-dx,mainh+dy,True); } else if (dy > 0) { XCopyArea(disp,mainwin,mainwin,wingc,-dx,0,mainw+dx,mainh-dy,0,dy); XClearArea(disp,mainwin,0,0,0,dy,True); XClearArea(disp,mainwin,mainw+dx,0,-dx,mainh-dy,True); } else { XCopyArea(disp,mainwin,mainwin,wingc,-dx,0,mainw+dx,mainh,0,0); XClearArea(disp,mainwin,mainw+dx,0,-dx,mainh,True); } } else if (dx > 0) { if (dy < 0) { XCopyArea(disp,mainwin,mainwin,wingc,0,-dy,mainw-dx,mainh+dy,dx,0); XClearArea(disp,mainwin,0,mainh+dy,0,-dy,True); XClearArea(disp,mainwin,0,0,dx,mainh+dy,True); } else if (dy > 0) { XCopyArea(disp,mainwin,mainwin,wingc,0,0,mainw-dx,mainh-dy,dx,dy); XClearArea(disp,mainwin,0,0,0,dy,True); XClearArea(disp,mainwin,0,dy,dx,mainh-dy,True); } else { XCopyArea(disp,mainwin,mainwin,wingc,0,0,mainw-dx,mainh,dx,0); XClearArea(disp,mainwin,0,0,dx,mainh,True); } } else { if (dy < 0) { XCopyArea(disp,mainwin,mainwin,wingc,0,-dy,mainw,mainh+dy,0,0); XClearArea(disp,mainwin,0,mainh+dy,0,-dy,True); } else if (dy > 0) { XCopyArea(disp,mainwin,mainwin,wingc,0,0,mainw,mainh-dy,0,dy); XClearArea(disp,mainwin,0,0,0,dy,True); } else { } } cx += dx; cy += dy; } break; case MENU_ZOOM_IN: if (active_scale > 1e-3) { int x0; int y0; x0 = mainw / 2; y0 = mainh / 2; cx = x0 + ((cx - x0) / active_scale); cy = y0 + ((cy - y0) / active_scale); scale /= active_scale; XClearArea(disp,mainwin,0,0,0,0,True); } break; case MENU_ZOOM_OUT: if (active_scale > 1e-3) { int x0; int y0; x0 = mainw / 2; y0 = mainh / 2; cx = x0 + ((cx - x0) * active_scale); cy = y0 + ((cy - y0) * active_scale); scale *= active_scale; XClearArea(disp,mainwin,0,0,0,0,True); } break; default: fprintf(stderr,"%s: menu item `%s' active but no code for it?\n",__progname,m->str); abort(); break; } } else { but = button_number(but); if (ptr_accel[but]) menu_choice(ptr_accel[but]); } return; } for (i=0;i sy) ? sx : sy; } static void pointer_moved(Window win __attribute__((__unused__)), int x, int y) { if (menu_active == 0) return; erase_active(); active_x = x; active_y = y; compute_active_scale(); draw_active(); } static void handle_event(XEvent *e) { switch (e->type) { default: break; case ConfigureNotify: /* XConfigureEvent - xconfigure */ resize(e->xconfigure.width,e->xconfigure.height); break; case Expose: /* XExposeEvent - xexpose */ redisplay(e->xexpose.window,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); break; case GraphicsExpose: /* XGraphicsExposeEvent - xgraphicsexpose */ /* XXX - shouldn't depend on being able to cast a Drawable into a Window (but how else?) */ redisplay((Window)e->xgraphicsexpose.drawable,e->xgraphicsexpose.x,e->xgraphicsexpose.y,e->xgraphicsexpose.width,e->xgraphicsexpose.height,e->xgraphicsexpose.count); break; case ButtonPress: /* XButtonPressedEvent/XButtonEvent - xbutton */ button_pressed(e->xbutton.window,e->xbutton.x,e->xbutton.y,e->xbutton.button); break; case MotionNotify: /* XPointerMovedEvent/XMotionEvent - xmotion */ pointer_moved(e->xmotion.window,e->xmotion.x,e->xmotion.y); /* XXX use e->xmotion.is_hint */ break; } } static void run(void) { int xfd; int selrv; int mask; XEvent e; fd_set fds; struct timeval labeltime; struct timeval lastrun; int canrun; int lastcan; signal(SIGALRM,sigalrm_handler); setspeed(); xfd = XConnectionNumber(disp); mask = sigblock(sigmask(SIGALRM)); timechanged = 0; sigsetmask(mask); lastcan = 0; flat_count = 0; while (1) { canrun = running && !menu_active; #if 0 printf("loop: running %d canrun %d skipticks %d\n",running,canrun,skipticks); #endif if (canrun != lastcan) { lastcan = canrun; if (canrun) skipticks = 1; } if (skipticks) { gettimeofday(&lastrun,(struct timezone *)0); } if (canrun) { if (!ticker_running) startticker(); } else { if (ticker_running) stopticker(); flat_out = 0; } skipticks = 0; if (flat_out) { timechanged = 0; do tick(); while (! timechanged); gettimeofday(&curtime,0); if (curtime.tv_sec != labeltime.tv_sec) { update_label(); if (flat_out) XFlush(disp); labeltime.tv_sec = curtime.tv_sec; } } else { mask = sigblock(sigmask(SIGALRM)); if (timechanged) { timechanged = 0; sigsetmask(mask); if (canrun) { gettimeofday(&curtime,0); while ( (lastrun.tv_sec < curtime.tv_sec) || ( (lastrun.tv_sec == curtime.tv_sec) && (lastrun.tv_usec < curtime.tv_usec) ) ) { tick(); lastrun.tv_sec += speed.tv_sec; lastrun.tv_usec += speed.tv_usec; if (lastrun.tv_usec >= SECUSEC) { lastrun.tv_usec -= SECUSEC; lastrun.tv_sec ++; } } if (curtime.tv_sec != labeltime.tv_sec) { update_label(); labeltime.tv_sec = curtime.tv_sec; } } } else { sigsetmask(mask); } } while (XPending(disp) > 0) { XNextEvent(disp,&e); handle_event(&e); } if (flat_out) continue; if (timechanged) { flat_count ++; if (flat_count > 10) { flat_out = 1; } continue; } flat_count = 0; FD_ZERO(&fds); FD_SET(xfd,&fds); selrv = select(FD_SETSIZE,&fds,(fd_set *)0,(fd_set *)0,(struct timeval *)0); } } static void init_program(void) { pgm_1 = PGM_A; pgm_2 = PGM_A; pgm_3[0] = PGM_A; pgm_3[1] = PGM_A; pgm_3[2] = PGM_A; pgm_3[3] = PGM_A; pgm_4 = PGM_A; load_program(); } static Display *open_display(char *const 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); } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); disp = open_display(displayname); scr = XDefaultScreenOfDisplay(disp); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); depth = XDefaultDepthOfScreen(scr); rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); defgc = XDefaultGCOfScreen(scr); visual = XDefaultVisualOfScreen(scr); nptr = XGetPointerMapping(disp,&ptr_map[0],MAXPTRMAP); check_ptr_map(); setup_db(); maybeset(&geometryspec,get_default_value("xhexworm.geometry","XHexworm.Geometry")); maybeset(&fontname,get_default_value("xhexworm.font","XHexworm.Font")); maybeset(&foreground,get_default_value("xhexworm.foreground","XHexworm.Foreground")); maybeset(&background,get_default_value("xhexworm.background","XHexworm.Background")); maybeset(&bordercstr,get_default_value("xhexworm.borderColor","XHexworm.BorderColor")); maybeset(&borderwstr,get_default_value("xhexworm.borderWidth","XHexworm.BorderWidth")); maybeset(&name,get_default_value("xhexworm.name","XHexworm.Name")); maybeset(&iconname,get_default_value("xhexworm.iconName","XHexworm.IconName")); setup_font(); setup_colors(); setup_numbers(); setup_menu(); setup_repgm(); setup_windows(); init_program(); alloc_nodes(); home(); recenter(0); length = 0; running = 0; scale = 10; speedp2 = -1; run(); exit(0); }