From: ian Date: Tue, 27 May 2008 23:35:25 +0000 (+0000) Subject: can display X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=d4e2fe8f163f4029cf82838f2fc09484c0191097;p=trains.git can display --- diff --git a/hostside/gui-plan-testdata b/hostside/gui-plan-testdata new file mode 100644 index 0000000..2c72d4a --- /dev/null +++ b/hostside/gui-plan-testdata @@ -0,0 +1,12 @@ +train shinkansen has X7/P0. X5. X6. Q1. X8/P0.! X10* X12 Q2 A6/P0 +picio out on +detect X8 1 +detect X6 1 +movpos A6 feat P 1 point +movpos X2 feat R 0 relay +movpos X7 feat P ? point +movpos X7 gunk P 1 point +movpos A5 feat P 2 point +movpos Q0 feat Q 0 two +movpos X8 feat Q 0 three +picio out polarity diff --git a/hostside/gui-plan.c b/hostside/gui-plan.c index 6306bb6..92e17aa 100644 --- a/hostside/gui-plan.c +++ b/hostside/gui-plan.c @@ -23,11 +23,14 @@ typedef int ErrorCode; #include "daemons.h" +#include "dliste.h" #include "../layout/plan-data-format.h" #include #include +#define NSEGMENTS (ui_plan_data.n_segments) + typedef struct MaskState MaskState; struct MaskState { int x, y, width, height; @@ -41,10 +44,16 @@ struct PosnState { MaskState edge; }; +typedef struct { + unsigned on:1, inv:1, det:1, trainown:2, updated_tmp:1; +} SegFlags; + typedef struct SegmovfeatState SegmovfeatState; struct SegmovfeatState { SegmovfeatState *next; - int invert, det, trainown, posn, redraw_needed; + SegFlags flags; + unsigned redraw_needed:1; + int posn; MaskState whole; PosnState (*posns)[2/*i*/][2/*det*/]; /* posns[n_posns]=unknown if n_posns>1 */ @@ -53,9 +62,12 @@ struct SegmovfeatState { oop_source *events; const char *progname= "gui-plan"; +typedef struct TrainState TrainState; + typedef struct { SegmovfeatState *mfs; - unsigned inverted:1, updated:1; + SegFlags flags; /* used by multiplexer client */ + TrainState *owner; } SegState; static FILE *debug; @@ -74,6 +86,8 @@ static size_t badcmdreport_recsz; void die_hook(void) { } void die_vprintf_hook(const char *fmt, va_list al) { } +static void xlib_process(void); + static void diex(const char *fn, const char *w) __attribute__((noreturn)); static void diex(const char *fn, const char *w) { die("Xlib call failed: %s%s%s%s", fn, @@ -99,65 +113,36 @@ static void diexpm(const char *fn, const char *w, int xpmst) { diexpm(#f, (w), xpmcall_xpmst); \ }while(0) -int vbadcmd(ParseState *ps, const char *fmt, va_list al) { - fprintf(stderr,"gui-plan: incorrect input: `%.*s': ", - (int)badcmdreport_recsz, badcmdreport_data); - vfprintf(stderr,fmt,al); - putc('\n',stderr); - exit(8); -} - -static int lstrpdbsearch(const char *str, int l, - const char *what, - const void *items, int n_items, - int itemsz) { - int min, maxe, try, cmp; - const void *try_item; - const char *try_name; - - min= 0; - maxe= n_items; - for (;;) { - if (min >= maxe) badcmd(0,"unknown %s `%.*s'",what,l,str); - try= min + (maxe - min) / 2; - try_item= (const char*)items + itemsz * try; - try_name= *(const char *const *)try_item; - cmp= lstrstrcmp(str, l, try_name ? try_name : ""); - if (!cmp) return try; - if (cmp < 0) maxe= try; - else min= try + 1; - } -} - /*---------- input handling ----------*/ typedef struct { const char *name; void (*on_eof)(void); void (*on_input_line)(ParseState *ps); + int (*on_badcmd)(void); } InputStream; +static const InputStream *instream; + static void *input_iferr(oop_source *evts, oop_read *stdin_read, oop_rd_event evt, const char *errmsg, int errnoval, - const char *data, size_t recsz, void *is_v) { - const InputStream *is= is_v; + const char *data, size_t recsz, void *u_v) { const char *emsg; emsg= oop_rd_errmsg(stdin_read, evt, errnoval, OOP_RD_STYLE_GETLINE); - die("%s: %s", is->name, emsg); + die("%s: %s", instream->name, emsg); } static void *input_ifok(oop_source *evts, oop_read *cl_read, oop_rd_event evt, const char *errmsg, int errnoval, - const char *data, size_t recsz, void *is_v) { - const InputStream *is= is_v; + const char *data, size_t recsz, void *u_v) { ParseState ps; if (evt == OOP_RD_EOF) { - is->on_eof(); + instream->on_eof(); abort(); } if (evt != OOP_RD_OK) { - input_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,is_v); + input_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,0); return OOP_CONTINUE; } @@ -165,8 +150,8 @@ static void *input_ifok(oop_source *evts, oop_read *cl_read, badcmdreport_recsz= recsz; ps.remain= data; - is->on_input_line(&ps); - + instream->on_input_line(&ps); + xlib_process(); return OOP_CONTINUE; } @@ -176,6 +161,47 @@ static void *some_exception(oop_source *evts, int fd, die("poll exception on %s (fd %d)",name,fd); } +int vbadcmd(ParseState *ps, const char *fmt, va_list al) { + fprintf(stderr,"gui-plan: incorrect input: `%.*s': ", + (int)badcmdreport_recsz, badcmdreport_data); + vfprintf(stderr,fmt,al); + putc('\n',stderr); + return instream->on_badcmd(); +} + +static int lstrpdbsearch(const char *str, int l, + const char *what, + const void *items, int n_items, + int itemsz) { + int min, maxe, try, cmp; + const void *try_item; + const char *try_name; + + min= 0; + maxe= n_items; + for (;;) { + if (min >= maxe) { badcmd(0,"unknown %s `%.*s'",what,l,str); return -1; } + try= min + (maxe - min) / 2; + try_item= (const char*)items + itemsz * try; + try_name= *(const char *const *)try_item; + cmp= lstrstrcmp(str, l, try_name ? try_name : ""); + if (!cmp) return try; + if (cmp < 0) maxe= try; + else min= try + 1; + } +} + +static int lstrpdbsearch_movfeat(const char *str, int l, + const PlanSegmentData *sd) { + return lstrpdbsearch(str, l, "movfeat", sd->movfeats, sd->n_movfeats, + sizeof(*sd->movfeats)); +} +static int lstrpdbsearch_segment(const char *str, int l) { + return lstrpdbsearch(str, l, "segment", + ui_plan_data.segments, ui_plan_data.n_segments, + sizeof(*ui_plan_data.segments)); +} + /*---------- drawing etc. ----------*/ static int range_overlap(int x1, int width1, int x2, int width2) { @@ -187,6 +213,12 @@ static int range_overlap(int x1, int width1, int x2, int width2) { return 1; } +static void redraw_mark(SegmovfeatState *fs) { + if (fs->redraw_needed) return; + fs->redraw_needed= 1; + redraw_needed_count++; +} + static void xlib_expose(XExposeEvent *ev) { SegmovfeatState *fs; @@ -196,14 +228,11 @@ static void xlib_expose(XExposeEvent *ev) { for (fs= states_head; fs; fs= fs->next) { - if (fs->redraw_needed) - continue; if (!range_overlap(fs->whole.x, fs->whole.width, ev->x, ev->width)) continue; if (!range_overlap(fs->whole.y, fs->whole.height, ev->y, ev->height)) continue; - fs->redraw_needed= 1; - redraw_needed_count++; + redraw_mark(fs); } } @@ -221,15 +250,15 @@ static void redraw(SegmovfeatState *fs) { fs->whole.x, fs->whole.y, fs->whole.width, fs->whole.height, fs->whole.x, fs->whole.y) ); - if (fs->invert >= 0) { - src= &fs->posns[fs->posn][fs->invert][fs->det]; + if (fs->flags.on) { + src= &fs->posns[fs->posn][fs->flags.inv][fs->flags.det]; XCALL( XCopyArea, "redraw", (d, src->pm, w, fs->whole.gc, 0,0, src->width, src->height, src->x, src->y) ); } - if (fs->trainown && src && src->edge.x >= 0) { - gcv.foreground= fs->trainown>1 ? train_pixel : owned_pixel; + if (fs->flags.trainown && src && src->edge.x >= 0) { + gcv.foreground= fs->flags.trainown>1 ? train_pixel : owned_pixel; XCALL( XChangeGC, "train/own", (d, src->edge.gc, GCForeground, &gcv) ); XCALL( XFillRectangle, "train/own", @@ -322,6 +351,7 @@ static void loadmask(MaskState *out, const PlanPixmapDataRef *ppd, /*---------- stdin input handling ----------*/ static void stdin_eof(void) { exit(0); } +static int stdin_badcmd(void) { exit(8); } static void stdin_input_line(ParseState *ps) { const char *slash, *movfeatname; @@ -356,14 +386,10 @@ static void stdin_input_line(ParseState *ps) { movfeatname= 0; lmovfeatname= 0; } - segment_ix= lstrpdbsearch(ps->thisword, ps->lthisword, - "segment", ui_plan_data.segments, - ui_plan_data.n_segments, sizeof(*segment_d)); + segment_ix= lstrpdbsearch_segment(ps->thisword,ps->lthisword); segment_d= &ui_plan_data.segments[segment_ix]; - - movfeat_ix= lstrpdbsearch(movfeatname, lmovfeatname, - "movfeat", segment_d->movfeats, - segment_d->n_movfeats, sizeof(*movfeat_d)); + + movfeat_ix= lstrpdbsearch_movfeat(movfeatname, lmovfeatname, segment_d); movfeat_d= &segment_d->movfeats[movfeat_ix]; if (ps->remain) { @@ -380,16 +406,23 @@ static void stdin_input_line(ParseState *ps) { ps_neednoargs(ps); fs= &state[segment_ix].mfs[movfeat_ix]; - fs->invert= invert; - fs->det= det; - fs->trainown= trainown; + if (invert>=0) { + fs->flags.on= 1; + fs->flags.inv= invert; + } else { + fs->flags.on= 0; + fs->flags.inv= 0; + } + fs->flags.det= det; + fs->flags.trainown= trainown; fs->posn= posn; redraw(fs); - xlib_process(); } -static const InputStream stdin_is= { "stdin", stdin_eof, stdin_input_line }; +static const InputStream stdin_is= { + "stdin", stdin_eof, stdin_input_line, stdin_badcmd +}; /*---------- multiplexer client ----------*/ @@ -454,26 +487,166 @@ static int sock_clientconnect(const char *node, const char *service) { } static void sock_eof(void) { die("EOF on multiplexer connection"); } +static int sock_badcmd(void) { return -1; } /*---------- multiplexer protocol ----------*/ +struct TrainState { + struct { struct TrainState *next, *back; } others; + char *name; +}; + +static int poweron; +static struct { TrainState *head, *tail; } trains; + +#define FOR_S for (s=0; smfs[f]; + fs->flags= ss->flags; + fs->flags.on= poweron; + redraw_mark(fs); +} + +static void mx_redraw_seg(int s) { + int f; + assert(!!state[s].flags.trainown == !!state[s].owner); + for (f=0; f < ui_plan_data.segments[s].n_movfeats; f++) + mx_redraw_feat(s,f); +} + +static void mx_redraw_all(void) { + int s; + FOR_S + mx_redraw_seg(s); +} + static void si_detect(ParseState *ps) { + long dl; + int r, s; + + r= ps_needword(ps); if (r) return; + s= lstrpdbsearch_segment(ps->thisword,ps->lthisword); if (s==-1) return; + r= ps_neednumber(ps,&dl,0,1,"detection flag"); if (r) return; + state[s].flags.det= dl; + mx_redraw_seg(s); } static void si_polarity(ParseState *ps) { + char *delim, *end; + int s; + + mx_clear_updated(); + if (*ps->remain++ != '<') { badcmd(ps,"missing <"); return; } + + end= strchr(ps->remain,'>'); + if (!end) { badcmd(ps,"missing >"); return; } + + while (ps->remain < end) { + delim= memchr(ps->remain, ',', end - ps->remain); + if (!delim) delim= end; + + ps->thisword= ps->remain; + ps->lthisword= delim - ps->remain; + ps->remain= delim+1; + + s= lstrpdbsearch_segment(ps->thisword,ps->lthisword); if (s<0) continue; + state[s].flags.updated_tmp= 1; + } + FOR_S { + if (state[s].flags.inv == state[s].flags.updated_tmp) + continue; + state[s].flags.inv= state[s].flags.updated_tmp; + mx_redraw_seg(s); + } } static void si_movpos(ParseState *ps) { + long pl; + int r,s,f, n_posns; + r= ps_needword(ps); if (r) return; + s= lstrpdbsearch_segment(ps->thisword,ps->lthisword); if (s<0) return; + r= ps_needword(ps); if (r) return; + if (thiswordstrcmp(ps,"feat")) { badcmd(ps,"weird movpos"); return; } + + r= ps_needword(ps); if (r) return; + f= lstrpdbsearch_movfeat(ps->thisword, ps->lthisword, + &ui_plan_data.segments[s]); if (f<0) return; + + n_posns= ui_plan_data.segments[s].movfeats[f].n_posns; + if (ps->remain[0]=='?') goto unknown; + r= ps_neednumber(ps,&pl,0,n_posns-1,"movfeat posn"); if (r) goto unknown; + state[s].mfs[f].posn= pl; + mx_redraw_seg(s); + return; + +unknown: + state[s].mfs[f].posn= n_posns; + mx_redraw_seg(s); } static void si_on(ParseState *ps) { + poweron= 1; + mx_redraw_all(); } static void si_off(ParseState *ps) { + poweron= 0; + mx_redraw_all(); } static void si_train(ParseState *ps) { - + int r,sl,s; + int lastchar; + TrainState *train; + const char *seg; + + mx_clear_updated(); + r= ps_needword(ps); /* */ if (r) return; + + /* atomise the train name */ + for (train=trains.head; + train; + train= train->others.next) + if (!thiswordstrcmp(ps,train->name)) + goto found; + /* not found */ + train= mmalloc(sizeof(*train)); + train->name= mmalloc(ps->lthisword+1); + memcpy(train->name,ps->thisword,ps->lthisword); + train->name[ps->lthisword]= 0; + DLIST2_APPEND(trains,train,others); +found: + + r= ps_needword(ps); if (r) return; + if (thiswordstrcmp(ps,"has")) { badcmd(ps,"weird train"); return; } + while (ps_word(ps) >= 0) { + sl= strcspn(ps->thisword, "/.!*~#+"); + if (sl > ps->lthisword) sl= ps->lthisword; + seg= ps->thisword; + if (*seg=='-') { seg++; sl--; } + s= lstrpdbsearch_segment(seg,sl); if (s<0) continue; + lastchar= ps->thisword[ps->lthisword-1]; + state[s].owner= train; + state[s].flags.updated_tmp= 1; + state[s].flags.trainown= 1 + (lastchar=='!' || lastchar=='*'); + } + FOR_S { + if (state[s].flags.updated_tmp) { + mx_redraw_seg(s); + } else if (state[s].owner==train) { + state[s].owner= 0; + state[s].flags.trainown= 0; + mx_redraw_seg(s); + } + } } typedef struct MuxEventInfo MuxEventInfo; @@ -507,14 +680,14 @@ static void si_connected(ParseState *ps) { sockprintf("\n"); } -static void si_problem(ParseState *ps) { +static void si_fatal(ParseState *ps) { die("multiplexer reports problem: %.*s\n", (int)badcmdreport_recsz, badcmdreport_data); } static void si_ack(ParseState *ps) { ps_needword(ps); /* command */ ps_needword(ps); /* status */ - if (thiswordstrcmp(ps,"ok")) si_problem(0); + if (thiswordstrcmp(ps,"ok")) si_fatal(0); } static const MuxEventInfo muxeventinfos[]= { @@ -530,9 +703,9 @@ static const MuxEventInfo muxeventinfos[]= { { "+executing", 0, 0 }, { "+ack", 0, si_ack }, - { "+nak", 0, si_problem }, - { "=failed", 0, si_problem }, - { "=denied", 0, si_problem }, + { "+nak", 0, si_fatal }, + { "=failed", 0, si_fatal }, + { "=denied", 0, si_fatal }, { 0 } }; @@ -564,7 +737,8 @@ found: static const InputStream sock_is= { "multiplexer connection", sock_eof, - sock_input_line + sock_input_line, + sock_badcmd }; /*---------- main program including much of the initialisation ----------*/ @@ -581,7 +755,6 @@ int main(int argc, const char *const *argv) { const PlanSegmentData *segment_d; const PlanSegmovfeatData *movfeat_d; char *ep; - const InputStream *instream; sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new"); events= oop_sys_source(sys_events); assert(events); @@ -642,19 +815,22 @@ int main(int argc, const char *const *argv) { &colour, &colour) ); owned_pixel= colour.pixel; - state= mmalloc(sizeof(*state) * ui_plan_data.n_segments); + state= mmalloc(sizeof(*state) * NSEGMENTS); for (segment_ix= 0, segment_d= ui_plan_data.segments; segment_ix < ui_plan_data.n_segments; segment_ix++, segment_d++) { + state[segment_ix].flags.on= 0; + state[segment_ix].flags.inv= 0; + state[segment_ix].flags.det= 0; + state[segment_ix].flags.trainown= 0; + state[segment_ix].flags.updated_tmp= 0; state[segment_ix].mfs= fs= mmalloc(sizeof(*state[segment_ix].mfs) * segment_d->n_movfeats); for (movfeat_ix= 0, movfeat_d= segment_d->movfeats; movfeat_ix < segment_d->n_movfeats; movfeat_ix++, movfeat_d++, fs++) { fs->next= states_head; states_head= fs; - fs->invert= -1; - fs->det= 0; - fs->trainown= 0; + fs->flags= state[segment_ix].flags; fs->posn= movfeat_d->n_posns; if (fs->posn==1) fs->posn= 0; fs->redraw_needed= 0; @@ -707,8 +883,7 @@ int main(int argc, const char *const *argv) { if (!rd) diee("oop_rd_new_fd"); oor= oop_rd_read(rd, OOP_RD_STYLE_GETLINE, 1024, - input_ifok, (void*)instream, - input_iferr, (void*)instream); + input_ifok,0, input_iferr,0); if (oor) diee("oop_rd_read"); events->on_fd(events, ConnectionNumber(d), OOP_READ, xlib_readable, 0);