typedef int ErrorCode;
#include "daemons.h"
+#include "dliste.h"
#include "../layout/plan-data-format.h"
#include <oop.h>
#include <oop-read.h>
+#define NSEGMENTS (ui_plan_data.n_segments)
+
typedef struct MaskState MaskState;
struct MaskState {
int x, y, width, height;
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 */
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;
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,
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;
}
badcmdreport_recsz= recsz;
ps.remain= data;
- is->on_input_line(&ps);
-
+ instream->on_input_line(&ps);
+ xlib_process();
return OOP_CONTINUE;
}
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) {
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;
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);
}
}
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",
/*---------- 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;
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) {
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 ----------*/
}
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; s<NSEGMENTS; s++)
+
+static void mx_clear_updated(void) {
+ int s;
+ FOR_S
+ state[s].flags.updated_tmp= 0;
+}
+
+static void mx_redraw_feat(int s, int f) {
+ SegState *ss= &state[s];
+ SegmovfeatState *fs= &ss->mfs[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); /* <train> */ 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;
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[]= {
{ "+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 }
};
static const InputStream sock_is= {
"multiplexer connection",
sock_eof,
- sock_input_line
+ sock_input_line,
+ sock_badcmd
};
/*---------- main program including much of the initialisation ----------*/
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);
&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;
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);