chiark / gitweb /
Merge and end branch-hostside-wip-2008-01-25 PROPERLY; cvs up -j branch-hostside...
[trains.git] / hostside / gui-plan.c
index 55fb48a7877fe8b7cca361d23412d81518e643f1..9e1f5922ba34ab74cb76076efef3bb9127f17695 100644 (file)
@@ -3,8 +3,8 @@
  * protocol on stdin:
  *  series of lines
  *   off <segname>[/[<movfeat>]
- *   [i]on <segname>[[/<movfeat>] <movpos>]
- *   [i]det <segname>[[/<movfeat>] <movpos>]
+ *   [t|f][i]on <segname>[[/<movfeat>] <movpos>]
+ *   [t|f][i]det <segname>[[/<movfeat>] <movpos>]
  */
 
 #include <stdlib.h>
 #include <X11/Xlib.h>
 #include <X11/xpm.h>
 
-#include "hostside.h"
+#include "common.h"
 #include "../layout/plan-data-format.h"
 
 #include <oop.h>
 #include <oop-read.h>
 
+typedef struct MaskState MaskState;
+struct MaskState {
+  int x, y, width, height;
+  GC gc;
+};
+
+typedef struct PosnState PosnState;
+struct PosnState {
+  int x, y, width, height;
+  Pixmap pm;
+  MaskState edge;
+};
+
 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*/];
+  int invert, det, trainown, posn, redraw_needed;
+  MaskState whole;
+  PosnState (*posns)[2/*i*/][2/*det*/];
+    /* posns[n_posns]=unknown if n_posns>1 */
 };
 
 oop_source *events;
+const char *progname= "gui-plan";
 
 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 Pixmap bg_pixmap;
+static unsigned long train_pixel, owned_pixel;
+
+static const char *badcmdreport_data;
+static size_t badcmdreport_recsz;
+
+void die_hook(void) { }
+void die_vprintf_hook(const char *fmt, va_list al) { }
 
 static void diex(const char *fn, const char *w) __attribute__((noreturn));
 static void diex(const char *fn, const char *w) {
@@ -63,7 +86,8 @@ static void diexpm(const char *fn, const char *w, int xpmst) {
   }while(0)
 
 void vbadcmd(ParseState *ps, const char *fmt, va_list al) {
-  fprintf(stderr,"gui-plan: incorrect input: ");
+  fprintf(stderr,"gui-plan: incorrect input: `%.*s': ",
+         (int)badcmdreport_recsz, badcmdreport_data);
   vfprintf(stderr,fmt,al);
   putc('\n',stderr);
   exit(8);
@@ -88,7 +112,7 @@ static int lstrpdbsearch(const char *str, int l,
   min= 0;
   maxe= n_items;
   for (;;) {
-    if (min >= maxe) badcmd(0,"unknown %s",what);
+    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;
@@ -124,9 +148,9 @@ static void xlib_expose(XExposeEvent *ev) {
        fs= fs->next) {
     if (fs->redraw_needed)
       continue;
-    if (!range_overlap(fs->x, fs->width,
+    if (!range_overlap(fs->whole.x, fs->whole.width,
                       ev->x, ev->width)) continue;
-    if (!range_overlap(fs->y, fs->height,
+    if (!range_overlap(fs->whole.y, fs->whole.height,
                       ev->y, ev->height)) continue;
     fs->redraw_needed= 1;
     redraw_needed_count++;
@@ -134,23 +158,34 @@ static void xlib_expose(XExposeEvent *ev) {
 }
 
 static void redraw(SegmovfeatState *fs) {
-  Pixmap src;
+  PosnState *src;
+  XGCValues gcv;
   
   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];
+  src= 0;
+  XCALL( XCopyArea, "redraw",
+        (d, bg_pixmap, w, fs->whole.gc,
+         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];
     XCALL( XCopyArea, "redraw",
-          (d, src, w, fs->gc,
-           0,0, fs->width, fs->height,
-           fs->x, fs->y) );
+          (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;
+    XCALL( XChangeGC, "train/own",
+          (d, src->edge.gc, GCForeground, &gcv) );
+    XCALL( XFillRectangle, "train/own",
+          (d,w, src->edge.gc,
+           src->edge.x, src->edge.y,
+           src->edge.width, src->edge.height) );
   }
 }
 
@@ -195,13 +230,21 @@ static void *xlib_readable(oop_source *evts, int fd,
   xlib_process();
   return OOP_CONTINUE;
 }
-                          
+
+static int thiswordeatonechar(ParseState *ps, int c) {
+  if (ps->thisword[0] == c) {
+    ps->thisword++; ps->lthisword--;
+    return 1;
+  }
+  return 0;
+}
+
 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;
+  int invert, det, trainown, segment_ix, movfeat_ix, lmovfeatname;
   long posn;
   const PlanSegmentData *segment_d;
   const PlanSegmovfeatData *movfeat_d;
@@ -213,16 +256,22 @@ static void *stdin_ifok(oop_source *evts, oop_read *cl_read,
   if (evt != OOP_RD_OK)
     return stdin_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,cl_v);
 
+  badcmdreport_data= data;
+  badcmdreport_recsz= recsz;
+
   ps.cl= 0;
   ps.remain= data;
   ps_needword(&ps);
 
   if (!thiswordstrcmp(&ps,"off")) {
     invert= -1;
+    trainown= 0;
     det= 0;
   } else {
-    invert= (ps.thisword[0]=='i');
-    if (invert) { ps.thisword++; ps.lthisword--; }
+    trainown=
+      thiswordeatonechar(&ps,'t') ? 2 :
+      thiswordeatonechar(&ps,'f') ? 1 : 0;
+    invert= thiswordeatonechar(&ps,'i');
     det= (!thiswordstrcmp(&ps,"on") ? 0 :
          !thiswordstrcmp(&ps,"det") ? 1 :
          (badcmd(&ps,"unknown command"),-1));
@@ -251,8 +300,11 @@ static void *stdin_ifok(oop_source *evts, oop_read *cl_read,
     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;
+    if (invert>=0 && movfeat_d->n_posns > 1) {
+      posn= movfeat_d->n_posns;
+    } else {
+      posn= 0;
+    }
   }
 
   ps_neednoargs(&ps);
@@ -260,6 +312,7 @@ static void *stdin_ifok(oop_source *evts, oop_read *cl_read,
   fs= &state[segment_ix][movfeat_ix];
   fs->invert= invert;
   fs->det= det;
+  fs->trainown= trainown;
   fs->posn= posn;
 
   redraw(fs);
@@ -268,30 +321,60 @@ static void *stdin_ifok(oop_source *evts, oop_read *cl_read,
   return OOP_CONTINUE;
 }
 
-int main(int argc, const char *const *argv) {
+static void loadmask(MaskState *out, const PlanPixmapDataRef *ppd,
+                    XGCValues *gcv, long gcv_mask) {
   static XpmColorSymbol coloursymbols[2]= {
     { (char*)"space", 0, 0 },
     { (char*)"mark",  0, 1 }
   };
   
+  XpmAttributes mattribs;
+  Pixmap pm;
+
+  out->x= ppd->x;
+  out->y= ppd->y;
+  mattribs.valuemask= XpmDepth | XpmColorSymbols;
+  mattribs.depth= 1;
+  mattribs.colorsymbols= coloursymbols;
+  mattribs.numsymbols= sizeof(coloursymbols) / sizeof(*coloursymbols);
+  XPMCALL( XpmCreatePixmapFromData, "mask",
+          (d,w, (char**)ppd->d, &pm,0, &mattribs) );
+  out->width= mattribs.width;
+  out->height= mattribs.height;
+
+  gcv->clip_x_origin= out->x;
+  gcv->clip_y_origin= out->y;
+  gcv->clip_mask= pm;
+  out->gc= XCreateGC(d,w,
+                    gcv_mask | GCClipXOrigin | GCClipYOrigin | GCClipMask,
+                    gcv);
+  XCALL( XFreePixmap, "mask", (d,pm) );
+}
+
+int main(int argc, const char *const *argv) {
   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;
+  XGCValues gcv;
+  XColor colour;
   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");
+
+    if (!strcmp(arg,"--sizes")) {
+      printf("%d\n%d\n", ui_plan_data.xsz, ui_plan_data.ysz);
+      if (ferror(stdout) || fflush(stdout)) diee("print stdout");
+      exit(0);
+    }
+
+    if (arg[0]=='-') die("invalid option(s)");
 
     errno=0; w= strtoul(arg,&ep,0);
     if (errno || ep==arg || *ep) die("bad windowid");
@@ -308,11 +391,16 @@ int main(int argc, const char *const *argv) {
           (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) );
+  XCALL( XAllocNamedColor, "white",
+         (d, wattribs.colormap, "#ffffff",
+          &colour, &colour) );
+  train_pixel= colour.pixel;
+
+  XCALL( XAllocNamedColor, "owned",
+         (d, wattribs.colormap, "#a0a0a0",
+          &colour, &colour) );
+  owned_pixel= colour.pixel;
 
   state= mmalloc(sizeof(*state) * ui_plan_data.n_segments);
   for (segment_ix= 0, segment_d= ui_plan_data.segments;
@@ -325,38 +413,41 @@ int main(int argc, const char *const *argv) {
         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->det= 0;
+      fs->trainown= 0;
+      fs->posn= movfeat_d->n_posns;
+      if (fs->posn==1) fs->posn= 0;
       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++)
+
+      loadmask(&fs->whole, &movfeat_d->mask, &gcv, 0);
+
+      fs->posns= mmalloc(sizeof(*fs->posns)*(fs->posn+1));
+      for (posn= 0; posn <= fs->posn; posn++)
        for (invert=0; invert<2; invert++)
          for (det=0; det<2; det++) {
+           PosnState *ps= &fs->posns[posn][invert][det];
+           const PlanPixmapOnData *ppod=
+             posn < movfeat_d->n_posns
+             ? &movfeat_d->posns[posn] : 0;
+           const PlanPixmapDataRef *ppdr= ppod
+             ? &ppod->on[invert][det]
+             : &movfeat_d->unknown[invert][det];
+           ps->x= ppdr->x;
+           ps->y= ppdr->y;
+           mattribs.valuemask= 0;
            XPMCALL( XpmCreatePixmapFromData, "main",
                     (d,w,
-                     (char**)(movfeat_d->posns[posn].a[invert][det]),
-                     &fs->posns[posn][invert][det],
-                     0,0) );
+                     (char**)ppdr->d,
+                     &ps->pm,
+                     0, &mattribs) );
+           ps->width= mattribs.width;
+           ps->height= mattribs.height;
+           if (ppod) {
+             loadmask(&ps->edge, &ppod->pedge, &gcv, 0);
+           } else {
+             ps->edge.x= -1;
+             ps->edge.gc= None;
+           }
          }
     }
   }