/* display and operate the GUI for INDI Server or Device. * * Overall GUI is one big RowColumn, devrc_w * First entry is a ScrolledText for the common message area, msg_w * Then follows for each Device: * One Form, gd->f_w * Light, TB to reveal/hide gd->grc_w, Name, label * One RowColumn for all Groups, gd->grc_w, containing for each group: * One Form (unless no group name, then group is not hideable) * TB to reveal/hide gg->prc_w, name * One RowColumn for each Group, gg->prc_w, containing for each property: * Form * Light, Name, label * One H RowColumn for each vector member, each of which contains * Form * Name label, value Label for read and/or TextField for write * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" #include "lilxml.h" #include "base64.h" #include "zlib.h" #define MSGR 10 /* number of rows in the message TF */ #define MSGC 60 /* number of columns in the message TF*/ #define LIGHTW 9 /* overall light width, pixels */ #define LIGHTH 9 /* overall light height, pixels */ #define WFLPOS 35 /* left writable field position */ #define WFRPOS 75 /* right writable field position */ #define INDENT 12 /* indent */ #define INDITWIDTH 500 /* overall width of INDI table, pixels*/ /* one RC per group to implement spacial grouping of its properties */ typedef struct { char group[MAXINDIGROUP]; /* name of this group, NULL if anon */ Widget prc_w; /* RC for properties in this group */ } IGUIGroup; /* GUI for one Device */ typedef struct { char device[MAXINDIDEVICE]; /* handy (same as in each property) */ Widget f_w; /* form for controls row */ Widget grc_w; /* RC for all groups for show/hide */ Widget lda_w; /* status light drawing area */ IGUIGroup **grp; /* malloced group ptr list */ int ngrp; /* number in grp[] */ INumberVectorProperty **nvpp; /* malloced number vector ptr list */ int nnvpp; /* number in nvpp[] */ ITextVectorProperty **tvpp; /* malloced text vector ptr list */ int ntvpp; /* number in tvpp[] */ ISwitchVectorProperty **svpp; /* malloced switch vector ptr list */ int nsvpp; /* number in svpp[] */ ILightVectorProperty **lvpp; /* malloced light vector ptr list */ int nlvpp; /* number in lvpp[] */ IBLOBVectorProperty **bvpp; /* malloced BLOB vector ptr list */ int nbvpp; /* number in bvpp[] */ } IGUIDevice; static Widget createLt (Widget p_w, char *name, Arg args[], int n); static void closeCB (Widget w, XtPointer client, XtPointer call); static void connectCB (Widget w, XtPointer client, XtPointer call); static void eraseCB (Widget w, XtPointer client, XtPointer call); static void devLtCB (Widget w, XtPointer client, XtPointer call); static void getLtColors (void); static void drawLt (Widget ltw, IPState s); static void drawDevLt (IGUIDevice *); static void devTogCB (Widget w, XtPointer client, XtPointer call); static void groupTogCB (Widget w, XtPointer client, XtPointer call); static void displayBLOBCB (Widget w, XtPointer client, XtPointer call); static void saveBLOBCB (Widget w, XtPointer client, XtPointer call); static void indi_disconnect(void); static void newBLOB_cb (Widget w, XtPointer client, XtPointer call); static void newnum_cb (Widget w, XtPointer client, XtPointer call); static void newnumdw_cb (Widget w, XtPointer client, XtPointer call); static void newnumup_cb (Widget w, XtPointer client, XtPointer call); static void newtext_cb (Widget w, XtPointer client, XtPointer call); static void newswitch_cb (Widget w, XtPointer client, XtPointer call); static void sendNumberVectorProperty (INumberVectorProperty *nvp); static void sendTextVectorProperty (ITextVectorProperty *tvp); static void sendSwitchVectorProperty (ISwitchVectorProperty *svp); static void sendEnableBLOBs (IBLOBVectorProperty *bvp, int on); static void traceon_cb (Widget w, XtPointer client, XtPointer call); static int indi_connect(void); static int launchCmd (XMLEle *root, char errmsg[]); static int defTextCmd (XMLEle *root, char errmsg[]); static int defNumberCmd (XMLEle *root, char errmsg[]); static int defSwitchCmd (XMLEle *root, char errmsg[]); static int defLightCmd (XMLEle *root, char errmsg[]); static int defBLOBCmd (XMLEle *root, char errmsg[]); static int setNumberCmd (XMLEle *root, char errmsg[]); static int setTextCmd (XMLEle *root, char errmsg[]); static int setSwitchCmd (XMLEle *root, char errmsg[]); static int setLightCmd (XMLEle *root, char errmsg[]); static int setBLOBCmd (XMLEle *root, char errmsg[]); static int doMessage (XMLEle *root, char errmsg[]); static void newGUIDevice (IGUIDevice *gd); static void newGUIGroup (IGUIDevice *gd, IGUIGroup *gg); static void newGUIWrNumber (INumber *np, Widget p_w, int lpos); static void freeTextVectorProperty (ITextVectorProperty *tvp); static void freeNumberVectorProperty (INumberVectorProperty *nvp); static void freeSwitchVectorProperty (ISwitchVectorProperty *svp); static void freeLightVectorProperty (ILightVectorProperty *lvp); static void freeBLOBVectorProperty (IBLOBVectorProperty *lvp); static int newGUINumberVector(INumberVectorProperty *nvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg); static int setGUINumberVector (XMLEle *root, INumberVectorProperty *nvp, IGUIDevice *gd, char errmsg[]); static int newGUITextVector(ITextVectorProperty *nvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg); static int setGUITextVector (XMLEle *root, ITextVectorProperty *nvp, IGUIDevice *gd, char errmsg[]); static int newGUISwitchVector(ISwitchVectorProperty *nvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg); static int setGUISwitchVector (XMLEle *root, ISwitchVectorProperty *svp, IGUIDevice *gd, char errmsg[]); static int newGUILightVector(ILightVectorProperty *nvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg); static int setGUILightVector (XMLEle *root, ILightVectorProperty *lvp, IGUIDevice *gd, char errmsg[]); static int newGUIBLOBVector(IBLOBVectorProperty *nbp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg); static int setGUIBLOBVector (XMLEle *root, IBLOBVectorProperty *bvp, IGUIDevice *gd, char errmsg[]); static int handleOneBLOB (XMLEle *root, IBLOB *bp, char errmsg[]); static void showMessage (XMLEle *root); static char *setTStampNow (char tstamp[MAXINDITSTAMP]); static void traceMessage (char *buf); static void logMessage (char *timestr, char *device, char *str); static IGUIDevice *findDev (char *device, int create, char errmsg[]); static IGUIGroup *findGroup (IGUIDevice *gd, char *group, int create, char errmsg[]); static INumber *findNumber (INumberVectorProperty *nvp, char *name); static INumberVectorProperty *findNumberProperty (IGUIDevice *gd, char *name, char whynot[]); static ITextVectorProperty *findTextProperty (IGUIDevice *gd, char *name, char whynot[]); static ISwitchVectorProperty *findSwitchProperty (IGUIDevice *gd, char *name, char whynot[]); static ILightVectorProperty *findLightProperty (IGUIDevice *gd, char *name, char whynot[]); static IBLOBVectorProperty *findBLOBProperty (IGUIDevice *gd, char *name, char whynot[]); static IText *findText (ITextVectorProperty *tvp, char *name); static ISwitch *findSwitch (ISwitchVectorProperty *svp, char *name); static ILight *findLight (ILightVectorProperty *lvp, char *name); static IBLOB *findBLOB (IBLOBVectorProperty *bvp, char *name); static int crackCharAtt (XMLEle *root, char *vp, int maxl, char *name, char errmsg[]); static int crackPerm (XMLEle *root, IPerm *p, char errmsg[]); static int crackPState (XMLEle *root, IPState *s, char errmsg[]); static int crackPString (char *str, IPState *s, char errmsg[]); static int crackSState (XMLEle *root, ISState *s, char *errmsg); static int crackRule (XMLEle *root, ISRule *r, char *errmsg); static INumberVectorProperty *crackdefNumberVector (XMLEle *root,char errmsg[]); static ITextVectorProperty *crackdefTextVector (XMLEle *root, char errmsg[]); static ISwitchVectorProperty *crackdefSwitchVector (XMLEle *root,char errmsg[]); static ILightVectorProperty *crackdefLightVector (XMLEle *root, char *errmsg); static IBLOBVectorProperty *crackdefBLOBVector (XMLEle *root, char *errmsg); static int crackDoubleAtt (XMLEle *root, double *dop, char *name, char *msg); static int numberFormat (char *buf, const char *format, double value); static void pushDisplay(void); static void xmlv1(void); static IGUIDevice *devices; /* list of devices */ static int ndevices; /* number devices[] */ static Pixmap opendev_pm; /* icon to open a device section */ static Pixmap closedev_pm; /* icon to close a device section */ static GC lt_gc; /* GC for drawing lights */ static Pixel tshad_p, bshad_p; /* top bottom shadow colors */ static char *state_r[] = { /* resource names of color states */ "INDIIdleColor", "INDIOkColor", "INDIBusyColor", "INDIAlertColor" }; #define NSTATES XtNumber(state_r) /* number of color states */ static Pixel state_p[NSTATES]; /* pixels to draw each state color */ static Widget indi_w; /* main shell */ static Widget connect_w; /* TB for connect/disconnect */ static Widget devrc_w; /* main RC for msg window and all dev */ static Widget msg_w; /* main message ST */ static Widget mttb_w; /* message trace TB */ static Widget mttf_w; /* message trace file name TF */ static int svrsocket = -1; /* socket to INDI device or server */ static FILE *swfp; /* unbuffered server write FILE for fprintf */ static XtInputId scbid; /* socket callback id */ static LilXML *lillp; /* XML parser context */ static char indicategory[] = "INDI Panel"; /* Save category */ /* bring up an INDI control panel */ void indi_manage() { FILE *fp; /* create shell if first time */ if (!indi_w) indi_createShell (); /* just for testing, build GUI from test file if found, else run real */ fp = fopen ("x.xml", "r"); if (fp) { LilXML *lp = newLilXML(); int c; if (!swfp) swfp = stdout; while ((c = fgetc(fp)) != EOF) { char msg[1024]; XMLEle *root = readXMLEle (lp, c, msg); if ((!root && msg[0]) || (root && launchCmd(root, msg) < 0)) { if (root) { prXMLEle (stdout, root, 0); xe_msg (0, "%s", msg); } xe_msg (0, "%s", msg); } if (root) delXMLEle (root); } delLilXML (lp); fclose (fp); } /* up we go */ XtPopup (indi_w, XtGrabNone); set_something (indi_w, XmNiconic, (XtArgVal)False); } /* return 0 if currently connected to an INDI server, else -1 */ int indi_connected() { return (svrsocket < 0 ? -1 : 0); } /* send a new set of values for the given Number device/property. * return 0 if ok else -1 if trouble with reason on why[] */ int indi_setNProperty (char *dev, char *prop, char *n[], double v[], int nv, char whynot[]) { INumberVectorProperty *nvp; IGUIDevice *gd; char buf[256]; int i; gd = findDev (dev, 0, whynot); if (!gd) return (-1); nvp = findNumberProperty (gd, prop, whynot); if (!nvp) return (-1); if (nvp->p != IP_WO && nvp->p != IP_RW) { sprintf (whynot, "Attempt to write to RO property %s.%s", nvp->device, nvp->name); return (-1); } for (i = 0; i < nv; i++) { INumber *np = findNumber (nvp, n[i]); if (np) { np->value = v[i]; numberFormat (buf, np->format, np->value); XmTextFieldSetString (np->aux1, buf); /* echo */ } else { sprintf (whynot, "INDI %s.%s.%s: not found", dev, prop, n[i]); return (-1); } } sendNumberVectorProperty (nvp); return (0); } /* send a new set of values for the given Text device/property. * return 0 if ok else -1 if trouble with reason on why[] */ int indi_setTProperty (char *dev, char *prop, char *n[], char *v[], int nv, char whynot[]) { ITextVectorProperty *tvp; IGUIDevice *gd; int i; gd = findDev (dev, 0, whynot); if (!gd) return (-1); tvp = findTextProperty (gd, prop, whynot); if (!tvp) return (-1); if (tvp->p != IP_WO && tvp->p != IP_RW) { sprintf (whynot, "Attempt to write to RO property %s.%s", tvp->device, tvp->name); return (-1); } for (i = 0; i < nv; i++) { IText *tp = findText (tvp, n[i]); if (tp) { tp->text = strcpy (XtRealloc (tp->text, strlen(v[i])+1), v[i]); XmTextFieldSetString (tp->aux1, tp->text); /* echo */ } else { sprintf (whynot, "INDI %s.%s.%s: not found", dev, prop, n[i]); return (-1); } } sendTextVectorProperty (tvp); return (0); } /* return the current Number vector for the given device/property, else NULL. * N.B. caller should not modify returned storage. */ INumberVectorProperty * indi_getNProperty (char *dev, char *prop) { IGUIDevice *gd = findDev (dev, 0, NULL); if (!gd) return (NULL); return (findNumberProperty (gd, prop, NULL)); } /* called when saveres is changing colors. * only thing we need to do is change the pixmaps in the dev TBs. */ void indi_newres() { /* plagerize from saveres's + and - */ sr_getDirPM (&opendev_pm, &closedev_pm); /* install in each device TB */ } /* create the main INDI shell, indi_w and main rc, devrc_w */ void indi_createShell () { Widget w, f_w, mf_w, sw_w, sep_w, pw_w; Arg args[20]; int n; /* create shell */ n = 0; XtSetArg (args[n], XmNallowShellResize, False); n++; XtSetArg (args[n], XmNcolormap, xe_cm); n++; XtSetArg (args[n], XmNtitle, "xephem INDI Control Panel"); n++; XtSetArg (args[n], XmNiconName, "INDI"); n++; XtSetArg (args[n], XmNdeleteResponse, XmUNMAP); n++; indi_w = XtCreatePopupShell ("INDI", topLevelShellWidgetClass, toplevel_w, args, n); setup_icon (indi_w); set_something (indi_w, XmNcolormap, (XtArgVal)xe_cm); sr_reg (indi_w, "XEphem*INDI.x", indicategory, 0); sr_reg (indi_w, "XEphem*INDI.y", indicategory, 0); sr_reg (indi_w, "XEphem*INDI.width", indicategory, 0); sr_reg (indi_w, "XEphem*INDI.height", indicategory, 0); /* everything in a form */ n = 0; XtSetArg (args[n], XmNmarginHeight, 10); n++; XtSetArg (args[n], XmNmarginWidth, 10); n++; XtSetArg (args[n], XmNverticalSpacing, 10); n++; f_w = XmCreateForm (indi_w, "INDIF", args, n); XtManageChild (f_w); /* controls across the bottom */ n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNverticalSpacing, 10); n++; mf_w = XmCreateForm (f_w, "IMF", args, n); XtManageChild (mf_w); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 5); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 20); n++; w = XmCreatePushButton (mf_w, "Close", args, n); XtAddCallback (w, XmNactivateCallback, closeCB, NULL); XtManageChild (w); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 30); n++; connect_w = XmCreateToggleButton (mf_w, "Connect", args, n); wtip (connect_w, "Connect and disconnect from the configured INDI server"); XtAddCallback (connect_w, XmNvalueChangedCallback, connectCB, NULL); XtManageChild (connect_w); sr_reg (connect_w, NULL, indicategory, 1); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, mf_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; sep_w = XmCreateSeparator (f_w, "S", args, n); XtManageChild (sep_w); /* top messages and bottom device area in a paned window */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, sep_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNsashHeight, 10); n++; XtSetArg (args[n], XmNspacing, 14); n++; pw_w = XmCreatePanedWindow (f_w, "IPW", args, n); XtManageChild (pw_w); /* message area in a form in top of pw */ n = 0; XtSetArg (args[n], XmNverticalSpacing, 10); n++; mf_w = XmCreateForm (pw_w, "IMF", args, n); XtManageChild (mf_w); /* erase */ n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 5); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 20); n++; w = XmCreatePushButton (mf_w, "Erase", args, n); wtip (w, "Clear message area"); XtAddCallback (w, XmNactivateCallback, eraseCB, NULL); XtManageChild (w); /* save TB and TF */ n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, 30); n++; mttb_w = XmCreateToggleButton (mf_w, "traceOn", args, n); set_xmstring (mttb_w, XmNlabelString, "Trace to "); XtAddCallback (mttb_w, XmNvalueChangedCallback, traceon_cb, NULL); sr_reg (mttb_w, NULL, indicategory, 0); wtip (mttb_w, "Whether to save messages to the file at right"); XtManageChild (mttb_w); n = 0; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, mttb_w); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 95); n++; mttf_w = XmCreateTextField (mf_w, "traceFile", args, n); defaultTextFN (mttf_w, 0, "inditrace.txt", NULL); sr_reg (mttf_w, NULL, indicategory, 0); wtip (mttf_w, "File in which to append messages, if on"); XtManageChild (mttf_w); /* message window */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNbottomWidget, mttf_w); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrows, MSGR); n++; XtSetArg (args[n], XmNcolumns, MSGC); n++; XtSetArg (args[n], XmNeditable, False); n++; XtSetArg (args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++; XtSetArg (args[n], XmNcursorPositionVisible, False); n++; XtSetArg (args[n], XmNblinkRate, 0); n++; msg_w = XmCreateScrolledText (mf_w, "INDIMsg", args, n); wtip (msg_w, "Messages from all INDI Devices"); XtManageChild (msg_w); /* main RC for all devices in a scrolled window */ n = 0; XtSetArg (args[n], XmNscrollingPolicy, XmAUTOMATIC); n++; sw_w = XmCreateScrolledWindow (pw_w, "ISW", args, n); XtManageChild (sw_w); n = 0; XtSetArg (args[n], XmNwidth, INDITWIDTH); n++; XtSetArg (args[n], XmNresizeWidth, False); n++; XtSetArg (args[n], XmNpacking, XmPACK_TIGHT); n++; XtSetArg (args[n], XmNorientation, XmVERTICAL); n++; XtSetArg (args[n], XmNisAligned, False); n++; XtSetArg (args[n], XmNadjustMargin, False); n++; devrc_w = XmCreateRowColumn (sw_w, "IMRC", args, n); XtManageChild (devrc_w); /* set up colors for lights and pixmaps for dev TB's */ getLtColors (); indi_newres(); /* start now if default Connect is On */ if (XmToggleButtonGetState(connect_w)) if (indi_connect() < 0) XmToggleButtonSetState (connect_w, False, True); } /* using indi_w as a reference, set up various colors needed for our * home-made light. */ static void getLtColors () { Pixel bg_p, fg_p, sel_p; Arg args[20]; int n; if (lt_gc) return; /* get motif shadow colors */ n = 0; XtSetArg (args[n], XmNbackground, &bg_p); n++; XtGetValues (indi_w, args, n); XmGetColors (DefaultScreenOfDisplay(XtD), xe_cm, bg_p, &fg_p, &tshad_p, &bshad_p, &sel_p); /* get INDI state colors */ for (n = 0; n < NSTATES; n++) get_color_resource (indi_w, state_r[n], &state_p[n]); /* make a GC to use for everything */ lt_gc = XCreateGC (XtD, RootWindow (XtD, DefaultScreen(XtD)), 0L, NULL); } /* called when user wants to close the INDI window */ static void closeCB (Widget w, XtPointer client, XtPointer call) { XtPopdown (indi_w); } static void connectCB (Widget w, XtPointer client, XtPointer call) { if (XmToggleButtonGetState(w)) { if (indi_connect() < 0) XmToggleButtonSetState (w, False, True); } else indi_disconnect(); } /* called to change whether to save the given BLOB given in call. * the actual saving is done whenever w is On by handleOneBLOB(). */ static void saveBLOBCB (Widget w, XtPointer client, XtPointer call) { IBLOBVectorProperty *bvp = (IBLOBVectorProperty *)client; sendEnableBLOBs (bvp, XmToggleButtonGetState(w)); } /* called to change whether to display the given BLOB given in call. * the actual displaying is done whenever w is On by handleOneBLOB(). */ static void displayBLOBCB (Widget w, XtPointer client, XtPointer call) { IBLOBVectorProperty *bvp = (IBLOBVectorProperty *)client; sendEnableBLOBs (bvp, XmToggleButtonGetState(w)); } static void eraseCB (Widget w, XtPointer client, XtPointer call) { XmTextSetString (msg_w, ""); } /* draw the given light (aka drawing area) corresponding to state s */ static void drawLt (Widget ltw, IPState s) { Display *dsp = XtDisplay(ltw); Window win = XtWindow(ltw); Pixel p = state_p[s]; Arg args[20]; Dimension w, h; int x, y; int n; if (!win) return; n = 0; XtSetArg(args[n], XmNwidth, (XtArgVal)&w); n++; XtSetArg(args[n], XmNheight, (XtArgVal)&h); n++; XtGetValues (ltw, args, n); x = (w - LIGHTW)/2; y = (h - LIGHTH)/2; XSetForeground (dsp, lt_gc, p); XFillRectangle (dsp, win, lt_gc, x+1, y+1, LIGHTW-2, LIGHTH-2); XSetForeground (dsp, lt_gc, BlackPixel(dsp, DefaultScreen(dsp))); XDrawRectangle (dsp, win, lt_gc, x, y, LIGHTW-1, LIGHTH-1); } /* process incoming INDI commands, disconnect if comm trouble */ static void inputCB (XtPointer client, int *fdp, XtInputId *idp) { static int trace = -1; char ibuf[32]; /* not so much user input lags */ char msg[1024]; int i, nr; /* set trace from env once */ if (trace < 0) { char *ie = getenv ("INDITRACE"); trace = (ie && atoi(ie) > 0); } /* read INDI command */ nr = read (*fdp, ibuf, sizeof(ibuf)); if (nr <= 0) { if (nr < 0) xe_msg (0, "INDI: input error: %s", syserrstr()); else xe_msg (0, "INDI: agent closed connection"); indi_disconnect(); return; } /* process each char */ for (i = 0; i < nr; i++) { XMLEle *root = readXMLEle (lillp, (int)ibuf[i], msg); if (root) { if (launchCmd (root, msg) < 0) { prXMLEle (stdout, root, 0); xe_msg (0, "%s", msg); } else if (trace > 0) prXMLEle (stdout, root, 0); delXMLEle (root); } else if (msg[0]) { xe_msg (0, "%s", msg); } } } /* try to contact an INDI server */ static int indi_connect() { char *host, *port; char msg[1024]; /* connect */ sc_gethost (&host, &port); svrsocket = mkconnection (host, atoi(port), msg); if (svrsocket < 0) { xe_msg (1, "INDI: Connection error to %s %s:\n%s", host, port, msg); swfp = stdout; /* tracing at least */ XtFree (host); XtFree (port); return (-1); } XtFree (host); XtFree (port); /* build a fresh parser context */ if (lillp) delLilXML (lillp); lillp = newLilXML(); /* ready for server */ scbid = XtAppAddInput (xe_app, svrsocket, (XtPointer)XtInputReadMask, inputCB, NULL); /* handy FILE for fprintf output messages */ swfp = fdopen (svrsocket, "w"); setbuf (swfp, NULL); /* get info */ xmlv1(); fprintf (swfp, "\n", INDIV); return (0); } /* disconnect from INDI server, fine if not already */ static void indi_disconnect() { int i, j; /* remove input */ if (scbid) { XtRemoveInput (scbid); scbid = 0; } if (svrsocket >= 0) { fclose (swfp); /* closes svrsocket too */ swfp = NULL; svrsocket = -1; } /* clean up lilxml */ if (lillp) { delLilXML (lillp); lillp = NULL; } /* destroy gui and device properties info */ if (devices) { for (i = 0; i < ndevices; i++) { IGUIDevice *gd = &devices[i]; XtDestroyWidget (gd->f_w); XtDestroyWidget (gd->grc_w); if (gd->ntvpp > 0) { for (j = 0; j < gd->ntvpp; j++) freeTextVectorProperty (gd->tvpp[j]); XtFree ((char *)gd->tvpp); } if (gd->nnvpp > 0) { for (j = 0; j < gd->nnvpp; j++) freeNumberVectorProperty (gd->nvpp[j]); XtFree ((char *)gd->nvpp); } if (gd->nsvpp > 0) { for (j = 0; j < gd->nsvpp; j++) freeSwitchVectorProperty (gd->svpp[j]); XtFree ((char *)gd->svpp); } if (gd->nlvpp > 0) { for (j = 0; j < gd->nlvpp; j++) freeLightVectorProperty (gd->lvpp[j]); XtFree ((char *)gd->lvpp); } if (gd->ngrp > 0) { for (j = 0; j < gd->ngrp; j++) XtFree ((char *)gd->grp[j]); XtFree ((char *)gd->grp); } } XtFree ((char *)devices); devices = NULL; ndevices = 0; } if (XmToggleButtonGetState(connect_w)) XmToggleButtonSetState (connect_w, False, False); } /* called to read the GUI and send the new values of an INumberVector. * client is pointer to INumberVector. * N.B. do not use w or call; we are used for TF and PB */ static void newnum_cb (Widget w, XtPointer client, XtPointer call) { INumberVectorProperty *nvp = (INumberVectorProperty *)client; int i; for (i = 0; i < nvp->nnp; i++) { INumber *np = &nvp->np[i]; char *txt = XmTextFieldGetString ((Widget)np->aux1); (void) f_scansexa (txt, &np->value); XtFree (txt); } sendNumberVectorProperty (nvp); } /* called by the up arrow button for a writeable INumber. * client is pointer to INumber. */ static void newnumup_cb (Widget w, XtPointer client, XtPointer call) { INumber *np = (INumber *) client; char *txt = XmTextFieldGetString ((Widget)np->aux1); double v = strtod (txt, NULL), stepv = v + np->step; char buf[128]; XtFree (txt); if (stepv < np->min || stepv > np->max) { xe_msg (1, "%s: value must be %g .. %g", np->name, np->min,np->max); } else { np->value = stepv; numberFormat (buf, np->format, np->value); XmTextFieldSetString (np->aux1, buf); } } /* called by the down arrow button for a writeable INumber. * client is pointer to INumber. */ static void newnumdw_cb (Widget w, XtPointer client, XtPointer call) { INumber *np = (INumber *) client; char *txt = XmTextFieldGetString ((Widget)np->aux1); double v = strtod (txt, NULL), stepv = v - np->step; char buf[128]; XtFree (txt); if (stepv < np->min || stepv > np->max) { xe_msg (1, "%s: value must be %g .. %g", np->name, np->min,np->max); } else { np->value = stepv; numberFormat (buf, np->format, np->value); XmTextFieldSetString (np->aux1, buf); } } /* called to read the GUI and send the new values of an ITextVector. * client is pointer to ITextVectorProperty. * N.B. do not use w or call, we are called from PB and TF */ static void newtext_cb (Widget w, XtPointer client, XtPointer call) { ITextVectorProperty *tvp = (ITextVectorProperty *)client; int i; for (i = 0; i < tvp->ntp; i++) { IText *tp = &tvp->tp[i]; char *txt = XmTextFieldGetString ((Widget)tp->aux1); tp->text = strcpy (XtRealloc (tp->text, strlen(txt)+1), txt); XtFree (txt); } sendTextVectorProperty (tvp); } /* called when the GUI TB for an ISwitch changes. * client is pointer to ISwitch. * send the new collection of its ISwitchVector. */ static void newswitch_cb (Widget w, XtPointer client, XtPointer call) { ISwitch *sp = (ISwitch *) client; int set = XmToggleButtonGetState(w); sp->s = set ? ISS_ON : ISS_OFF; /* don't send 1OFMANY going Off because some other will soon be On */ if (!set && sp->svp->r == ISR_1OFMANY) return; sendSwitchVectorProperty (sp->svp); } /* called when the GUI Set PB or filename TF for an IBLOBVectorProperty changes. * send the file named in the TF. * client is pointer to IBLOBVectoryProperty. * N.B. don't use client, this is used by PB and TF widgets */ static void newBLOB_cb (Widget w, XtPointer client, XtPointer call) { IBLOBVectorProperty *bvp = (IBLOBVectorProperty *) client; char tstamp[MAXINDITSTAMP]; int started = 0; int i; watch_cursor(1); for (i = 0; i < bvp->nbp; i++) { /* open the file */ IBLOB *bp = &bvp->bp[i]; char *fn = XmTextFieldGetString ((Widget)bp->aux2); FILE *fp = fopend (fn, NULL, "r"); unsigned char *file, *file64; int filen, file64n; char *format; char msg[1024]; int i; /* skip if didn't open ok */ if (!fp) { /* already told user */ XtFree (fn); break; } /* read file into memory */ if (fseek (fp, 0L, SEEK_END) < 0) { fclose (fp); XtFree (fn); xe_msg (1, "%s: can not seek", fn); break; } filen = ftell (fp); file = malloc (filen); if (!file) { fclose (fp); XtFree (fn); xe_msg (1, "Can not get %d bytes to read %s", filen, fn); break; } rewind (fp); if (fread (file, 1, filen, fp) != filen) { fclose (fp); XtFree (fn); free (file); xe_msg (1, "%s: can not read", filen); break; } fclose (fp); /* convery to base64 */ file64n = 4*filen/3+4; file64 = malloc (file64n); if (!file64) { XtFree (fn); free (file); xe_msg (1, "Can not get %d bytes for %s as base64", file64n, fn); break; } file64n = to64frombits (file64, file, filen); free (file); /* format is last suffix */ format = strrchr (fn, '.'); if (!format) format = ""; /* send */ sprintf (msg, "Sending %s", fn); logMessage (NULL, bvp->device, msg); if (!started) { xmlv1(); fprintf (swfp, "\n", bvp->device, bvp->name, setTStampNow (tstamp)); started = 1; } fprintf (swfp, " \n", bp->name, filen, format); for (i = 0; i < file64n; i += 72) fprintf (swfp, " %.72s\n", file64+i); fprintf (swfp, " \n"); free (file64); XtFree (fn); } if (started) { fprintf (swfp, "\n"); bvp->s = i < bvp->nbp ? IPS_ALERT : IPS_BUSY; drawDevLt (findDev (bvp->device, 0, NULL)); } watch_cursor(0); } /* send the given NumberVector to the connected INDI device */ static void sendNumberVectorProperty (INumberVectorProperty *nvp) { char tstamp[MAXINDITSTAMP]; int i; xmlv1(); fprintf(swfp,"\n", nvp->device, nvp->name, setTStampNow (tstamp)); for (i = 0; i < nvp->nnp; i++) { INumber *np = &nvp->np[i]; fprintf (swfp, " %.20g\n", np->name, np->value); } fprintf (swfp, "\n"); nvp->s = IPS_BUSY; drawDevLt (findDev (nvp->device, 0, NULL)); } /* send the given TextVector to the connected INDI device */ static void sendTextVectorProperty (ITextVectorProperty *tvp) { char tstamp[MAXINDITSTAMP]; int i; xmlv1(); fprintf (swfp, "\n", tvp->device, tvp->name, setTStampNow (tstamp)); for (i = 0; i < tvp->ntp; i++) { fprintf (swfp, " %s\n", tvp->tp[i].name, tvp->tp[i].text); } fprintf (swfp, "\n"); tvp->s = IPS_BUSY; drawDevLt (findDev (tvp->device, 0, NULL)); } /* send the given ISwitchVectorProperty to the INDI device and set our state to BUSY */ static void sendSwitchVectorProperty (ISwitchVectorProperty *svp) { char tstamp[MAXINDITSTAMP]; int i; xmlv1(); fprintf(swfp,"\n", svp->device, svp->name, setTStampNow (tstamp)); for (i = 0; i < svp->nsp; i++) { fprintf (swfp, " %s\n", svp->sp[i].name, svp->sp[i].s == ISS_ON ? "On" : "Off"); } fprintf (swfp, "\n"); svp->s = IPS_BUSY; drawDevLt (findDev (svp->device, 0, NULL)); } /* send a message to the server to enable or disable intermixed BLOB messages */ static void sendEnableBLOBs (IBLOBVectorProperty *bvp, int on) { xmlv1(); fprintf(swfp, "%s\n", bvp->device, bvp->name, on ? "Also" : "Never"); } /* implement the given INDI command received from the INDI server. * return 0 if ok else -1 with reason in errmsg[] */ static int launchCmd (XMLEle *root, char *errmsg) { static struct { char *cmdname; int (*fp)(XMLEle *root, char errmsg[]); } commands[] = { /* N.B. must be sorted by cmdname */ {"defBLOBVector", defBLOBCmd}, {"defLightVector", defLightCmd}, {"defNumberVector", defNumberCmd}, {"defSwitchVector", defSwitchCmd}, {"defTextVector", defTextCmd}, {"message", doMessage}, {"newNumberVector", setNumberCmd}, {"newSwitchVector", setSwitchCmd}, {"newTextVector", setTextCmd}, {"setBLOBVector", setBLOBCmd}, {"setLightVector", setLightCmd}, {"setNumberVector", setNumberCmd}, {"setSwitchVector", setSwitchCmd}, {"setTextVector", setTextCmd}, }; char *tag = tagXMLEle(root); int u = XtNumber(commands)-1; int l = 0; int diff = -1; int m = -1; /* binary search */ while (l <= u) { m = (l+u)/2; diff = strcmp (tag, commands[m].cmdname); if (diff == 0) break; if (diff < 0) u = m-1; else l = m+1; } if (diff == 0) return ((*commands[m].fp) (root, errmsg)); sprintf (errmsg, "INDI: unrecognized tag %s", tag); return (-1); } /* set a new value for a Number property */ static int setNumberCmd (XMLEle *root, char errmsg[]) { INumberVectorProperty *nvp; IGUIDevice *gd; char *name; gd = findDev (findXMLAttValu (root, "device"), 0, errmsg); if (!gd) return (-1); name = findXMLAttValu (root, "name"); nvp = findNumberProperty (gd, name, errmsg); if (!nvp) return (-1); if(crackCharAtt(root,nvp->timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (nvp->timestamp); return (setGUINumberVector(root, nvp, gd, errmsg)); } /* set a new value for a Text property */ static int setTextCmd (XMLEle *root, char errmsg[]) { ITextVectorProperty *tvp; IGUIDevice *gd; char *name; gd = findDev (findXMLAttValu (root, "device"), 0, errmsg); if (!gd) return (-1); name = findXMLAttValu (root, "name"); tvp = findTextProperty (gd, name, errmsg); if (!tvp) return (-1); if(crackCharAtt(root,tvp->timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (tvp->timestamp); return (setGUITextVector(root, tvp, gd, errmsg)); } /* set a new value for a Switch property */ static int setSwitchCmd (XMLEle *root, char errmsg[]) { ISwitchVectorProperty *svp; IGUIDevice *gd; char *name; gd = findDev (findXMLAttValu (root, "device"), 0, errmsg); if (!gd) return (-1); name = findXMLAttValu (root, "name"); svp = findSwitchProperty (gd, name, errmsg); if (!svp) return (-1); if(crackCharAtt(root,svp->timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (svp->timestamp); return (setGUISwitchVector(root, svp, gd, errmsg)); } /* set a new value for a Light property */ static int setLightCmd (XMLEle *root, char errmsg[]) { ILightVectorProperty *lvp; IGUIDevice *gd; char *name; gd = findDev (findXMLAttValu (root, "device"), 0, errmsg); if (!gd) return (-1); name = findXMLAttValu (root, "name"); lvp = findLightProperty (gd, name, errmsg); if (!lvp) return (-1); if(crackCharAtt(root,lvp->timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (lvp->timestamp); return (setGUILightVector(root, lvp, gd, errmsg)); } /* set a new value for a BLOB property */ static int setBLOBCmd (XMLEle *root, char errmsg[]) { IBLOBVectorProperty *bvp; IGUIDevice *gd; char *name; gd = findDev (findXMLAttValu (root, "device"), 0, errmsg); if (!gd) return (-1); name = findXMLAttValu (root, "name"); bvp = findBLOBProperty (gd, name, errmsg); if (!bvp) return (-1); if(crackCharAtt(root,bvp->timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (bvp->timestamp); return (setGUIBLOBVector(root, bvp, gd, errmsg)); } /* display a message from the server */ static int doMessage (XMLEle *root, char errmsg[]) { showMessage (root); return (0); } /* define a new Text property, ignore if already known */ static int defTextCmd (XMLEle *root, char errmsg[]) { ITextVectorProperty *tvp; IGUIDevice *gd; IGUIGroup *gg; char buf[256], *bp = buf; int i; /* crack new text vector */ if ((tvp = crackdefTextVector (root, errmsg)) == NULL) return (-1); /* ignore if already known */ gd = findDev (tvp->device, 0, errmsg); if (gd && findTextProperty (gd, tvp->name, errmsg)) { freeTextVectorProperty (tvp); return (0); } /* record */ bp += sprintf (bp, "New text vector %s[", tvp->name); for (i = 0; i < tvp->ntp; i++) { bp += sprintf (bp, "%s", tvp->tp[i].name); if (i < tvp->ntp-1) bp += sprintf (bp, ","); if (bp > &buf[sizeof(buf)] - MAXINDINAME) { bp += sprintf (bp, "..."); break; } } bp += sprintf (bp, "]"); logMessage (tvp->timestamp, tvp->device, buf); /* add to existing or create new device, group and property */ if (!gd) gd = findDev (tvp->device, 1, errmsg); /* can't fail w/create */ gg = findGroup (gd, tvp->group, 1, errmsg); /* can't fail w/create */ return (newGUITextVector(tvp, gd, gg, errmsg)); } /* define a new Number property, ignore if already known */ static int defNumberCmd (XMLEle *root, char errmsg[]) { INumberVectorProperty *nvp; IGUIDevice *gd; IGUIGroup *gg; char buf[256], *bp = buf; int i; /* crack new number vector */ if ((nvp = crackdefNumberVector (root, errmsg)) == NULL) return (-1); /* ignore if already known */ gd = findDev (nvp->device, 0, errmsg); if (gd && findNumberProperty (gd, nvp->name, errmsg)) { freeNumberVectorProperty (nvp); return (0); } /* record */ bp += sprintf (bp, "New number vector %s[", nvp->name); for (i = 0; i < nvp->nnp; i++) { bp += sprintf (bp, "%s", nvp->np[i].name); if (i < nvp->nnp-1) bp += sprintf (bp, ","); if (bp > &buf[sizeof(buf)] - MAXINDINAME) { bp += sprintf (bp, "..."); break; } } bp += sprintf (bp, "]"); logMessage (nvp->timestamp, nvp->device, buf); /* add to existing or create new device, group and property */ if (!gd) gd = findDev (nvp->device, 1, errmsg); /* can't fail w/create */ gg = findGroup (gd, nvp->group, 1, errmsg); /* can't fail w/create */ return (newGUINumberVector(nvp, gd, gg, errmsg)); } /* define a new Switch property, ignore if already known */ static int defSwitchCmd (XMLEle *root, char errmsg[]) { ISwitchVectorProperty *svp; IGUIDevice *gd; IGUIGroup *gg; char buf[256], *bp = buf; int i; /* crack new Switch vector */ if ((svp = crackdefSwitchVector (root, errmsg)) == NULL) return (-1); /* ignore if already known */ gd = findDev (svp->device, 0, errmsg); if (gd && findSwitchProperty (gd, svp->name, errmsg)) { freeSwitchVectorProperty (svp); return (0); } /* record */ bp += sprintf (bp, "New switch vector %s[", svp->name); for (i = 0; i < svp->nsp; i++) { bp += sprintf (bp, "%s", svp->sp[i].name); if (i < svp->nsp-1) bp += sprintf (bp, ","); if (bp > &buf[sizeof(buf)] - MAXINDINAME) { bp += sprintf (bp, "..."); break; } } bp += sprintf (bp, "]"); logMessage (svp->timestamp, svp->device, buf); /* add to existing or create new device, group and property */ if (!gd) gd = findDev (svp->device, 1, errmsg); /* can't fail w/create */ gg = findGroup (gd, svp->group, 1, errmsg); /* can't fail w/create */ return (newGUISwitchVector(svp, gd, gg, errmsg)); } /* define a new Lights property, ignore if already known */ static int defLightCmd (XMLEle *root, char errmsg[]) { ILightVectorProperty *lvp; IGUIDevice *gd; IGUIGroup *gg; char buf[256], *bp = buf; int i; /* crack new Light vector */ if ((lvp = crackdefLightVector (root, errmsg)) == NULL) return (-1); /* ignore if already known */ gd = findDev (lvp->device, 0, errmsg); if (gd && findLightProperty (gd, lvp->name, errmsg)) { freeLightVectorProperty (lvp); return (0); } /* record */ bp += sprintf (bp, "New light vector %s[", lvp->name); for (i = 0; i < lvp->nlp; i++) { bp += sprintf (bp, "%s", lvp->lp[i].name); if (i < lvp->nlp-1) bp += sprintf (bp, ","); if (bp > &buf[sizeof(buf)] - MAXINDINAME) { bp += sprintf (bp, "..."); break; } } bp += sprintf (bp, "]"); logMessage (lvp->timestamp, lvp->device, buf); /* add to existing or create new device, group and property */ if (!gd) gd = findDev (lvp->device, 1, errmsg); /* can't fail w/create */ gg = findGroup (gd, lvp->group, 1, errmsg); /* can't fail w/create */ return (newGUILightVector(lvp, gd, gg, errmsg)); } /* define a new BLOB property, ignore if already known */ static int defBLOBCmd (XMLEle *root, char errmsg[]) { IBLOBVectorProperty *bvp; IGUIDevice *gd; IGUIGroup *gg; char buf[256], *bp = buf; int i; /* crack new BLOB vector */ if ((bvp = crackdefBLOBVector (root, errmsg)) == NULL) return (-1); /* ignore if already known */ gd = findDev (bvp->device, 0, errmsg); if (gd && findBLOBProperty (gd, bvp->name, errmsg)) { freeBLOBVectorProperty (bvp); return (0); } /* record */ bp += sprintf (bp, "New BLOB vector %s[", bvp->name); for (i = 0; i < bvp->nbp; i++) { bp += sprintf (bp, "%s", bvp->bp[i].name); if (i < bvp->nbp-1) bp += sprintf (bp, ","); if (bp > &buf[sizeof(buf)] - MAXINDINAME) { bp += sprintf (bp, "..."); break; } } bp += sprintf (bp, "]"); logMessage (bvp->timestamp, bvp->device, buf); /* add to existing or create new device, group and property */ if (!gd) gd = findDev (bvp->device, 1, errmsg); /* can't fail w/create */ gg = findGroup (gd, bvp->group, 1, errmsg); /* can't fail w/create */ return (newGUIBLOBVector(bvp, gd, gg, errmsg)); } /* free the given TVP and any supporting memory */ static void freeTextVectorProperty (ITextVectorProperty *tvp) { if (tvp->tp) { int i; for (i = 0; i < tvp->ntp; i++) if (tvp->tp[i].text) XtFree (tvp->tp[i].text); XtFree ((char *) tvp->tp); } XtFree ((char *) tvp); } /* free the given NVP and any supporting memory */ static void freeNumberVectorProperty (INumberVectorProperty *ivp) { if (ivp->np) XtFree ((char *) ivp->np); XtFree ((char *) ivp); } /* free the given SVP and any supporting memory */ static void freeSwitchVectorProperty (ISwitchVectorProperty *svp) { if (svp->sp) XtFree ((char *) svp->sp); XtFree ((char *) svp); } /* free the given LVP and any supporting memory */ static void freeLightVectorProperty (ILightVectorProperty *lvp) { if (lvp->lp) XtFree ((char *) lvp->lp); XtFree ((char *) lvp); } /* free the given BVP and any supporting memory */ static void freeBLOBVectorProperty (IBLOBVectorProperty *bvp) { if (bvp->bp) { int i; for (i = 0; i < bvp->nbp; i++) if (bvp->bp[i].blob) XtFree (bvp->bp[i].blob); XtFree ((char *) bvp->bp); } XtFree ((char *) bvp); } /* make a da for use as state indicator. * when exposed, it draws an indicator using its foreground color */ static Widget createLt (Widget p_w, char *name, Arg args[], int n) { Widget da_w; XtSetArg (args[n], XmNwidth, LIGHTW); n++; XtSetArg (args[n], XmNheight, LIGHTH); n++; XtSetArg (args[n], XmNresizePolicy, XmRESIZE_NONE); n++; da_w = XmCreateDrawingArea (p_w, name, args, n); return (da_w); } /* called to expose or hide all the properties for a device. * client is index into devices[] */ static void devTogCB (Widget w, XtPointer client, XtPointer call) { IGUIDevice *gd = &devices[(long int)client]; if (XmToggleButtonGetState(w)) { XtManageChild (gd->grc_w); drawDevLt (gd); } else XtUnmanageChild (gd->grc_w); } /* called to expose or hide the properties in a group. * client is IGUIGroup to un/manage. */ static void groupTogCB (Widget w, XtPointer client, XtPointer call) { IGUIGroup *gg = (IGUIGroup *) client; if (XmToggleButtonGetState(w)) XtManageChild (gg->prc_w); else XtUnmanageChild (gg->prc_w); } /* called when exposing a device status light. * client is index into devices[] */ static void devLtCB (Widget w, XtPointer client, XtPointer call) { IGUIDevice *gd = &devices[(long int)client]; drawDevLt (gd); } /* create a new GUIDevice */ static void newGUIDevice (IGUIDevice *gd) { Widget w; Arg args[20]; int n; n = 0; gd->f_w = XmCreateForm (devrc_w, "Device", args, n); XtManageChild (gd->f_w); /* worst-property light */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; gd->lda_w = createLt (gd->f_w, "DL", args, n); XtAddCallback (gd->lda_w, XmNexposeCallback, devLtCB, (XtPointer)(gd-devices)); XtManageChild (gd->lda_w); /* open/close TB */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, INDENT); n++; XtSetArg (args[n], XmNlabelType, XmPIXMAP); n++; XtSetArg (args[n], XmNlabelPixmap, opendev_pm); n++; XtSetArg (args[n], XmNselectPixmap, closedev_pm); n++; XtSetArg (args[n], XmNindicatorOn, False); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; w = XmCreateToggleButton (gd->f_w, "DTB", args, n); XtAddCallback (w, XmNvalueChangedCallback, devTogCB, (XtPointer)(gd-devices)); XtManageChild (w); /* device name */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, w); n++; w = XmCreateLabel (gd->f_w, "DL", args, n); set_xmstring (w, XmNlabelString, gd->device); XtManageChild (w); /* RC for groups -- leave unmanaged initially */ n = 0; gd->grc_w = XmCreateRowColumn (devrc_w, "DGRC", args, n); } /* create a new GUIGroup within the GUIDevice. * if the group has a name, give it a control to show/hide and set it initially * hidden, otherwise it is always visible. */ static void newGUIGroup (IGUIDevice *gd, IGUIGroup *gg) { int hideable = gg->group[0] != '\0'; Widget f_w = (Widget)0; /* avoid compiler warning */ Widget w; Arg args[20]; int n; if (hideable) { n = 0; f_w = XmCreateForm (gd->grc_w, "Group", args, n); XtManageChild (f_w); } n = 0; gg->prc_w = XmCreateRowColumn (gd->grc_w, "PRC", args, n); if (!hideable) XtManageChild (gg->prc_w); if (hideable) { /* open/close TB */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, INDENT); n++; XtSetArg (args[n], XmNlabelType, XmPIXMAP); n++; XtSetArg (args[n], XmNlabelPixmap, opendev_pm); n++; XtSetArg (args[n], XmNselectPixmap, closedev_pm); n++; XtSetArg (args[n], XmNindicatorOn, False); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; w = XmCreateToggleButton (f_w, "GTB", args, n); XtAddCallback (w, XmNvalueChangedCallback, groupTogCB, gg); XtManageChild (w); /* device name */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNleftWidget, w); n++; w = XmCreateLabel (f_w, "GL", args, n); set_xmstring (w,XmNlabelString, gg->group); XtManageChild (w); } } /* add the given number onto the given device list in the given group and * build its GUI. * return 0 if ok else -1 with reason in errmsg[] */ static int newGUINumberVector(INumberVectorProperty *nvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg) { Widget nrc_w, set_w; Widget f_w, hrc_w, l_w; Arg args[20]; int i; int n; /* add nvp to device list */ gd->nvpp = (INumberVectorProperty **) XtRealloc ((char *)(gd->nvpp), (gd->nnvpp+1) * sizeof(INumberVectorProperty*)); gd->nvpp[gd->nnvpp++] = nvp; /* master rc, works faster to delay managing until finished */ n = 0; XtSetArg (args[n], XmNspacing, 0); n++; nrc_w = XmCreateRowColumn (gg->prc_w, "NRC", args, n); /* property rc */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNmarginWidth, INDENT); n++; XtSetArg (args[n], XmNspacing, 3); n++; hrc_w = XmCreateRowColumn (nrc_w, "NPF", args, n); XtManageChild (hrc_w); /* property's status light */ n = 0; nvp->aux = (void *) createLt (hrc_w, "NLT", args, n); XtAddCallback ((Widget)nvp->aux, XmNexposeCallback, devLtCB, (XtPointer)(gd-devices)); XtManageChild ((Widget)nvp->aux); /* set PB , unless RO */ if (nvp->p != IP_RO) { n = 0; set_w = XmCreatePushButton (hrc_w, "Set", args, n); XtAddCallback (set_w, XmNactivateCallback, newnum_cb, nvp); XtManageChild (set_w); } /* label */ n = 0; l_w = XmCreateLabel (hrc_w, "NL", args, n); set_xmstring (l_w, XmNlabelString, (char *) nvp->label); XtManageChild (l_w); /* one row for each vector element */ for (i = 0; i < nvp->nnp; i++) { INumber *np = &nvp->np[i]; char buf[128]; numberFormat (buf, np->format, np->value); n = 0; f_w = XmCreateForm (nrc_w, "NF", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, 2*INDENT); n++; l_w = XmCreateLabel (f_w, "NL", args, n); set_xmstring (l_w, XmNlabelString, (char *) np->label); XtManageChild (l_w); switch (nvp->p) { case IP_RO: n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 100); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++; np->aux0 = (void *) XmCreateLabel (f_w, "NRO", args, n); set_xmstring ((Widget) np->aux0, XmNlabelString, buf); XtManageChild ((Widget) np->aux0); break; case IP_RW: n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, WFLPOS); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, WFRPOS-2); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++; XtSetArg (args[n], XmNrecomputeSize, False); n++; np->aux0 = (void *) XmCreateLabel (f_w, "NRO", args, n); set_xmstring ((Widget) np->aux0, XmNlabelString, buf); XtManageChild ((Widget) np->aux0); /* FALLTHRU */ case IP_WO: newGUIWrNumber (np, f_w, WFRPOS); XmTextFieldSetString ((Widget)np->aux1, buf); break; } } drawDevLt (gd); XtManageChild (nrc_w); pushDisplay(); return (0); } /* build a gui for the writable portion of an INumber */ static void newGUIWrNumber (INumber *np, Widget p_w, int lpos) { Arg args[20]; int n; if (np->step) { Widget up_w, dw_w; n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 100); n++; XtSetArg (args[n], XmNarrowDirection, XmARROW_UP); n++; up_w = XmCreateArrowButton (p_w, "IDW", args, n); XtAddCallback (up_w, XmNactivateCallback, newnumup_cb, np); XtManageChild (up_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, up_w); n++; XtSetArg (args[n], XmNarrowDirection, XmARROW_DOWN); n++; dw_w = XmCreateArrowButton (p_w, "IUP", args, n); XtAddCallback (dw_w, XmNactivateCallback, newnumdw_cb, np); XtManageChild (dw_w); n = 0; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, dw_w); n++; } else { n = 0; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 100); n++; } XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, lpos); n++; XtSetArg (args[n], XmNcolumns, 10); n++; np->aux1 = (void *) XmCreateTextField (p_w, "NWO", args, n); XtAddCallback ((Widget) np->aux1,XmNactivateCallback,newnum_cb,np->nvp); XtManageChild ((Widget) np->aux1); } /* capture and display state, msg and each value(s) for a set/newNumberVector */ static int setGUINumberVector (XMLEle *root, INumberVectorProperty *nvp, IGUIDevice *gd, char errmsg[]) { int new = !strcmp (tagXMLEle(root), "newNumberVector"); XMLEle *ep; char buf[64]; for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { if (strcmp(tagXMLEle(ep), "oneNumber") == 0) { char *name = findXMLAttValu (ep, "name"); INumber *np = findNumber (nvp, name); if (np) { np->value = strtod (pcdataXMLEle(ep), NULL); numberFormat (buf, np->format, np->value); if (np->nvp->p == IP_WO || (np->nvp->p == IP_RW && new)) XmTextFieldSetString ((Widget)np->aux1, buf); else set_xmstring ((Widget)np->aux0, XmNlabelString, buf); } else { sprintf (errmsg, "INDI: set %s.%s.%s not found", gd->device, nvp->name, name); return (-1); } } } showMessage (root); if (crackPState (root, &nvp->s, errmsg) == 0) drawDevLt (gd); return (0); } /* add the given text onto the given device list in the given group and * build its GUI. * return 0 if ok else -1 with reason in errmsg[] */ static int newGUITextVector(ITextVectorProperty *tvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg) { Widget set_w, trc_w; Widget hrc_w, f_w, l_w; Arg args[20]; int i; int n; /* add tvp to device list */ gd->tvpp = (ITextVectorProperty **) XtRealloc ((char *)(gd->tvpp), (gd->ntvpp+1) * sizeof(ITextVectorProperty*)); gd->tvpp[gd->ntvpp++] = tvp; /* master rc, works faster to delay managing until finished */ n = 0; XtSetArg (args[n], XmNspacing, 0); n++; trc_w = XmCreateRowColumn (gg->prc_w, "TRC", args, n); /* property rc */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNmarginWidth, INDENT); n++; XtSetArg (args[n], XmNspacing, 3); n++; hrc_w = XmCreateRowColumn (trc_w, "TPF", args, n); XtManageChild (hrc_w); /* property's status light */ n = 0; tvp->aux = (void *) createLt (hrc_w, "TLT", args, n); XtAddCallback ((Widget)tvp->aux, XmNexposeCallback, devLtCB, (XtPointer)(gd-devices)); XtManageChild ((Widget)tvp->aux); /* set PB, unless RO */ if (tvp->p != IP_RO) { n = 0; set_w = XmCreatePushButton (hrc_w, "Set", args, n); XtAddCallback (set_w, XmNactivateCallback, newtext_cb, tvp); XtManageChild (set_w); } /* label */ n = 0; l_w = XmCreateLabel (hrc_w, "TL", args, n); set_xmstring (l_w, XmNlabelString, (char *) tvp->label); XtManageChild (l_w); /* one row for each vector element */ for (i = 0; i < tvp->ntp; i++) { IText *tp = &tvp->tp[i]; n = 0; f_w = XmCreateForm (trc_w, "TF", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, 2*INDENT); n++; l_w = XmCreateLabel (f_w, "TL", args, n); set_xmstring (l_w, XmNlabelString, (char *) tp->label); XtManageChild (l_w); switch (tvp->p) { case IP_RO: n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 100); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++; tp->aux0 = (void *) XmCreateLabel (f_w, "TRO", args, n); set_xmstring ((Widget) tp->aux0, XmNlabelString, tp->text); XtManageChild ((Widget) tp->aux0); break; case IP_RW: n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, WFLPOS); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, WFRPOS-2); n++; XtSetArg (args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; XtSetArg (args[n], XmNrecomputeSize, False); n++; tp->aux0 = (void *) XmCreateLabel (f_w, "TRO", args, n); set_xmstring ((Widget) tp->aux0, XmNlabelString, tp->text); XtManageChild ((Widget) tp->aux0); /* FALLTHRU */ case IP_WO: n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNleftPosition, WFRPOS); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 100); n++; XtSetArg (args[n], XmNcolumns, 10); n++; XtSetArg (args[n], XmNvalue, tp->text); n++; tp->aux1 = (void *) XmCreateTextField (f_w, "NWO", args, n); XtAddCallback ((Widget) tp->aux1, XmNactivateCallback, newtext_cb, tvp); XtManageChild ((Widget) tp->aux1); break; } } drawDevLt (gd); XtManageChild (trc_w); pushDisplay(); return (0); } /* capture and display state, msg and each value(s) for a set/newTextVector */ static int setGUITextVector (XMLEle *root, ITextVectorProperty *tvp, IGUIDevice *gd, char errmsg[]) { int new = !strcmp (tagXMLEle(root), "newTextVector"); XMLEle *ep; for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { if (strcmp(tagXMLEle(ep), "oneText") == 0) { char *name = findXMLAttValu (ep, "name"); IText *tp = findText (tvp, name); if (tp) { char *t = pcdataXMLEle (ep); tp->text = strcpy (XtRealloc(tp->text,strlen(t)+1), t); if (tp->tvp->p == IP_WO || (tp->tvp->p == IP_RW && new)) XmTextFieldSetString ((Widget)tp->aux1, t); else set_xmstring ((Widget)tp->aux0, XmNlabelString, t); } else { sprintf (errmsg, "INDI: set %s.%s.%s not found", gd->device, tvp->name, name); return (-1); } } } showMessage (root); if (crackPState (root, &tvp->s, errmsg) == 0) drawDevLt (gd); return (0); } /* add the given switch onto the given device list in the given group and * build its GUI. * return 0 if ok else -1 with reason in errmsg[] */ static int newGUISwitchVector(ISwitchVectorProperty *svp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg) { Widget w, l_w, hrc_w; Widget src_w; Arg args[20]; int i; int n; /* add svp to device list */ gd->svpp = (ISwitchVectorProperty **) XtRealloc ((char *)(gd->svpp), (gd->nsvpp+1) * sizeof(ISwitchVectorProperty*)); gd->svpp[gd->nsvpp++] = svp; /* master rc, work faster delay managing until finished */ n = 0; XtSetArg (args[n], XmNspacing, 0); n++; src_w = XmCreateRowColumn (gg->prc_w, "SRC", args, n); /* property rc */ n = 0; XtSetArg (args[n], XmNmarginWidth, INDENT); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; hrc_w = XmCreateRowColumn (src_w, "SPF", args, n); XtManageChild (hrc_w); /* property's status light */ n = 0; svp->aux = (void *) createLt (hrc_w, "NLS", args, n); XtAddCallback ((Widget)svp->aux, XmNexposeCallback, devLtCB, (XtPointer)(gd-devices)); XtManageChild ((Widget)svp->aux); /* label */ n = 0; l_w = XmCreateLabel (hrc_w, "SL", args, n); set_xmstring (l_w, XmNlabelString, (char *) svp->label); XtManageChild (l_w); /* each element in a RC */ n = 0; XtSetArg (args[n], XmNspacing, 4); n++; XtSetArg (args[n], XmNmarginWidth, 2*INDENT); n++; XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNnumColumns, svp->nsp/5+1); n++; switch (svp->r) { case ISR_1OFMANY: XtSetArg (args[n], XmNradioBehavior, 1); n++; XtSetArg (args[n], XmNradioAlwaysOne, 1); n++; break; case ISR_ATMOST1: XtSetArg (args[n], XmNradioBehavior, 1); n++; XtSetArg (args[n], XmNradioAlwaysOne, 0); n++; break; case ISR_NOFMANY: XtSetArg (args[n], XmNradioBehavior, 0); n++; XtSetArg (args[n], XmNradioAlwaysOne, 0); n++; break; } hrc_w = XmCreateRowColumn (src_w, "SRC", args, n); XtManageChild (hrc_w); for (i = 0; i < svp->nsp; i++) { ISwitch *sp = &svp->sp[i]; n = 0; XtSetArg (args[n], XmNindicatorType, svp->r == ISR_NOFMANY ? XmN_OF_MANY : XmONE_OF_MANY); n++; XtSetArg (args[n], XmNset, sp->s == ISS_ON); n++; w = XmCreateToggleButton (hrc_w, "STB", args, n); XtAddCallback (w, XmNvalueChangedCallback, newswitch_cb, sp); set_xmstring (w, XmNlabelString, sp->label); XtManageChild (w); sp->aux = (void *) w; } drawDevLt (gd); XtManageChild (src_w); pushDisplay(); return (0); } /* capture and display state, msg and each value(s) for a set/newSwitchVector */ static int setGUISwitchVector (XMLEle *root, ISwitchVectorProperty *svp, IGUIDevice *gd, char errmsg[]) { XMLEle *ep; for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { if (strcmp(tagXMLEle(ep), "oneSwitch") == 0) { char *name = findXMLAttValu (ep, "name"); ISwitch *sp = findSwitch (svp, name); if (sp) { if (crackSState (ep, &sp->s, errmsg) < 0) return (-1); XmToggleButtonSetState ((Widget)sp->aux, sp->s==ISS_ON, 0); } else { sprintf (errmsg, "INDI: set %s.%s.%s not found", gd->device, svp->name, name); return (-1); } } } showMessage (root); if (crackPState (root, &svp->s, errmsg) == 0) drawDevLt (gd); return (0); } /* add the given light onto the given device list in the given group and * build its GUI. * return 0 if ok else -1 with reason in errmsg[] */ static int newGUILightVector(ILightVectorProperty *lvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg) { Widget lrc_w; Widget w, l_w, hrc_w; Arg args[20]; int i; int n; /* add lvp to device list */ gd->lvpp = (ILightVectorProperty **) XtRealloc ((char *)(gd->lvpp), (gd->nlvpp+1) * sizeof(ILightVectorProperty*)); gd->lvpp[gd->nlvpp++] = lvp; /* master rc, works faster to delay managing until finished */ n = 0; XtSetArg (args[n], XmNspacing, 0); n++; lrc_w = XmCreateRowColumn (gg->prc_w, "LRC", args, n); /* property rc */ n = 0; XtSetArg (args[n], XmNmarginWidth, INDENT); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; hrc_w = XmCreateRowColumn (lrc_w, "LPF", args, n); XtManageChild (hrc_w); /* property's status light */ n = 0; lvp->aux = (void *) createLt (hrc_w, "NLL", args, n); XtAddCallback ((Widget)lvp->aux, XmNexposeCallback, devLtCB, (XtPointer)(gd-devices)); XtManageChild ((Widget)lvp->aux); /* label */ n = 0; l_w = XmCreateLabel (hrc_w, "LL", args, n); set_xmstring (l_w, XmNlabelString, (char *) lvp->label); XtManageChild (l_w); /* each element in a RC */ n = 0; XtSetArg (args[n], XmNmarginWidth, 2*INDENT); n++; XtSetArg (args[n], XmNpacking, XmPACK_COLUMN); n++; XtSetArg (args[n], XmNnumColumns, lvp->nlp); n++; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; hrc_w = XmCreateRowColumn (lrc_w, "SRC", args, n); XtManageChild (hrc_w); for (i = 0; i < lvp->nlp; i++) { ILight *lp = &lvp->lp[i]; char buf[128]; n = 0; w = createLt (hrc_w, "LLt", args, n); XtManageChild (w); lp->aux = (void *) w; n = 0; w = XmCreateLabel (hrc_w, "LL", args, n); sprintf (buf, "%s ", lp->label); set_xmstring (w, XmNlabelString, buf); XtManageChild (w); } drawDevLt (gd); XtManageChild (lrc_w); pushDisplay(); return (0); } /* capture and display state, msg and each new value(s) for a Light vector */ static int setGUILightVector (XMLEle *root, ILightVectorProperty *lvp, IGUIDevice *gd, char errmsg[]) { XMLEle *ep; for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { if (strcmp(tagXMLEle(ep), "oneLight") == 0) { char *name = findXMLAttValu (ep, "name"); ILight *lp = findLight (lvp, name); if (lp) { if (crackPString (pcdataXMLEle(ep), &lp->s, errmsg) < 0) return (-1); drawLt ((Widget)lp->aux, lp->s); } else { sprintf (errmsg, "INDI: set %s.%s.%s not found", gd->device, lvp->name, name); return (-1); } } } showMessage (root); if (crackPState (root, &lvp->s, errmsg) == 0) drawDevLt (gd); return (0); } /* add the given BLOB onto the given device list in the given group and * build its GUI. set up display and saving callbacks to enable BLOBs on the * service channel. * return 0 if ok else -1 with reason in errmsg[] */ static int newGUIBLOBVector(IBLOBVectorProperty *bvp, IGUIDevice *gd, IGUIGroup *gg, char *errmsg) { Widget set_w, brc_w; Widget w, hrc_w, f_w, l_w; Arg args[20]; int i; int n; /* add bvp to device list */ gd->bvpp = (IBLOBVectorProperty **) XtRealloc ((char *)(gd->bvpp), (gd->nbvpp+1) * sizeof(IBLOBVectorProperty*)); gd->bvpp[gd->nbvpp++] = bvp; /* master rc, works faster to delay managing until finished */ n = 0; XtSetArg (args[n], XmNspacing, 0); n++; brc_w = XmCreateRowColumn (gg->prc_w, "BRC", args, n); /* property rc */ n = 0; XtSetArg (args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg (args[n], XmNmarginWidth, INDENT); n++; XtSetArg (args[n], XmNspacing, 3); n++; hrc_w = XmCreateRowColumn (brc_w, "BPF", args, n); XtManageChild (hrc_w); /* property's status light */ n = 0; bvp->aux = (void *) createLt (hrc_w, "BLT", args, n); XtAddCallback ((Widget)bvp->aux, XmNexposeCallback, devLtCB, (XtPointer)(gd-devices)); XtManageChild ((Widget)bvp->aux); /* set PB, unless RO */ if (bvp->p != IP_RO) { n = 0; set_w = XmCreatePushButton (hrc_w, "Set", args, n); XtAddCallback (set_w, XmNactivateCallback, newBLOB_cb, bvp); wtip (set_w, "Send the named file"); XtManageChild (set_w); } /* label */ n = 0; l_w = XmCreateLabel (hrc_w, "BVL", args, n); set_xmstring (l_w, XmNlabelString, (char *) bvp->label); XtManageChild (l_w); /* one row for each vector element */ for (i = 0; i < bvp->nbp; i++) { IBLOB *bp = &bvp->bp[i]; /* TODO: this is really only correct for RO perm only */ n = 0; f_w = XmCreateForm (brc_w, "BF", args, n); XtManageChild (f_w); n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNleftOffset, 2*INDENT); n++; l_w = XmCreateLabel (f_w, "BL", args, n); set_xmstring (l_w, XmNlabelString, (char *) bp->label); XtManageChild (l_w); /* aux2 is filename T */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++; XtSetArg (args[n], XmNrightPosition, 100); n++; XtSetArg (args[n], XmNcolumns, 23); n++; /* ISO date + .fts */ w = XmCreateTextField (f_w, "BFN", args, n); switch (bvp->p) { case IP_WO: wtip (w, "Name of file to send"); XtAddCallback (w, XmNactivateCallback, newBLOB_cb, bvp); break; case IP_RO: wtip (w, "Name of file to save"); break; case IP_RW: wtip (w, "Name of file to send or save"); break; } XtManageChild (w); bp->aux2 = w; if (bvp->p != IP_WO) { /* aux1 is Save TB */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, w); n++; XtSetArg (args[n], XmNrightOffset, 10); n++; w = XmCreateToggleButton (f_w, "Save", args, n); wtip (w, "Automatically save each incoming image"); set_xmstring (w, XmNlabelString, "Auto save to:"); XtAddCallback (w, XmNvalueChangedCallback, saveBLOBCB, bvp); XtManageChild (w); bp->aux1 = w; /* aux0 is Display TB */ n = 0; XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg (args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg (args[n], XmNrightWidget, w); n++; XtSetArg (args[n], XmNrightOffset, 10); n++; w = XmCreateToggleButton (f_w, "Display", args, n); wtip (w, "Automatically display each incoming FITS image"); XtAddCallback (w, XmNvalueChangedCallback, displayBLOBCB, bvp); set_xmstring (w, XmNlabelString, "Auto display"); XtManageChild (w); bp->aux0 = w; } } drawDevLt (gd); XtManageChild (brc_w); pushDisplay(); return (0); } /* capture and display state, msg and each new value(s) for a BLOB vector */ static int setGUIBLOBVector (XMLEle *root, IBLOBVectorProperty *bvp, IGUIDevice *gd, char errmsg[]) { XMLEle *ep; for (ep = nextXMLEle(root,1); ep; ep = nextXMLEle(root,0)) { if (strcmp(tagXMLEle(ep), "oneBLOB") == 0) { char *name = findXMLAttValu (ep, "name"); IBLOB *bp = findBLOB (bvp, name); if (bp) { if (handleOneBLOB (ep, bp, errmsg) < 0) return (-1); } else { sprintf (errmsg, "INDI: set %s.%s.%s not found", gd->device, bvp->name, name); return (-1); } } } showMessage (root); if (crackPState (root, &bvp->s, errmsg) == 0) drawDevLt (gd); return (0); } /* process the given XML just arrived for the given oneBLOB. * N.B. beware persistence: the XML memory is freed shortly after we return */ static int handleOneBLOB (XMLEle *root, IBLOB *bp, char errmsg[]) { IBLOBVectorProperty *bvp = bp->bvp; char fn[64]; int isz; int i; char *tsp; /* get uncompressed size */ bp->size = atoi(findXMLAttValu (root, "size")); if (bp->size < 0) { sprintf (errmsg, "INDI: %s.%s.%s no size", bvp->device, bvp->name, bp->name); return (-1); } if (bp->size == 0) return (0); /* well, ok, that was easy */ /* get format */ if (crackCharAtt (root, bp->format, MAXINDIBLOBFMT, "format", errmsg)<0) return (-1); isz = !strcmp (bp->format,".fts.z") || !strcmp (bp->format,".fits.z"); if (!isz && strcmp(bp->format,".fts") && strcmp(bp->format,".fits")) { sprintf (errmsg, "INDI: %s.%s.%s not FITS", bvp->device, bvp->name, bp->name); return (-1); } /* decode blob */ if (bp->blob) free (bp->blob); bp->blob = malloc (3*pcdatalenXMLEle(root)/4); bp->bloblen = from64tobits (bp->blob, pcdataXMLEle(root)); if (bp->bloblen < 0) { free (bp->blob); sprintf (errmsg, "INDI: %s.%s.%s bad base64", bvp->device, bvp->name, bp->name); return (-1); } /* uncompress effectively in place if z */ if (isz) { uLong nuncomp = bp->size; unsigned char *uncomp = malloc (nuncomp); int ok = uncompress (uncomp, &nuncomp, bp->blob, bp->bloblen); if (ok != Z_OK) { sprintf (errmsg, "INDI: %s.%s.%s uncompress error %d", bvp->device, bvp->name, bp->name, ok); free (uncomp); return (-1); } free (bp->blob); bp->blob = uncomp; bp->bloblen = nuncomp; } /* rig up a file name from the timestamp and format */ for (i = 0, tsp = bvp->timestamp; *tsp != '\0'; tsp++) if (isdigit(*tsp)) fn[i++] = *tsp; fn[i] = '\0'; strcat (fn, bp->format); if (isz) *(strstr (fn,".z")) = '\0'; /* chop off .z */ XmTextFieldSetString ((Widget)bp->aux2, fn); /* display, if enabled */ if (XmToggleButtonGetState ((Widget)bp->aux0)) { FImage fim, *fip = &fim; if (readFITSmem (bp->blob, bp->bloblen, fip, errmsg) < 0) return (-1); sf_newFITS (fip, fn, 1); } /* save, if enabled */ if (XmToggleButtonGetState ((Widget)bp->aux1)) { FILE *fp = fopend (fn, NULL, "w"); if (fp) { fwrite (bp->blob, bp->bloblen, 1, fp); fclose (fp); strcat (fn, ": saved"); logMessage (bvp->timestamp, bvp->device, fn); } else { /* fopend() already notified user */ XmToggleButtonSetState ((Widget)bp->aux0, False, True); } } return (0); } /* display device, timestamp and message attributes, if message */ static void showMessage (XMLEle *root) { char *m, *t, *d; m = findXMLAttValu (root, "message"); if (!m[0]) return; t = findXMLAttValu (root, "timestamp"); d = findXMLAttValu (root, "device"); logMessage (t, d, m); } /* fill the given string with the current time as an INDI timestamp */ static char * setTStampNow (char tstamp[MAXINDITSTAMP]) { time_t t = time(NULL); struct tm *tp = gmtime (&t); strftime (tstamp, MAXINDITSTAMP, "%Y-%m-%dT%H:%M:%S", tp); return (tstamp); } /* log a message prefixed with time and device */ static void logMessage (char *tstamp, char *device, char *msg) { char buf[1024], *bp = buf; char *msgnow; int msgnowl; /* time and device first */ if (tstamp && tstamp[0]) bp += sprintf (bp, "%s: ", tstamp); else { char ts[MAXINDITSTAMP]; bp += sprintf (bp, "%s: ", setTStampNow(ts)); } if (device && device[0]) bp += sprintf (bp, "%s: ", device); /* add the message */ bp += sprintf (bp, "%s", msg); if (bp[-1] != '\n') { *bp++ = '\n'; *bp = '\0'; } /* append to scrolled text */ msgnow = XmTextGetString (msg_w); msgnowl = strlen(msgnow); XtFree (msgnow); XmTextReplace (msg_w, msgnowl, msgnowl, buf); /* append to trace file, if on */ if (XmToggleButtonGetState (mttb_w)) traceMessage (buf); } /* append buf in the trace file */ static void traceMessage (char *buf) { char *fn = XmTextFieldGetString (mttf_w); FILE *fp = fopend (fn, NULL, "a"); if (fp) { fputs (buf, fp); fclose (fp); } else { XmToggleButtonSetState (mttb_w, False, True); xe_msg (1, "%s:\n%s", fn, syserrstr()); } XtFree (fn); } /* called when tracing is turned on or off */ static void traceon_cb (Widget w, XtPointer client, XtPointer call) { if (XmToggleButtonGetState(w)) { char *msgnow = XmTextGetString (msg_w); traceMessage (msgnow); XtFree (msgnow); } } /* draw the status light for the given property. * visit each property and draw its light, then draw the worst state for * device as a whole */ static void drawDevLt (IGUIDevice *gd) { IPState s, maxs = IPS_IDLE; int i, j; for (i = 0; i < gd->ntvpp; i++) { drawLt ((Widget)gd->tvpp[i]->aux, s = gd->tvpp[i]->s); if (s > maxs) maxs = s; } for (i = 0; i < gd->nnvpp; i++) { drawLt ((Widget)gd->nvpp[i]->aux, s = gd->nvpp[i]->s); if (s > maxs) maxs = s; } for (i = 0; i < gd->nsvpp; i++) { drawLt ((Widget)gd->svpp[i]->aux, s = gd->svpp[i]->s); if (s > maxs) maxs = s; } for (i = 0; i < gd->nlvpp; i++) { drawLt ((Widget)gd->lvpp[i]->aux, s = gd->lvpp[i]->s); if (s > maxs) maxs = s; for (j = 0; j < gd->lvpp[i]->nlp; j++) drawLt ((Widget)gd->lvpp[i]->lp[j].aux, gd->lvpp[i]->lp[j].s); } for (i = 0; i < gd->nbvpp; i++) { drawLt ((Widget)gd->bvpp[i]->aux, s = gd->bvpp[i]->s); if (s > maxs) maxs = s; } drawLt (gd->lda_w, maxs); } /* find existing or optionally create new IGUIDevice for the given device. * return pointer if ok (always true if create) else NULL with why in errmsg[] */ static IGUIDevice * findDev (char *device, int create, char errmsg[]) { IGUIDevice *gd; int i; for (i = 0; i < ndevices; i++) if (strcmp (devices[i].device, device) == 0) break; if (i < ndevices) gd = &devices[i]; else if (create) { devices = (IGUIDevice *) XtRealloc ((void *) devices, (ndevices+1)*sizeof(IGUIDevice)); gd = &devices[ndevices++]; memset (gd, 0, sizeof(*gd)); strcpy (gd->device, device); newGUIDevice (gd); } else { if (errmsg) sprintf (errmsg, "Device %s not found", device); return (NULL); } return (gd); } /* find existing or optionally create new IGUIGroup for the given device. * return pointer if ok (always true if create) else NULL with why in errmsg[] */ static IGUIGroup * findGroup (IGUIDevice *gd, char *group, int create, char errmsg[]) { int i; for (i = 0; i < gd->ngrp; i++) if(!strcmp (gd->grp[i]->group, group)) return (gd->grp[i]); if (create) { IGUIGroup *gg; gd->grp = (IGUIGroup **) XtRealloc ((void *) gd->grp, (gd->ngrp+1)*sizeof(IGUIGroup*)); gg = gd->grp[gd->ngrp++] = (IGUIGroup *)XtMalloc(sizeof(IGUIGroup)); memset (gg, 0, sizeof(*gg)); strcpy (gg->group, group); newGUIGroup (gd, gg); return (gg); } else { sprintf (errmsg, "Group %s not found", group); return (NULL); } } /* find the named Number Vector in the given device, else return NULL with * reason in optional whynot[] */ static INumberVectorProperty * findNumberProperty (IGUIDevice *gd, char *name, char whynot[]) { int i; for (i = 0; i < gd->nnvpp; i++) if (strcmp (gd->nvpp[i]->name, name) == 0) return (gd->nvpp[i]); if (whynot) sprintf (whynot, "INDI %s.%s: number not found", gd->device, name); return (NULL); } /* find the named Text Vector in the given device, else return NULL * reason in optional whynot[] */ static ITextVectorProperty * findTextProperty (IGUIDevice *gd, char *name, char whynot[]) { int i; for (i = 0; i < gd->ntvpp; i++) if (strcmp (gd->tvpp[i]->name, name) == 0) return (gd->tvpp[i]); if (whynot) sprintf (whynot, "INDI %s.%s: text not found", gd->device, name); return (NULL); } /* find the named Switch Vector in the given device, else return NULL * reason in optional whynot[] */ static ISwitchVectorProperty * findSwitchProperty (IGUIDevice *gd, char *name, char whynot[]) { int i; for (i = 0; i < gd->nsvpp; i++) if (strcmp (gd->svpp[i]->name, name) == 0) return (gd->svpp[i]); if (whynot) sprintf (whynot, "INDI %s.%s: switch not found", gd->device, name); return (NULL); } /* find the named Light Vector in the given device, else return NULL * reason in optional whynot[] */ static ILightVectorProperty * findLightProperty (IGUIDevice *gd, char *name, char whynot[]) { int i; for (i = 0; i < gd->nlvpp; i++) if (strcmp (gd->lvpp[i]->name, name) == 0) return (gd->lvpp[i]); if (whynot) sprintf (whynot, "INDI %s.%s: light not found", gd->device, name); return (NULL); } /* find the named BLOB Vector in the given device, else return NULL * reason in optional whynot[] */ static IBLOBVectorProperty * findBLOBProperty (IGUIDevice *gd, char *name, char whynot[]) { int i; for (i = 0; i < gd->nbvpp; i++) if (strcmp (gd->bvpp[i]->name, name) == 0) return (gd->bvpp[i]); if (whynot) sprintf (whynot, "INDI %s.%s: BLOB not found", gd->device, name); return (NULL); } /* find a particular Number in a NumberVector */ static INumber * findNumber (INumberVectorProperty *nvp, char *name) { INumber *np; for (np = nvp->np; np < &nvp->np[nvp->nnp]; np++) if (strcmp (np->name, name) == 0) return (np); return (NULL); } /* find a particular Text in a TextVector */ static IText * findText (ITextVectorProperty *tvp, char *name) { IText *tp; for (tp = tvp->tp; tp < &tvp->tp[tvp->ntp]; tp++) if (strcmp (tp->name, name) == 0) return (tp); return (NULL); } /* find a particular Switch in a SwitchVector */ static ISwitch * findSwitch (ISwitchVectorProperty *svp, char *name) { ISwitch *sp; for (sp = svp->sp; sp < &svp->sp[svp->nsp]; sp++) if (strcmp (sp->name, name) == 0) return (sp); return (NULL); } /* find a particular Light in a LightVector */ static ILight * findLight (ILightVectorProperty *lvp, char *name) { ILight *lp; for (lp = lvp->lp; lp < &lvp->lp[lvp->nlp]; lp++) if (strcmp (lp->name, name) == 0) return (lp); return (NULL); } /* find a particular BLOB in a BLOBVector */ static IBLOB * findBLOB (IBLOBVectorProperty *bvp, char *name) { IBLOB *bp; for (bp = bvp->bp; bp < &bvp->bp[bvp->nbp]; bp++) if (strcmp (bp->name, name) == 0) return (bp); return (NULL); } /* crack any char[] attribute of the given XML element. * return 0 if ok else -1 with reason in errmsg[] */ static int crackCharAtt (XMLEle *root, char *vp, int maxl, char *name, char *errmsg) { char *p = findXMLAttValu (root, name); if (!p[0]) { sprintf (errmsg, "%s has no %s attribute", tagXMLEle(root), name); return (-1); } strncpy (vp, p, maxl); vp[maxl-1] = '\0'; return (0); } /* crack a double attribute of the given XML element. * return 0 if ok else -1 with reason in errmsg[] */ static int crackDoubleAtt (XMLEle *root, double *dop, char *name, char *errmsg) { char *t = findXMLAttValu (root, name); if (!t[0]) { sprintf (errmsg, "%s has no %s attribute", tagXMLEle(root), name); return (-1); } *dop = strtod (t, NULL); return (0); } /* crack the perm attribute of the given XML element. * return 0 if ok else -1 with reason in errmsg[] */ static int crackPerm (XMLEle *root, IPerm *p, char *errmsg) { char *pstr = findXMLAttValu (root, "perm"); if (!pstr[0]) { sprintf (errmsg, "%s has no perm attribute", tagXMLEle(root)); return (-1); } if (strcmp (pstr, "ro") == 0) *p = IP_RO; else if (strcmp (pstr, "wo") == 0) *p = IP_WO; else if (strcmp (pstr, "rw") == 0) *p = IP_RW; else { sprintf (errmsg, "%s has bogus perm %s", tagXMLEle(root), pstr); return (-1); } return (0); } /* crack the rule attribute of the given XML element. * return 0 if ok else -1 with reason in errmsg[] */ static int crackRule (XMLEle *root, ISRule *r, char *errmsg) { char *rstr = findXMLAttValu (root, "rule"); if (!rstr[0]) { sprintf (errmsg, "%s has no rule attribute", tagXMLEle(root)); return (-1); } if (strcmp (rstr, "OneOfMany") == 0) *r = ISR_1OFMANY; else if (strcmp (rstr, "AtMostOne") == 0) *r = ISR_ATMOST1; else if (strcmp (rstr, "AnyOfMany") == 0) *r = ISR_NOFMANY; else { sprintf (errmsg, "%s has bogus rule %s", tagXMLEle(root), rstr); return (-1); } return (0); } /* crack the switch state pcdata value of the given XML element. * return 0 if ok else -1 with reason in errmsg[] */ static int crackSState (XMLEle *ep, ISState *s, char *errmsg) { char *sstr = pcdataXMLEle (ep); if (strcmp (sstr, "On") == 0) *s = ISS_ON; else if (strcmp (sstr, "Off") == 0) *s = ISS_OFF; else { sprintf(errmsg, "%s.%s: bogus switch state %s", tagXMLEle(parentXMLEle(ep)), tagXMLEle(ep), sstr); return (-1); } return (0); } /* crack the property state attribute of the given XML element. * return 0 if ok else -1 with reason in errmsg[] */ static int crackPState (XMLEle *root, IPState *sp, char *errmsg) { char *sstr = findXMLAttValu (root, "state"); if (!sstr[0]) { sprintf (errmsg, "%s has no state attribute", tagXMLEle(root)); return (-1); } return (crackPString (sstr, sp, errmsg)); } static int crackPString (char *str, IPState *sp, char errmsg[]) { if (strstr (str, "Idle")) *sp = IPS_IDLE; else if (strstr (str, "Ok")) *sp = IPS_OK; else if (strstr (str, "Busy")) *sp = IPS_BUSY; else if (strstr (str, "Alert")) *sp = IPS_ALERT; else { sprintf (errmsg, "IPState %s not found", str); return (-1); } return (0); } /* crack the given defNumberVector XML element. * return a malloced INumberVectorProperty if ok else NULL and reason in errmsg */ static INumberVectorProperty * crackdefNumberVector (XMLEle *root, char *errmsg) { INumberVectorProperty nv, *nvp; /* use stack until sure */ INumber *np; XMLEle *e; /* init */ memset (&nv, 0, sizeof(nv)); /* crack the attributes */ if (crackCharAtt (root, nv.device, MAXINDIDEVICE,"device",errmsg) < 0) return (NULL); if (crackCharAtt (root, nv.name, MAXINDINAME, "name", errmsg) < 0) return (NULL); if (crackCharAtt (root, nv.label, MAXINDILABEL, "label", errmsg) < 0) strcpy (nv.label, nv.name); /* default label = name */ if (crackCharAtt (root, nv.group, MAXINDIGROUP, "group", errmsg) < 0) nv.group[0] = '\0'; /* default group is "" */ if (crackPerm (root, &nv.p, errmsg) < 0) return (NULL); if (crackDoubleAtt (root, &nv.timeout, "timeout", errmsg) < 0) nv.timeout = 0; if (crackPState (root, &nv.s, errmsg) < 0) return (NULL); if (crackCharAtt (root,nv.timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (nv.timestamp); /* crack each number element */ for (e = nextXMLEle(root,1); e != NULL; e = nextXMLEle(root,0)) { if (strcmp (tagXMLEle(e), "defNumber") == 0) { /* grow list, but don't count until sure */ nv.np = (INumber *) XtRealloc ((char *)nv.np, (nv.nnp+1)*sizeof(INumber)); np = &nv.np[nv.nnp]; memset (np, 0, sizeof(*np)); /* add each field */ if (crackCharAtt (e, np->name, MAXINDINAME, "name",errmsg) < 0) return (NULL); if (crackCharAtt (e, np->label, MAXINDILABEL,"label",errmsg)<0) strcpy (np->label, np->name); /* default */ if (crackCharAtt(e, np->format,MAXINDIFORMAT,"format",errmsg)<0) return (NULL); if (crackDoubleAtt (e, &np->min, "min", errmsg) < 0) return (NULL); if (crackDoubleAtt (e, &np->max, "max", errmsg) < 0) return (NULL); if (crackDoubleAtt (e, &np->step,"step", errmsg) < 0) return (NULL); np->value = strtod (pcdataXMLEle(e), NULL); /* confident now in this Number */ nv.nnp++; } } /* make the persistent copy, go back and set parent pointers */ nvp = (INumberVectorProperty *) memcpy (XtMalloc(sizeof(nv)), &nv, sizeof(nv)); for (np = &nvp->np[0]; np < &nvp->np[nvp->nnp]; np++) np->nvp = nvp; /* return malloced copy */ return (nvp); } /* crack the given defTextVector XML element. * return a malloced ITextVectorProperty if ok else NULL and reason in errmsg */ static ITextVectorProperty * crackdefTextVector (XMLEle *root, char *errmsg) { ITextVectorProperty tv, *tvp; /* use stack until sure */ IText *tp; XMLEle *e; /* init */ memset (&tv, 0, sizeof(tv)); /* crack the attributes */ if (crackCharAtt (root, tv.device, MAXINDIDEVICE,"device", errmsg) < 0) return (NULL); if (crackCharAtt (root, tv.name, MAXINDINAME, "name", errmsg) < 0) return (NULL); if (crackCharAtt (root, tv.label, MAXINDILABEL, "label", errmsg) < 0) strcpy (tv.label, tv.name); /* default label = name */ if (crackCharAtt (root, tv.group, MAXINDIGROUP, "group", errmsg) < 0) tv.group[0] = '\0'; /* default group is "" */ if (crackPerm (root, &tv.p, errmsg) < 0) return (NULL); if (crackDoubleAtt (root, &tv.timeout, "timeout", errmsg) < 0) tv.timeout = 0; if (crackPState (root, &tv.s, errmsg) < 0) return (NULL); if (crackCharAtt (root,tv.timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (tv.timestamp); /* crack each text element */ for (e = nextXMLEle(root,1); e != NULL; e = nextXMLEle(root,0)) { if (strcmp (tagXMLEle(e), "defText") == 0) { /* grow list, but don't count until sure */ tv.tp = (IText *) XtRealloc ((char *)tv.tp, (tv.ntp+1)*sizeof(IText)); tp = &tv.tp[tv.ntp]; memset (tp, 0, sizeof(*tp)); /* add each field */ if (crackCharAtt (e, tp->name, MAXINDINAME, "name",errmsg) < 0) return (NULL); if (crackCharAtt (e, tp->label, MAXINDILABEL,"label",errmsg)<0) strcpy (tp->label, tp->name); /* default */ tp->text = XtNewString(pcdataXMLEle(e)); /* confident now in this Text */ tv.ntp++; } } /* make the persistent copy, go back and set parent pointers */ tvp = (ITextVectorProperty *) memcpy (XtMalloc(sizeof(tv)), &tv, sizeof(tv)); for (tp = &tvp->tp[0]; tp < &tvp->tp[tvp->ntp]; tp++) tp->tvp = tvp; /* return malloced copy */ return (tvp); } /* crack the given defSwitchVector XML element. * return a malloced ISwitchVectorProperty if ok else NULL and reason in errmsg */ static ISwitchVectorProperty * crackdefSwitchVector (XMLEle *root, char *errmsg) { ISwitchVectorProperty sv, *svp; /* use stack until sure */ ISwitch *sp; XMLEle *e; /* init */ memset (&sv, 0, sizeof(sv)); /* crack the attributes */ if (crackCharAtt (root, sv.device, MAXINDIDEVICE,"device", errmsg) < 0) return (NULL); if (crackCharAtt (root, sv.name, MAXINDINAME, "name", errmsg) < 0) return (NULL); if (crackCharAtt (root, sv.label, MAXINDILABEL, "label", errmsg) < 0) strcpy (sv.label, sv.name); /* default label = name */ if (crackCharAtt (root, sv.group, MAXINDIGROUP, "group", errmsg) < 0) sv.group[0] = '\0'; /* default group is "" */ if (crackPerm (root, &sv.p, errmsg) < 0) return (NULL); if (crackDoubleAtt (root, &sv.timeout, "timeout", errmsg) < 0) sv.timeout = 0; if (crackPState (root, &sv.s, errmsg) < 0) return (NULL); if (crackRule (root, &sv.r, errmsg) < 0) return (NULL); if (crackCharAtt (root,sv.timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (sv.timestamp); /* crack each switch element */ for (e = nextXMLEle(root,1); e != NULL; e = nextXMLEle(root,0)) { if (strcmp (tagXMLEle(e), "defSwitch") == 0) { /* grow list, but don't count until sure */ sv.sp = (ISwitch *) XtRealloc ((char *)sv.sp, (sv.nsp+1)*sizeof(ISwitch)); sp = &sv.sp[sv.nsp]; memset (sp, 0, sizeof(*sp)); /* add each field */ if (crackCharAtt (e, sp->name, MAXINDINAME, "name",errmsg) < 0) return (NULL); if (crackCharAtt (e, sp->label, MAXINDILABEL,"label",errmsg)<0) strcpy (sp->label, sp->name); /* default */ if (crackSState (e, &sp->s, errmsg) < 0) return (NULL); /* confident now in this Switch */ sv.nsp++; } } /* make the persistent copy, go back and set parent pointers */ svp = (ISwitchVectorProperty *) memcpy (XtMalloc(sizeof(sv)), &sv, sizeof(sv)); for (sp = &svp->sp[0]; sp < &svp->sp[svp->nsp]; sp++) sp->svp = svp; /* return malloced copy */ return (svp); } /* crack the given defLightVectory XML element. * return a malloced ILightVectorProperty if ok else NULL and reason in errmsg */ static ILightVectorProperty * crackdefLightVector (XMLEle *root, char *errmsg) { ILightVectorProperty lv, *lvp; /* use stack until sure */ ILight *lp; XMLEle *e; /* init */ memset (&lv, 0, sizeof(lv)); /* crack the attributes */ if (crackCharAtt (root, lv.device, MAXINDIDEVICE,"device", errmsg) < 0) return (NULL); if (crackCharAtt (root, lv.name, MAXINDINAME, "name", errmsg) < 0) return (NULL); if (crackCharAtt (root, lv.label, MAXINDILABEL, "label", errmsg) < 0) strcpy (lv.label, lv.name); /* default label = name */ if (crackCharAtt (root, lv.group, MAXINDIGROUP, "group", errmsg) < 0) lv.group[0] = '\0'; /* default group is "" */ if (crackPState (root, &lv.s, errmsg) < 0) return (NULL); if (crackCharAtt (root,lv.timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (lv.timestamp); /* crack each light element */ for (e = nextXMLEle(root,1); e != NULL; e = nextXMLEle(root,0)) { if (strcmp (tagXMLEle(e), "defLight") == 0) { /* grow list, but don't count until sure */ lv.lp = (ILight *) XtRealloc ((char *)lv.lp, (lv.nlp+1)*sizeof(ILight)); lp = &lv.lp[lv.nlp]; memset (lp, 0, sizeof(*lp)); /* add each field */ if (crackCharAtt (e, lp->name, MAXINDINAME, "name",errmsg) < 0) return (NULL); if (crackCharAtt (e, lp->label, MAXINDILABEL,"label",errmsg)<0) strcpy (lp->label, lp->name); /* default */ if (crackPString (pcdataXMLEle(e), &lp->s, errmsg) < 0) return (NULL); /* confident now in this Light */ lv.nlp++; } } /* make the persistent copy, go back and set parent pointers */ lvp = (ILightVectorProperty *) memcpy (XtMalloc(sizeof(lv)), &lv, sizeof(lv)); for (lp = &lvp->lp[0]; lp < &lvp->lp[lvp->nlp]; lp++) lp->lvp = lvp; /* return malloced copy */ return (lvp); } /* crack the given defBLOBVector XML element. * return a malloced IBLOBVectorProperty if ok else NULL and reason in errmsg */ static IBLOBVectorProperty * crackdefBLOBVector (XMLEle *root, char *errmsg) { IBLOBVectorProperty bv, *bvp; /* use stack until sure */ IBLOB *bp; XMLEle *e; /* init */ memset (&bv, 0, sizeof(bv)); /* crack the attributes */ if (crackCharAtt (root, bv.device, MAXINDIDEVICE,"device", errmsg) < 0) return (NULL); if (crackCharAtt (root, bv.name, MAXINDINAME, "name", errmsg) < 0) return (NULL); if (crackCharAtt (root, bv.label, MAXINDILABEL, "label", errmsg) < 0) strcpy (bv.label, bv.name); /* default label = name */ if (crackCharAtt (root, bv.group, MAXINDIGROUP, "group", errmsg) < 0) bv.group[0] = '\0'; /* default group is "" */ if (crackPState (root, &bv.s, errmsg) < 0) return (NULL); if (crackPerm (root, &bv.p, errmsg) < 0) return (NULL); if (crackDoubleAtt (root, &bv.timeout, "timeout", errmsg) < 0) bv.timeout = 0; if (crackCharAtt (root,bv.timestamp,MAXINDITSTAMP,"timestamp",errmsg)<0) setTStampNow (bv.timestamp); /* crack each BLOB element */ for (e = nextXMLEle(root,1); e != NULL; e = nextXMLEle(root,0)) { if (strcmp (tagXMLEle(e), "defBLOB") == 0) { /* grow list, but don't count until sure */ bv.bp = (IBLOB *) XtRealloc ((char *)bv.bp, (bv.nbp+1)*sizeof(IBLOB)); bp = &bv.bp[bv.nbp]; memset (bp, 0, sizeof(*bp)); /* add each field */ if (crackCharAtt (e, bp->name, MAXINDINAME, "name",errmsg) < 0) return (NULL); if (crackCharAtt (e, bp->label, MAXINDILABEL,"label",errmsg)<0) strcpy (bp->label, bp->name); /* default */ /* confident now in this BLOB */ bv.nbp++; } } /* make the persistent copy, go back and set parent pointers */ bvp = (IBLOBVectorProperty *) memcpy (XtMalloc(sizeof(bv)), &bv, sizeof(bv)); for (bp = &bvp->bp[0]; bp < &bvp->bp[bvp->nbp]; bp++) bp->bvp = bvp; /* return malloced copy */ return (bvp); } /* fill buf with properly formatted INumber string. return length */ static int numberFormat (char *buf, const char *format, double value) { int w, f, s; char m; if (sscanf (format, "%%%d.%d%c", &w, &f, &m) == 3 && m == 'm') { /* INDI sexi format */ switch (f) { case 9: s = 360000; break; case 8: s = 36000; break; case 6: s = 3600; break; case 5: s = 600; break; default: s = 60; break; } return (fs_sexa (buf, value, w-f, s)); } else { /* normal printf format */ return (sprintf (buf, format, value)); } } /* sometimes the indi window does not seem to finish its geo management, no * clue why. this hack just tries to goose things into action again. it helps * some, but still is not perfect. */ static void pushDisplay() { Dimension w; get_something (indi_w, XmNwidth, (XtArgVal)&w); w += 1; set_something (indi_w, XmNwidth, (XtArgVal)w); XmUpdateDisplay (indi_w); } /* print the boilerplate comment introducing xml */ static void xmlv1() { fprintf (swfp, "\n"); }