#include <string.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
#include <X11/Xlib.h>
#include <X11/xpm.h>
static SegmovfeatState **state, *states_head;
static Display *d;
static oop_source_sys *sys_events;
-static Window w;
+static Window w=None;
static int redraw_needed_count, expose_count;
static Pixmap bg_pixmap;
static unsigned long train_pixel, owned_pixel;
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,
return 0;
}
-static void *stdin_ifok(oop_source *evts, oop_read *cl_read,
+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) );
+}
+
+/*---------- input handling ----------*/
+
+typedef struct {
+ const char *name;
+ void (*on_eof)(void);
+ void (*on_input_line)(ParseState *ps);
+} InputStream;
+
+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 *emsg;
+ emsg= oop_rd_errmsg(stdin_read, evt, errnoval, OOP_RD_STYLE_GETLINE)
+ die("%s: %s", is->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 *cl_v) {
- const char *slash, *movfeatname;
+ const InputStream *is= is_v;
ParseState ps;
- int invert, det, trainown, 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);
+ if (evt == OOP_RD_EOF) {
+ is->on_eof();
+ abort;
+ }
+ if (evt != OOP_RD_OK) {
+ input_iferr(evts,cl_read,evt,errmsg,errnoval,data,recsz,cl_v);
+ return OOP_CONTINUE;
+ }
badcmdreport_data= data;
badcmdreport_recsz= recsz;
ps.remain= data;
ps_needword(&ps);
- if (!thiswordstrcmp(&ps,"off")) {
+ is->on_input_line(&ps);
+
+ return OOP_CONTINUE;
+}
+
+/*---------- stdin input handling ----------*/
+
+static void stdin_eof(void) { exit(0); }
+
+static void stdin_input_line() {
+ const char *slash, *movfeatname;
+ int invert, det, trainown, segment_ix, movfeat_ix, lmovfeatname;
+ long posn;
+ const PlanSegmentData *segment_d;
+ const PlanSegmovfeatData *movfeat_d;
+ SegmovfeatState *fs;
+
+ if (!thiswordstrcmp(ps,"off")) {
invert= -1;
trainown= 0;
det= 0;
} else {
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));
+ 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));
}
- ps_needword(&ps);
+ ps_needword(ps);
slash= memchr(ps.thisword, '/', ps.lthisword);
if (slash) {
movfeatname= slash + 1;
if (ps.remain) {
if (invert<0) badcmd(0,"off may not take movfeatpos");
- ps_neednumber(&ps, &posn, 0, movfeat_d->n_posns-1, "movfeatpos");
+ ps_neednumber(ps, &posn, 0, movfeat_d->n_posns-1, "movfeatpos");
} else {
if (invert>=0 && movfeat_d->n_posns > 1) {
posn= movfeat_d->n_posns;
}
}
- ps_neednoargs(&ps);
+ ps_neednoargs(ps);
fs= &state[segment_ix][movfeat_ix];
fs->invert= invert;
return OOP_CONTINUE;
}
-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 }
- };
+static const InputStream stdin_is= { "stdin", stdin_eof, stdin_input_line };
+
+/*---------- multiplexer client ----------*/
+
+static int sock_clientconnect(const char *node, const char *service) {
+ struct addrinfo *aires, *try, hints;
+ char niaddrbuf[256], niportbuf[64];
+ int r, sock;
- XpmAttributes mattribs;
- Pixmap pm;
+ memset(&hints,0,sizeof(hints));
+ hints.ai_family= PF_UNSPEC;
+ hints.ai_socktype= SOCK_STREAM;
+ r= getaddrinfo(node,service,&hints,&aires);
+ if (r) die("getaddrinfo node `%s' service `%s' failed: %s\n",
+ node,service,gai_strerror(r));
+
+ for (try=aires; try; try=try->ai_next) {
+ assert(try->ai_socktype == SOCK_STREAM);
+
+ r= getnameinfo(try->ai_addr, try->ai_addrlen,
+ niaddrbuf, sizeof(niaddrbuf),
+ niportbuf, sizeof(niportbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ assert(!r);
+
+ printf("trying %s,%s... ",niaddrbuf,niportbuf);
+ mflushstdout();
+
+ sock= socket(try->ai_family, SOCK_STREAM, try->ai_protocol);
+ if (sock<0) {
+ printf("couldn't create socket: %s\n",strerror(errno));
+ continue;
+ }
- 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;
+ r= connect(sock, try->ai_addr, try->ai_addrlen);
+ if (!r) {
+ printf("connected\n");
+ return sock;
+ }
- 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) );
+ printf("connect failed: %s\n",strerror(errno));
+ close(sock);
+ }
+ mflushstdout();
+ return -1;
}
+static void sock_eof(void) { die("EOF on multiplexer connection"); }
+
+static void
+
+static const char[]= {
+ { "?detect", si_detect },
+ { "?picio_out_polarity", si_polarity },
+ { "?movpos_*_feat", si_movpos },
+ { "?stastate", si_stastate },
+ { "?picio_out_on", si_pic_on },
+ { "?picio_out_off", si_pic_off },
+ { "=connected", si_connected },
+ { "=permission", 0 },
+ { 0 }
+}
+
+static void sock_input_line(ParseStatee *ps) {
+ if (thiswordstrcmp(ps,"=connected")) {
+ write sock ("select ?detect ?stastate ?picio_out_on ?picio_out_off"
+ " ?picio_out_polarity
+}
+
+static const InputStream sock_is= {
+ "multiplexer connection",
+ sock_eof,
+ sock_input_line
+};
+
+/*---------- main program including much of the initialisation ----------*/
+
int main(int argc, const char *const *argv) {
oop_read *rd;
const char *arg;
XpmAttributes mattribs;
XWindowAttributes wattribs;
- int segment_ix, movfeat_ix, posn, invert, det, oor;
+ int segment_ix, movfeat_ix, posn, invert, det, oor, sock=-1;
XGCValues gcv;
XColor colour;
SegmovfeatState *fs;
const PlanSegmentData *segment_d;
const PlanSegmovfeatData *movfeat_d;
+ char *ep;
- d= XOpenDisplay(0); if (!d) die("XOpenDisplay failed");
-
- if ((arg= *++argv)) {
- char *ep;
-
+ while ((arg= *++argv)) {
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);
+ } else if (arg[0]=='@') {
+ char *node, *comma;
+ const char *service;
+ node= (char*)arg+1;
+ comma= strchr(node,',');
+ if (comma) {
+ *comma= 0;
+ service= comma+1;
+ } else {
+ service= STR(TRAINMX_PORT);
+ }
+ sock= clientconnect(node,service);
+ if (sock<0) die("unable to connect to multiplexer");
+ if (comma) *comma= ',';
+ } else if (arg[0]=='-') {
+ die("invalid option(s)");
+ } else {
+ errno=0; w= strtoul(arg,&ep,0);
+ if (errno || ep==arg || *ep || w==None) die("bad windowid");
}
+ }
- if (arg[0]=='-') die("invalid option(s)");
+ d= XOpenDisplay(0); if (!d) die("XOpenDisplay failed");
- errno=0; w= strtoul(arg,&ep,0);
- if (errno || ep==arg || *ep) die("bad windowid");
- } else {
+ if (w==None) {
w= XCreateSimpleWindow(d, DefaultRootWindow(d),
0,0, ui_plan_data.xsz, ui_plan_data.ysz,
0,0, 0);
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 (sock==-1)
+ oor= oop_rd_read(rd, OOP_RD_STYLE_GETLINE, 1024,
+ stdin_ifok, (void*)&stdin_is,
+ stdin_iferr, (void*)&stdin_is);
+ else
+ oor= oop_rd_read(rd, OOP_RD_STYLE_GETLINE, 1024,
+ sock_ifok, (void*)&sock_is,
+ sock_iferr, (void*)&sock_is);
+
if (oor) diee("oop_rd_read");
events->on_fd(events, 0, OOP_EXCEPTION, some_exception, 0);