#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 struct mpop MPOP; typedef struct mpop_item MPOP_ITEM; typedef struct menubar_item MENUBAR_ITEM; struct mpop_item { MPOP *menu; char *text; int textlen; int textw; int textx; int texty; int y; unsigned int flags; #define MPIF_DIRTY 0x00000001 } ; struct mpop { frontend *fe; int nitems; MPOP_ITEM *items; int itemsa; int w; int h; void (*choose)(int, Time); int curitem; int drawn_curitem; int wantquery; int items_dirty; int id; } ; struct menubar_item { MENUBAR_ITEM *link; const char *text; int textlen; int winx; int winw; int textw; int textx; void (*click)(frontend *, Time); Window win; unsigned int actbit; Pixmap actpm; Pixmap inactpm; int barx; Window barafter; } ; struct frontend { midend *me; Window topwin; Window menuwin; Window menubar; MENUBAR_ITEM *menubar_items; Window drawwin; Pixmap drawpix; 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; Window statwin; char *statbuf; int statalloc; int statlen; MENUBAR_ITEM item_quit; MENUBAR_ITEM item_undo; MENUBAR_ITEM item_redo; MENUBAR_ITEM item_solve; MENUBAR_ITEM item_config; MENUBAR_ITEM item_new; int timerfd; int running; TIME lasttime; GC copygc; GC drawgc; 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; Window mpop_top; Window mpop_win; int mpop_up; } ; struct blitter { int w; int h; int x; int y; Pixmap pm; } ; static frontend *fe; static const struct drawing_api x_drawing; // forward static FILE *dlog = 0; 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 h; pm = *cache; if (pm != None) return(pm); h = margin + fe->baselineskip + margin; pm = XCreatePixmap(disp,rootwin,i->winw,h,depth); XSetForeground(disp,fe->drawgc,fe->bgcolour.pixel); XFillRectangle(disp,pm,fe->drawgc,0,0,i->winw,h); if (active) { XSetForeground(disp,fe->drawgc,fe->fgcolour.pixel); XDrawString(disp,pm,fe->drawgc,i->textx,margin+fe->font->ascent,i->text,i->textlen); } *cache = pm; return(pm); } static void draw_menubar_item(frontend *fe, MENUBAR_ITEM *i) { Pixmap pm; if (!i->actbit || (fe->flags & i->actbit)) { pm = menu_cached_pm(&i->actpm,i,1); } else { pm = menu_cached_pm(&i->inactpm,i,0); } XCopyArea(disp,pm,i->win,fe->copygc,0,0,32767,32767,0,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; } static void expose(XExposeEvent *e) { MENUBAR_ITEM *i; if (e->window == fe->drawwin) { XCopyArea(disp,fe->drawpix,fe->drawwin,fe->copygc,e->x,e->y,e->width,e->height,e->x,e->y); } else if ((fe->flags & FEF_STATBAR) && (e->window == fe->statwin)) { if (! e->count) draw_statbar_text(fe); } else if (fe->mpop_up && (e->window == fe->mpop_win)) { mpop_dirty_items(&fe->mpop,e->y,e->y+e->height-1); } else { for (i=fe->menubar_items;i;i=i->link) { if (e->window == i->win) { if (! e->count) draw_menubar_item(fe,i); return; } } } } static void resync_button(frontend *fe, bool (*test)(midend *), unsigned int bit, MENUBAR_ITEM *item) { if ((*test)(fe->me)) { if (! (fe->flags & bit)) { fe->flags |= bit; draw_menubar_item(fe,item); } } else { if (fe->flags & bit) { fe->flags &= ~bit; draw_menubar_item(fe,item); } } } static void resync_buttons(frontend *fe) { resync_button(fe,&midend_can_undo,FEF_CAN_UNDO,&fe->item_undo); resync_button(fe,&midend_can_redo,FEF_CAN_REDO,&fe->item_redo); } 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 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; } static void mpop_cancel(MPOP *mp) { mpop_popdown(mp); (*mp->choose)(-1,CurrentTime); } 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); } static void mpop_choose_curitem(MPOP *mp, Time when) { mpop_popdown(mp); (*mp->choose)(mp->curitem,when); } 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); } static void button_down(XButtonEvent *e) { int b; MENUBAR_ITEM *i; if (e->window == fe->drawwin) { switch (e->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->x,e->y,b); } else if (fe->mpop_up && (e->window == fe->mpop_win)) { mpop_click_event(fe,e); } else { for (i=fe->menubar_items;i;i=i->link) { if (e->window == i->win) { (*i->click)(fe,e->time); return; } } } } static void button_up(XButtonEvent *e) { int b; if (e->window != fe->drawwin) return; switch (e->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->x,e->y,b); } static void motion(XMotionEvent *e) { int b; if (e->window == fe->drawwin) { if (e->state & Button1Mask) { b = LEFT_DRAG; } else if (e->state & Button2Mask) { b = MIDDLE_DRAG; } else if (e->state & Button3Mask) { b = RIGHT_DRAG; } else { return; } process_key(e->x,e->y,b); } else if (fe->mpop_up && (e->window == fe->mpop_win)) { fe->mpop.wantquery = 1; } } static void mouse_in(XCrossingEvent *e) { if (fe->mpop_up && (e->window == fe->mpop_win)) { mpop_curitem_xy(&fe->mpop,e->x,e->y); } } static void mouse_out(XCrossingEvent *e) { if (fe->mpop_up && (e->window == fe->mpop_win)) { if (! e->same_screen) { mpop_cancel(&fe->mpop); } else { mpop_curitem_xy(&fe->mpop,e->x,e->y); } } } static void handle_event(XEvent *e) { switch (e->type) { default: break; case Expose: // XExposeEvent - xexpose expose(&e->xexpose); break; case MappingNotify: // XMappingEvent - xmapping XRefreshKeyboardMapping(&e->xmapping); break; case KeyPress: // XKeyPressedEvent - XKeyEvent - xkey keystroke(&e->xkey); break; case ButtonPress: // XButtonPressedEvent - XButtonEvent - xbutton button_down(&e->xbutton); break; case ButtonRelease: // XButtonReleasedEvent - XButtonEvent - xbutton button_up(&e->xbutton); break; case MotionNotify: // XPointerMovedEvent - XMotionEvent - xmotion motion(&e->xmotion); break; case EnterNotify: // XEnterWindowEvent - XCrossingEvent - xcrossing mouse_in(&e->xcrossing); break; case LeaveNotify: // XLeaveWindowEvent - XCrossingEvent - xcrossing mouse_out(&e->xcrossing); 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 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); } 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); } static void mpop_init(MPOP *mp) { mp->fe = 0; mp->nitems = 0; mp->items = 0; mp->itemsa = 0; 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->textlen = strlen(text); i->text = malloc(i->textlen); bcopy(text,i->text,i->textlen); } 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 mpop_pop_up(MPOP *mp, Time when) { frontend *fe; int x; int y; int w; int h; MPOP_ITEM *i; XCharStruct cs; Window ret_root; Window ret_child; int ret_rootx; int ret_rooty; int ret_winx; int ret_winy; unsigned int ret_mask; fe = mp->fe; if (fe->mpop_up) abort(); if (mp->nitems < 1) abort(); mp->w = 0; y = 0; for (x=0;xnitems;x++) { i = &mp->items[x]; XTextExtents(fe->font,i->text,i->textlen,XTE_JUNK,&cs); i->textw = cs.width; if (i->textw > mp->w) mp->w = i->textw; i->y = y; i->texty = y + fe->font->ascent; y += fe->baselineskip; i->flags = MPIF_DIRTY; } mp->h = y; for (x=0;xnitems;x++) { i = &mp->items[x]; i->textx = (mp->w - i->textw) / 2; } if (XGrabPointer(disp,rootwin,False,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; } w = mp->w + (2 * margin) + (2 * borderwidth); h = mp->h + (2 * margin) + (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 (fe->mpop_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; fe->mpop_top = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,CopyFromParent,attrmask,&attr); attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask; attrmask |= CWEventMask; attr.colormap = fe->wincmap; attrmask |= CWColormap; fe->mpop_win = XCreateWindow(disp,fe->mpop_top,margin,margin,mp->w,mp->h,0,depth,InputOutput,CopyFromParent,attrmask,&attr); XMapWindow(disp,fe->mpop_win); } else { XResizeWindow(disp,fe->mpop_win,mp->w,mp->h); XMoveResizeWindow(disp,fe->mpop_top,x,y,w,h); } XMapRaised(disp,fe->mpop_top); if (XGrabPointer(disp,fe->mpop_win,False,ButtonPressMask|ButtonReleaseMask|PointerMotionMask|PointerMotionHintMask|EnterWindowMask|LeaveWindowMask,GrabModeSync,GrabModeAsync,None,None,when) != GrabSuccess) { XUngrabPointer(disp,CurrentTime); XUnmapWindow(disp,fe->mpop_win); return; } XAllowEvents(disp,AsyncPointer,when); mp->drawn_curitem = -1; mp->items_dirty = 1; mp->wantquery = 1; fe->mpop_up = 1; mpop_curitem_xy(mp,ret_rootx-x,ret_rooty-y); if (mp->id != AIO_NOID) abort(); mp->id = aio_add_block(&mpop_block,mp); } static void walk_choices(const char *s, void (*each)(int, const char *, int)) { char sep; int x; char *n; sep = *s++; if (! sep) abort(); // XXX can this happen? x = 0; while (1) { n = index(s,sep); if (n) { (*each)(x,s,n-s); } else { (*each)(x,s,strlen(s)); } if (! n) break; s = n + 1; x ++; } } static void menu_add_button(frontend *fe, unsigned int havebit, unsigned int actbit, MENUBAR_ITEM *item, const char *text, void (*click)(frontend *, Time)) { XCharStruct cs; int tl; fe->flags |= havebit; item->actbit = actbit; tl = strlen(text); XTextExtents(fe->font,text,tl,XTE_JUNK,&cs); item->text = text; item->textlen = tl; item->textw = cs.width; item->click = click; item->win = None; item->barafter = None; item->link = fe->menubar_items; fe->menubar_items = item; } static void menu_plan(frontend *fe) { int totw; int n; MENUBAR_ITEM *i; int x; int totpad; int pad; int w; totw = 0; n = 0; for (i=fe->menubar_items;i;i=i->link) { totw += i->textw; n ++; } totpad = (margin + fe->draww + margin) - (totw + (2 * margin * n) + (borderwidth * (n-1))); x = 0; for (i=fe->menubar_items;i;i=i->link) { pad = totpad / n; w = margin + i->textw+pad + margin; i->winx = x; i->winw = w; x += w; if (i->link) { i->barx = x; x += borderwidth; } i->textx = margin + (pad / 2); totpad -= pad; n --; } if (x != margin+fe->draww+margin) abort(); } static void menu_implement(frontend *fe) { MENUBAR_ITEM *i; unsigned long int attrmask; XSetWindowAttributes attr; for (i=fe->menubar_items;i;i=i->link) { if (i->win == None) { attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.event_mask = ExposureMask | ButtonPressMask; attrmask |= CWEventMask; attr.colormap = fe->wincmap; attrmask |= CWColormap; i->win = XCreateWindow(disp,fe->menuwin,i->winx,0,i->winw,margin+fe->baselineskip+margin,0,depth,InputOutput,CopyFromParent,attrmask,&attr); if (i->link) { attrmask = 0; attr.background_pixel = fe->bdcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.colormap = fe->wincmap; attrmask |= CWColormap; i->barafter = XCreateWindow(disp,fe->menuwin,i->barx,0,borderwidth,32767,0,depth,InputOutput,CopyFromParent,attrmask,&attr); } else { i->barafter = None; } i->actpm = None; i->inactpm = None; } else { XMoveResizeWindow(disp,i->win,i->winx,0,i->winw,margin+fe->baselineskip+margin); if (i->barafter != None) XMoveResizeWindow(disp,i->barafter,i->barx,0,borderwidth,32767); if (i->actpm != None) { XFreePixmap(disp,i->actpm); i->actpm = None; } if (i->inactpm != None) { XFreePixmap(disp,i->inactpm); i->inactpm = None; } } } } static void menu_layout(frontend *fe) { menu_plan(fe); menu_implement(fe); } static void new_game(frontend *fe) { int xsize; int ysize; int w; int h; int y; XWindowChanges chg; midend_new_game(fe->me); xsize = scrwidth; ysize = scrheight; midend_size(fe->me,&xsize,&ysize,false); if ((xsize != fe->draww) || (ysize != fe->drawh)) { fe->draww = xsize; fe->drawh = ysize; w = borderwidth + margin + xsize + margin + borderwidth; h = borderwidth + margin + fe->baselineskip + margin + borderwidth + margin + ysize + margin; if (fe->flags & FEF_STATBAR) h += borderwidth + margin + fe->baselineskip + margin; h += borderwidth; chg.x = (scrwidth - w) / 2; chg.y = (scrheight - h) / 2; chg.width = w - (2 * borderwidth); chg.height = h - (2 * borderwidth); XReconfigureWMWindow(disp,fe->topwin,XScreenNumberOfScreen(scr),CWX|CWY|CWWidth|CWHeight,&chg); XResizeWindow(disp,fe->menuwin,margin+xsize+margin,margin+fe->baselineskip+margin); XResizeWindow(disp,fe->drawwin,xsize,ysize); XFreePixmap(disp,fe->drawpix); fe->drawpix = XCreatePixmap(disp,fe->drawwin,xsize,ysize,depth); if (fe->flags & FEF_STATBAR) { y = margin + fe->baselineskip + margin + borderwidth + margin + ysize + margin; XMoveWindow(disp,fe->statbar,0,y); XMoveResizeWindow(disp,fe->statwin,margin,y+borderwidth+margin,xsize,fe->baselineskip); } menu_layout(fe); } midend_redraw(fe->me); } static void menu_click_new(frontend *fe, Time when) { (void)when; new_game(fe); } static void choose_config(int n, Time when) { struct preset_menu_entry *e; if (n < 0) return; if (n >= fe->cur_presets->n_entries) { if ((fe->cur_presets == fe->presets) && (n == fe->cur_presets->n_entries)) { // "Custom..." config_item *cfg; char *title; int i; cfg = midend_get_config(fe->me,CFG_SETTINGS,&title); printf("Config title = %s\n",title); for <"cfg"> (i=0;;i++) { printf("[%d] name=%s type=",i,cfg[i].name); switch (cfg[i].type) { case C_STRING: printf("STRING, str=%s\n",cfg[i].u.string.sval); break; case C_BOOLEAN: printf("BOOLEAN, val=%s\n",cfg[i].u.boolean.bval?"TRUE":"FALSE"); break; case C_CHOICES: { void choice(int x, const char *s, int l) { x = (x == cfg[i].u.choices.selected); printf(" %s%.*s%s",x?"<":"",l,s,x?">":""); } printf("CHOICES, list:"); walk_choices(cfg[i].u.choices.choicenames,&choice); } printf("\n"); break; case C_END: printf("END\n"); break <"cfg">; } } return; } abort(); } 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 setup_game(void) { int xsize; int ysize; int x; int y; int w; int h; unsigned long int attrmask; XSetWindowAttributes attr; int suby; XCharStruct cs; fe = malloc(sizeof(frontend)); fe->flags = 0; fe->copygc = XCreateGC(disp,rootwin,0,0); fe->drawgc = XCreateGC(disp,rootwin,0,0); 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; w = borderwidth + margin + xsize + margin + borderwidth; h = borderwidth + margin + fe->baselineskip + margin + borderwidth + margin + ysize + margin; if (fe->flags & FEF_STATBAR) h += borderwidth + margin + fe->baselineskip + margin; h += 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); suby = margin; attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.event_mask = ExposureMask; attrmask |= CWEventMask; attr.colormap = fe->wincmap; attrmask |= CWColormap; fe->menuwin = XCreateWindow(disp,fe->topwin,0,suby-margin,margin+xsize+margin,margin+fe->baselineskip+margin,0,depth,InputOutput,CopyFromParent,attrmask,&attr); suby += fe->baselineskip + margin; attrmask = 0; attr.background_pixel = fe->bdcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.event_mask = ExposureMask; attrmask |= CWEventMask; attr.colormap = fe->wincmap; attrmask |= CWColormap; fe->menubar = XCreateWindow(disp,fe->topwin,0,suby,32767,borderwidth,0,depth,InputOutput,CopyFromParent,attrmask,&attr); suby += borderwidth + margin; attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask; attrmask |= CWEventMask; attr.colormap = fe->wincmap; attrmask |= CWColormap; fe->drawwin = XCreateWindow(disp,fe->topwin,margin,suby,xsize,ysize,0,depth,InputOutput,CopyFromParent,attrmask,&attr); fe->drawpix = XCreatePixmap(disp,fe->drawwin,xsize,ysize,depth); suby += ysize + margin; if (fe->flags & FEF_STATBAR) { attrmask = 0; attr.background_pixel = fe->bdcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.colormap = fe->wincmap; attrmask |= CWColormap; fe->statbar = XCreateWindow(disp,fe->topwin,0,suby,32767,borderwidth,0,depth,InputOutput,CopyFromParent,attrmask,&attr); suby += borderwidth + margin; attrmask = 0; attr.background_pixel = fe->bgcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = 0; attrmask |= CWBorderPixel; attr.event_mask = ExposureMask; attrmask |= CWEventMask; attr.colormap = fe->wincmap; attrmask |= CWColormap; fe->statwin = XCreateWindow(disp,fe->topwin,margin,suby,xsize,fe->baselineskip,0,depth,InputOutput,CopyFromParent,attrmask,&attr); } fe->menubar_items = 0; menu_add_button(fe,0,0,&fe->item_new,"New",&menu_click_new); if (fe->flags & FEF_CONFIG) menu_add_button(fe,0,0,&fe->item_config,"Config",&menu_click_config); if (thegame.can_solve) menu_add_button(fe,FEF_SOLVE,0,&fe->item_solve,"Solve",&menu_click_solve); menu_add_button(fe,0,FEF_CAN_REDO,&fe->item_redo,"Redo",&menu_click_redo); menu_add_button(fe,0,FEF_CAN_UNDO,&fe->item_undo,"Undo",&menu_click_undo); menu_add_button(fe,0,0,&fe->item_quit,"Quit",&menu_click_quit); menu_layout(fe); XMapSubwindows(disp,fe->menuwin); XMapSubwindows(disp,fe->topwin); XMapRaised(disp,fe->topwin); mpop_init(&fe->mpop); fe->mpop_up = 0; fe->mpop_win = None; 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(); break; } 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(); break; } 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); XCopyArea(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; XCopyArea(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; } XCopyArea(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); }