#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "puzzles.h" /* * Foreground black and Background white because most of the games * simply do not work if you reverse those! They interpolate between * background and black, effectively forcing black for foreground. */ static XrmDatabase db; static const char *defaults = "\ *Foreground: black\n\ *Background: white\n\ *BorderColour: blue\n\ *BorderMargin: 4\n\ *BorderWidth: 1\n\ *MenuMargin: 25\n\ "; typedef unsigned long long int TIME; static char *displayname = 0; static char *geometryspec = 0; static char *foreground = 0; static char *background = 0; static char *bordercstr = 0; static char *borderwstr = 0; static char *bordermstr = 0; static char *menumstr = 0; static char *fontstr = 0; static int synch = 0; static char *rmstring = 0; static int argc; static char **argv; static const char *av0bn; static Display *disp; static Screen *scr; static int scrwidth; static int scrheight; static int depth; static Window rootwin; static int margin; static int menumargin; static int borderwidth; static int (*oldioerr)(Display *); static int (*olderr)(Display *, XErrorEvent *); static int dir_junk; static int asc_junk; static int dsc_junk; #define XTE_JUNK &dir_junk, &asc_junk, &dsc_junk typedef enum { LT_LEAF = 1, LT_GLUE, LT_HBOX, LT_VBOX, LT_WRITE_LOC, } LAYOUT_TYPE; typedef enum { INSERT_DROP = 1, INSERT_SAME, INSERT_DEEPEN, } INSERT_STATUS; typedef struct xy XY; typedef struct layout LAYOUT; typedef struct leafops LEAFOPS; typedef struct menubar_item MENUBAR_ITEM; typedef struct mpop MPOP; typedef struct mpop_item MPOP_ITEM; typedef struct evhent EVHENT; typedef struct evhandle EVHANDLE; struct xy { int x; int y; } ; struct evhandle { const char *name; EVHENT *root; } ; #define EVHANDLE_INIT(n) {\ .name = #n, \ .root = 0 } struct evhent { EVHENT *u; EVHENT *l; EVHENT *r; int bal; Window w; void (*handler)(void *, XEvent *); void *arg; } ; struct menubar_item { const char *text; MENUBAR_ITEM *link; int textlen; int extend_l; int extend_r; int textw; void (*click)(frontend *, Time); bool (*acttest)(midend *); int curact; LAYOUT *l; Window win; XY pmsize; Pixmap actpm; Pixmap inactpm; } ; struct leafops { const char *name; void (*init)(void *, LAYOUT *); XY (*minsize)(void *); void (*pre_create)(void *, LAYOUT *, unsigned long int *, XSetWindowAttributes *); void (*post_create)(void *, Window); void (*destroy)(void *); void (*pre_update)(void *, LAYOUT *); void (*post_update)(void *); } ; #define LEAFOPS_INIT(name) {\ #name, \ &leafop_##name##_init, \ &leafop_##name##_minsize, \ &leafop_##name##_pre_create, \ &leafop_##name##_post_create, \ &leafop_##name##_destroy, \ &leafop_##name##_pre_update, \ &leafop_##name##_post_update, \ } struct layout { LAYOUT_TYPE type; XY minsize; XY loc; XY size; union { struct { const LEAFOPS *ops; void *priv; XY winloc; XY winsize; Window win; } leaf; struct { int min; double stretch; } glue; struct { int n; LAYOUT **v; double stretch; } box; int *write_loc; } u; } ; struct mpop_item { MPOP *menu; int inx; char *text; int textlen; int textw; LAYOUT *l; Window win; Pixmap pm; } ; struct mpop { frontend *fe; int nitems; MPOP_ITEM *items; int itemsa; void (*choose)(int, Time); LAYOUT *l; int up; Window win; int curitem; int wantquery; int id; } ; struct frontend { midend *me; Window topwin; Window menuwin; Window menubar; MENUBAR_ITEM *menubar_items; Window drawwin; Pixmap drawpix; XY drawpsize; int draww; int drawh; unsigned int flags; #define FEF_STATBAR 0x00000001 #define FEF_CONFIG 0x00000002 #define FEF_SOLVE 0x00000004 #define FEF_CAN_UNDO 0x00000008 #define FEF_CAN_REDO 0x00000010 #define FEF_CUSTOM 0x00000020 Window statbar; int statusbar_r; Window statwin; char *statbuf; int statalloc; int statlen; LAYOUT *layout_game; MENUBAR_ITEM menubar_quit; MENUBAR_ITEM menubar_undo; MENUBAR_ITEM menubar_redo; MENUBAR_ITEM menubar_solve; MENUBAR_ITEM menubar_config; MENUBAR_ITEM menubar_new; LAYOUT *gl; int timerfd; int running; TIME lasttime; GC copygc; GC drawgc; GC bitgc; Colormap wincmap; int defcmap; XColor fgcolour; XColor bgcolour; XColor bdcolour; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints *normal_hints; XWMHints *wm_hints; XClassHint *class_hints; XFontStruct *font; int baselineskip; int vcentre; Cursor arrowcurs; struct preset_menu *cur_presets; struct preset_menu *presets; int ncolours; XColor *colours; MPOP mpop; } ; struct blitter { int w; int h; int x; int y; Pixmap pm; } ; static frontend *fe; static EVHANDLE evh_expose = EVHANDLE_INIT(Expose); static EVHANDLE evh_buttonpress = EVHANDLE_INIT(ButtonPress); static EVHANDLE evh_buttonrelease = EVHANDLE_INIT(ButtonRelease); static EVHANDLE evh_motionnotify = EVHANDLE_INIT(MotionNotify); static EVHANDLE evh_enternotify = EVHANDLE_INIT(EnterNotify); static EVHANDLE evh_leavenotify = EVHANDLE_INIT(LeaveNotify); static const struct drawing_api x_drawing; // forward static FILE *dlog = 0; #define DEFAULT_ABORT() default: abort(); break static char *deconst(const char *s) { char *rv; bcopy(&s,&rv,sizeof(rv)); return(rv); } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; const char *s; 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,"-bordercolour") || !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,"-menumargin") || !strcmp(*av,"-mm")) { WANTARG(); menumstr = av[skip]; continue; } if (!strcmp(*av,"-font")) { WANTARG(); fontstr = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&rmstring,"\n",av[skip],(char *)0); continue; } if (!strcmp(*av,"-sync")) { synch = 1; continue; } if (!strcmp(*av,"-dlog")) { WANTARG(); drawlog_open(av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static void draw_statbar_text(frontend *fe) { if (fe->statlen > 0) { XSetForeground(disp,fe->drawgc,fe->fgcolour.pixel); XDrawString(disp,fe->statwin,fe->drawgc,0,fe->font->ascent,fe->statbuf,fe->statlen); } } static Pixmap menu_cached_pm(Pixmap *cache, MENUBAR_ITEM *i, int active) { Pixmap pm; int x; int y; if ( (i->l->size.x != i->pmsize.x) || (i->l->size.y != i->pmsize.y) ) { if (i->actpm != None) { XFreePixmap(disp,i->actpm); i->actpm = None; } if (i->inactpm != None) { XFreePixmap(disp,i->inactpm); i->inactpm = None; } i->pmsize = i->l->size; } pm = *cache; if (pm != None) return(pm); pm = XCreatePixmap(disp,rootwin,i->pmsize.x,i->pmsize.y,depth); XSetForeground(disp,fe->drawgc,fe->bgcolour.pixel); x = (i->pmsize.x - i->textw) / 2; y = (i->pmsize.y - fe->baselineskip) / 2; XFillRectangle(disp,pm,fe->drawgc,0,0,i->pmsize.x,i->pmsize.y); if (active) { XSetForeground(disp,fe->drawgc,fe->fgcolour.pixel); XDrawString(disp,pm,fe->drawgc,x,y+fe->font->ascent,i->text,i->textlen); } *cache = pm; return(pm); } static void copyarea(Display *d, Drawable f, Drawable t, GC gc, int fx, int fy, unsigned int w, unsigned int h, int tx, int ty) { if ((f == 0) || (t == 0)) abort(); XCopyArea(d,f,t,gc,fx,fy,w,h,tx,ty); } static void draw_menubar_item(frontend *fe, MENUBAR_ITEM *i) { copyarea(disp,menu_cached_pm(i->curact?&i->actpm:&i->inactpm,i,i->curact),i->win,fe->copygc,0,0,32767,32767,0,0); } #if 0 static void mpop_dirty_items(MPOP *mp, int y1, int y2) { if (y1 < 0) y1 = 0; if (y2 >= mp->h) y2 = mp->h - 1; if (y1 > y2) return; y1 /= mp->fe->baselineskip; y2 /= mp->fe->baselineskip; for (;y1<=y2;y1++) mp->items[y1].flags |= MPIF_DIRTY; mp->items_dirty = 1; } #endif static void resync_buttons(frontend *fe) { MENUBAR_ITEM *i; int act; for (i=fe->menubar_items;i;i=i->link) { act = !i->acttest || (*i->acttest)(fe->me); if (act != i->curact) { i->curact = act; draw_menubar_item(fe,i); } } } static void process_key(int x, int y, int b) { if (! midend_process_key(fe->me,x,y,b)) exit(0); resync_buttons(fe); } static void keystroke(XKeyEvent *e) { int b; KeySym ks; ks = XLookupKeysym(e,0); switch (ks) { case XK_Left: b = CURSOR_LEFT; break; case XK_Right: b = CURSOR_RIGHT; break; case XK_Up: b = CURSOR_UP; break; case XK_Down: b = CURSOR_DOWN; break; case XK_0: b = '0'; break; case XK_1: b = '1'; break; case XK_2: b = '2'; break; case XK_3: b = '3'; break; case XK_4: b = '4'; break; case XK_5: b = '5'; break; case XK_6: b = '6'; break; case XK_7: b = '7'; break; case XK_8: b = '8'; break; case XK_9: b = '9'; break; case XK_A: case XK_a: b = 'a'; break; case XK_B: case XK_b: b = 'b'; break; case XK_C: case XK_c: b = 'c'; break; case XK_D: case XK_d: b = 'd'; break; case XK_E: case XK_e: b = 'e'; break; case XK_F: case XK_f: b = 'f'; break; case XK_G: case XK_g: b = 'g'; break; case XK_H: case XK_h: b = 'h'; break; case XK_I: case XK_i: b = 'i'; break; case XK_J: case XK_j: b = 'j'; break; case XK_K: case XK_k: b = 'k'; break; case XK_L: case XK_l: b = 'l'; break; case XK_M: case XK_m: b = 'm'; break; case XK_N: case XK_n: b = 'n'; break; case XK_O: case XK_o: b = 'o'; break; case XK_P: case XK_p: b = 'p'; break; case XK_Q: case XK_q: b = 'q'; break; case XK_R: case XK_r: b = 'r'; break; case XK_S: case XK_s: b = 's'; break; case XK_T: case XK_t: b = 't'; break; case XK_U: case XK_u: b = 'u'; break; case XK_V: case XK_v: b = 'v'; break; case XK_W: case XK_w: b = 'w'; break; case XK_X: case XK_x: b = 'x'; break; case XK_Y: case XK_y: b = 'y'; break; case XK_Z: case XK_z: b = 'z'; break; case XK_space: b = ' '; break; default: printf("Unhandled keystroke: keycode=%d keysym=%lx\n",e->keycode,(long int)ks); return; break; } process_key(0,0,b); } static void leafop_null_init(void *arg __attribute__((__unused__)), LAYOUT *l __attribute__((__unused__))) { } static void leafop_null_post_create(void *arg __attribute__((__unused__)), Window w __attribute__((__unused__))) { } static void leafop_null_destroy(void *arg __attribute__((__unused__))) { } static void leafop_null_pre_update(void *arg __attribute__((__unused__)), LAYOUT *l __attribute__((__unused__))) { } static void leafop_null_post_update(void *arg __attribute__((__unused__))) { } static void leafop_abort_destroy(void *arg __attribute__((__unused__))) { abort(); } static void leafop_abort_pre_update(void *arg __attribute__((__unused__)), LAYOUT *l __attribute__((__unused__))) { abort(); } static void leafop_abort_post_update(void *arg __attribute__((__unused__))) { abort(); } static LAYOUT *layout_new(void) { return(malloc(sizeof(LAYOUT))); } static LAYOUT *layout_setup_leaf(const LEAFOPS *ops, void *priv) { LAYOUT *l; l = layout_new(); l->type = LT_LEAF; l->u.leaf.ops = ops; l->u.leaf.priv = priv; (*ops->init)(priv,l); return(l); } static LAYOUT *layout_setup_glue(int min, double stretch) { LAYOUT *l; l = layout_new(); l->type = LT_GLUE; l->u.glue.min = min; l->u.glue.stretch = stretch; return(l); } static LAYOUT *layout_write_loc(int *to) { LAYOUT *l; l = layout_new(); l->type = LT_WRITE_LOC; l->u.write_loc = to; return(l); } static LAYOUT *layout_setup_box(LAYOUT_TYPE t, ...) #define LAYOUT_END ((LAYOUT *)0) { va_list ap; LAYOUT *l; LAYOUT *sub; int i; switch (t) { case LT_HBOX: case LT_VBOX: break; DEFAULT_ABORT(); } l = layout_new(); l->type = t; l->u.box.n = 0; va_start(ap,t); while (1) { sub = va_arg(ap,LAYOUT *); if (sub == LAYOUT_END) break; l->u.box.n ++; } va_end(ap); l->u.box.v = malloc(l->u.box.n*sizeof(*l->u.box.v)); i = 0; va_start(ap,t); while (1) { sub = va_arg(ap,LAYOUT *); if (sub == LAYOUT_END) break; l->u.box.v[i++] = sub; } va_end(ap); if (i != l->u.box.n) abort(); return(l); } static void layout_box_append(LAYOUT *l, ...) #define LAYOUT_END ((LAYOUT *)0) { va_list ap; LAYOUT *sub; int n; switch (l->type) { case LT_HBOX: case LT_VBOX: break; DEFAULT_ABORT(); } n = 0; va_start(ap,l); while (1) { sub = va_arg(ap,LAYOUT *); if (sub == LAYOUT_END) break; n ++; } va_end(ap); l->u.box.v = realloc(l->u.box.v,(l->u.box.n+n)*sizeof(*l->u.box.v)); va_start(ap,l); while (1) { sub = va_arg(ap,LAYOUT *); if (sub == LAYOUT_END) break; l->u.box.v[l->u.box.n++] = sub; n --; } va_end(ap); if (n != 0) abort(); } static int *coord_p(LAYOUT_TYPE t, XY *xy) { switch (t) { case LT_HBOX: return(&xy->x); break; case LT_VBOX: return(&xy->y); break; DEFAULT_ABORT(); } } static int *other_coord_p(LAYOUT_TYPE t, XY *xy) { switch (t) { case LT_HBOX: return(&xy->y); break; case LT_VBOX: return(&xy->x); break; DEFAULT_ABORT(); } } static int choose_h_v(LAYOUT_TYPE t, int h, int v) { switch (t) { case LT_HBOX: return(h); break; case LT_VBOX: return(v); break; DEFAULT_ABORT(); } } static void layout_pass1(LAYOUT *l) { int sz; int psz; int i; LAYOUT *l2; int d; switch (l->type) { case LT_HBOX: case LT_VBOX: l->u.box.stretch = 0; sz = 0; psz = 0; for (i=0;iu.box.n;i++) { l2 = l->u.box.v[i]; switch (l2->type) { case LT_LEAF: l2->minsize = (*l2->u.leaf.ops->minsize)(l2->u.leaf.priv); break; case LT_GLUE: l2->minsize.x = choose_h_v(l->type,l2->u.glue.min,0); l2->minsize.y = choose_h_v(l->type,0,l2->u.glue.min); l->u.box.stretch += l2->u.glue.stretch; break; case LT_WRITE_LOC: l2->minsize = (XY){.x=0,.y=0}; break; case LT_HBOX: case LT_VBOX: layout_pass1(l2); break; } sz += *coord_p(l->type,&l2->minsize); d = *other_coord_p(l->type,&l2->minsize); if (d > psz) psz = d; } *coord_p(l->type,&l->minsize) = sz; *other_coord_p(l->type,&l->minsize) = psz; break; DEFAULT_ABORT(); } } static void layout_pass2(LAYOUT *l, int x, int y, int w, int h) { int stretch; double sleft; LAYOUT *l2; int x2; int y2; int *cp; int g; int i; l->loc.x = x; l->loc.y = y; l->size.x = w; l->size.y = h; switch (l->type) { case LT_HBOX: stretch = w - l->minsize.x; cp = &x2; if (0) { case LT_VBOX: stretch = h - l->minsize.y; cp = &y2; } sleft = l->u.box.stretch; x2 = x; y2 = y; for (i=0;iu.box.n;i++) { l2 = l->u.box.v[i]; switch (l2->type) { case LT_GLUE: if ((sleft > 1e-3) && (l2->u.glue.stretch > 1e-3)) { g = ((stretch * l2->u.glue.stretch) / sleft) + .5; stretch -= g; sleft -= l2->u.glue.stretch; g += l2->u.glue.min; } else { g = l2->u.glue.min; } layout_pass2(l2,x2,y2,choose_h_v(l->type,g,w),choose_h_v(l->type,h,g)); *cp += g; break; case LT_WRITE_LOC: *l2->u.write_loc = choose_h_v(l->type,x2,y2); layout_pass2(l2,x2,y2,choose_h_v(l->type,0,w),choose_h_v(l->type,h,0)); break; case LT_HBOX: case LT_VBOX: case LT_LEAF: layout_pass2(l2,x2,y2,choose_h_v(l->type,l2->minsize.x,w),choose_h_v(l->type,h,l2->minsize.y)); *cp += choose_h_v(l->type,l2->minsize.x,l2->minsize.y); break; } } break; case LT_LEAF: case LT_GLUE: case LT_WRITE_LOC: break; DEFAULT_ABORT(); } } static void layout_dump(LAYOUT *) __attribute__((__used__)); static void layout_dump(LAYOUT *top) { void dump(LAYOUT *l, int indent) { printf("%*smin %dx%d, %dx%d at %d,%d: ",indent,"",l->minsize.x,l->minsize.y,l->size.x,l->size.y,l->loc.x,l->loc.y); switch (l->type) { case LT_HBOX: printf("HBOX"); if (0) { case LT_VBOX: printf("VBOX"); } { int i; printf(" stretch %g, containing %d\n",l->u.box.stretch,l->u.box.n); for (i=0;iu.box.n;i++) dump(l->u.box.v[i],indent+4); } break; case LT_GLUE: printf("GLUE, min %d stretch %g\n",l->u.glue.min,l->u.glue.stretch); break; case LT_LEAF: printf("LEAF, ops %s priv %p\n",l->u.leaf.ops->name,l->u.leaf.priv); break; case LT_WRITE_LOC: printf("WRITE_LOC, to %p\n",(void *)l->u.write_loc); break; DEFAULT_ABORT(); } } dump(top,0); } static void layout_arrange(LAYOUT *l) { layout_pass1(l); layout_pass2(l,0,0,l->minsize.x,l->minsize.y); } static void layout_leaf_create(LAYOUT *l, Window parent) { unsigned long int attrmask; XSetWindowAttributes attr; if (l->type != LT_LEAF) abort(); attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.colormap = fe->wincmap; attrmask |= CWColormap; (*l->u.leaf.ops->pre_create)(l->u.leaf.priv,l,&attrmask,&attr); l->u.leaf.win = XCreateWindow(disp,parent,l->loc.x,l->loc.y,l->size.x,l->size.y,0,depth,InputOutput,CopyFromParent,attrmask,&attr); l->u.leaf.winloc = l->loc; l->u.leaf.winsize = l->size; (*l->u.leaf.ops->post_create)(l->u.leaf.priv,l->u.leaf.win); } static void layout_create_walk(LAYOUT *l, Window parent) { switch (l->type) { case LT_HBOX: case LT_VBOX: { int i; for (i=0;iu.box.n;i++) layout_create_walk(l->u.box.v[i],parent); } break; case LT_GLUE: case LT_WRITE_LOC: break; case LT_LEAF: layout_leaf_create(l,parent); break; DEFAULT_ABORT(); } } static void layout_create(LAYOUT *top, Window parent) { layout_create_walk(top,parent); } static void layout_leaf_destroy(LAYOUT *l) { (*l->u.leaf.ops->destroy)(l->u.leaf.priv); } static void layout_destroy_walk(LAYOUT *l) { switch (l->type) { case LT_HBOX: case LT_VBOX: { int i; for (i=0;iu.box.n;i++) layout_destroy_walk(l->u.box.v[i]); free(l->u.box.v); } break; case LT_GLUE: case LT_WRITE_LOC: break; case LT_LEAF: layout_leaf_destroy(l); break; DEFAULT_ABORT(); } free(l); } static void layout_destroy(LAYOUT *top, Window parent) { layout_destroy_walk(top); XDestroySubwindows(disp,parent); } static void layout_leaf_update(LAYOUT *l) { unsigned long int chgmask; XWindowChanges chg; if (l->type != LT_LEAF) abort(); chgmask = 0; (*l->u.leaf.ops->pre_update)(l->u.leaf.priv,l); if (l->u.leaf.winloc.x != l->loc.x) { chg.x = l->loc.x; chgmask |= CWX; } if (l->u.leaf.winloc.y != l->loc.y) { chg.y = l->loc.y; chgmask |= CWY; } if (l->u.leaf.winsize.x != l->size.x) { chg.width = l->size.x; chgmask |= CWWidth; } if (l->u.leaf.winsize.y != l->size.y) { chg.height = l->size.y; chgmask |= CWHeight; } if (chgmask) { XConfigureWindow(disp,l->u.leaf.win,chgmask,&chg); l->u.leaf.winloc = l->loc; l->u.leaf.winsize = l->size; } (*l->u.leaf.ops->post_update)(l->u.leaf.priv); } static void layout_update(LAYOUT *top) { void walk(LAYOUT *l) { switch (l->type) { case LT_HBOX: case LT_VBOX: { int i; for (i=0;iu.box.n;i++) walk(l->u.box.v[i]); } break; case LT_GLUE: case LT_WRITE_LOC: break; case LT_LEAF: layout_leaf_update(l); break; DEFAULT_ABORT(); } } walk(top); } #if 0 static void mpop_popdown(MPOP *mp) { if (mp->id != AIO_NOID) { aio_remove_block(mp->id); mp->id = AIO_NOID; } XUnmapWindow(disp,mp->fe->mpop_top); mp->fe->mpop_up = 0; } #endif static void mpop_pop_down(MPOP *mp) { XUnmapWindow(disp,mp->win); layout_destroy(mp->l,mp->win); mp->up = 0; if (mp->id != AIO_NOID) { aio_remove_block(mp->id); mp->id = AIO_NOID; } } static void mpop_cancel(MPOP *mp) { mpop_pop_down(mp); (*mp->choose)(-1,CurrentTime); } #if 0 static int mpop_curitem_xy(MPOP *mp, int x, int y) { if ( (x < 0) || (y < 0) || (x >= mp->w) || (y >= mp->h) ) { mp->curitem = -1; #define M (menumargin+margin+borderwidth) if ( (x < -M) || (y < -M) || (x >= mp->w+M) || (y >= mp->h+M) ) { mpop_cancel(mp); return(1); } #undef M } else { mp->curitem = y / mp->fe->baselineskip; } return(0); } #endif #if 0 static void mpop_click_event(frontend *fe, XButtonEvent *e) { if (! e->same_screen) { mpop_cancel(&fe->mpop); return; } if (mpop_curitem_xy(&fe->mpop,e->x,e->y)) return; mpop_choose_curitem(&fe->mpop,e->time); } #endif static void ev_handle(XEvent *e, EVHANDLE *evh) { EVHENT *h; h = evh->root; while (1) { if (! h) return; if (e->xany.window < h->w) { h = h->l; } else if (e->xany.window > h->w) { h = h->r; } else { (*h->handler)(h->arg,e); return; } } } static void handle_event(XEvent *e) { switch (e->type) { default: break; case Expose: // XExposeEvent - xexpose ev_handle(e,&evh_expose); break; case MappingNotify: // XMappingEvent - xmapping XRefreshKeyboardMapping(&e->xmapping); break; case KeyPress: // XKeyPressedEvent - XKeyEvent - xkey keystroke(&e->xkey); break; case ButtonPress: // XButtonPressedEvent - XButtonEvent - xbutton ev_handle(e,&evh_buttonpress); break; case ButtonRelease: // XButtonReleasedEvent - XButtonEvent - xbutton ev_handle(e,&evh_buttonrelease); break; case MotionNotify: // XPointerMovedEvent - XMotionEvent - xmotion ev_handle(e,&evh_motionnotify); break; case EnterNotify: // XEnterWindowEvent - XCrossingEvent - xcrossing ev_handle(e,&evh_enternotify); break; case LeaveNotify: // XLeaveWindowEvent - XCrossingEvent - xcrossing ev_handle(e,&evh_leavenotify); break; } } static void rd_X(void *arg __attribute__((__unused__))) { XEvent e; while (XEventsQueued(disp,QueuedAfterFlush)) { XNextEvent(disp,&e); handle_event(&e); } } static int flush_X(void *arg __attribute__((__unused__))) { XFlush(disp); fflush(0); return(AIO_BLOCK_NIL); } static void setup_aio(void) { aio_poll_init(); aio_add_poll(XConnectionNumber(disp),&aio_rwtest_always,&aio_rwtest_never,&rd_X,0,0); aio_add_block(&flush_X,0); } static int ioerror_handler(Display *d) { return((*oldioerr)(d)); } static int error_handler(Display *d, XErrorEvent *e) { return((*olderr)(d,e)); } static void setup_error(void) { olderr = XSetErrorHandler(error_handler); oldioerr = XSetIOErrorHandler(ioerror_handler); } static void setup_db(void) { char *str; char *home; char hostname[256]; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } 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); gethostname(&hostname[0],(sizeof(hostname)/sizeof(hostname[0]))-1); hostname[(sizeof(hostname)/sizeof(hostname[0]))-1] = '\0'; str = malloc(strlen(home)+1+11+strlen(&hostname[0])+1); sprintf(str,"%s/.Xdefaults-%s",home,&hostname[0]); db2 = XrmGetFileDatabase(str); if (db2) XrmMergeDatabases(db2,&db); free(str); } if (rmstring) { db2 = XrmGetStringDatabase(rmstring); XrmMergeDatabases(db2,&db); } } static char *get_default_value(const char *name, const char *class) { char *type; XrmValue value; static char *n = 0; static char *c = 0; free(n); free(c); asprintf(&n,"%s.%s",av0bn,name); asprintf(&c,"Puzzle.%s",class); if (XrmGetResource(db,n,c,&type,&value) == False) return(0); return(value.addr); } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static void setup_numbers(void) { if (!bordermstr || !borderwstr || !menumstr) abort(); margin = atoi(bordermstr); borderwidth = atoi(borderwstr); menumargin = atoi(menumstr); } static void setup_colour_core(frontend *fe, XColor *col, void (*prwhat)(FILE *), void (*prsuff)(FILE *)) { while (XAllocColor(disp,fe->wincmap,col) == 0) { if (! fe->defcmap) { fprintf(stderr,"%s: can't allocate colormap cell for ",__progname); (*prwhat)(stderr); fprintf(stderr," colour"); (*prsuff)(stderr); fprintf(stderr,"\n"); exit(1); } fe->wincmap = XCopyColormapAndFree(disp,fe->wincmap); fe->defcmap = 0; } } static void setup_colour_rgb(frontend *, XColor *, const char *, ...) __attribute__((__format__(__printf__,3,4))); static void setup_colour_rgb(frontend *fe, XColor *col, const char *whatfmt, ...) { va_list ap; void prwhat(FILE *f) { vfprintf(f,whatfmt,ap); } void prsuff(FILE *f __attribute__((__unused__))) { } va_start(ap,whatfmt); setup_colour_core(fe,col,&prwhat,&prsuff); va_end(ap); } static void setup_colour_text(frontend *, const char *, XColor *, const char *, ...) __attribute__((__format__(__printf__,4,5))); static void setup_colour_text(frontend *fe, const char *str, XColor *col, const char *whatfmt, ...) { va_list ap; void prwhat(FILE *f) { vfprintf(f,whatfmt,ap); } void prsuff(FILE *f __attribute__((__unused__))) { fprintf(f," `%s'",str); } va_start(ap,whatfmt); if (XParseColor(disp,fe->wincmap,str,col) == 0) { fprintf(stderr,"%s: bad ",__progname); vfprintf(stderr,whatfmt,ap); fprintf(stderr," colour `%s'\n",str); exit(1); } setup_colour_core(fe,col,&prwhat,&prsuff); va_end(ap); } static void set_nbio(int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); } static int evhandle_rebalance(EVHENT **up, EVHENT *uptr) { EVHENT *u; EVHENT *f; EVHENT *b; EVHENT *c; u = *up; if (uptr != u->u) abort(); switch (u->bal) { case 0: return(0); break; case -1: case 1: return(1); break; case -2: if (u->l->bal <= 0) { u->bal = -1 - u->l->bal; u->l->bal ++; *up = u->l; u->l->u = uptr; f = u->l->r; u->l->r = u; u->u = u->l; u->l = f; if (f) f->u = u; if (u->bal) return(1); } else if (u->l->bal > 0) { f = u->l->r; b = f->l; c = f->r; *up = f; f->u = uptr; f->l = u->l; f->l->u = f; f->r = u; u->u = f; f->l->r = b; if (b) b->u = f->l; u->l = c; if (c) c->u = u; f->l->bal = (f->bal > 0) ? -1 : 0; f->r->bal = (f->bal < 0) ? 1 : 0; f->bal = 0; } return(0); break; case 2: if (u->r->bal >= 0) { u->bal = 1 - u->r->bal; u->r->bal --; *up = u->r; u->r->u = uptr; f = u->r->l; u->r->l = u; u->u = u->r; u->r = f; if (f) f->u = u; if (u->bal) return(1); } else if (u->r->bal < 0) { f = u->r->l; b = f->r; c = f->l; *up = f; f->u = uptr; f->r = u->r; f->r->u = f; f->l = u; u->u = f; f->r->l = b; if (b) b->u = f->r; u->r = c; if (c) c->u = u; f->r->bal = (f->bal < 0) ? 1 : 0; f->l->bal = (f->bal > 0) ? -1 : 0; f->bal = 0; } return(0); break; } abort(); } static INSERT_STATUS evhandle_insert(EVHENT *new, EVHENT **up, EVHENT *uptr) { EVHENT *u; u = *up; if (! u) { *up = new; new->u = uptr; return(INSERT_DEEPEN); } if (new->w < u->w) { switch (evhandle_insert(new,&u->l,u)) { case INSERT_SAME: return(INSERT_SAME); break; case INSERT_DEEPEN: u->bal --; return(evhandle_rebalance(up,uptr)?INSERT_DEEPEN:INSERT_SAME); break; DEFAULT_ABORT(); } } else if (new->w > u->w) { switch (evhandle_insert(new,&u->r,u)) { case INSERT_SAME: return(INSERT_SAME); break; case INSERT_DEEPEN: u->bal ++; return(evhandle_rebalance(up,uptr)?INSERT_DEEPEN:INSERT_SAME); break; DEFAULT_ABORT(); } } else { abort(); } } static void evh_add(Window w, EVHANDLE *evh, void (*handler)(void *, XEvent *), void *arg) { EVHENT *e; e = malloc(sizeof(EVHENT)); e->w = w; e->handler = handler; e->arg = arg; e->l = 0; e->r = 0; e->bal = 0; evhandle_insert(e,&evh->root,0); } static void evh_remove(Window w, EVHANDLE *evh) { EVHENT *e; EVHENT *f; EVHENT *u; EVHENT *l; EVHENT *r; EVHENT **up; int dr; EVHENT *s; e = evh->root; while (1) { if (! e) abort(); if (w < e->w) { e = e->l; } else if (w > e->w) { e = e->r; } else { break; } } f = e; u = f->u; l = f->l; r = f->r; up = u ? (u->l == f) ? &u->l : &u->r : &evh->root; dr = u ? (u->l == f) ? 1 : -1 : 0; if (! f->r) { if (! f->l) { *up = 0; } else { f->l->u = u; *up = f->l; } } else if (! f->l) { f->r->u = u; *up = f->r; } else if (! f->r->l) { f->r->l = f->l; f->l->u = f->r; f->r->u = u; *up = f->r; u = f->r; u->bal = f->bal; dr = -1; } else { s = f->r; while (s->l) s = s->l; s->u->l = s->r; if (s->r) s->r->u = s->u; s->l = f->l; f->l->u = s; s->r = f->r; f->r->u = s; s->bal = f->bal; f = s->u; s->u = u; *up = s; u = f; dr = 1; } if (u) { u->bal += dr; while <"delrebal"> (1) { switch (u->bal) { case 0: if (u->u) { u->u->bal += (u == u->u->l) ? 1 : -1; u = u->u; continue; } break <"delrebal">; break; case -1: case 1: break <"delrebal">; break; case -2: case 2: { EVHENT *v; v = u->u; if (v) { int ob; ob = v->bal; v->bal += (u == v->l) ? 1 : -1; if (evhandle_rebalance((u==v->l)?&v->l:&v->r,v)) { v->bal = ob; break <"delrebal">; } u = v; continue; } evhandle_rebalance(&evh->root,0); break <"delrebal">; } break; DEFAULT_ABORT(); } } } free(e); } static void rd_timer(void *fev) { frontend *fe; struct timersock_event tse[64]; int n; TIME now; TIME d; fe = fev; n = read(fe->timerfd,&tse[0],sizeof(tse)); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: timer socket read: %s\n",__progname,strerror(errno)); exit(1); } if (! fe->running) return; now = get_now(); if (now < fe->lasttime) { // clockwarp? fe->lasttime = now; return; } d = now - fe->lasttime; fe->lasttime = now; midend_timer(fe->me,d*1e-6); } #if 0 static int mpop_block(void *mpv) { MPOP *mp; frontend *fe; Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; int rv; int ix; MPOP_ITEM *i; mp = mpv; fe = mp->fe; rv = AIO_BLOCK_NIL; if (mp->wantquery) { mp->wantquery = 0; if (XQueryPointer(disp,fe->mpop_win,&ret_root,&ret_child,&ret_rootx,&ret_rooty,&ret_winx,&ret_winy,&ret_mask) != True) { mpop_cancel(mp); // return(), not rv = ..., so the rest doesn't run return(AIO_BLOCK_LOOP); } if (mpop_curitem_xy(mp,ret_winx,ret_winy)) return(AIO_BLOCK_LOOP); rv = AIO_BLOCK_LOOP; } if (mp->curitem != mp->drawn_curitem) { if ((mp->drawn_curitem >= 0) && (mp->drawn_curitem >= mp->nitems)) abort(); if ((mp->curitem >= 0) && (mp->curitem >= mp->nitems)) abort(); if (mp->drawn_curitem >= 0) mp->items[mp->drawn_curitem].flags |= MPIF_DIRTY; if (mp->curitem >= 0) mp->items[mp->curitem].flags |= MPIF_DIRTY; mp->drawn_curitem = mp->curitem; mp->items_dirty = 1; rv = AIO_BLOCK_LOOP; } if (mp->items_dirty) { mp->items_dirty = 0; for (ix=mp->nitems-1;ix>=0;ix--) { i = &mp->items[ix]; if (i->flags & MPIF_DIRTY) { i->flags ^= ~MPIF_DIRTY; if (ix == mp->curitem) { XSetForeground(disp,fe->drawgc,fe->bgcolour.pixel); XSetBackground(disp,fe->drawgc,fe->fgcolour.pixel); } else { XSetForeground(disp,fe->drawgc,fe->fgcolour.pixel); XSetBackground(disp,fe->drawgc,fe->bgcolour.pixel); } XDrawImageString(disp,fe->mpop_win,fe->drawgc,i->textx,i->texty,i->text,i->textlen); } } XSetBackground(disp,fe->drawgc,fe->bgcolour.pixel); rv = AIO_BLOCK_LOOP; } return(rv); } #endif static void mpop_init(MPOP *mp) { mp->fe = 0; mp->nitems = 0; mp->items = 0; mp->itemsa = 0; mp->up = 0; mp->win = None; mp->id = AIO_NOID; } static void mpop_reset(MPOP *mp, frontend *fe, void (*choose)(int, Time)) { int i; for (i=mp->nitems-1;i>=0;i--) free(mp->items[i].text); mp->nitems = 0; mp->fe = fe; mp->choose = choose; } static void mpop_append_entry(MPOP *mp, const char *text) { MPOP_ITEM *i; if (mp->nitems >= mp->itemsa) { mp->itemsa = mp->nitems + 8; mp->items = realloc(mp->items,mp->itemsa*sizeof(*mp->items)); } i = &mp->items[mp->nitems]; i->menu = mp; i->inx = mp->nitems; i->textlen = strlen(text); i->text = malloc(i->textlen); bcopy(text,i->text,i->textlen); mp->nitems ++; } static void mpop_append_presets(MPOP *mp, struct preset_menu *pm) { int i; for (i=0;in_entries;i++) { mpop_append_entry(mp,pm->entries[i].title); } } static void leafop_mpop_item_init(void *mpiv, LAYOUT *l) { ((MPOP_ITEM *)mpiv)->l = l; } static XY leafop_mpop_item_minsize(void *mpiv) { MPOP_ITEM *mpi; XCharStruct cs; mpi = mpiv; XTextExtents(fe->font,mpi->text,mpi->textlen,XTE_JUNK,&cs); mpi->textw = cs.width; return((XY){.x=mpi->textw,.y=fe->baselineskip}); } static void leafop_mpop_item_pre_create(void *mpiv, LAYOUT *l, unsigned long int *maskp, XSetWindowAttributes *attrp) { attrp->event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask; *maskp |= CWEventMask; l->loc.x = 0; l->size.x = ((MPOP_ITEM *)mpiv)->menu->l->size.x; } static void mpop_redraw_item(MPOP_ITEM *i) { frontend *fe; fe = i->menu->fe; if (i->inx == i->menu->curitem) { XSetForeground(disp,fe->copygc,fe->bgcolour.pixel); XSetBackground(disp,fe->copygc,fe->fgcolour.pixel); } else { XSetForeground(disp,fe->copygc,fe->fgcolour.pixel); XSetBackground(disp,fe->copygc,fe->bgcolour.pixel); } XCopyPlane(disp,i->pm,i->win,fe->copygc,0,0,i->l->size.x,i->l->size.y,0,0,1); } static void expose_mpop_item(void *mpiv, XEvent *e) { MPOP_ITEM *mpi; if (e->xexpose.count != 0) return; mpi = mpiv; if (! mpi->menu->up) return; mpop_redraw_item(mpi); } static void enternotify_mpop_item(void *mpiv, XEvent *e __attribute__((__unused__))) { MPOP_ITEM *mpi; int oldcur; mpi = mpiv; if (! mpi->menu->up) return; oldcur = mpi->menu->curitem; if (oldcur != mpi->inx) { mpi->menu->curitem = mpi->inx; if (oldcur >= 0) mpop_redraw_item(&mpi->menu->items[oldcur]); mpop_redraw_item(mpi); } } static void leavenotify_mpop_item(void *mpiv, XEvent *e __attribute__((__unused__))) { MPOP_ITEM *mpi; mpi = mpiv; if (! mpi->menu->up) return; if (mpi->menu->curitem == mpi->inx) { mpi->menu->curitem = -1; mpop_redraw_item(mpi); } } static void buttonpress_mpop_item(void *mpiv, XEvent *e) { MPOP_ITEM *mpi; int i; void (*choose)(int, Time); mpi = mpiv; if (! mpi->menu->up) return; i = mpi->inx; choose = mpi->menu->choose; mpop_pop_down(mpi->menu); (*choose)(i,e->xbutton.time); } static void leafop_mpop_item_post_create(void *mpiv, Window w) { MPOP_ITEM *mpi; frontend *fe; mpi = mpiv; fe = mpi->menu->fe; evh_add(w,&evh_expose,&expose_mpop_item,mpi); evh_add(w,&evh_enternotify,&enternotify_mpop_item,mpi); evh_add(w,&evh_leavenotify,&leavenotify_mpop_item,mpi); evh_add(w,&evh_buttonpress,&buttonpress_mpop_item,mpi); mpi->win = w; mpi->pm = XCreatePixmap(disp,rootwin,mpi->l->size.x,mpi->l->size.y,1); XSetForeground(disp,fe->bitgc,0); XFillRectangle(disp,mpi->pm,fe->bitgc,0,0,32767,32767); XSetBackground(disp,fe->bitgc,0); XSetForeground(disp,fe->bitgc,1); XDrawString(disp,mpi->pm,fe->bitgc,(mpi->l->size.x-mpi->textw)/2,((mpi->l->size.y-fe->baselineskip)/2)+fe->font->ascent,mpi->text,mpi->textlen); } static int mpop_at_xy(MPOP *mp, int x, int y) { #define M (menumargin+borderwidth) if ( (x < -M) || (y < -M) || (x >= mp->l->size.x+M) || (y >= mp->l->size.y+M) ) { mpop_cancel(mp); return(1); } #undef M return(0); } static void leafop_mpop_item_destroy(void *mpiv) { MPOP_ITEM *mpi; mpi = mpiv; evh_remove(mpi->win,&evh_expose); evh_remove(mpi->win,&evh_enternotify); evh_remove(mpi->win,&evh_leavenotify); evh_remove(mpi->win,&evh_buttonpress); } #define leafop_mpop_item_pre_update leafop_abort_pre_update #define leafop_mpop_item_post_update leafop_abort_post_update static const LEAFOPS ops_mpop_item = LEAFOPS_INIT(mpop_item); static int mpop_block(void *mpv) { MPOP *mp; int rv; Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; mp = mpv; rv = AIO_BLOCK_NIL; if (mp->wantquery) { /* * When we return() here, we do so, rather than just assigning to * rv, so that the rest of the code doesn't run. (At this * writing, there is no "rest of the code"; this is * future-proofing.) */ mp->wantquery = 0; if (XQueryPointer(disp,mp->win,&ret_root,&ret_child,&ret_rootx,&ret_rooty,&ret_winx,&ret_winy,&ret_mask) != True) { mpop_cancel(mp); return(AIO_BLOCK_LOOP); } if (mpop_at_xy(mp,ret_winx,ret_winy)) return(AIO_BLOCK_LOOP); rv = AIO_BLOCK_LOOP; } return(rv); } static void motionnotify_mpop(void *mpv, XEvent *e) { MPOP *mp; mp = mpv; if (! mp->up) return; if (e->xmotion.is_hint) mp->wantquery = 1; mpop_at_xy(mp,e->xmotion.x,e->xmotion.y); } static void mpop_pop_up(MPOP *mp, Time when) { frontend *fe; int x; int y; int w; int h; Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; LAYOUT *l; fe = mp->fe; if (mp->up) abort(); if (mp->nitems < 1) abort(); if (XGrabPointer(disp,rootwin,True,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,None,None,when) != GrabSuccess) { return; } if (XQueryPointer(disp,rootwin,&ret_root,&ret_child,&ret_rootx,&ret_rooty,&ret_winx,&ret_winy,&ret_mask) != True) { XUngrabPointer(disp,CurrentTime); return; } l = layout_setup_box(LT_VBOX,LAYOUT_END); for (x=0;xnitems;x++) { layout_box_append(l, layout_setup_box(LT_HBOX, layout_setup_glue(0,1), layout_setup_leaf(&ops_mpop_item,mp->items+x), layout_setup_glue(0,1), LAYOUT_END), LAYOUT_END); } mp->l = layout_setup_box(LT_HBOX, layout_setup_glue(margin,1), l, layout_setup_glue(margin,1), LAYOUT_END); layout_arrange(mp->l); w = mp->l->size.x + (2 * borderwidth); h = mp->l->size.y + (2 * borderwidth); x = ret_rootx - (w / 2); y = ret_rooty - (h / 2); if (x+w > scrwidth) x = scrwidth - w; if (y+h > scrheight) y = scrheight - h; if (x < 0) x = 0; if (y < 0) y = 0; w -= 2 * borderwidth; h -= 2 * borderwidth; if (mp->win == None) { unsigned long attrmask; XSetWindowAttributes attr; attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = fe->bdcolour.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = 0; attrmask |= CWEventMask; attr.override_redirect = True; attrmask |= CWOverrideRedirect; attr.colormap = fe->wincmap; attrmask |= CWColormap; attr.cursor = fe->arrowcurs; attrmask |= CWCursor; mp->win = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,CopyFromParent,attrmask,&attr); evh_add(mp->win,&evh_motionnotify,&motionnotify_mpop,mp); } else { XMoveResizeWindow(disp,mp->win,x,y,w,h); } mp->curitem = -1; layout_create(mp->l,mp->win); XMapSubwindows(disp,mp->win); XMapRaised(disp,mp->win); if (XGrabPointer(disp,mp->win,True,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,None,None,when) != GrabSuccess) { XUngrabPointer(disp,CurrentTime); mpop_pop_down(mp); return; } XAllowEvents(disp,AsyncPointer,when); mp->up = 1; mp->wantquery = 1; if (mp->id != AIO_NOID) abort(); mp->id = aio_add_block(&mpop_block,mp); } static void new_game(frontend *fe) { int w; int h; XWindowChanges chg; midend_new_game(fe->me); w = scrwidth; h = scrheight; midend_size(fe->me,&w,&h,false); if ((w != fe->draww) || (h != fe->drawh)) { fe->draww = w; fe->drawh = h; layout_arrange(fe->layout_game); w = fe->layout_game->size.x; h = fe->layout_game->size.y; chg.x = (scrwidth - w - (2 * borderwidth)) / 2; chg.y = (scrheight - h - (2 * borderwidth)) / 2; chg.width = w; chg.height = h; fe->layout_game->loc.x = chg.x; fe->layout_game->loc.y = chg.y; XReconfigureWMWindow(disp,fe->topwin,XScreenNumberOfScreen(scr),CWX|CWY|CWWidth|CWHeight,&chg); layout_update(fe->layout_game); } midend_redraw(fe->me); } static void menu_click_new(frontend *fe, Time when __attribute__((__unused__))) { new_game(fe); } static void popup_custom_menu(frontend *fe, Time when) { (void)fe; (void)when; } static void choose_config(int n, Time when) { struct preset_menu_entry *e; if (n < 0) return; if (n >= fe->cur_presets->n_entries) { popup_custom_menu(fe,when); return; } e = &fe->cur_presets->entries[n]; if (e->submenu) { fe->cur_presets = e->submenu; mpop_reset(&fe->mpop,fe,&choose_config); mpop_append_presets(&fe->mpop,fe->cur_presets); mpop_pop_up(&fe->mpop,when); } else if (e->params) { midend_set_params(fe->me,e->params); new_game(fe); } } static void menu_click_config(frontend *fe, Time when) { fe->cur_presets = fe->presets; mpop_reset(&fe->mpop,fe,&choose_config); mpop_append_presets(&fe->mpop,fe->presets); if (fe->flags & FEF_CUSTOM) mpop_append_entry(&fe->mpop,"Custom..."); mpop_pop_up(&fe->mpop,when); } static void menu_click_solve(frontend *fe __attribute__((__unused__)), Time when __attribute__((__unused__))) { process_key(0,0,UI_SOLVE); } static void menu_click_redo(frontend *fe, Time when __attribute__((__unused__))) { if (midend_can_redo(fe->me)) { process_key(0,0,UI_REDO); } else { XBell(disp,0); } } static void menu_click_undo(frontend *fe, Time when __attribute__((__unused__))) { if (midend_can_undo(fe->me)) { process_key(0,0,UI_UNDO); } else { XBell(disp,0); } } static void menu_click_quit(frontend *fe __attribute__((__unused__)), Time when __attribute__((__unused__))) { exit(0); } static void leafop_menubar_item_init(void *mbiv, LAYOUT *l) { ((MENUBAR_ITEM *)mbiv)->l = l; } static XY leafop_menubar_item_minsize(void *mbiv) { return((XY){.x=margin+((MENUBAR_ITEM *)mbiv)->textw+margin,.y=margin+fe->baselineskip+margin}); } static void menubar_item_extend_size(MENUBAR_ITEM *mbi, LAYOUT *l) { if (mbi->extend_l >= 0) { l->size.x += l->loc.x - mbi->extend_l; l->loc.x = mbi->extend_l; } if (mbi->extend_r >= 0) { l->size.x = mbi->extend_r - l->loc.x; } if (l->size.x < 1) abort(); } static void leafop_menubar_item_pre_create(void *mbiv, LAYOUT *l, unsigned long int *maskp, XSetWindowAttributes *attrp) { MENUBAR_ITEM *mbi; mbi = mbiv; attrp->event_mask = ExposureMask | ButtonPressMask; *maskp |= CWEventMask; menubar_item_extend_size(mbi,l); } static void expose_menubar_item(void *mbiv, XEvent *e) { if (e->xexpose.count != 0) return; draw_menubar_item(fe,mbiv); } static void buttonpress_menubar_item(void *mbiv, XEvent *e) { (*((MENUBAR_ITEM *)mbiv)->click)(fe,e->xbutton.time); } static void leafop_menubar_item_post_create(void *mbiv, Window w) { MENUBAR_ITEM *mbi; mbi = mbiv; mbi->win = w; mbi->pmsize = (XY){.x=0,.y=0}; mbi->actpm = None; mbi->inactpm = None; evh_add(w,&evh_expose,&expose_menubar_item,mbiv); evh_add(w,&evh_buttonpress,&buttonpress_menubar_item,mbiv); } #define leafop_menubar_item_destroy leafop_abort_destroy static void leafop_menubar_item_pre_update(void *mbiv, LAYOUT *l) { menubar_item_extend_size(mbiv,l); } #define leafop_menubar_item_post_update leafop_null_post_update static const LEAFOPS ops_menubar_item = LEAFOPS_INIT(menubar_item); static void leafop_border_pre_create( void *arg __attribute__((__unused__)), LAYOUT *l __attribute__((__unused__)), unsigned long int *maskp, XSetWindowAttributes *attrp ) { attrp->background_pixel = fe->bdcolour.pixel; *maskp |= CWBackPixel; } #define leafop_hborder_init leafop_null_init static XY leafop_hborder_minsize(void *mbiv __attribute__((__unused__))) { return((XY){.x=0,.y=borderwidth}); } #define leafop_hborder_pre_create leafop_border_pre_create #define leafop_hborder_post_create leafop_null_post_create #define leafop_hborder_destroy leafop_null_destroy #define leafop_hborder_pre_update leafop_null_pre_update #define leafop_hborder_post_update leafop_null_post_update static const LEAFOPS ops_hborder = LEAFOPS_INIT(hborder); #define leafop_vborder_init leafop_null_init static XY leafop_vborder_minsize(void *mbiv __attribute__((__unused__))) { return((XY){.x=borderwidth,.y=0}); } #define leafop_vborder_pre_create leafop_border_pre_create #define leafop_vborder_post_create leafop_null_post_create #define leafop_vborder_destroy leafop_null_destroy #define leafop_vborder_pre_update leafop_null_pre_update #define leafop_vborder_post_update leafop_null_post_update static const LEAFOPS ops_vborder = LEAFOPS_INIT(vborder); static void leafop_gamewin_init(void *fev, LAYOUT *l) { if (fev != (void *)fe) abort(); fe->gl = l; } static XY leafop_gamewin_minsize(void *fev) { frontend *fe; fe = fev; return((XY){.x=fe->draww,.y=fe->drawh}); } static void leafop_gamewin_pre_create(void *fev, LAYOUT *l __attribute__((__unused__)), unsigned long int *maskp, XSetWindowAttributes *attrp) { if (fev != (void *)fe) abort(); attrp->event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask; *maskp |= CWEventMask; } static void expose_gamewin(void *fev, XEvent *e) { if (fev != fe) abort(); copyarea(disp,fe->drawpix,fe->drawwin,fe->copygc,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.x,e->xexpose.y); } static void buttonpress_gamewin(void *fev, XEvent *e) { int b; if (fev != fe) abort(); switch (e->xbutton.button) { case 1: b = LEFT_BUTTON; break; case 2: b = MIDDLE_BUTTON; break; case 3: b = RIGHT_BUTTON; break; default: return; break; } process_key(e->xbutton.x,e->xbutton.y,b); } static void buttonrelease_gamewin(void *fev, XEvent *e) { int b; if (fev != fe) abort(); switch (e->xbutton.button) { case 1: b = LEFT_RELEASE; break; case 2: b = MIDDLE_RELEASE; break; case 3: b = RIGHT_RELEASE; break; default: return; break; } process_key(e->xbutton.x,e->xbutton.y,b); } static void motionnotify_gamewin(void *fev, XEvent *e) { int b; if (fev != (void *)fe) abort(); if (e->xmotion.state & Button1Mask) { b = LEFT_DRAG; } else if (e->xmotion.state & Button2Mask) { b = MIDDLE_DRAG; } else if (e->xmotion.state & Button3Mask) { b = RIGHT_DRAG; } else { return; } process_key(e->xmotion.x,e->xmotion.y,b); } static void leafop_gamewin_post_create(void *fev, Window w) { if (fev != (void *)fe) abort(); fe->drawwin = w; fe->drawpix = XCreatePixmap(disp,fe->drawwin,fe->draww,fe->drawh,depth); fe->drawpsize = (XY){.x=fe->draww,.y=fe->drawh}; evh_add(w,&evh_expose,&expose_gamewin,fe); evh_add(w,&evh_buttonpress,&buttonpress_gamewin,fe); evh_add(w,&evh_buttonrelease,&buttonrelease_gamewin,fe); evh_add(w,&evh_motionnotify,&motionnotify_gamewin,fe); } #define leafop_gamewin_destroy leafop_abort_destroy #define leafop_gamewin_pre_update leafop_null_pre_update static void leafop_gamewin_post_update(void *fev) { if (fev != (void *)fe) abort(); if ((fe->gl->size.x != fe->drawpsize.x) || (fe->gl->size.y != fe->drawpsize.y)) { XFreePixmap(disp,fe->drawpix); fe->drawpsize = fe->gl->size; fe->drawpix = XCreatePixmap(disp,fe->drawwin,fe->gl->size.x,fe->gl->size.y,depth); } } static const LEAFOPS ops_gamewin = LEAFOPS_INIT(gamewin); #define leafop_statusbar_init leafop_null_init static XY leafop_statusbar_minsize(void *fev) { return((XY){.x=0,.y=((frontend *)fev)->baselineskip}); } static void leafop_statusbar_pre_create(void *fev, LAYOUT *l, unsigned long int *maskp, XSetWindowAttributes *attrp) { if (fev != (void *)fe) abort(); if (! (fe->flags & FEF_STATBAR)) abort(); attrp->event_mask = ExposureMask; *maskp |= CWEventMask; l->size.x = fe->statusbar_r - l->loc.x; if (l->size.x < 1) abort(); } static void expose_statwin(void *fev, XEvent *e) { if (e->xexpose.count != 0) return; if (fev != (void *)fe) abort(); draw_statbar_text(fev); } static void leafop_statusbar_post_create(void *fev, Window w) { if (fev != (void *)fe) abort(); if (! (fe->flags & FEF_STATBAR)) abort(); fe->statwin = w; evh_add(w,&evh_expose,&expose_statwin,fe); } #define leafop_statusbar_destroy leafop_abort_destroy static void leafop_statusbar_pre_update(void *fev, LAYOUT *l) { if (fev != (void *)fe) abort(); if (! (fe->flags & FEF_STATBAR)) abort(); l->size.x = fe->statusbar_r - l->loc.x; if (l->size.x < 1) abort(); } #define leafop_statusbar_post_update leafop_null_post_update static const LEAFOPS ops_statusbar = LEAFOPS_INIT(statusbar); static void setup_menubar_button(frontend *fe, MENUBAR_ITEM *mbi, bool (*acttest)(midend *), const char *text, void (*click)(frontend *, Time)) { XCharStruct cs; int tl; tl = strlen(text); mbi->text = text; mbi->textlen = tl; mbi->extend_l = -1; mbi->extend_r = -1; XTextExtents(fe->font,text,tl,XTE_JUNK,&cs); mbi->textw = cs.width; mbi->click = click; mbi->acttest = acttest; mbi->curact = -1; // l already set in leafop_menubar_item_init // See leafop_menubar_item_post_create for the rest mbi->link = fe->menubar_items; fe->menubar_items = mbi; } static void setup_game(void) { int xsize; int ysize; int x; int y; int w; int h; unsigned long int attrmask; XSetWindowAttributes attr; XCharStruct cs; LAYOUT *mbl; Pixmap pm; fe = malloc(sizeof(frontend)); fe->flags = 0; fe->copygc = XCreateGC(disp,rootwin,0,0); fe->drawgc = XCreateGC(disp,rootwin,0,0); pm = XCreatePixmap(disp,rootwin,1,1,1); fe->bitgc = XCreateGC(disp,pm,0,0); XFreePixmap(disp,pm); if (fontstr) { fe->font = XLoadQueryFont(disp,fontstr); if (! fe->font) { fprintf(stderr,"%s: can't load font: %s\n",__progname,fontstr); exit(1); } } else { fe->font = XQueryFont(disp,XGContextFromGC(fe->drawgc)); } fe->baselineskip = fe->font->ascent + fe->font->descent; XTextExtents(fe->font,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",26,XTE_JUNK,&cs); fe->vcentre = cs.ascent / 2; fe->wincmap = XDefaultColormapOfScreen(scr); fe->defcmap = 1; setup_colour_text(fe,foreground,&fe->fgcolour,"foreground"); setup_colour_text(fe,background,&fe->bgcolour,"background"); setup_colour_text(fe,bordercstr,&fe->bdcolour,"border"); fe->timerfd = socket(AF_TIMER,SOCK_STREAM,0); if (fe->timerfd < 0) { fprintf(stderr,"%s: timer socket: %s\n",__progname,strerror(errno)); exit(1); } set_nbio(fe->timerfd); fe->running = 0; aio_add_poll(fe->timerfd,&aio_rwtest_always,&aio_rwtest_never,&rd_timer,0,fe); fe->me = midend_new(fe,&thegame,&x_drawing,fe); fe->presets = midend_get_presets(fe->me,0); if (fe->presets->n_entries > 0) fe->flags |= FEF_CONFIG; if (thegame.can_configure) fe->flags |= FEF_CONFIG | FEF_CUSTOM; // if argument provided // do things // else just midend_new_game(fe->me); // if thegame.can_format_as_text_ever, "copy" command // if thegame.flags & REQUIRE_NUMPAD, create soft numpad { int i; int r; int g; int b; float *cols; float *cp; cols = midend_colours(fe->me,&fe->ncolours); drawlog("Colour count %d:",fe->ncolours); fe->colours = malloc(fe->ncolours*sizeof(*fe->colours)); cp = cols; for (i=0;incolours;i++) { r = *cp++ * 256; if (r < 0) r = 0; else if (r > 255) r = 255; g = *cp++ * 256; if (g < 0) g = 0; else if (g > 255) g = 255; b = *cp++ * 256; if (b < 0) b = 0; else if (b > 255) b = 255; fe->colours[i].red = r * 0x0101; fe->colours[i].green = g * 0x0101; fe->colours[i].blue = b * 0x0101; fe->colours[i].flags = DoRed | DoGreen | DoBlue; setup_colour_rgb(fe,&fe->colours[i],"game #%d (%d,%d,%d)",i,r,g,b); drawlog(" [%d] (%g,%g,%g) -> (%d,%d,%d) -> %lx",i,cp[-3],cp[-2],cp[-1],r,g,b,fe->colours[i].pixel); } } xsize = scrwidth; ysize = scrheight; // must call midend_new_game() before midend_size() midend_size(fe->me,&xsize,&ysize,false); if (midend_wants_statusbar(fe->me)) { fe->flags |= FEF_STATBAR; fe->statbuf = 0; fe->statalloc = 0; fe->statlen = 0; } fe->draww = xsize; fe->drawh = ysize; mbl = layout_setup_box(LT_HBOX, layout_write_loc(&fe->menubar_quit.extend_l), layout_setup_glue(0,1), layout_setup_leaf(&ops_menubar_item,&fe->menubar_quit), layout_setup_glue(0,1), layout_write_loc(&fe->menubar_quit.extend_r), layout_setup_leaf(&ops_vborder,0), layout_write_loc(&fe->menubar_undo.extend_l), layout_setup_glue(0,1), layout_setup_leaf(&ops_menubar_item,&fe->menubar_undo), layout_setup_glue(0,1), layout_write_loc(&fe->menubar_undo.extend_r), layout_setup_leaf(&ops_vborder,0), layout_write_loc(&fe->menubar_redo.extend_l), layout_setup_glue(0,1), layout_setup_leaf(&ops_menubar_item,&fe->menubar_redo), layout_setup_glue(0,1), layout_write_loc(&fe->menubar_redo.extend_r), LAYOUT_END); if (thegame.can_solve) { layout_box_append(mbl, layout_setup_leaf(&ops_vborder,0), layout_write_loc(&fe->menubar_solve.extend_l), layout_setup_glue(0,1), layout_setup_leaf(&ops_menubar_item,&fe->menubar_solve), layout_setup_glue(0,1), layout_write_loc(&fe->menubar_solve.extend_r), LAYOUT_END); } if (fe->flags & FEF_CONFIG) { layout_box_append(mbl, layout_setup_leaf(&ops_vborder,0), layout_write_loc(&fe->menubar_config.extend_l), layout_setup_glue(0,1), layout_setup_leaf(&ops_menubar_item,&fe->menubar_config), layout_setup_glue(0,1), layout_write_loc(&fe->menubar_config.extend_r), LAYOUT_END); } layout_box_append(mbl, layout_setup_leaf(&ops_vborder,0), layout_write_loc(&fe->menubar_new.extend_l), layout_setup_glue(0,1), layout_setup_leaf(&ops_menubar_item,&fe->menubar_new), layout_setup_glue(0,1), layout_write_loc(&fe->menubar_new.extend_r), LAYOUT_END), fe->layout_game = layout_setup_box(LT_VBOX, mbl, layout_setup_leaf(&ops_hborder,0), layout_setup_glue(margin,1), layout_setup_box(LT_HBOX, layout_setup_glue(margin,1), layout_setup_leaf(&ops_gamewin,fe), layout_setup_glue(margin,1), LAYOUT_END), layout_setup_glue(margin,1), LAYOUT_END); if (fe->flags & FEF_STATBAR) { layout_box_append(fe->layout_game, layout_setup_leaf(&ops_hborder,0), layout_setup_glue(margin,0), layout_setup_box(LT_HBOX, layout_setup_glue(margin,0), layout_setup_leaf(&ops_statusbar,fe), layout_setup_glue(0,1), layout_write_loc(&fe->statusbar_r), layout_setup_glue(margin,0), LAYOUT_END), layout_setup_glue(margin,0), LAYOUT_END); } fe->menubar_items = 0; setup_menubar_button(fe,&fe->menubar_new,0,"New",&menu_click_new); if (fe->flags & FEF_CONFIG) setup_menubar_button(fe,&fe->menubar_config,0,"Config",&menu_click_config); if (thegame.can_solve) { fe->flags |= FEF_SOLVE; setup_menubar_button(fe,&fe->menubar_solve,0,"Solve",&menu_click_solve); } setup_menubar_button(fe,&fe->menubar_redo,&midend_can_redo,"Redo",&menu_click_redo); setup_menubar_button(fe,&fe->menubar_undo,&midend_can_undo,"Undo",&menu_click_undo); setup_menubar_button(fe,&fe->menubar_quit,0,"Quit",&menu_click_quit); layout_arrange(fe->layout_game); w = borderwidth + fe->layout_game->size.x + borderwidth; h = borderwidth + fe->layout_game->size.y + borderwidth; x = (scrwidth - w) / 2; y = (scrheight - h) / 2; attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = fe->bdcolour.pixel; attrmask |= CWBorderPixel; attr.event_mask = StructureNotifyMask | KeyPressMask; attrmask |= CWEventMask; attr.do_not_propagate_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask; attrmask |= CWDontPropagate; attr.colormap = fe->wincmap; attrmask |= CWColormap; w -= 2 * borderwidth; h -= 2 * borderwidth; fe->topwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,CopyFromParent,attrmask,&attr); fe->wn_prop.value = (unsigned char *) deconst(av0bn); fe->wn_prop.encoding = XA_STRING; fe->wn_prop.format = 8; fe->wn_prop.nitems = strlen(fe->wn_prop.value); fe->in_prop.value = (unsigned char *) deconst(av0bn); fe->in_prop.encoding = XA_STRING; fe->in_prop.format = 8; fe->in_prop.nitems = strlen(fe->in_prop.value); fe->normal_hints = XAllocSizeHints(); fe->normal_hints->flags = PMinSize | PResizeInc; fe->normal_hints->x = x; fe->normal_hints->y = y; fe->normal_hints->flags |= PPosition; fe->normal_hints->width = w; fe->normal_hints->height = h; fe->normal_hints->flags |= PSize; fe->normal_hints->min_width = w; fe->normal_hints->min_height = h; fe->normal_hints->width_inc = 1; fe->normal_hints->height_inc = 1; fe->wm_hints = XAllocWMHints(); fe->wm_hints->flags = InputHint; fe->wm_hints->input = True; fe->class_hints = XAllocClassHint(); fe->class_hints->res_name = deconst(av0bn); fe->class_hints->res_class = deconst("Puzzle"); XSetWMProperties(disp,fe->topwin,&fe->wn_prop,&fe->in_prop,argv,argc,fe->normal_hints,fe->wm_hints,fe->class_hints); layout_create(fe->layout_game,fe->topwin); XMapSubwindows(disp,fe->topwin); XMapRaised(disp,fe->topwin); mpop_init(&fe->mpop); fe->arrowcurs = XCreateFontCursor(disp,XC_left_ptr); XRecolorCursor(disp,fe->arrowcurs,&fe->fgcolour,&fe->bgcolour); resync_buttons(fe); midend_redraw(fe->me); } /* * We currently support only one font. If we want to support the ftype * and fsize arguments properly, we'll need to improve all * font-handling code. */ static void x_draw_text( void *fev, int x, int y, int ftype __attribute__((__unused__)), int fsize __attribute__((__unused__)), int align, int colour, const char *text ) { int len; XCharStruct cs; if (fev != fe) abort(); len = strlen(text); drawlog("%s, x=%d y=%d ftype=%d fsize=%d align=%d colour=%d text=%s",__func__,x,y,ftype,fsize,align,colour,text); XTextExtents(fe->font,text,len,XTE_JUNK,&cs); switch (align & (ALIGN_VNORMAL|ALIGN_VCENTRE)) { case ALIGN_VNORMAL: break; case ALIGN_VCENTRE: // XXX should we query cap-height somehow? y += fe->vcentre; break; DEFAULT_ABORT(); } switch (align & (ALIGN_HLEFT|ALIGN_HCENTRE|ALIGN_HRIGHT)) { case ALIGN_HLEFT: break; case ALIGN_HCENTRE: x -= cs.width / 2; break; case ALIGN_HRIGHT: x -= cs.width; break; DEFAULT_ABORT(); } XSetForeground(disp,fe->drawgc,fe->colours[colour].pixel); XDrawString(disp,fe->drawpix,fe->drawgc,x,y,text,len); } static void x_draw_rect(void *fev, int x, int y, int w, int h, int colour) { if (fev != fe) abort(); drawlog("%s, x=%d y=%d w=%d h=%d colour=%d",__func__,x,y,w,h,colour); if ((w < 1) || (h < 1)) return; XSetForeground(disp,fe->drawgc,fe->colours[colour].pixel); XFillRectangle(disp,fe->drawpix,fe->drawgc,x,y,w,h); } static void x_draw_line(void *fev, int x1, int y1, int x2, int y2, int colour) { if (fev != fe) abort(); drawlog("%s, x1=%d y1=%d x2=%d y2=%d colour=%d",__func__,x1,y1,x2,y2,colour); XSetForeground(disp,fe->drawgc,fe->colours[colour].pixel); XDrawLine(disp,fe->drawpix,fe->drawgc,x1,y1,x2,y2); } static void x_draw_polygon(void *fev, int *coords, int n, int fillc, int borderc) { static XPoint *pts = 0; static int apts = 0; int i; if (fev != fe) abort(); if (n+1 > apts) { free(pts); apts = n + 1; pts = malloc(apts*sizeof(XPoint)); } for (i=n-1;i>=0;i--) { pts[i].y = coords[i+i+1]; pts[i].x = coords[i+i]; } pts[n] = pts[0]; if (dlog) { drawlog("%s, n=%d fillc=%d borderc=%d",__func__,n,fillc,borderc); for (i=0;i= 0) { XSetForeground(disp,fe->drawgc,fe->colours[fillc].pixel); XFillPolygon(disp,fe->drawpix,fe->drawgc,pts,n,Complex,CoordModeOrigin); } XSetForeground(disp,fe->drawgc,fe->colours[borderc].pixel); XDrawLines(disp,fe->drawpix,fe->drawgc,pts,n+1,CoordModeOrigin); } static void x_draw_circle(void *fev, int cx, int cy, int r, int fillc, int borderc) { if (fev != fe) abort(); drawlog("%s, cx=%d cy=%d r=%d fillc=%d borderc=%d",__func__,cx,cy,r,fillc,borderc); if (r < 1) return; if (r == 1) { XSetForeground(disp,fe->drawgc,fe->colours[borderc].pixel); XDrawPoint(disp,fe->drawpix,fe->drawgc,cx,cy); } else { cx -= r - 1; cy -= r - 1; r = r + r - 2; if (fillc >= 0) { XSetForeground(disp,fe->drawgc,fe->colours[fillc].pixel); XFillArc(disp,fe->drawpix,fe->drawgc,cx,cy,r,r,0,360*64); } XSetForeground(disp,fe->drawgc,fe->colours[borderc].pixel); XDrawArc(disp,fe->drawpix,fe->drawgc,cx,cy,r,r,0,360*64); } } static void x_draw_update(void *fev, int x, int y, int w, int h) { if (fev != fe) abort(); drawlog("%s, x=%d y=%d w=%d h=%d",__func__,x,y,w,h); copyarea(disp,fe->drawpix,fe->drawwin,fe->copygc,x,y,w,h,x,y); } static void x_clip(void *fev, int x, int y, int w, int h) { XRectangle r; if (fev != fe) abort(); drawlog("%s, x=%d y=%d w=%d h=%d",__func__,x,y,w,h); r.x = x; r.y = y; r.width = w; r.height = h; XSetClipRectangles(disp,fe->drawgc,0,0,&r,1,Unsorted); } static void x_unclip(void *fev) { if (fev != fe) abort(); drawlog("%s",__func__); XSetClipMask(disp,fe->drawgc,None); } static void x_start_draw(void *fev) { if (fev != fe) abort(); } static void x_end_draw(void *fev) { if (fev != fe) abort(); } static void x_status_bar(void *fev, const char *text) { int l; if (fev != fe) abort(); drawlog("%s, text=%s",__func__,text); if (! (fe->flags & FEF_STATBAR)) abort(); l = strlen(text); if (l > fe->statlen) { free(fe->statbuf); fe->statlen = l; fe->statbuf = malloc(l); } bcopy(text,fe->statbuf,l); fe->statlen = l; XClearWindow(disp,fe->statwin); draw_statbar_text(fe); } static blitter *x_blitter_new(void *fev, int w, int h) { blitter *b; if (fev != fe) abort(); b = malloc(sizeof(blitter)); drawlog("%s, w=%d h=%d returning %p",__func__,w,h,(void *)b); b->w = w; b->h = h; b->x = 0; b->y = 0; b->pm = XCreatePixmap(disp,rootwin,w,h,depth); return(b); } static void x_blitter_free(void *fev, blitter *b) { if (fev != fe) abort(); drawlog("%s, b=%p",__func__,(void *)b); XFreePixmap(disp,b->pm); free(b); } static void x_blitter_save(void *fev, blitter *b, int x, int y) { if (fev != fe) abort(); drawlog("%s, b=%p x=%d y=%d",__func__,(void *)b,x,y); b->x = x; b->y = y; copyarea(disp,fe->drawpix,b->pm,fe->copygc,x,y,b->w,b->h,0,0); } static void x_blitter_load(void *fev, blitter *b, int x, int y) { if (fev != fe) abort(); drawlog("%s, b=%p x=%d y=%d",__func__,(void *)b,x,y); if ((x == BLITTER_FROMSAVED) && (y == BLITTER_FROMSAVED)) { x = b->x; y = b->y; } copyarea(disp,b->pm,fe->drawpix,fe->copygc,0,0,b->w,b->h,x,y); } static void x_draw_thick_line(void *fev, float w, float x1, float y1, float x2, float y2, int colour) { XPoint lr[4]; double ll; double s; double c; if (fev != fe) abort(); drawlog("%s, w=%g x1=%g y1=%g x2=%g y2=%g colour=%d",__func__,w,x1,y1,x2,y2,colour); if ((fabs(x2-x1) < 1e-3) && (fabs(y2-y1) < 1e-3)) return; if (w < 1) w = 1; ll = hypot(x2-x1,y2-y1); s = (y2 - y1) * w * .5 / ll; c = (x2 - x1) * w * .5 / ll; lr[0].x = x1 + s; lr[0].y = y1 - c; lr[1].x = x1 - s; lr[1].y = y1 + c; lr[2].x = x2 - s; lr[2].y = y2 + c; lr[3].x = x2 + s; lr[3].y = y2 - c; XSetForeground(disp,fe->drawgc,fe->colours[colour].pixel); XFillPolygon(disp,fe->drawpix,fe->drawgc,&lr[0],4,Convex,CoordModeOrigin); } static const struct drawing_api x_drawing = { &x_draw_text, &x_draw_rect, &x_draw_line, &x_draw_polygon, &x_draw_circle, &x_draw_update, &x_clip, &x_unclip, &x_start_draw, &x_end_draw, &x_status_bar, &x_blitter_new, &x_blitter_free, &x_blitter_save, &x_blitter_load, 0, // begin_doc 0, // begin_page 0, // begin_puzzle 0, // end_puzzle 0, // end_page 0, // end_doc 0, // line_width 0, // line_dotted 0, // text_fallback &x_draw_thick_line }; void get_random_seed(void **seed, int *size) { struct timeval *tv; tv = malloc(sizeof(struct timeval)); gettimeofday(tv,0); *seed = tv; *size = sizeof(*tv); } void fatal(const char *, ...) __attribute__((__format__(__printf__,1,2))); void fatal(const char *fmt, ...) { va_list ap; char *s; va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); fprintf(stderr,"Fatal error: %s\n",s); exit(1); } void activate_timer(frontend *fe) { struct itimerval itv; if (fe->running) return; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 40000; itv.it_interval = itv.it_value; write(fe->timerfd,&itv,sizeof(itv)); fe->lasttime = get_now(); fe->running = 1; } void deactivate_timer(frontend *fe) { struct itimerval itv; if (! fe->running) return; itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0; itv.it_interval = itv.it_value; write(fe->timerfd,&itv,sizeof(itv)); fe->running = 0; } void frontend_default_colour(frontend *fe __attribute__((__unused__)), float *cp) { cp[0] = fe->bgcolour.red / 65535.0; cp[1] = fe->bgcolour.green / 65535.0; cp[2] = fe->bgcolour.blue / 65535.0; } // This is called only when printing; it appears to be undocumented. void document_add_puzzle( document *d __attribute__((__unused__)), const game *g __attribute__((__unused__)), game_params *p __attribute__((__unused__)), game_state *s __attribute__((__unused__)), game_state *s2 __attribute__((__unused__)) ) { } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); disp = XOpenDisplay(displayname); if (disp == 0) { fprintf(stderr,"%s: can't open display\n",__progname); exit(1); } setup_aio(); setup_error(); if (synch) XSynchronize(disp,True); scr = XDefaultScreenOfDisplay(disp); scrwidth = XWidthOfScreen(scr); scrheight = XHeightOfScreen(scr); depth = XDefaultDepthOfScreen(scr); rootwin = XRootWindowOfScreen(scr); setup_db(); maybeset(&geometryspec,get_default_value("geometry","Geometry")); maybeset(&foreground,get_default_value("foreground","Foreground")); maybeset(&background,get_default_value("background","Background")); maybeset(&bordercstr,get_default_value("borderColour","BorderColour")); maybeset(&borderwstr,get_default_value("borderWidth","BorderWidth")); maybeset(&bordermstr,get_default_value("borderMargin","BorderMargin")); maybeset(&menumstr,get_default_value("menuMargin","MenuMargin")); maybeset(&fontstr,get_default_value("font","Font")); setup_numbers(); setup_game(); aio_event_loop(); return(0); }