#include #include #include #include #include "gene.h" #define TITLE "P2" extern int WorldWidth, WorldHeight; extern double CellSize; extern unsigned char MessageStdout, FullScreen; extern unsigned long PopSize, Step, Generation, NumberOfCells; Display *dpy; Window RootWin, MainWin, win, msgwin; int screen, depth; GC gc; XGCValues gcv; unsigned long fg, bg; Colormap cmap; XFontStruct *fst; XSetWindowAttributes att; Cursor crs_hand; char MyStipple[] = { 0x55, 0xaa, 0x55, 0xaa }; int WinX = 0, WinY = 0; int WinWidth, WinHeight; int ButtonWidth, ButtonHeight, ButtonX, ButtonBase; unsigned long ButtonFG, ButtonBG, TitleFG, TitleBG, WorkBG; double ViewX = 0.0, ViewY = 0.0, ViewSize = 1.0; struct base_rec { int base; unsigned long *target, prev; } Monitor[] = { {0, &PopSize}, {0, &Generation}, {0, &Step}, {0, &NumberOfCells}, {0,NULL} }; #define Ncolors 16 #define ButtonPad 10 int color_flag; #define MONOCHROME 0 #define COL_CELL_STATE 1 #define COL_INDIVIDUAL 2 #define COL_ROOTS 3 #define COL_GENE_0 4 void c_cell_state() { color_flag = COL_CELL_STATE; } void c_individual() { color_flag = COL_INDIVIDUAL; } void c_roots() { color_flag = COL_ROOTS; } void c_gene_0() { color_flag = COL_GENE_0; } int pixel[Ncolors]; void b_run(), b_gene(), b_sele(), b_step(), b_return(), b_reset(), b_zoom(), b_unzoom(), b_save(), b_quit(), b_color(), b_scroll(); struct button_rec { char *label; void (*proc)(); short int flag, x; Window win; } buttons[] = { {"Quit", b_quit, 0}, {"Reset", b_reset, 0}, {"Return", b_return, 0}, {"Run", b_run, 0}, {"Generation", b_gene, 0}, {"Step", b_step, 0}, {"Color", b_color, 0}, {"Zoom", b_zoom, 0}, {"Unzoom", b_unzoom, 0}, {"Scroll", b_scroll, 0}, {"Save", b_save, 0}, { NULL, b_quit, 0 } }, color_menu_buttons[] = { {"cell state", c_cell_state, 0}, {"individual", c_individual, 0}, {"roots", c_roots, 0}, {"gene 0", c_gene_0, 0}, { NULL, b_quit, 0 } }; typedef struct popup_menu_rec { char *title; struct button_rec *buttons; Window win; int width, height; } popup_menu; popup_menu color_menu = {"Color Menu", color_menu_buttons, 0, 0}; static unsigned char RedirectFlag = 0; parse_geom(str) char *str; { WinX = WinY = -9999; XParseGeometry(str,&WinX,&WinY,&WorldWidth,&WorldHeight); if (WinX != -9999 || WinY != -9999) RedirectFlag = 1; if (WinX == -9999) WinX = 0; if (WinY == -9999) WinY = 0; } gr_init(argc,argv) int argc; char **argv; { int w; struct button_rec *b; extern char *DisplayName, *FontName; if(!(dpy = XOpenDisplay(DisplayName))) { fprintf(stderr,"%s: cannot open %s.\n",argv[0], XDisplayName(DisplayName)); exit(1); } if (!(fst = XLoadQueryFont(dpy, FontName))) { fprintf(stderr,"%s: has not font %s.\n", argv[0], FontName); exit(1); } screen = DefaultScreen(dpy); depth = DefaultDepth(dpy,screen); bg = BlackPixel(dpy,screen); fg = WhitePixel(dpy,screen); ButtonHeight = (fst->max_bounds.ascent + fst->max_bounds.descent) * 1.5; ButtonBase = ButtonHeight * 0.2 + fst->max_bounds.ascent; for (ButtonWidth = 0, b = buttons; b->label; b ++) { w = XTextWidth(fst,b->label,strlen(b->label)) + fst->max_bounds.width; if (ButtonWidth < w) ButtonWidth = w; } if (FullScreen) { RedirectFlag = 1; WorldWidth = DisplayWidth(dpy,screen)-ButtonPad*2-ButtonWidth-2; WorldHeight = DisplayHeight(dpy,screen)-2; if (WorldWidth > WorldHeight) { WinX = (WorldWidth - WorldHeight) / 2; WinY = 0; WorldWidth = WorldHeight; } else { WinX = 0; WinY = (WorldHeight - WorldWidth) / 2; WorldHeight = WorldWidth; } } ButtonX = WorldWidth + ButtonPad; WinWidth = ButtonX + ButtonWidth + ButtonPad ; WinHeight = WorldHeight; RootWin = DefaultRootWindow(dpy); MainWin = XCreateSimpleWindow(dpy,RootWin, WinX, WinY, WinWidth, WinHeight, 1, fg, bg); XSetStandardProperties(dpy, MainWin, TITLE, argv[0], None, argv, argc, NULL); att.override_redirect = RedirectFlag; att.event_mask = ExposureMask | ResizeRedirectMask | ButtonPressMask | ButtonReleaseMask; XChangeWindowAttributes(dpy,MainWin, (CWEventMask|CWOverrideRedirect),&att); win = XCreateSimpleWindow(dpy,MainWin, 0, 0, WorldWidth, WorldHeight, 0, fg, bg); att.backing_store = Always; att.event_mask = ButtonPressMask | ButtonReleaseMask; XChangeWindowAttributes(dpy,win,(CWBackingStore|CWEventMask),&att); for (w = ButtonPad, b = buttons; b->label; b ++) { b->win = XCreateSimpleWindow(dpy,MainWin,ButtonX,w, ButtonWidth,ButtonHeight,1,ButtonFG,ButtonBG); XSelectInput(dpy,b->win,ExposureMask| ButtonPressMask|ButtonReleaseMask| EnterWindowMask|LeaveWindowMask); XMapRaised(dpy,b->win); w += ButtonHeight + ButtonPad; b->x=(ButtonWidth-XTextWidth(fst,b->label,strlen(b->label)))/2; } { struct base_rec *p; for (p = Monitor; p->target; p ++) { p->base = (w += ButtonHeight); p->prev = 9999; }} if (!MessageStdout) { msgwin = XCreateSimpleWindow(dpy,RootWin,0,0,100,100,1,fg,bg); att.override_redirect = True; XChangeWindowAttributes(dpy,msgwin,CWOverrideRedirect,&att); XSelectInput(dpy,msgwin,ExposureMask); } gcv.foreground = fg; gcv.background = bg; gcv.stipple = XCreateBitmapFromData(dpy,win,MyStipple,4,4); gcv.font = fst->fid; gc = XCreateGC(dpy,win,GCForeground|GCBackground|GCStipple|GCFont,&gcv); crs_hand = XCreateFontCursor(dpy,XC_hand1); if (color_flag = (depth >= 8)) color_setup(); else bw_setup(); XMapRaised(dpy, MainWin); XMapRaised(dpy, win); clear_world(); XFlush(dpy); } bw_setup() { ButtonFG = bg; ButtonBG = fg; } color_setup() { XColor scr, exc, acolor[Ncolors]; int pl[1], i, j, w; struct button_rec *b; cmap = DefaultColormap(dpy,screen); XAllocNamedColor(dpy,cmap,"NavyBlue",&scr,&exc); ButtonFG = scr.pixel; XAllocNamedColor(dpy,cmap,"LightSkyBlue",&scr,&exc); ButtonBG = scr.pixel; XAllocNamedColor(dpy,cmap,"DarkGreen",&scr,&exc); TitleFG = scr.pixel; XAllocNamedColor(dpy,cmap,"PaleGreen",&scr,&exc); TitleBG = scr.pixel; XAllocNamedColor(dpy,cmap,"grey50",&scr,&exc); WorkBG = scr.pixel; if (!XAllocColorCells(dpy, cmap, True, pl, 0, pixel, Ncolors)) { fprintf(stderr,"There is no enough room for %d colors.\n", Ncolors); exit(1); } for (i = 0; i < Ncolors; i ++) { acolor[i].pixel = pixel[i]; acolor[i].flags = DoRed | DoGreen | DoBlue; } set_colors(acolor,Ncolors); XStoreColors(dpy, cmap, acolor, Ncolors); w = XTextWidth(fst,color_menu.title,strlen(color_menu.title)); for (i = 0; color_menu_buttons[i].label; i++) { j = XTextWidth(fst,color_menu_buttons[i].label, strlen(color_menu_buttons[i].label)); if (j > w) w = j; } color_menu.width = w + fst->max_bounds.width + 2; color_menu.height = ButtonHeight * (i+1) + 2; color_menu.win = XCreateSimpleWindow(dpy,RootWin,0,0, color_menu.width,color_menu.height,1,TitleFG,TitleBG); att.event_mask = ExposureMask; att.override_redirect = True; XChangeWindowAttributes(dpy,color_menu.win, (CWEventMask|CWOverrideRedirect),&att); for (i = 0, b = color_menu_buttons; b->label; i++, b++) { b->win = XCreateSimpleWindow(dpy,color_menu.win, 0,ButtonHeight*(i+1),color_menu.width-2,ButtonHeight, 1,ButtonFG,ButtonBG); XSelectInput(dpy,b->win,ExposureMask|ButtonPressMask |ButtonReleaseMask|EnterWindowMask|LeaveWindowMask); XMapRaised(dpy,b->win); b->x = (color_menu.width-2 -XTextWidth(fst,b->label,strlen(b->label))) / 2; } } set_colors(ac, n) XColor *ac; int n; { int i, j, s, e; float r1,g1,b1, r2,g2,b2; static struct { float r, g, b, p; } d[] = { { 0.0, 1.0, 1.0, 0.33}, /* cyan */ { 1.0, 1.0, 0.0, 0.33}, /* yellow */ { 1.0, 0.0, 1.0, 0.34}, /* magenta */ { 1.0, 1.0, 1.0, 999.}};/* white */ for (j = e = 0; e < n; j ++) { r1 = d[j].r; g1 = d[j].g; b1 = d[j].b; r2 = d[j+1].r; g2 = d[j+1].g; b2 = d[j+1].b; for (i = s = e, e += n * d[j].p; i < e && i < n; i ++) { ac[i].red = (r1 + (i-s)*(r2-r1)/(e-s)) * 65535; ac[i].green = (g1 + (i-s)*(g2-g1)/(e-s)) * 65535; ac[i].blue = (b1 + (i-s)*(b2-b1)/(e-s)) * 65535; } } } event_loop() { XEvent event; while (1) { XNextEvent(dpy,&event); switch (event.type) { case Expose: redisplay(&event); break; case EnterNotify: case LeaveNotify: redraw_button(&event); break; case ButtonRelease: exec_button(&event); } } } check_event() { XEvent event; while (XCheckMaskEvent(dpy,ButtonPressMask|ButtonReleaseMask| ExposureMask,&event)) switch (event.type) { case ButtonRelease: return True; case Expose: redisplay(&event); } return False; } redraw_button(e) XCrossingEvent *e; { struct button_rec *b; for (b = buttons; b->label; b ++) if (e->window == b->win) { b->flag = (e->type == EnterNotify); draw_button(b); break; } } exec_button(e) XButtonEvent *e; { struct button_rec *b; extern char *ReplayFileName; if (ReplayFileName) { clear_world(); replay_logfile(); return; } for (b = buttons; b->label; b ++) if (e->window == b->win) { (*b->proc)(); break; } if (e->window == win) { double cx, cy; if ((cx = e->x*ViewSize/WorldWidth + ViewX) >= 1.0) cx -= 1.0; if ((cy = e->y*ViewSize/WorldHeight + ViewY) >= 1.0) cy -= 1.0; show_cell(cx,cy); } } draw_button(b) struct button_rec *b; { unsigned long bfg = ButtonFG, bbg = ButtonBG; if (b->flag) { bfg = ButtonBG; bbg = ButtonFG; } XSetForeground(dpy,gc,bbg); XFillRectangle(dpy,b->win,gc,0,0,ButtonWidth,ButtonHeight); XSetForeground(dpy,gc,bfg); XDrawString(dpy,b->win,gc,b->x,ButtonBase,b->label,strlen(b->label)); } draw_circle(px,py,pr) double px,py,pr; { int x, y, r; r = pr / ViewSize * WorldWidth; if ((px -= ViewX) < -pr) px += 1.0; x = px / ViewSize * WorldWidth; if (x - r >= WorldWidth) return; if ((py -= ViewY) < -pr) py += 1.0; y = py / ViewSize * WorldHeight; if (y - r >= WorldHeight) return; if (r < 1) XDrawPoint(dpy,win,gc,x,y); else XFillArc(dpy,win,gc,x-r,y-r,r+r,r+r,0,64*360); } gr_draw_cell(c) cell *c; { double th; unsigned long col; switch (color_flag) { case MONOCHROME: col = fg; break; case COL_CELL_STATE: col = pixel[c->state]; break; case COL_INDIVIDUAL: col = pixel[c->ind->id % Ncolors]; break; case COL_ROOTS: col = pixel[c->ind->root_id % Ncolors]; break; case COL_GENE_0: col = pixel[c->ind->gene[0] % Ncolors]; break; } XSetForeground(dpy,gc,col); draw_circle(c->x,c->y,CellSize); /* XSetForeground(dpy,gc,fg); th = c->d * M_PI / 128.0; draw_circle(c->x+CellSize/2*cos(th),c->y+CellSize/2*sin(th), CellSize*0.45); */ } gr_erase_cell(c) cell *c; { XSetForeground(dpy,gc,bg); draw_circle(c->x,c->y,CellSize); } redraw_cell(oldstate,c) int oldstate; cell *c; { if (color_flag == COL_CELL_STATE && oldstate != c->state) gr_draw_cell(c); } clear_world() { XClearWindow(dpy,win); } void b_run() { draw_message("Click mouse button to stop."); do { exec_step(); XFlush(dpy); if (PopSize == 0) world_reset(); } while (!check_event()); if (!MessageStdout) XUnmapWindow(dpy,msgwin); } void b_gene() { extern int LifeSpan; draw_message("Click mouse button to stop."); while (Step < LifeSpan && !check_event()) { exec_step(); XFlush(dpy); } if (!MessageStdout) XUnmapWindow(dpy,msgwin); } void b_step() { exec_step(); } void b_reset() { world_reset(); } void b_return() { world_return(); } void b_quit() { world_close(); } void b_color() { call_popup_menu(color_menu); for_all_cells(gr_draw_cell); } static void get_position(msg,x,y) char *msg; int *x, *y; { XEvent event; draw_message(msg); do { XNextEvent(dpy,&event); if (event.type == LeaveNotify) redraw_button(&event); } while (event.xany.window != win || event.type != ButtonRelease); *x = event.xbutton.x; *y = event.xbutton.y; } static void adjust_position(v,d) double *v, d; { *v += d; *v -= (double)floor(*v); } #define ZoomStackSize 128 struct { double x,y,s; } ZoomStack[ZoomStackSize]; int ZoomStackPointer = 0; void b_zoom() { int x, y; get_position("Specify the center of zoomed area.",&x,&y); if (ZoomStackPointer < ZoomStackSize) { ZoomStack[ZoomStackPointer].x = ViewX; ZoomStack[ZoomStackPointer].y = ViewY; ZoomStack[ZoomStackPointer].s = ViewSize; ZoomStackPointer ++; } adjust_position(&ViewX,(ViewSize * (x - WorldWidth/4)) / WorldWidth); adjust_position(&ViewY,(ViewSize * (y - WorldHeight/4)) / WorldHeight); ViewSize *= 0.5; clear_world(); for_all_cells(gr_draw_cell); if (!MessageStdout) XUnmapWindow(dpy,msgwin); } void b_unzoom() { if (ZoomStackPointer <= 0) return; ZoomStackPointer --; ViewX = ZoomStack[ZoomStackPointer].x; ViewY = ZoomStack[ZoomStackPointer].y; ViewSize = ZoomStack[ZoomStackPointer].s; clear_world(); for_all_cells(gr_draw_cell); } void b_scroll() { int x, y; get_position("Specify the center of new screen.",&x,&y); adjust_position(&ViewX,(ViewSize * (x - WorldWidth/2)) / WorldWidth); adjust_position(&ViewY,(ViewSize * (y - WorldHeight/2)) / WorldHeight); clear_world(); for_all_cells(gr_draw_cell); if (!MessageStdout) XUnmapWindow(dpy,msgwin); } char MassageString[1300]; draw_message(msg) char *msg; { int len, x, y, w, h, ww, wh, bw, d; Window cwin, root,parent,*children; if (MessageStdout) { puts(msg); return; } strcpy(MassageString,msg); for (w = 0, h = ButtonHeight; *msg; msg += len) { if (*msg == '\n') { msg ++; h += ButtonHeight; } for (len = 0; msg[len] && msg[len] != '\n'; len++); if (len && w < (ww = XTextWidth(fst,msg,len))) w = ww; } parent = MainWin; do { cwin = parent; XQueryTree(dpy,cwin,&root,&parent,&children,&d); } while (root != parent); XGetGeometry(dpy,cwin,&root,&x,&y,&ww,&wh,&bw,&d); XMoveResizeWindow(dpy,msgwin,x,y+wh+bw+1,w+ButtonPad*2,h); XMapRaised(dpy,msgwin); display_message(); } display_message() { int len, y; char *msg; XSetForeground(dpy,gc,fg); for (msg = MassageString, y = ButtonBase; *msg; msg += len) { if (*msg == '\n') { msg ++; y += ButtonHeight; } for (len = 0; msg[len] && msg[len] != '\n'; len++); if (len) XDrawImageString(dpy,msgwin,gc,ButtonPad,y,msg,len); } } gr_draw_info() { int x; struct base_rec *p; char buf[32]; XSetForeground(dpy,gc,fg); for (p = Monitor; p->target; p++) { if (p->prev > *(p->target)) { XSetForeground(dpy,gc,bg); XFillRectangle(dpy,MainWin,gc,ButtonX, p->base-fst->max_bounds.ascent,ButtonWidth, fst->max_bounds.ascent+fst->max_bounds.descent); XSetForeground(dpy,gc,fg); } sprintf(buf,"%5d ",*(p->target)); x = ButtonX + ButtonWidth - XTextWidth(fst,buf,strlen(buf)); XDrawImageString(dpy,MainWin,gc,x,p->base,buf,strlen(buf)); p->prev = *(p->target); } XFlush(dpy); } call_popup_menu(menu) popup_menu *menu; { XEvent event; int i, rx, ry, wx, wy, mask, flag = 0; Window root, child; struct button_rec *b; if (!menu->win) return 0; XQueryPointer(dpy,MainWin,&root,&child,&rx,&ry,&wx,&wy,&mask); rx ++; if (rx+menu->width > XDisplayWidth(dpy,screen)) rx = XDisplayWidth(dpy,screen) - menu->width; if (ry+menu->height > XDisplayHeight(dpy,screen)) ry = XDisplayHeight(dpy,screen) - menu->height; XMoveWindow(dpy,menu->win,rx,ry); for (b = menu->buttons; b->label; b ++) b->flag = 0; XMapRaised(dpy,menu->win); XGrabPointer(dpy,RootWin,True,ButtonReleaseMask, GrabModeAsync,GrabModeAsync,None,crs_hand,CurrentTime); do { XNextEvent(dpy,&event); for (b = menu->buttons; b->label; b ++) if (b->win == event.xany.window) break; if (!b->label) b = NULL; switch (event.type) { case ButtonRelease: XUnmapWindow(dpy,menu->win); XWarpPointer(dpy,None,MainWin,0,0,0,0,wx,wy); XUngrabPointer(dpy,CurrentTime); XFlush(dpy); if (b) (*b->proc)(); flag = 1; break; case EnterNotify: if (b) { b->flag = 1; draw_button(b); } break; case LeaveNotify: if (b) { b->flag = 0; draw_button(b); } break; case Expose: redisplay(&event); break; } } while (!flag); } redisplay(e) XExposeEvent *e; { struct button_rec *b; cell *p; if (e->count) return 0; if (e->window == MainWin) { if (color_flag != MONOCHROME) XSetForeground(dpy,gc,WorkBG); else { XSetFillStyle(dpy,gc,FillOpaqueStippled); XSetForeground(dpy,gc,fg); } XFillRectangle(dpy,MainWin,gc,WorldWidth,0, WinWidth-WorldWidth,WinHeight); if (color_flag == MONOCHROME) XSetFillStyle(dpy,gc,FillSolid); draw_info(); } else if (e->window == msgwin) { display_message(); } else if (e->window == color_menu.win) { XSetForeground(dpy,gc,TitleFG); XDrawString(dpy,color_menu.win,gc,fst->max_bounds.width/2, ButtonBase,color_menu.title,strlen(color_menu.title)); } else { for (b = buttons; b->label; b ++) if (e->window == b->win) { draw_button(b); break; } if (!b->label) for (b = color_menu_buttons; b->label; b ++) if (e->window == b->win) { draw_button(b); break; } } }