From: ian Date: Sun, 19 Jun 2005 13:26:02 +0000 (+0000) Subject: gui-plan seems to work, yay X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=b83c5936a3ceb4ca884798b33d9b7f6dcf474604;p=trains.git gui-plan seems to work, yay --- diff --git a/hostside/.cvsignore b/hostside/.cvsignore index a45bb35..889d7ba 100644 --- a/hostside/.cvsignore +++ b/hostside/.cvsignore @@ -6,3 +6,4 @@ layoutinfo.h *.d proto-expanded auproto-* +gui-plan diff --git a/hostside/Makefile b/hostside/Makefile index d988188..97a1a13 100644 --- a/hostside/Makefile +++ b/hostside/Makefile @@ -3,21 +3,26 @@ include ../common.make include ../cprogs.make -TARGETS= hostside hostside-old +TARGETS= hostside hostside-old gui-plan all: $(TARGETS) -hostside-old: serialio.o nmra.o main.o encode.o +hostside-old: serialio.o nmra.o main.o encode.o utils.o $(LINK) -hostside: hostside.o serialio.o client.o obc.o commands.o \ +hostside: hostside.o serialio.o client.o obc.o commands.o utils.o \ nmra.o encode.o retransmit.o output.o auproto-pic.o \ + parseutils.o \ -loop $(LINK) proto-expanded: ../cebpic/README.protocol expand <$< $o +gui-plan: gui-plan.o utils.o parseutils.o ../layout/ui-plan-bot.o \ + __oop-read-copy.o -loop + $(LINK) -L/usr/X11R6/lib -lXpm -lX11 + commands.o auproto-pic.o: auproto-pic.h auproto-%: parse-proto-spec proto-expanded skelproto-% @@ -28,7 +33,7 @@ layoutinfo.h: ../layout/ours.layout-data.c Makefile %.c: layoutinfo.h -safety: safety.o utils.o ../layout/ours.layout-data.o +safety: safety.o utils.o trackloc.o ../layout/ours.layout-data.o $(LINK) clean: diff --git a/hostside/__oop-read-copy.c b/hostside/__oop-read-copy.c new file mode 100644 index 0000000..8cd5c67 --- /dev/null +++ b/hostside/__oop-read-copy.c @@ -0,0 +1,477 @@ +/* read.c, liboop, copyright 2000 Ian jackson + + This is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License, version 2.1 or later. + See the file COPYING for details. */ + +#include "oop.h" +#include "oop-read.h" + +#include +#include +#include +#include + +#undef MIN /* for systems that define it */ +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +static void *on_time(oop_source*, struct timeval, void*); +static void *on_readable(oop_source*, oop_readable*, void*); +static void *on_process(oop_source*, oop_read*, int try_read); + +static int set_time_ifbuf(oop_source *oop, oop_read *rd); +static void cancel_time(oop_source *oop, oop_read *rd); + +const oop_rd_style OOP_RD_STYLE_GETLINE[]= {{ + OOP_RD_DELIM_STRIP,'\n', OOP_RD_NUL_FORBID, OOP_RD_SHORTREC_EOF, +}}; +const oop_rd_style OOP_RD_STYLE_BLOCK[]= {{ + OOP_RD_DELIM_NONE, 0, OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_EOF, +}}; +const oop_rd_style OOP_RD_STYLE_IMMED[]= {{ + OOP_RD_DELIM_NONE, 0, OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_SOONEST, +}}; + +struct oop_read { + /* set at creation time: */ + oop_source *oop; + oop_readable *ra; + char *userbuf; + /* persistent state */ + oop_rd_bufctl_op readahead; /* _ENABLE or _DISABLE */ + char *allocbuf; + size_t alloc, used, discard; + size_t neednotcheck; /* data we've already searched for delimiter */ + int displacedchar; /* >=0, first unused */ + /* arguments to oop_rd_read */ + oop_rd_style style; + size_t maxrecsz; + oop_rd_call *call_ok, *call_err; + void *data_ok, *data_err; +}; + +/* Buffer is structured like this if displacedchar>=0 and delim found: + * + * done stuff, displaced readahead - read unused + * we've called delimiter| but not yet buffer + * back for || returned space + * ddddddddddddddddddddddDOaaaaaaaaaaaaaaaaaaa____________ + * <------- discard -----> + * <----------------------- used ------------> + * <------------------------------------- alloc ---------> + * + * If displacedchar>=0 then the the first character of readahead has + * been displaced by a nul byte and is stored in displacedchar. If + * _DELIM_STRIP and the delimiter is found then the nul overwrites the + * delimiter. + * + * Buffer when full {this,max} may need + * DELIM found? <-recval-> recdata buffer required readahead + * NONE n/a ddddddddddOaaa_ recsz recdata+1 == recsz+1 maxrecsz + * KEEP Yes dddddddddDOaaa_ recsz recdata+1 == recsz+1 maxrecsz + * KEEP No ddddddddddOaaa_ recsz recdata+1 == recsz+1 maxrecsz + * STRIP Yes dddddddddd0aaaa recsz+1 recdata == recsz+1 maxrecsz+1 + * STRIP No ddddddddddOaaaa recsz recdata+1 == recsz+1 maxrecsz+1 + * + * Key: d = data to be returned + * D = delimiter, being returned + * a = readahead, not to be returned + * O = readahead character displaced by a nul + * 0 = delimiter replaced by a nul + * _ = unused + */ + +static const char *const eventstrings_nl[]= { + "INTERNAL ERROR (_nl _OK) please report", + "End of file", + "Missing newline at end of file", + "Line too long", + "Nul byte", + "Nul byte, in line which is also too long", + "INTERNAL ERROR (_nl _SYSTEM) please report" +}; + +static const char *const eventstrings_other[]= { + "Record read successfully", + "End of file", + "Incomplete record at end of file", + "Record too long", + "Nul byte", + "Nul byte in record which is also too long", + "System error" +}; + +oop_read *oop_rd_new(oop_source *oop, oop_readable *ra, char *buf, size_t bufsz) { + oop_read *rd= 0; + + assert(buf ? bufsz>=2 : !bufsz); + + rd= oop_malloc(sizeof(*rd)); if (!rd) goto x_fail; + rd->oop= oop; + rd->ra= ra; + rd->userbuf= buf; + rd->readahead= OOP_RD_BUFCTL_ENABLE; + rd->allocbuf= 0; + rd->used= 0; + rd->alloc= buf ? bufsz : 0; + rd->discard= 0; + rd->neednotcheck= 0; + rd->displacedchar= -1; + rd->style= *OOP_RD_STYLE_IMMED; + + return rd; + +x_fail: + oop_free(rd); + return 0; +} + +static int set_time_ifbuf(oop_source *oop, oop_read *rd) { + if (rd->used > rd->discard) + return oop->on_time(oop,OOP_TIME_NOW,on_time,rd), 0; /* fixme */ + return 0; +} +static void cancel_time(oop_source *oop, oop_read *rd) { + oop->cancel_time(oop,OOP_TIME_NOW,on_time,rd); +} +static int set_read(oop_source *oop, oop_read *rd) { + return rd->ra->on_readable(rd->ra,on_readable,rd), 0; /* fixme */ +} +static void cancel_read(oop_source *oop, oop_read *rd) { + rd->ra->on_cancel(rd->ra); +} + +int oop_rd_read(oop_read *rd, const oop_rd_style *style, size_t maxrecsz, + oop_rd_call *ifok, void *data_ok, + oop_rd_call *iferr, void *data_err) { + oop_source *oop= rd->oop; + int er; + + cancel_time(oop,rd); + cancel_read(oop,rd); + + if (style->delim_mode == OOP_RD_DELIM_NONE || + rd->style.delim_mode == OOP_RD_DELIM_NONE || + style->delim != rd->style.delim) + rd->neednotcheck= 0; + + rd->style= *style; + rd->maxrecsz= maxrecsz; + rd->call_ok= ifok; rd->data_ok= data_ok; + rd->call_err= iferr; rd->data_err= data_err; + + er= set_read(oop,rd); if (er) return er; + er= set_time_ifbuf(oop,rd); if (er) return er; + return 0; +} + +void oop_rd_delete(oop_read *rd) { + rd->ra->on_cancel(rd->ra); + oop_free(rd->allocbuf); + oop_free(rd); +} + +void oop_rd_cancel(oop_read *rd) { + cancel_time(rd->oop,rd); + cancel_read(rd->oop,rd); +} + +const char *oop_rd_errmsg(oop_read *rd, oop_rd_event event, int errnoval, + const oop_rd_style *style) { + if (event == OOP_RD_SYSTEM) + return strerror(errnoval); + else if (style && style->delim_mode != OOP_RD_DELIM_NONE + && style->delim == '\n') + return eventstrings_nl[event]; + else + return eventstrings_other[event]; +} + +static void *on_readable(oop_source *oop, oop_readable *ra, void *rd_void) { + oop_read *rd= rd_void; + + assert(oop == rd->oop); + assert(ra == rd->ra); + return on_process(oop,rd,1); +} + +static void *on_time(oop_source *oop, struct timeval when, void *rd_void) { + oop_read *rd= rd_void; + + assert(oop == rd->oop); + return on_process(oop,rd,0); +} + +static size_t calc_dataspace(oop_read *rd) { + if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) { + return rd->alloc; + } else { + return rd->alloc ? rd->alloc-1 : 0; + } +} + +static void *on_process(oop_source *oop, oop_read *rd, int try_read) { + oop_rd_event event; + int evkind; /* 0=none, -1=error, 1=something */ + int errnoval, nread, cancelnow; + oop_rd_call *call; + char *buf, *delimp; + const char *errmsg; + size_t maxrecsz; /* like in arg to oop_rd_read, but 0 -> large val */ + size_t maxbufreqd; /* maximum buffer we might possibly want to alloc */ + size_t readahead; /* max amount of data we might want to readahead */ + size_t want; /* amount we want to allocate or data we want to read */ + size_t dataspace; /* amount of buffer we can usefully fill with data */ + size_t thisrecsz; /* length of the record we've found */ + size_t thisrecdata; /* length of data representing the record */ + void *call_data; + + cancel_time(oop,rd); + + if (rd->userbuf) { + buf= rd->userbuf; + } else { + buf= rd->allocbuf; + } + + if (rd->discard) { + rd->used -= rd->discard; + rd->neednotcheck -= rd->discard; + memmove(buf, buf + rd->discard, rd->used); + rd->discard= 0; + } + if (rd->displacedchar >= 0) { + assert(rd->used > 0); + buf[0]= rd->displacedchar; + rd->displacedchar= -1; + } + + maxrecsz= rd->maxrecsz ? rd->maxrecsz : INT_MAX / 5 /* allows +20 and *4 */; + maxbufreqd= maxrecsz+1; + + if (rd->userbuf && maxbufreqd > rd->alloc) { + maxrecsz -= (maxbufreqd - rd->alloc); + maxbufreqd= rd->alloc; + } + + if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) { + readahead= maxrecsz+1; + } else { + readahead= maxrecsz; + } + + for (;;) { + evkind= 0; + event= -1; + thisrecdata= thisrecsz= 0; + errnoval= 0; + + assert(rd->used <= rd->alloc); + dataspace= calc_dataspace(rd); + + if (/* delimiter already in buffer, within max record data ? */ + rd->style.delim_mode != OOP_RD_DELIM_NONE && + (delimp= memchr(buf + rd->neednotcheck, rd->style.delim, + MIN(rd->used, readahead) - rd->neednotcheck))) { + + thisrecsz= (delimp - buf); + thisrecdata= thisrecsz+1; + if (rd->style.delim_mode == OOP_RD_DELIM_KEEP) + thisrecsz= thisrecdata; + event= OOP_RD_OK; + evkind= +1; + + } else if (rd->used >= readahead) { + + thisrecsz= thisrecdata= maxrecsz; + evkind= +1; + + if (rd->style.delim_mode == OOP_RD_DELIM_NONE) { + event= OOP_RD_OK; + } else { + event= OOP_RD_LONG; + if (rd->style.shortrec_mode < OOP_RD_SHORTREC_LONG) { + evkind= -1; + thisrecsz= thisrecdata= 0; + } + } + + } else if (/* want to return ASAP, and we have something ? */ + rd->style.shortrec_mode == OOP_RD_SHORTREC_SOONEST && + rd->used > 0 && rd->alloc >= 2) { + + thisrecdata= rd->used; + if (thisrecdata == rd->alloc) thisrecdata--; + thisrecsz= thisrecdata; + event= OOP_RD_OK; + evkind= +1; + + } + + want= 0; + if (evkind && thisrecdata && thisrecsz >= rd->alloc) { + /* Need to make space for the trailing nul */ + want= rd->alloc+1; + } else if (!evkind && !rd->userbuf && + rd->used >= dataspace && rd->alloc < maxbufreqd) { + /* Need to make space to read more data */ + want= rd->alloc + 20; + want <<= 2; + want= MIN(want, maxbufreqd); + } + + if (want) { + assert(!rd->userbuf); + assert(want <= maxbufreqd); + + buf= oop_realloc(rd->allocbuf,want); + if (!buf) { + event= OOP_RD_SYSTEM; + evkind= -1; + errnoval= ENOMEM; + thisrecsz= thisrecdata= 0; + break; + } + rd->allocbuf= buf; + rd->alloc= want; + } + + if (evkind) break; /* OK, process it then */ + + if (!try_read) return OOP_CONTINUE; /* But we weren't told it was ready. */ + + dataspace= calc_dataspace(rd); + want= MIN(dataspace, readahead); + assert(rd->used < want); + + errno= 0; + nread= rd->ra->try_read(rd->ra, buf+rd->used, want-rd->used); + if (errno == EAGAIN) return OOP_CONTINUE; + + if (nread > 0) { + rd->neednotcheck= rd->used; + rd->used += nread; + continue; + } + + if (nread < 0) { /* read error */ + + event= OOP_RD_SYSTEM; + evkind= -1; + errnoval= errno; + thisrecsz= thisrecdata= rd->used; + break; + + } else { + + if (rd->used) { + event= OOP_RD_PARTREC; + evkind= (rd->style.shortrec_mode == OOP_RD_SHORTREC_FORBID) ? -1 : +1; + thisrecsz= thisrecdata= rd->used; + } else { + event= OOP_RD_EOF; + evkind= +1; + } + break; + + } + } + + /* OK, we have an event of some kind */ + + /* Nul byte handling */ + if (thisrecsz > 0 && rd->style.nul_mode != OOP_RD_NUL_PERMIT) { + size_t checked; + char *nul, *notnul; + + for (checked=0; + (nul= memchr(buf+checked,0,thisrecsz-checked)); + ) { + if (rd->style.nul_mode == OOP_RD_NUL_FORBID) { + event= OOP_RD_NUL; + evkind= -1; + thisrecdata= thisrecsz= 0; + break; + } + assert(rd->style.nul_mode == OOP_RD_NUL_DISCARD); + for (notnul= nul+1; + notnul < buf+thisrecsz && notnul == '\0'; + notnul++); + thisrecsz-= (notnul-nul); + checked= nul-buf; + memmove(nul,notnul,thisrecsz-checked); + } + } + + /* Checks that all is well */ + + assert(evkind); + assert(thisrecsz <= thisrecdata); + assert(!rd->maxrecsz || thisrecsz <= rd->maxrecsz); + assert(thisrecdata <= rd->used); + + rd->discard= thisrecdata; + + cancelnow= (evkind < 0) || (event == OOP_RD_EOF); + + if (!cancelnow) { + errnoval= set_time_ifbuf(oop,rd); + if (errnoval) { + event= OOP_RD_SYSTEM; + evkind= -1; + cancelnow= 1; + thisrecsz= thisrecdata= 0; + rd->discard= 0; + } + } + + if (evkind < 0) { + call= rd->call_err; + call_data= rd->data_err; + errmsg= oop_rd_errmsg(rd,event,errnoval,&rd->style); + } else { + call= rd->call_ok; + call_data= rd->data_ok; + errmsg= 0; + } + + if (thisrecdata) { + /* We have to fill in a nul byte. */ + assert(thisrecsz < rd->alloc); + if (thisrecsz == thisrecdata && thisrecsz < rd->used) + rd->displacedchar= (unsigned char)buf[thisrecdata]; + buf[thisrecsz]= 0; + } + + if (cancelnow) + oop_rd_cancel(rd); + + return + call(oop,rd, event,errmsg,errnoval, + (thisrecdata ? buf : 0), thisrecsz, call_data); +} + +oop_read *oop_rd_new_fd(oop_source *oop, int fd, char *buf, size_t bufsz) { + oop_readable *ra; + oop_read *rd; + + ra= oop_readable_fd(oop,fd); + if (!ra) return 0; + + rd= oop_rd_new(oop,ra,buf,bufsz); + if (!rd) { ra->delete_tidy(ra); return 0; } + + return rd; +} + +int oop_rd_delete_tidy(oop_read *rd) { + oop_readable *ra= rd->ra; + oop_rd_delete(rd); + return ra->delete_tidy(ra); +} + +void oop_rd_delete_kill(oop_read *rd) { + oop_readable *ra= rd->ra; + oop_rd_delete(rd); + ra->delete_kill(ra); +} diff --git a/hostside/client.c b/hostside/client.c index 22784ed..e3625ba 100644 --- a/hostside/client.c +++ b/hostside/client.c @@ -16,98 +16,6 @@ void vbadcmd(ParseState *ps, const char *fmt, va_list al) { owrite(&ps->cl->ch,"\n",1); } -void badcmd(ParseState *ps, const char *fmt, ...) { - va_list al; - va_start(al,fmt); - vbadcmd(ps,fmt,al); - va_end(al); -} - -int ps_word(ParseState *ps) { - const char *space; - if (!ps->remain) return 0; - space= strchr(ps->remain, ' '); - ps->thisword= ps->remain; - if (space) { - ps->lthisword= space - ps->thisword; - ps->remain= space + 1; - } else { - ps->lthisword= strlen(ps->remain); - ps->remain= 0; - } - return 1; -} - -int ps_needword(ParseState *ps) { - if (!ps_word(ps)) { badcmd(ps,"too few args"); return 0; } - return 1; -} - -int ps_neednumber(ParseState *ps, long *r, long mi, long mx, const char *wh) { - char *ep; - long v; - - if (!ps_needword(ps)) return 0; - errno= 0; v= strtol(ps->thisword,&ep,0); - if (errno || ep != ps->thisword + ps->lthisword) { - badcmd(ps,"invalid number for %s",wh); - return 0; - } - if (v < mi || v > mx) { - badcmd(ps,"%s %ld out of range %ld..%ld",wh,v,mi,mx); - return 0; - } - *r= v; - return 1; -} - -int ps_neednoargs(ParseState *ps) { - if (ps->remain) { - badcmd(ps,"too many arguments"); - return 0; - } - return 1; -} - -int ps_needhextoend(ParseState *ps, Byte *d, int *remain_io) { - Byte *d_end; - char buf[3], *ep; - - d_end= d + *remain_io; - buf[2]= 0; - - if (!ps->remain) { badcmd(ps,"need hex data block"); return 0; } - for (;;) { - if (!ps_word(ps)) return 0; - while (ps->lthisword > 0) { - if (ps->lthisword & 1) { - badcmd(ps,"hex data block with odd number of digits in part"); - return 0; - } - buf[0]= ps->thisword[0]; - buf[1]= ps->thisword[1]; - if (d >= d_end) { badcmd(ps,"hex data block too long"); return 0; } - *d++= strtoul(buf,&ep,16); - if (*ep) { badcmd(ps,"invalid digit in hex data block"); return 0; } - ps->lthisword -= 2; - ps->thisword += 2; - } - } - - *remain_io= d_end - d; - return 1; -} - -int lstrstrcmp(const char *a, int la, const char *b) { - int lb= strlen(b); - if (la != lb) return 1; - return memcmp(a,b,la); -} - -int thiswordstrcmp(ParseState *ps, const char *b) { - return lstrstrcmp(ps->thisword, ps->lthisword, b); -} - const void *any_lookup(ParseState *ps, const void *inf, size_t sz) { const char *tname; diff --git a/hostside/gui-plan.c b/hostside/gui-plan.c index 42140ed..55fb48a 100644 --- a/hostside/gui-plan.c +++ b/hostside/gui-plan.c @@ -1,12 +1,390 @@ /* - * usage: gui-plan + * usage: .../gui-plan [] * protocol on stdin: - * series of uint32_t's in network byte order - * top byte is opcode - * 0x00 off - * 0x01 on - * 0x01 detect - * next byte is movposcomb - * remaining bytes are segment number + * series of lines + * off [/[] + * [i]on [[/] ] + * [i]det [[/] ] */ +#include +#include +#include +#include + +#include +#include + +#include "hostside.h" +#include "../layout/plan-data-format.h" + +#include +#include + +typedef struct SegmovfeatState SegmovfeatState; +struct SegmovfeatState { + SegmovfeatState *next; + int invert, det, posn, x, y, width, height, redraw_needed; + GC gc; + Pixmap (*posns)[2/*i*/][2/*det*/]; +}; + +oop_source *events; + +static SegmovfeatState **state, *states_head; +static Display *d; +static oop_source_sys *sys_events; +static Window w; +static int redraw_needed_count, expose_count; + +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, + (w)?" (":"", (w), (w)?")":""); +} + +#define XCALL(f,w,al) do{ \ + if (!( (f) al )) \ + diex(#f, (w)); \ + }while(0) + +static void diexpm(const char *fn, const char *w, int xpmst) + __attribute__((noreturn)); +static void diexpm(const char *fn, const char *w, int xpmst) { + die("Xpm call failed: %s%s%s%s: %s", fn, + (w)?" (":"", (w), (w)?")":"", XpmGetErrorString(xpmst)); +} + +#define XPMCALL(f,w,al) do{ \ + int xpmcall_xpmst; \ + xpmcall_xpmst= ((f) al); \ + if (xpmcall_xpmst != XpmSuccess) \ + diexpm(#f, (w), xpmcall_xpmst); \ + }while(0) + +void vbadcmd(ParseState *ps, const char *fmt, va_list al) { + fprintf(stderr,"gui-plan: incorrect input: "); + vfprintf(stderr,fmt,al); + putc('\n',stderr); + exit(8); +} + +static void *stdin_iferr(oop_source *evts, oop_read *stdin_read, + oop_rd_event evt, const char *errmsg, int errnoval, + const char *data, size_t recsz, void *cl_v) { + die("read stdin: %s", oop_rd_errmsg(stdin_read, evt, + errnoval, OOP_RD_STYLE_GETLINE)); + return OOP_CONTINUE; +} + +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",what); + 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 void *some_exception(oop_source *evts, int fd, + oop_event evt, void *cl_v) { + die("poll exception on fd %d",fd); +} + +static int range_overlap(int x1, int width1, int x2, int width2) { + /* works for y's and heights too, obviously. */ + int rhs1, rhs2; + rhs1= x1 + width1; + rhs2= x2 + width2; + if (rhs1 <= x2 || rhs2 <= x1) return 0; + return 1; +} + +static void xlib_expose(XExposeEvent *ev) { + SegmovfeatState *fs; + + expose_count= ev->count; + if (!ev->width || !ev->height) return; + + for (fs= states_head; + fs; + fs= fs->next) { + if (fs->redraw_needed) + continue; + if (!range_overlap(fs->x, fs->width, + ev->x, ev->width)) continue; + if (!range_overlap(fs->y, fs->height, + ev->y, ev->height)) continue; + fs->redraw_needed= 1; + redraw_needed_count++; + } +} + +static void redraw(SegmovfeatState *fs) { + Pixmap src; + + if (fs->redraw_needed) { + fs->redraw_needed= 0; + redraw_needed_count--; + } + if (fs->invert < 0) { + XCALL( XFillRectangle, "redraw", + (d,w, fs->gc, + fs->x, fs->y, + fs->width, fs->height) ); + } else { + src= fs->posns[fs->posn][fs->invert][fs->det]; + XCALL( XCopyArea, "redraw", + (d, src, w, fs->gc, + 0,0, fs->width, fs->height, + fs->x, fs->y) ); + } +} + +static void redraw_as_needed(void) { + SegmovfeatState *fs; + + for (fs= states_head; + fs; + fs= fs->next) + if (fs->redraw_needed) + redraw(fs); + assert(!redraw_needed_count); +} + +static Bool evpredicate_always(Display *d, XEvent *ev, XPointer a) { + return True; +} + +static void xlib_process(void) { + XEvent ev; + Status xst; + + for (;;) { + xst= XCheckIfEvent(d,&ev,evpredicate_always,0); + if (!xst) { + if (!redraw_needed_count || expose_count) + return; + redraw_as_needed(); + continue; + } + + switch (ev.type) { + case Expose: xlib_expose(&ev.xexpose); break; + case NoExpose: break; + default: die("unrequested event type %d\n",ev.type); + } + } +} + +static void *xlib_readable(oop_source *evts, int fd, + oop_event evt, void *cl_v) { + xlib_process(); + return OOP_CONTINUE; +} + +static void *stdin_ifok(oop_source *evts, oop_read *cl_read, + oop_rd_event evt, const char *errmsg, int errnoval, + const char *data, size_t recsz, void *cl_v) { + const char *slash, *movfeatname; + ParseState ps; + int invert, det, segment_ix, movfeat_ix, lmovfeatname; + long posn; + const PlanSegmentData *segment_d; + const PlanSegmovfeatData *movfeat_d; + SegmovfeatState *fs; + + if (evt == OOP_RD_EOF) + exit(0); + + if (evt != OOP_RD_OK) + return stdin_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,cl_v); + + ps.cl= 0; + ps.remain= data; + ps_needword(&ps); + + if (!thiswordstrcmp(&ps,"off")) { + invert= -1; + det= 0; + } else { + invert= (ps.thisword[0]=='i'); + if (invert) { ps.thisword++; ps.lthisword--; } + det= (!thiswordstrcmp(&ps,"on") ? 0 : + !thiswordstrcmp(&ps,"det") ? 1 : + (badcmd(&ps,"unknown command"),-1)); + } + ps_needword(&ps); + slash= memchr(ps.thisword, '/', ps.lthisword); + if (slash) { + movfeatname= slash + 1; + lmovfeatname= (ps.thisword + ps.lthisword) - movfeatname; + ps.lthisword= slash - ps.thisword; + } else { + 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_d= &ui_plan_data.segments[segment_ix]; + + movfeat_ix= lstrpdbsearch(movfeatname, lmovfeatname, + "movfeat", segment_d->movfeats, + segment_d->n_movfeats, sizeof(*movfeat_d)); + movfeat_d= &segment_d->movfeats[movfeat_ix]; + + if (ps.remain) { + if (invert<0) badcmd(0,"off may not take movfeatpos"); + ps_neednumber(&ps, &posn, 0, movfeat_d->n_posns-1, "movfeatpos"); + } else { + if (invert>=0 && movfeat_d->n_posns > 1) badcmd(0,"movfeatpos needed"); + posn= 0; + } + + ps_neednoargs(&ps); + + fs= &state[segment_ix][movfeat_ix]; + fs->invert= invert; + fs->det= det; + fs->posn= posn; + + redraw(fs); + xlib_process(); + + return OOP_CONTINUE; +} + +int main(int argc, const char *const *argv) { + static XpmColorSymbol coloursymbols[2]= { + { (char*)"space", 0, 0 }, + { (char*)"mark", 0, 1 } + }; + + oop_read *rd; + const char *arg; + Pixmap bg_pixmap; + Pixmap mask; + XpmAttributes mattribs; + XWindowAttributes wattribs; + XColor background_colour; + int segment_ix, movfeat_ix, posn, invert, det, oor; + SegmovfeatState *fs; + const PlanSegmentData *segment_d; + const PlanSegmovfeatData *movfeat_d; + XGCValues gcv; + + d= XOpenDisplay(0); if (!d) die("XOpenDisplay failed"); + + if ((arg= *++argv)) { + char *ep; + if (arg[0]=='-') die("no options understood"); + + errno=0; w= strtoul(arg,&ep,0); + if (errno || ep==arg || *ep) die("bad windowid"); + } else { + w= XCreateSimpleWindow(d, DefaultRootWindow(d), + 0,0, ui_plan_data.xsz, ui_plan_data.ysz, + 0,0, 0); + if (w == None) diex("XCreateSimpleWindow", "initial"); + } + + XCALL( XGetWindowAttributes, 0, (d,w,&wattribs) ); + + XPMCALL( XpmCreatePixmapFromData, "background", + (d,w, (char**)ui_plan_data.background, &bg_pixmap,0,0) ); + + XCALL( XSetWindowBackgroundPixmap, 0, (d,w,bg_pixmap) ); + XCALL( XFreePixmap, "background", (d,bg_pixmap) ); + + XCALL( XAllocNamedColor, "background", + (d, wattribs.colormap, ui_plan_colour_off, + &background_colour, &background_colour) ); + + state= mmalloc(sizeof(*state) * ui_plan_data.n_segments); + for (segment_ix= 0, segment_d= ui_plan_data.segments; + segment_ix < ui_plan_data.n_segments; + segment_ix++, segment_d++) { + state[segment_ix]= fs= + mmalloc(sizeof(**state) * 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= fs->posn= 0; + fs->x= movfeat_d->x; + fs->y= movfeat_d->y; + fs->redraw_needed= 0; + mattribs.valuemask= XpmDepth | XpmColorSymbols; + mattribs.depth= 1; + mattribs.colorsymbols= coloursymbols; + mattribs.numsymbols= sizeof(coloursymbols) / sizeof(*coloursymbols); + XPMCALL( XpmCreatePixmapFromData, "mask", + (d,w, (char**)movfeat_d->off, &mask,0, &mattribs) ); + fs->width= mattribs.width; + fs->height= mattribs.height; + + gcv.clip_x_origin= fs->x; + gcv.clip_y_origin= fs->y; + gcv.clip_mask= mask; + gcv.foreground= background_colour.pixel; + fs->gc= XCreateGC(d,w, + GCClipXOrigin | GCClipYOrigin + | GCClipMask | GCForeground, + &gcv); + XCALL( XFreePixmap, "mask", (d,mask) ); + + fs->posns= mmalloc(sizeof(*fs->posns)*movfeat_d->n_posns); + for (posn= 0; posn < movfeat_d->n_posns; posn++) + for (invert=0; invert<2; invert++) + for (det=0; det<2; det++) { + XPMCALL( XpmCreatePixmapFromData, "main", + (d,w, + (char**)(movfeat_d->posns[posn].a[invert][det]), + &fs->posns[posn][invert][det], + 0,0) ); + } + } + } + + sys_events= oop_sys_new(); if (!sys_events) diee("oop_sys_new"); + events= oop_sys_source(sys_events); assert(events); + + rd= oop_rd_new_fd(events, 0, 0,0); + if (!rd) diee("oop_rd_new_fd"); + oor= oop_rd_read(rd, OOP_RD_STYLE_GETLINE, 1024, + stdin_ifok, 0, + stdin_iferr, 0); + if (oor) diee("oop_rd_read"); + + events->on_fd(events, 0, OOP_EXCEPTION, some_exception, 0); + events->on_fd(events, ConnectionNumber(d), OOP_READ, xlib_readable, 0); + events->on_fd(events, ConnectionNumber(d), OOP_EXCEPTION, some_exception, 0); + + XCALL( XSelectInput, 0, (d,w, ExposureMask) ); + + if (arg) { + XCALL( XClearArea, "initial", (d,w, 0,0,0,0, True) ); + } else { + XCALL( XMapWindow, 0, (d,w) ); + } + + xlib_process(); + + oop_sys_run(sys_events); + abort(); +} diff --git a/hostside/parseutils.c b/hostside/parseutils.c new file mode 100644 index 0000000..3c23454 --- /dev/null +++ b/hostside/parseutils.c @@ -0,0 +1,107 @@ +/**/ + +#include +#include +#include +#include + +#include "hostside.h" + +void badcmd(ParseState *ps, const char *fmt, ...) { + va_list al; + va_start(al,fmt); + vbadcmd(ps,fmt,al); + va_end(al); +} + +int ps_word(ParseState *ps) { + const char *space; + if (!ps->remain) return 0; + space= strchr(ps->remain, ' '); + ps->thisword= ps->remain; + if (space) { + ps->lthisword= space - ps->thisword; + ps->remain= space + 1; + } else { + ps->lthisword= strlen(ps->remain); + ps->remain= 0; + } + return 1; +} + +int ps_needword(ParseState *ps) { + if (!ps_word(ps)) { badcmd(ps,"too few args"); return 0; } + return 1; +} + +int ps_neednumber(ParseState *ps, long *r, long mi, long mx, const char *wh) { + char *ep; + long v; + + if (!ps_needword(ps)) return 0; + errno= 0; v= strtol(ps->thisword,&ep,0); + if (errno || ep != ps->thisword + ps->lthisword) { + badcmd(ps,"invalid number for %s",wh); + return 0; + } + if (v < mi || v > mx) { + badcmd(ps,"%s %ld out of range %ld..%ld",wh,v,mi,mx); + return 0; + } + *r= v; + return 1; +} + +int ps_neednoargs(ParseState *ps) { + if (ps->remain) { + badcmd(ps,"too many arguments"); + return 0; + } + return 1; +} + +int ps_needhextoend(ParseState *ps, Byte *d, int *remain_io) { + Byte *d_end; + char buf[3], *ep; + + d_end= d + *remain_io; + buf[2]= 0; + + if (!ps->remain) { badcmd(ps,"need hex data block"); return 0; } + for (;;) { + if (!ps_word(ps)) return 0; + while (ps->lthisword > 0) { + if (ps->lthisword & 1) { + badcmd(ps,"hex data block with odd number of digits in part"); + return 0; + } + buf[0]= ps->thisword[0]; + buf[1]= ps->thisword[1]; + if (d >= d_end) { badcmd(ps,"hex data block too long"); return 0; } + *d++= strtoul(buf,&ep,16); + if (*ep) { badcmd(ps,"invalid digit in hex data block"); return 0; } + ps->lthisword -= 2; + ps->thisword += 2; + } + } + + *remain_io= d_end - d; + return 1; +} + +int lstrstrcmp(const char *a, int la, const char *b) { + int lb, minl, r; + + lb= strlen(b); + minl= la < lb ? la : lb; + r= memcmp(a,b,minl); + if (r) return r; + + return (la < lb ? -1 : + la > lb ? 1 : 0); +} + +int thiswordstrcmp(ParseState *ps, const char *b) { + return lstrstrcmp(ps->thisword, ps->lthisword, b); +} + diff --git a/hostside/serialio.c b/hostside/serialio.c index 8232ebd..1075de9 100644 --- a/hostside/serialio.c +++ b/hostside/serialio.c @@ -3,14 +3,9 @@ */ #include -#include -#include #include -#include #include -#include -#include #include #include "common.h" @@ -18,38 +13,6 @@ int serial_fudge_delay= 0; int serial_fd= -1; -void vdie(const char *fmt, int ev, va_list al) { - vfprintf(stderr,fmt,al); - if (ev) fprintf(stderr,": %s",strerror(ev)); - fputc('\n',stderr); - exit(12); -} - -void die(const char *fmt, ...) - { va_list al; va_start(al,fmt); vdie(fmt,0,al); } -void diee(const char *fmt, ...) - { va_list al; va_start(al,fmt); vdie(fmt,errno,al); } -void diem(void) - { diee("malloc failed"); } - -void *mmalloc(size_t sz) { - void *p; - if (!sz) return 0; - p= malloc(sz); - if (!p) diem(); - return p; -} - -char *mstrdupl(const char *s, int l) { - char *p; - p= mmalloc(l+1); - memcpy(p,s,l); - p[l]= 0; - return p; -} - -char *mstrdup(const char *s) { return mstrdupl(s,strlen(s)); } - void serial_open(const char *device) { assert(serial_fd==-1); diff --git a/hostside/trackloc.c b/hostside/trackloc.c new file mode 100644 index 0000000..4a17714 --- /dev/null +++ b/hostside/trackloc.c @@ -0,0 +1,57 @@ +/* + */ + +#include + +#include "safety.h" + +const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc) { + State *s= &safety_state; + SegmentState *seg= &s->segments[tloc->segn]; + const SegmentInfo *segi= &info_segments[tloc->segn]; + + assert(seg->movposcomb < segi->n_poscombs); + return &segi->poscombs[seg->movposcomb]; +} + +const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc, + const SegPosCombInfo *pci, + unsigned far) { + return (tloc->backwards ^ far) ? &pci->backwards : &pci->forwards; +} + +long trackloc_remaininseg(const TrackLocation *tloc) { + const SegPosCombInfo *pci; + long segment_len; + + pci= trackloc_segposcomb(tloc); + segment_len= pci->dist; + assert(tloc->into <= segment_len); + return segment_len - tloc->into; +} + +void trackloc_further(TrackLocation *tloc, long *remain_io) { + const SegPosCombInfo *pci; + const SegmentLinkInfo *lnki_far; + long segment_remain; + + segment_remain= trackloc_remaininseg(tloc); + + if (*remain_io <= segment_remain) { + tloc->into += *remain_io; + *remain_io= 0; + } else { + pci= trackloc_segposcomb(tloc); + lnki_far= trackloc_segmentlink(tloc, pci, 1); + *remain_io -= segment_remain; + tloc->segn= lnki_far->next; + tloc->into= 0; + tloc->backwards ^= lnki_far->next_backwards; + } +} + +void trackloc_reverse(TrackLocation *tloc) { + tloc->into= trackloc_remaininseg(tloc); + tloc->backwards ^= 1; +} + diff --git a/hostside/utils.c b/hostside/utils.c index 4a17714..b6c4494 100644 --- a/hostside/utils.c +++ b/hostside/utils.c @@ -1,57 +1,41 @@ -/* - */ +/**/ -#include +#include +#include +#include +#include +#include -#include "safety.h" +#include "hostside.h" -const SegPosCombInfo *trackloc_segposcomb(const TrackLocation *tloc) { - State *s= &safety_state; - SegmentState *seg= &s->segments[tloc->segn]; - const SegmentInfo *segi= &info_segments[tloc->segn]; - - assert(seg->movposcomb < segi->n_poscombs); - return &segi->poscombs[seg->movposcomb]; -} - -const SegmentLinkInfo *trackloc_segmentlink(const TrackLocation *tloc, - const SegPosCombInfo *pci, - unsigned far) { - return (tloc->backwards ^ far) ? &pci->backwards : &pci->forwards; +void vdie(const char *fmt, int ev, va_list al) { + vfprintf(stderr,fmt,al); + if (ev) fprintf(stderr,": %s",strerror(ev)); + fputc('\n',stderr); + exit(12); } - -long trackloc_remaininseg(const TrackLocation *tloc) { - const SegPosCombInfo *pci; - long segment_len; - - pci= trackloc_segposcomb(tloc); - segment_len= pci->dist; - assert(tloc->into <= segment_len); - return segment_len - tloc->into; -} -void trackloc_further(TrackLocation *tloc, long *remain_io) { - const SegPosCombInfo *pci; - const SegmentLinkInfo *lnki_far; - long segment_remain; - - segment_remain= trackloc_remaininseg(tloc); - - if (*remain_io <= segment_remain) { - tloc->into += *remain_io; - *remain_io= 0; - } else { - pci= trackloc_segposcomb(tloc); - lnki_far= trackloc_segmentlink(tloc, pci, 1); - *remain_io -= segment_remain; - tloc->segn= lnki_far->next; - tloc->into= 0; - tloc->backwards ^= lnki_far->next_backwards; - } +void die(const char *fmt, ...) + { va_list al; va_start(al,fmt); vdie(fmt,0,al); } +void diee(const char *fmt, ...) + { va_list al; va_start(al,fmt); vdie(fmt,errno,al); } +void diem(void) + { diee("malloc failed"); } + +void *mmalloc(size_t sz) { + void *p; + if (!sz) return 0; + p= malloc(sz); + if (!p) diem(); + return p; } - -void trackloc_reverse(TrackLocation *tloc) { - tloc->into= trackloc_remaininseg(tloc); - tloc->backwards ^= 1; + +char *mstrdupl(const char *s, int l) { + char *p; + p= mmalloc(l+1); + memcpy(p,s,l); + p[l]= 0; + return p; } - + +char *mstrdup(const char *s) { return mstrdupl(s,strlen(s)); } diff --git a/layout/Makefile b/layout/Makefile index 3d64645..fc1c3b4 100644 --- a/layout/Makefile +++ b/layout/Makefile @@ -25,7 +25,8 @@ default: $(CPROGS) for-test-ui all: default lpages layers extras for-test-ui: ours.graph.c ours.redactgraph ours.raw.neato.ps \ ours.redacted.neato.ps ours.layout-data.o \ - ours.dgram-bot.segcmap subseg2display ui-plan-bot.ppm + ours.dgram-bot.segcmap subseg2display ui-plan-bot.ppm \ + ui-plan-bot.o layers: $(LAYERS) lpages: $(LPAGES) diff --git a/layout/plan-data-format.h b/layout/plan-data-format.h index c5f9081..e9ce03a 100644 --- a/layout/plan-data-format.h +++ b/layout/plan-data-format.h @@ -10,21 +10,27 @@ typedef struct { } PlanPixmapOnData; typedef struct { - unsigned long code; + const char *movfeatname; + int x, y; PlanPixmapDataRef off; int n_posns; const PlanPixmapOnData *posns; } PlanSegmovfeatData; typedef struct { + const char *segname; int n_movfeats; const PlanSegmovfeatData *movfeats; } PlanSegmentData; typedef struct { + int xsz, ysz; PlanPixmapDataRef background; int n_segments; const PlanSegmentData *segments; } PlanData; +extern const PlanData ui_plan_data; +extern const char ui_plan_colour_off[]; + #endif /*PLAN_DATA_FORMAT_H*/ diff --git a/layout/plan-to-gui-data b/layout/plan-to-gui-data index 391334f..9deb5d0 100755 --- a/layout/plan-to-gui-data +++ b/layout/plan-to-gui-data @@ -219,6 +219,7 @@ sub in_pixel () { } die "$pp $t{Movfeatpos}" unless defined $found; + $xpmname= $segname.'_'.$found; for ($bitno=0; $bitno < $movfeat_configbits{$xpmname}; $bitno++) { xpmdata_pixel("on","_${xpmname}_${bitno}", ((1<<$bitno) & $t{Movfeatpos}) ? $angstr : '-'); @@ -242,6 +243,7 @@ our (%cmap,%stylecmaps); # $cmap{$cmapname}{$pixchars}= $xpm_data_string_rhs # $cmap{$cmapname}{''}= [ string names for including in xpm ] # (after cmapdata_output_all) +$cmap{''}= {}; # fixed colours sub xpm_cmap ($$) { my ($style,$cmapname) = @_; @@ -252,6 +254,7 @@ sub xpm_cmap ($$) { sub xpm_cmap_entry ($$$) { my ($cmapname,$pixchars,$rhs) = @_; + die unless exists $cmap{$cmapname}; die if exists $cmap{$cmapname}{$pixchars}; $cmap{$cmapname}{$pixchars}= $rhs; } @@ -317,19 +320,22 @@ sub xpm_cmap_angular($$$@) { sub cmaps_define () { my ($inv,$ondet); - + xpm_cmap("background","background"); + xpm_cmap_rgbpermil('',qw(off 0 0 0)); xpm_cmap_rgbpermil("background",qw(- 100 100 100 - + 999 0 999 + + 999 0 990 : 75 75 75 - ! 999 0 999)); + ! 999 0 980)); xpm_cmap("off","off"); xpm_cmap_fixedbitmap("off",'*'); foreach $inv (('','i')) { foreach $ondet (qw(on det)) { xpm_cmap("on","${inv}${ondet}"); - xpm_cmap_rgbpermil("${inv}${ondet}",qw(- 300 300 300)); + xpm_cmap_rgbpermil("${inv}${ondet}", + ' ', qw(999 0 970), + qw(- 50 50 50)); } xpm_cmap_angular("${inv}on", !!$inv, 600, qw(0 0 0)); xpm_cmap_angular("${inv}det",!!$inv, 330, qw(1000 1000 1000)); @@ -342,7 +348,9 @@ cmaps_define(); sub cmapdata_output_all () { my ($cmapname, $stuff, $cmap, $sname, $pixchars); + foreach $cmapname (sort keys %cmap) { + next unless length $cmapname; $stuff= [ ]; $cmap= $cmap{$cmapname}; foreach $pixchars (sort keys %$cmap) { @@ -354,6 +362,16 @@ sub cmapdata_output_all () { } $cmap->{''}= $stuff; } + + my ($colour, $rhs); + $cmap= $cmap{''}; + foreach $colour (sort keys %$cmap) { + $rhs= $cmap->{$colour}; + $rhs =~ s/^c // or die "$colour $rhs ?"; + printf("const char ui_plan_colour_%s[]= \"%s\";\n", + $colour, $rhs) + or die $!; + } } sub xpmdata_output_all () { @@ -409,6 +427,7 @@ die "$pp ?" if $xp->{X}{Max} >= 642; sub plandata_output_all () { my ($i, @segnames, $segname); my (@movfeats, $movfeat, $dname, $xpmname, $n_posns, $code, $posn, $mfd); + my ($n_movfeats, $xpmd); for ($i=1; $i<@segnum_name; $i++) { next unless defined $segnum_name[$i]; push @segnames, $segnum_name[$i]; @@ -425,11 +444,11 @@ sub plandata_output_all () { if (!length $movfeat) { $xpmname= $segname; $n_posns= 1; - $code= 0; + $code= '0'; } else { $xpmname= "${segname}_${movfeat}"; $n_posns= $movfeat_configbits{$xpmname}; - $code= $movfeat_prefix{$xpmname}; + $code= "\"$movfeat\""; } printf("static const PlanPixmapOnData pon_%s[]= {\n", $xpmname) @@ -446,8 +465,11 @@ sub plandata_output_all () { } printf("};\n") or die $!; - $mfd .= sprintf(" { 0x%x, p_off_%s, %d, pon_%s },\n", - $code, $xpmname, $n_posns, $xpmname); + $xpmd= $xpmdata{'off'}{"_$xpmname"}; + $mfd .= sprintf(" { %s, %d,%d, p_off_%s, %d, pon_%s },\n", + $code, + $xpmd->{X}{Min}, $xpmd->{Y}{Min}, + $xpmname, $n_posns, $xpmname); } printf("static const PlanSegmovfeatData mf_%s[]= {\n". "%s". @@ -459,19 +481,20 @@ sub plandata_output_all () { or die $!; for $segname (@segnames) { if (exists $movfeats{$segname}) { - printf(" { %d, mf_%s },\n", - scalar(@{ $movfeats{$segname} })+1, - $segname) - or die $!; + $n_movfeats= scalar(@{ $movfeats{$segname} })+1; } else { - printf(" { 1, mf_%s },\n", - $segname) - or die $!; + $n_movfeats= 1; } + printf(" { \"%s\", %d, mf_%s },\n", + $segname, + $n_movfeats, + $segname) + or die $!; } printf("};\n") or die $!; printf("const PlanData ui_plan_data= {\n". + " $sz{X}, $sz{Y},\n". " p_background,\n". " %d, segments\n". "};\n",